Add ragged tensor input support to Keras via the 'ragged' arg in keras.Input.

PiperOrigin-RevId: 254256357
This commit is contained in:
A. Unique TensorFlower 2019-06-20 13:07:48 -07:00 committed by TensorFlower Gardener
parent 4ab0dc8887
commit 7ed84ad814
16 changed files with 410 additions and 333 deletions

View File

@ -1168,6 +1168,7 @@ tf_py_test(
"//tensorflow/python:sparse_ops",
"//tensorflow/python:sparse_tensor",
],
shard_count = 4,
)
tf_py_test(

View File

@ -38,7 +38,6 @@ from tensorflow.python.distribute import distribute_coordinator_context as dc_co
from tensorflow.python.distribute import distribution_strategy_context
from tensorflow.python.distribute import multi_worker_util
from tensorflow.python.eager import context
from tensorflow.python.framework import composite_tensor_utils
from tensorflow.python.eager import function as eager_function
from tensorflow.python.eager import lift_to_graph
from tensorflow.python.framework import composite_tensor
@ -70,6 +69,7 @@ from tensorflow.python.ops import state_ops
from tensorflow.python.ops import tensor_array_grad # pylint: disable=unused-import
from tensorflow.python.ops import tensor_array_ops
from tensorflow.python.ops import variables as variables_module
from tensorflow.python.ops.ragged import ragged_factory_ops
from tensorflow.python.platform import tf_logging as logging
from tensorflow.python.util import nest
from tensorflow.python.util import tf_contextlib
@ -958,7 +958,12 @@ def is_keras_tensor(x):
@keras_export('keras.backend.placeholder')
def placeholder(shape=None, ndim=None, dtype=None, sparse=False, name=None):
def placeholder(shape=None,
ndim=None,
dtype=None,
sparse=False,
name=None,
ragged=False):
"""Instantiates a placeholder tensor and returns it.
Arguments:
@ -970,9 +975,14 @@ def placeholder(shape=None, ndim=None, dtype=None, sparse=False, name=None):
dtype: Placeholder type.
sparse: Boolean, whether the placeholder should have a sparse type.
name: Optional name string for the placeholder.
ragged: Boolean, whether the placeholder should have a ragged type.
In this case, values of 'None' in the 'shape' argument represent
ragged dimensions. For more information about RaggedTensors, see this
[guide](https://www.tensorflow.org/guide/ragged_tensors).
Raises:
ValueError: If called with eager execution.
ValueError: If called with eager execution
ValueError: If called with sparse = True and ragged = True.
Returns:
Tensor instance (with Keras metadata included).
@ -985,6 +995,11 @@ def placeholder(shape=None, ndim=None, dtype=None, sparse=False, name=None):
<tf.Tensor 'Placeholder_4:0' shape=(2, 4, 5) dtype=float32>
```
"""
if sparse and ragged:
raise ValueError(
'Cannot set both sparse and ragged to True when creating a placeholder.'
)
if dtype is None:
dtype = floatx()
if not shape:
@ -993,6 +1008,20 @@ def placeholder(shape=None, ndim=None, dtype=None, sparse=False, name=None):
with get_graph().as_default():
if sparse:
x = array_ops.sparse_placeholder(dtype, shape=shape, name=name)
elif ragged:
ragged_rank = 0
for i in range(1, len(shape)):
if shape[i] is None:
ragged_rank += 1
else:
break
value_shape = shape[(ragged_rank + 1):]
x = ragged_factory_ops.placeholder(
dtype=dtype,
ragged_rank=ragged_rank,
value_shape=value_shape,
name=name)
else:
x = array_ops.placeholder(dtype, shape=shape, name=name)
return x
@ -1008,7 +1037,11 @@ def is_placeholder(x):
Boolean.
"""
try:
return x.op.type == 'Placeholder'
if isinstance(x, composite_tensor.CompositeTensor):
flat_components = nest.flatten(x, expand_composites=True)
return py_any(is_placeholder(c) for c in flat_components)
else:
return x.op.type == 'Placeholder'
except AttributeError:
return False
@ -3108,63 +3141,6 @@ def print_tensor(x, message=''):
logging_ops.print_v2(message, x, output_stream=sys.stdout)
return x
def is_tensor_or_composite_tensor(value):
"""Test if a passed value object is a tensor-like or composite tensor."""
return (tensor_util.is_tensor(value) or isinstance(value, np.ndarray) or
composite_tensor_utils.is_composite_or_composite_value(value))
def _try_process_scipy_sparse_input(value):
"""Converts 'value' to a SparseTensor if it is a scipy sparse matrix.
Arguments:
value: An object that may have the attributes of a scipy sparse matrix.
Returns:
Either a SparseTensor based off of 'value' or 'value' itself.
"""
try:
sparse_coo = value.tocoo()
row, col = sparse_coo.row, sparse_coo.col
data, shape = sparse_coo.data, sparse_coo.shape
except AttributeError:
# If we can't convert this object, it could be either a single data
# element (ie, a bool/int/float) which is OK to pass on, or something
# that we don't understand (which may or may not be OK). In either
# case, don't die here: the data standardization code will catch
# those issues.
return value
indices = np.concatenate((np.expand_dims(row, 1), np.expand_dims(col, 1)), 1)
return sparse_tensor.SparseTensor(indices, data, shape)
def try_convert_scipy_to_sparse(values):
"""Converts scipy sparse matrices in 'values' to SparseTensors, if possible.
Arguments:
values: An input or list of inputs to convert. These may be TensorLikes,
ndarrays, composite tensors, or scipy sparse values.
Returns:
An input or list of inputs where scipy sparse tensors have been converted
to tf.SparseTensors.
Raises:
ValueError: If input cannot be converted to a SparseTensor.
"""
# Convert scipy sparse data into sparse tensors.
value_structure = values
values = nest.flatten(values)
for idx, value in enumerate(values):
if not is_tensor_or_composite_tensor(value):
values[idx] = _try_process_scipy_sparse_input(value)
values = nest.pack_sequence_as(value_structure, values)
return values
# GRAPH MANIPULATION
@ -3194,6 +3170,7 @@ class GraphExecutionFunction(object):
if not isinstance(updates, (list, tuple)):
raise TypeError('`updates` in a Keras backend function '
'should be a list or tuple.')
self._inputs_structure = inputs
self.inputs = nest.flatten(inputs, expand_composites=True)
self._outputs_structure = outputs
@ -3311,10 +3288,6 @@ class GraphExecutionFunction(object):
return tensor
def __call__(self, inputs):
inputs = try_convert_scipy_to_sparse(inputs)
# Ensure that input value types match any expected composite tensor types.
# TODO(momernick): Once TensorSpecs are implemented for CTs, use that here.
inputs = nest.flatten(inputs, expand_composites=True)
session = get_session(inputs)
@ -3488,10 +3461,8 @@ class EagerExecutionFunction(object):
x.op.inputs[0])
def __call__(self, inputs):
# Convert scipy sparse data into sparse tensors.
inputs = try_convert_scipy_to_sparse(inputs)
input_values = nest.flatten(inputs, expand_composites=True)
if self._freezable_vars_values:
input_values = input_values + self._freezable_vars_values
converted_inputs = []

View File

@ -34,12 +34,15 @@ class InputLayer(base_layer.Layer):
"""Layer to be used as an entry point into a Network (a graph of layers).
It can either wrap an existing tensor (pass an `input_tensor` argument)
or create its a placeholder tensor (pass arguments `input_shape`, and
or create a placeholder tensor (pass arguments `input_shape`, and
optionally, `dtype`).
It is generally recommend to use the functional layer API via `Input`,
(which creates an `InputLayer`) without directly using `InputLayer`.
This class can create placeholders for tf.Tensors, tf.SparseTensors, and
tf.RaggedTensors by choosing 'sparse=True' or 'ragged=True'.
Arguments:
input_shape: Shape tuple (not including the batch axis), or `TensorShape`
instance (not including the batch axis).
@ -47,8 +50,11 @@ class InputLayer(base_layer.Layer):
dtype: Datatype of the input.
input_tensor: Optional tensor to use as layer input
instead of creating a placeholder.
sparse: Boolean, whether the placeholder created
is meant to be sparse.
sparse: Boolean, whether the placeholder created is meant to be sparse.
ragged: Boolean, whether the placeholder created is meant to be ragged.
In this case, values of 'None' in the 'shape' argument represent
ragged dimensions. For more information about RaggedTensors, see
https://www.tensorflow.org/guide/ragged_tensors.
name: Name of the layer (string).
"""
@ -59,6 +65,7 @@ class InputLayer(base_layer.Layer):
input_tensor=None,
sparse=False,
name=None,
ragged=False,
**kwargs):
strategy = distribution_strategy_context.get_strategy()
if strategy and batch_size is not None and \
@ -110,18 +117,12 @@ class InputLayer(base_layer.Layer):
batch_input_shape = None
graph = backend.get_graph()
with graph.as_default():
# In graph mode, create a graph placeholder to call the layer on.
if sparse:
input_tensor = backend.placeholder(
shape=batch_input_shape,
dtype=dtype,
name=self.name,
sparse=True)
else:
input_tensor = backend.placeholder(
shape=batch_input_shape,
dtype=dtype,
name=self.name)
input_tensor = backend.placeholder(
shape=batch_input_shape,
dtype=dtype,
name=self.name,
sparse=sparse,
ragged=ragged)
self.is_placeholder = True
self._batch_input_shape = batch_input_shape
@ -164,6 +165,7 @@ def Input( # pylint: disable=invalid-name
dtype=None,
sparse=False,
tensor=None,
ragged=False,
**kwargs):
"""`Input()` is used to instantiate a Keras tensor.
@ -184,17 +186,24 @@ def Input( # pylint: disable=invalid-name
Arguments:
shape: A shape tuple (integers), not including the batch size.
For instance, `shape=(32,)` indicates that the expected input
will be batches of 32-dimensional vectors.
will be batches of 32-dimensional vectors. Elements of this tuple
can be None; 'None' elements represent dimensions where the shape is
not known.
batch_size: optional static batch size (integer).
name: An optional name string for the layer.
Should be unique in a model (do not reuse the same name twice).
It will be autogenerated if it isn't provided.
dtype: The data type expected by the input, as a string
(`float32`, `float64`, `int32`...)
sparse: A boolean specifying whether the placeholder
to be created is sparse.
sparse: A boolean specifying whether the placeholder to be created is
sparse. Only one of 'ragged' and 'sparse' can be True.
tensor: Optional existing tensor to wrap into the `Input` layer.
If set, the layer will not create a placeholder tensor.
ragged: A boolean specifying whether the placeholder to be created is
ragged. Only one of 'ragged' and 'sparse' can be True. In this case,
values of 'None' in the 'shape' argument represent ragged dimensions.
For more information about RaggedTensors, see
https://www.tensorflow.org/guide/ragged_tensors.
**kwargs: deprecated arguments support.
Returns:
@ -222,6 +231,10 @@ def Input( # pylint: disable=invalid-name
Raises:
ValueError: in case of invalid arguments.
"""
if sparse and ragged:
raise ValueError(
'Cannot set both sparse and ragged to True in a Keras input.')
batch_shape = None
if 'batch_shape' in kwargs:
batch_shape = kwargs.pop('batch_shape')
@ -246,6 +259,7 @@ def Input( # pylint: disable=invalid-name
name=name,
dtype=dtype,
sparse=sparse,
ragged=ragged,
input_tensor=tensor)
else:
input_layer = InputLayer(
@ -254,6 +268,7 @@ def Input( # pylint: disable=invalid-name
name=name,
dtype=dtype,
sparse=sparse,
ragged=ragged,
input_tensor=tensor)
# Return tensor including `_keras_history`.

View File

@ -25,6 +25,7 @@ import numpy as np
from tensorflow.python import tf2
from tensorflow.python.data.ops import dataset_ops
from tensorflow.python.data.ops import iterator_ops
from tensorflow.python.data.util import structure
from tensorflow.python.distribute import distribution_strategy_context
from tensorflow.python.distribute import multi_worker_util
from tensorflow.python.eager import context
@ -33,9 +34,11 @@ from tensorflow.python.eager import monitoring
from tensorflow.python.framework import composite_tensor_utils
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import ops
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import tensor_shape
from tensorflow.python.framework import tensor_spec
from tensorflow.python.framework import tensor_util
from tensorflow.python.framework import type_spec
from tensorflow.python.keras import backend as K
from tensorflow.python.keras import losses
from tensorflow.python.keras import metrics as metrics_module
@ -61,6 +64,10 @@ from tensorflow.python.util import nest
from tensorflow.python.util import serialization
from tensorflow.python.util.tf_export import keras_export
try:
from scipy.sparse import issparse # pylint: disable=g-import-not-at-top
except ImportError:
issparse = None
_keras_api_gauge = monitoring.BoolGauge('/tensorflow/api/keras',
'keras api usage', 'method')
@ -2444,6 +2451,39 @@ class Model(network.Network):
check_batch_axis=False, # Don't enforce the batch size.
exception_prefix='input')
# Get typespecs for the input data and sanitize it if necessary.
# TODO(momernick): This should be capable of doing full input validation
# at all times - validate that this is so and refactor the standardization
# code.
if isinstance(x, dataset_ops.DatasetV2):
x_shapes = dataset_ops.get_structure(x)
# TODO(momernick): Remove this once NestedStructure goes away. Right
# now, Dataset outputs one of these instead of an actual python structure.
if isinstance(x_shapes, structure.NestedStructure):
x_shapes = x_shapes._component_specs # pylint: disable=protected-access
if isinstance(x_shapes, tuple):
# If the output of a Dataset is a tuple, we assume it's either of the
# form (x_data, y_data) or (x_data, y_data, sample_weights). In either
# case, we only care about x_data here.
x_shapes = x_shapes[0]
else:
flat_inputs = nest.flatten(x, expand_composites=False)
flat_expected_inputs = nest.flatten(self.inputs, expand_composites=False)
converted_x = []
for (a, b) in zip(flat_inputs, flat_expected_inputs):
converted_x.append(_convert_scipy_sparse_tensor(a, b))
x = nest.pack_sequence_as(x, converted_x, expand_composites=False)
x_shapes = nest.map_structure(type_spec.type_spec_from_value, x)
# If the inputs are still a NestedStructure, then we have a dict-input to
# this model. We can't yet validate this. (It's only relevant for feature
# columns).
if not isinstance(x_shapes, structure.NestedStructure):
flat_inputs = nest.flatten(x_shapes, expand_composites=False)
flat_expected_inputs = nest.flatten(self.inputs, expand_composites=False)
for (a, b) in zip(flat_inputs, flat_expected_inputs):
nest.assert_same_structure(a, b, expand_composites=True)
if y is not None:
if not self._is_graph_network:
feed_output_names = self._feed_output_names
@ -3049,3 +3089,34 @@ class _TrainingTarget(object):
def _is_symbolic_tensor(x):
return tensor_util.is_tensor(x) and not isinstance(x, ops.EagerTensor)
def _convert_scipy_sparse_tensor(value, expected_input):
"""Handle scipy sparse tensor conversions.
This method takes a value 'value' and returns the proper conversion. If
value is a scipy sparse tensor and the expected input is a dense tensor,
we densify 'value'. If value is a scipy sparse tensor and the expected input
is a TF SparseTensor, we convert 'value' to a SparseTensor. If 'value' is
not a scipy sparse tensor, or scipy is not imported, we pass it through
unchanged.
Arguments:
value: An object that may be a scipy sparse tensor
expected_input: The expected input placeholder.
Returns:
The possibly-converted 'value'.
"""
if issparse is not None and issparse(value):
if ops.is_dense_tensor_like(expected_input):
return value.toarray()
else:
sparse_coo = value.tocoo()
row, col = sparse_coo.row, sparse_coo.col
data, shape = sparse_coo.data, sparse_coo.shape
indices = np.concatenate((np.expand_dims(row, 1), np.expand_dims(col, 1)),
1)
return sparse_tensor.SparseTensor(indices, data, shape)
else:
return value

View File

@ -353,21 +353,26 @@ def model_iteration(model,
elif shuffle:
np.random.shuffle(index_array)
batches = make_batches(num_samples_or_steps, batch_size)
for batch_index, (batch_start, batch_end) in enumerate(batches):
batch_ids = index_array[batch_start:batch_end]
# Slice into a batch.
try:
if ins and isinstance(ins[-1], int):
# Do not slice the training phase flag.
ins_batch = slice_arrays(ins[:-1], batch_ids) + [ins[-1]]
else:
ins_batch = slice_arrays(ins, batch_ids)
except TypeError:
raise TypeError('TypeError while preparing batch. '
'If using HDF5 input data, '
'pass shuffle="batch".')
if len(batches) == 1:
# If we only have one batch, do not slice. This takes care of
# composite tensors in non-Dataset modes; we currently don't support
# slicing them.
# TODO(b/133517906): Add slicing support.
ins_batch = ins
else:
try:
if ins and isinstance(ins[-1], int):
# Do not slice the training phase flag.
ins_batch = slice_arrays(ins[:-1], batch_ids) + [ins[-1]]
else:
ins_batch = slice_arrays(ins, batch_ids)
except TypeError:
raise TypeError('TypeError while preparing batch. '
'If using HDF5 input data, '
'pass shuffle="batch".')
# Sparse to dense conversion.
if issparse is not None:

View File

@ -382,6 +382,7 @@ def check_num_samples(ins, batch_size=None, steps=None, steps_name='steps'):
' is set, the `batch_size` must be None.')
if check_steps_argument(ins, steps, steps_name):
return None
if hasattr(ins[0], 'shape'):
return int(ins[0].shape[0])
return None # Edge case where ins == [static_learning_phase]
@ -501,7 +502,8 @@ def standardize_input_data(data,
continue
data_shape = tuple(tensorshape.as_list())
elif composite_tensor_utils.is_composite_or_composite_value(data[i]):
data_shape = composite_tensor_utils.get_shape(data[i])
tensorshape = composite_tensor_utils.get_shape(data[i])
data_shape = tuple(tensorshape.as_list())
else:
data_shape = data[i].shape
@ -591,6 +593,10 @@ def check_array_lengths(inputs, targets, weights=None):
ValueError: in case of incorrectly formatted data.
"""
def is_tensor_or_composite_tensor(x):
return tensor_util.is_tensor(
x) or composite_tensor_utils.is_composite_or_composite_value(x)
def set_of_lengths(x):
# Returns a set with the variation between
# different shapes, with None => 0
@ -600,7 +606,7 @@ def check_array_lengths(inputs, targets, weights=None):
return set([
y.shape[0]
for y in x
if y is not None and not tensor_util.is_tensor(y)
if y is not None and not is_tensor_or_composite_tensor(y)
])
set_x = set_of_lengths(inputs)

View File

@ -18,6 +18,8 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from absl.testing import parameterized
import numpy as np
import scipy.sparse
@ -27,6 +29,7 @@ from tensorflow.python.data.ops import dataset_ops
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import test_util
from tensorflow.python.keras import keras_parameterized
from tensorflow.python.keras import testing_utils
from tensorflow.python.keras.engine import input_layer
@ -35,6 +38,7 @@ from tensorflow.python.keras.layers import Layer
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import sparse_ops
from tensorflow.python.ops.ragged import ragged_factory_ops
from tensorflow.python.ops.ragged import ragged_tensor
from tensorflow.python.ops.ragged import ragged_test_util
from tensorflow.python.platform import test
@ -51,15 +55,18 @@ class ToDense(Layer):
def call(self, inputs):
if isinstance(inputs, ragged_tensor.RaggedTensor):
return inputs.to_tensor(default_value=self._default_value)
output = inputs.to_tensor(default_value=self._default_value)
elif isinstance(inputs, sparse_tensor.SparseTensor):
return sparse_ops.sparse_tensor_to_dense(
output = sparse_ops.sparse_tensor_to_dense(
inputs, default_value=self._default_value)
elif isinstance(inputs, ops.Tensor):
return inputs
output = inputs
else:
raise TypeError("Unexpected tensor type %s" % type(inputs).__name__)
# Return a float so that we can compile models with this as the final layer.
return math_ops.cast(output, dtypes.float32)
class ToRagged(Layer):
"""Create a ragged tensor based on a given dense tensor."""
@ -94,7 +101,7 @@ class _SubclassModel(keras.Model):
for i, layer in enumerate(layers):
setattr(self, self._layer_name_for_i(i), layer)
self.num_layers = len(layers)
if i_layer:
if i_layer is not None:
self._set_inputs(i_layer)
def _layer_name_for_i(self, i):
@ -131,7 +138,7 @@ def get_model_from_layers_with_input(layers,
return model
if model_type == "functional":
if model_input:
if model_input is not None:
inputs = model_input
else:
if not input_shape:
@ -267,16 +274,96 @@ class CompositeTensorOutputTest(keras_parameterized.TestCase,
self.assertAllEqual(output.dense_shape, expected_dense_shape)
def get_input_name(use_dict):
# Define the input name.
if not use_dict:
return None # This is the same as not setting 'name'.
elif testing_utils.get_model_type() == "subclass":
return "input_1" # Subclass models don"t support input names.
else:
return "test_input_name"
def get_steps():
# Determine the steps arg (if appropriate)
if not testing_utils.should_run_eagerly():
# CompositeTensors in graph mode are symbolic and so require a steps arg.
return 1
else:
return None
def prepare_inputs(data, use_dict, use_dataset, action, input_name):
input_data, expected_output = data
# Prepare the input data.
if use_dict:
input_data = {input_name: input_data}
if use_dataset:
if action == "predict":
input_data = dataset_ops.Dataset.from_tensors(input_data)
else:
input_data = dataset_ops.Dataset.from_tensors(
(input_data, expected_output))
expected_output = None
return (input_data, expected_output)
@keras_parameterized.run_with_all_model_types
@keras_parameterized.run_all_keras_modes
@parameterized.named_parameters(
*test_util.generate_combinations_with_testcase_name(
use_dict=[True, False],
use_dataset=[True, False],
action=["predict", "evaluate", "fit"]))
class SparseTensorInputTest(keras_parameterized.TestCase,
ragged_test_util.RaggedTensorTestCase):
def test_sparse_tensors(self, use_dict, use_dataset, action):
data = [(sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
[1, 2, 3], [2, 1, 3]),
np.array([[[1, -1, -1]], [[2, 3, -1]]])),
(sparse_tensor.SparseTensor(
[[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]], [5, 6, 7, 8],
[3, 1, 4]),
np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]], [[-1, 8, -1,
-1]]]))]
# Prepare the model to test.
input_name = get_input_name(use_dict)
model_input = input_layer.Input(
shape=(1, None), sparse=True, name=input_name, dtype=dtypes.int32)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
model.compile(optimizer="sgd", loss="mse", metrics=["accuracy"])
steps = get_steps()
# Prepare the input data
for data_element in data:
input_data, expected_output = prepare_inputs(data_element, use_dict,
use_dataset, action,
input_name)
# Perform the action.
if action == "predict":
result = model.predict(input_data, steps=steps)
self.assertAllEqual(expected_output, result)
if action == "evaluate":
result = model.evaluate(input_data, expected_output, steps=steps)
self.assertAllEqual(1.0, result[-1])
if action == "fit":
# TODO(momernick): What's the best way of validating that fit happened?
_ = model.fit(
input_data, expected_output, shuffle=False, steps_per_epoch=steps)
@keras_parameterized.run_with_all_model_types
@keras_parameterized.run_all_keras_modes
class ScipySparseTensorInputTest(keras_parameterized.TestCase,
ragged_test_util.RaggedTensorTestCase):
def test_sparse_scipy_predict_inputs_via_input_layer_args(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor. Scipy sparse matrices are limited to 2D, so use
# a one-dimensional shape.
model_input = input_layer.Input(shape=(3,), sparse=True)
# a one-dimensional shape; note also that scipy's default dtype is int64.
model_input = input_layer.Input(shape=(3,), sparse=True, dtype=dtypes.int64)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
@ -295,8 +382,8 @@ class SparseTensorInputTest(keras_parameterized.TestCase,
def test_sparse_scipy_eval_inputs(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor. Scipy sparse matrices are limited to 2D, so use
# a one-dimensional shape.
model_input = input_layer.Input(shape=(3,), sparse=True)
# a one-dimensional shape; note also that scipy's default dtype is int64.
model_input = input_layer.Input(shape=(3,), sparse=True, dtype=dtypes.int64)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
model.compile(optimizer="sgd", loss="mse", metrics=["accuracy"])
@ -317,12 +404,13 @@ class SparseTensorInputTest(keras_parameterized.TestCase,
def test_sparse_scipy_predict_input_dicts_via_input_layer_args(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor. Scipy sparse matrices are limited to 2D, so use
# a one-dimensional shape.
# a one-dimensional shape; note also that scipy's default dtype is int64.
if testing_utils.get_model_type() == "subclass":
input_name = "input_1" # Subclass models don"t support input names.
else:
input_name = "test_input_name"
model_input = input_layer.Input(shape=(3,), sparse=True, name=input_name)
model_input = input_layer.Input(
shape=(3,), sparse=True, name=input_name, dtype=dtypes.int64)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
@ -347,12 +435,13 @@ class SparseTensorInputTest(keras_parameterized.TestCase,
def test_sparse_scipy_eval_input_dicts(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor. Scipy sparse matrices are limited to 2D, so use
# a one-dimensional shape.
# a one-dimensional shape; note also that scipy's default dtype is int64.
if testing_utils.get_model_type() == "subclass":
input_name = "input_1" # Subclass models don"t support input names.
else:
input_name = "test_input_name"
model_input = input_layer.Input(shape=(3,), sparse=True, name=input_name)
model_input = input_layer.Input(
shape=(3,), sparse=True, name=input_name, dtype=dtypes.int64)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
model.compile(optimizer="sgd", loss="mse", metrics=["accuracy"])
@ -375,235 +464,132 @@ class SparseTensorInputTest(keras_parameterized.TestCase,
output_2 = model.evaluate(input_data_2, expected_output_2, steps=1)
self.assertAllEqual(1.0, output_2[-1])
def test_sparse_tensor_eval_inputs(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor.
model_input = input_layer.Input(shape=(1, None), sparse=True)
@keras_parameterized.run_with_all_model_types
@keras_parameterized.run_all_keras_modes
@parameterized.named_parameters(
*test_util.generate_combinations_with_testcase_name(
use_dict=[True, False],
use_dataset=[True, False],
action=["predict", "evaluate", "fit"]))
class RaggedTensorInputTest(keras_parameterized.TestCase,
ragged_test_util.RaggedTensorTestCase):
def test_ragged_input(self, use_dict, use_dataset, action):
data = [(ragged_factory_ops.constant([[[1]], [[2, 3]]]),
np.array([[[1, -1]], [[2, 3]]]))]
# Prepare the model to test.
input_name = get_input_name(use_dict)
model_input = input_layer.Input(
shape=(None, None), ragged=True, name=input_name, dtype=dtypes.int32)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
model.compile(optimizer="sgd", loss="mse", metrics=["accuracy"])
# Prepare the input data
for data_element in data:
input_data, expected_output = prepare_inputs(data_element, use_dict,
use_dataset, action,
input_name)
# Perform the action.
if action == "predict":
result = model.predict(input_data)
self.assertAllEqual(expected_output, result)
if action == "evaluate":
result = model.evaluate(input_data, expected_output)
self.assertAllEqual(1.0, result[-1])
if action == "fit":
# TODO(momernick): What's the best way of validating that fit happened?
_ = model.fit(input_data, expected_output, shuffle=False)
@keras_parameterized.run_with_all_model_types
@keras_parameterized.run_all_keras_modes
@parameterized.named_parameters(
*test_util.generate_combinations_with_testcase_name(
use_dict=[True, False], use_dataset=[True, False]))
class RaggedTensorInputValidationTest(keras_parameterized.TestCase,
ragged_test_util.RaggedTensorTestCase):
def test_ragged_tensor_input_with_one_none_dimension(self, use_dict,
use_dataset):
# Define some input data.
input_data = sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
[1, 2, 3], [2, 1, 3])
expected_output = np.array([[[1, -1, -1]], [[2, 3, -1]]])
output = model.evaluate(input_data, expected_output, steps=1)
self.assertAllEqual(1.0, output[-1])
data = [(ragged_factory_ops.constant([[[1, 0]], [[2, 3]]], ragged_rank=1),
np.array([[[1, 0]], [[2, 3]]]))]
input_data_2 = sparse_tensor.SparseTensor(
[[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]], [5, 6, 7, 8], [3, 1, 4])
expected_output_2 = np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]],
[[-1, 8, -1, -1]]])
output_2 = model.evaluate(input_data_2, expected_output_2, steps=1)
self.assertAllEqual(1.0, output_2[-1])
def test_sparse_tensor_predict_inputs_via_input_layer_args(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor.
model_input = input_layer.Input(shape=(1, None), sparse=True)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
# Define some input data.
input_data = sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
[1, 2, 3], [2, 1, 3])
expected_output = np.array([[[1, -1, -1]], [[2, 3, -1]]])
output = model.predict(input_data, steps=1)
self.assertAllEqual(expected_output, output)
input_data_2 = sparse_tensor.SparseTensor(
[[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]], [5, 6, 7, 8], [3, 1, 4])
expected_output_2 = np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]],
[[-1, 8, -1, -1]]])
output_2 = model.predict(input_data_2, steps=1)
self.assertAllEqual(expected_output_2, output_2)
def test_sparse_tensor_predict_input_dicts_via_input_layer_args(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor.
if testing_utils.get_model_type() == "subclass":
input_name = "input_1" # Subclass models don"t support input names.
else:
input_name = "test_input_name"
# Prepare the model to test.
input_shape = (None, 2) # RaggedTensorInputTest uses (None, None).
input_name = get_input_name(use_dict)
model_input = input_layer.Input(
shape=(1, None), sparse=True, name=input_name)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
# Define some input data.
input_data = {
input_name:
sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
[1, 2, 3], [2, 1, 3])
}
expected_output = np.array([[[1, -1, -1]], [[2, 3, -1]]])
output = model.predict(input_data, steps=1)
self.assertAllEqual(expected_output, output)
input_data_2 = {
input_name:
sparse_tensor.SparseTensor(
[[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]], [5, 6, 7, 8],
[3, 1, 4])
}
expected_output_2 = np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]],
[[-1, 8, -1, -1]]])
output_2 = model.predict(input_data_2, steps=1)
self.assertAllEqual(expected_output_2, output_2)
def test_sparse_tensor_eval_input_dicts_via_input_layer_args(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor.
if testing_utils.get_model_type() == "subclass":
input_name = "input_1" # Subclass models don"t support input names.
else:
input_name = "test_input_name"
model_input = input_layer.Input(
shape=(1, None), sparse=True, name=input_name)
shape=input_shape, ragged=True, name=input_name, dtype=dtypes.int32)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
model.compile(optimizer="sgd", loss="mse", metrics=["accuracy"])
for data_element in data:
input_data, expected_output = prepare_inputs(
data_element,
use_dict,
use_dataset,
action="predict",
input_name=input_name)
result = model.predict(input_data)
self.assertAllEqual(expected_output, result)
def test_ragged_tensor_input_with_no_none_dimension(self, use_dict,
use_dataset):
# Define some input data.
input_data = {
input_name:
sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
[1, 2, 3], [2, 1, 3])
}
expected_output = np.array([[[1, -1, -1]], [[2, 3, -1]]])
output = model.evaluate(input_data, expected_output, steps=1)
self.assertAllEqual(1.0, output[-1])
data = [(ragged_factory_ops.constant([[[1, 0]], [[2, 3]]], ragged_rank=0),
np.array([[[1, 0]], [[2, 3]]]))]
input_data_2 = {
input_name:
sparse_tensor.SparseTensor(
[[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]], [5, 6, 7, 8],
[3, 1, 4])
}
expected_output_2 = np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]],
[[-1, 8, -1, -1]]])
output_2 = model.evaluate(input_data_2, expected_output_2, steps=1)
self.assertAllEqual(1.0, output_2[-1])
def test_sparse_tensor_dataset_predict_inputs_via_input_layer_args(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor.
model_input = input_layer.Input(shape=(1, None), sparse=True)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
# Define some input data.
input_data = dataset_ops.Dataset.from_tensors(
sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]], [1, 2, 3],
[2, 1, 3]))
expected_output = np.array([[[1, -1, -1]], [[2, 3, -1]]])
output = model.predict(input_data)
self.assertAllEqual(expected_output, output)
input_data_2 = dataset_ops.Dataset.from_tensors(
sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]],
[5, 6, 7, 8], [3, 1, 4]))
expected_output_2 = np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]],
[[-1, 8, -1, -1]]])
output_2 = model.predict(input_data_2)
self.assertAllEqual(expected_output_2, output_2)
def test_sparse_tensor_dataset_eval_inputs_via_input_layer_args(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor.
model_input = input_layer.Input(shape=(1, None), sparse=True)
# Prepare the model to test.
input_shape = (1, 2) # RaggedTensorInputTest uses (None, None).
input_name = get_input_name(use_dict)
model_input = input_layer.Input(
shape=input_shape, ragged=True, name=input_name, dtype=dtypes.int32)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
model.compile(optimizer="sgd", loss="mse", metrics=["accuracy"])
# Define some input data.
input_tensor = sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
[1, 2, 3], [2, 1, 3])
expected_output = np.array([[[1, -1, -1]], [[2, 3, -1]]])
input_data = dataset_ops.Dataset.from_tensors(
(input_tensor, expected_output))
output = model.evaluate(input_data)
self.assertAllEqual(1.0, output[-1])
# The input is a symbolic tensor in non-Eager modes, so 'steps' is required
# for that case only.
steps = get_steps()
input_tensor_2 = sparse_tensor.SparseTensor(
[[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]], [5, 6, 7, 8], [3, 1, 4])
expected_output_2 = np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]],
[[-1, 8, -1, -1]]])
input_data_2 = dataset_ops.Dataset.from_tensors(
(input_tensor_2, expected_output_2))
output_2 = model.evaluate(input_data_2)
self.assertAllEqual(1.0, output_2[-1])
for data_element in data:
input_data, expected_output = prepare_inputs(
data_element,
use_dict,
use_dataset,
action="predict",
input_name=input_name)
result = model.predict(input_data, steps=steps)
self.assertAllEqual(expected_output, result)
def test_sparse_tensor_dataset_dict_predict_inputs_via_input_layer_args(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor.
if testing_utils.get_model_type() == "subclass":
input_name = "input_1" # Subclass models don"t support custom input names
else:
input_name = "test_input_name"
def test_ragged_tensor_input_with_wrong_ragged_rank_fails(
self, use_dict, use_dataset):
# Define some input data that will NOT match the input shape spec.
data = [(ragged_factory_ops.constant([[[1, 0]], [[2, 3]]]), None)]
# Prepare the model to test.
input_shape = (None, 2) # RaggedTensorInputTest uses (None, None).
input_name = get_input_name(use_dict)
model_input = input_layer.Input(
shape=(1, None), sparse=True, name=input_name)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
# Define some input data.
input_data = dataset_ops.Dataset.from_tensors({
input_name:
sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
[1, 2, 3], [2, 1, 3])
})
expected_output = np.array([[[1, -1, -1]], [[2, 3, -1]]])
output = model.predict(input_data)
self.assertAllEqual(expected_output, output)
input_data_2 = dataset_ops.Dataset.from_tensors({
input_name:
sparse_tensor.SparseTensor(
[[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]], [5, 6, 7, 8],
[3, 1, 4])
})
expected_output_2 = np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]],
[[-1, 8, -1, -1]]])
output_2 = model.predict(input_data_2)
self.assertAllEqual(expected_output_2, output_2)
def test_sparse_tensor_dataset_dict_eval_inputs_via_input_layer_args(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor.
if testing_utils.get_model_type() == "subclass":
input_name = "input_1" # Subclass models don"t support custom input names
else:
input_name = "test_input_name"
model_input = input_layer.Input(
shape=(1, None), sparse=True, name=input_name)
shape=input_shape, ragged=True, name=input_name, dtype=dtypes.int32)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
model.compile(optimizer="sgd", loss="mse", metrics=["accuracy"])
# Define some input data.
input_tensor = {
input_name:
sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
[1, 2, 3], [2, 1, 3])
}
expected_output = np.array([[[1, -1, -1]], [[2, 3, -1]]])
input_data = dataset_ops.Dataset.from_tensors(
(input_tensor, expected_output))
output = model.evaluate(input_data)
self.assertAllEqual(1.0, output[-1])
input_tensor_2 = {
input_name:
sparse_tensor.SparseTensor(
[[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]], [5, 6, 7, 8],
[3, 1, 4])
}
expected_output_2 = np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]],
[[-1, 8, -1, -1]]])
input_data_2 = dataset_ops.Dataset.from_tensors(
(input_tensor_2, expected_output_2))
output_2 = model.evaluate(input_data_2)
self.assertAllEqual(1.0, output_2[-1])
# Define some input data with the wrong ragged rank
for data_element in data:
input_data, _ = prepare_inputs(
data_element,
use_dict,
use_dataset,
action="predict",
input_name=input_name)
with self.assertRaisesRegex(ValueError, ".*don't have the same nested.*"):
_ = model.predict(input_data)
# CompositeTensor shape validation only happens in non-eager modes and in non-
@ -614,27 +600,48 @@ class SparseTensorInputValidationTest(keras_parameterized.TestCase,
ragged_test_util.RaggedTensorTestCase):
def test_sparse_scipy_input_checks_shape(self):
model_input = input_layer.Input(shape=(3,), sparse=True)
model_input = input_layer.Input(shape=(3,), sparse=True, dtype=dtypes.int32)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
input_data = scipy.sparse.coo_matrix(([1, 2, 3], ([0, 1, 1], [0, 0, 1])),
shape=[2, 4])
with self.assertRaisesRegex(ValueError, ".*got array with shape.*"):
_ = model.predict(input_data, steps=1)
_ = model.predict(input_data)
def test_sparse_tensor_input_checks_shapes(self):
# Create a model that accepts a sparse input and converts the sparse tensor
# back to a dense tensor.
model_input = input_layer.Input(shape=(2, None), sparse=True)
model_input = input_layer.Input(
shape=(2, None), sparse=True, dtype=dtypes.int32)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
# Define some input data.
input_data = sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
[1, 2, 3], [2, 1, 3])
if not testing_utils.should_run_eagerly():
# This ragged tensor is actually a standard tensor (as it has no ragged
# dimensions). Because of this, graph mode models will expect a steps
# arg to be passed (as SparseTensors in graph mode are symbolic).
steps = 1
else:
steps = None
with self.assertRaisesRegex(ValueError, ".*got array with shape.*"):
_ = model.predict(input_data, steps=1)
_ = model.predict(input_data, steps=steps)
def test_ragged_tensor_input_with_wrong_value_shape(self):
# Create a model that accepts a ragged input and converts it to dense.
model_input = input_layer.Input(
shape=(None, 4), ragged=True, dtype=dtypes.int32)
layers = [ToDense(default_value=-1)]
model = get_model_from_layers_with_input(layers, model_input=model_input)
# Define some input data with the wrong ragged rank
input_data = ragged_factory_ops.constant([[[1, 0]], [[2, 3]]],
ragged_rank=1)
with self.assertRaisesRegex(ValueError, ".*got array with shape.*"):
_ = model.predict(input_data)
@keras_parameterized.run_with_all_model_types(
@ -648,13 +655,14 @@ class UndefinedCompositeTensorInputsTest(keras_parameterized.TestCase,
# back to a dense tensor.
layers = [ToDense(default_value=-1)]
model = testing_utils.get_model_from_layers(layers)
steps = get_steps()
# Define some input data.
input_data = sparse_tensor.SparseTensor([[0, 0], [1, 0], [1, 1]], [1, 2, 3],
[2, 3])
with self.assertRaisesRegex(
ValueError, ".*All SparseTensor and RaggedTensor inputs .*"):
_ = model.predict(input_data, steps=1)
_ = model.predict(input_data, steps=steps)
def test_subclass_implicit_sparse_scipy_inputs_fails(self):
# Create a model that accepts a sparse input and converts the sparse tensor
@ -666,7 +674,7 @@ class UndefinedCompositeTensorInputsTest(keras_parameterized.TestCase,
input_data = scipy.sparse.coo_matrix(([1, 2, 3], ([0, 1, 1], [0, 0, 1])),
shape=[2, 3])
with self.assertRaisesRegex(ValueError, ".*either a single array.*"):
_ = model.predict(input_data, steps=1)
_ = model.predict(input_data)
if __name__ == "__main__":

View File

@ -106,7 +106,7 @@ def get_reachable_from_inputs(inputs, targets=None):
Returns:
A set of tensors reachable from the inputs (includes the inputs themselves).
"""
inputs = nest.flatten(inputs)
inputs = nest.flatten(inputs, expand_composites=True)
reachable = set(inputs)
if targets and not isinstance(targets, set):
targets = nest.flatten(targets)

View File

@ -342,7 +342,7 @@ tf_module {
}
member_method {
name: "placeholder"
argspec: "args=[\'shape\', \'ndim\', \'dtype\', \'sparse\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'False\', \'None\'], "
argspec: "args=[\'shape\', \'ndim\', \'dtype\', \'sparse\', \'name\', \'ragged\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'False\', \'None\', \'False\'], "
}
member_method {
name: "pool2d"

View File

@ -108,7 +108,7 @@ tf_class {
}
member_method {
name: "__init__"
argspec: "args=[\'self\', \'input_shape\', \'batch_size\', \'dtype\', \'input_tensor\', \'sparse\', \'name\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\'], "
argspec: "args=[\'self\', \'input_shape\', \'batch_size\', \'dtype\', \'input_tensor\', \'sparse\', \'name\', \'ragged\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\', \'False\'], "
}
member_method {
name: "add_loss"

View File

@ -418,7 +418,7 @@ tf_module {
}
member_method {
name: "Input"
argspec: "args=[\'shape\', \'batch_size\', \'name\', \'dtype\', \'sparse\', \'tensor\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\'], "
argspec: "args=[\'shape\', \'batch_size\', \'name\', \'dtype\', \'sparse\', \'tensor\', \'ragged\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\', \'False\'], "
}
member_method {
name: "add"

View File

@ -86,6 +86,6 @@ tf_module {
}
member_method {
name: "Input"
argspec: "args=[\'shape\', \'batch_size\', \'name\', \'dtype\', \'sparse\', \'tensor\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\'], "
argspec: "args=[\'shape\', \'batch_size\', \'name\', \'dtype\', \'sparse\', \'tensor\', \'ragged\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\', \'False\'], "
}
}

View File

@ -338,7 +338,7 @@ tf_module {
}
member_method {
name: "placeholder"
argspec: "args=[\'shape\', \'ndim\', \'dtype\', \'sparse\', \'name\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'False\', \'None\'], "
argspec: "args=[\'shape\', \'ndim\', \'dtype\', \'sparse\', \'name\', \'ragged\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'False\', \'None\', \'False\'], "
}
member_method {
name: "pool2d"

View File

@ -108,7 +108,7 @@ tf_class {
}
member_method {
name: "__init__"
argspec: "args=[\'self\', \'input_shape\', \'batch_size\', \'dtype\', \'input_tensor\', \'sparse\', \'name\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\'], "
argspec: "args=[\'self\', \'input_shape\', \'batch_size\', \'dtype\', \'input_tensor\', \'sparse\', \'name\', \'ragged\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\', \'False\'], "
}
member_method {
name: "add_loss"

View File

@ -410,7 +410,7 @@ tf_module {
}
member_method {
name: "Input"
argspec: "args=[\'shape\', \'batch_size\', \'name\', \'dtype\', \'sparse\', \'tensor\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\'], "
argspec: "args=[\'shape\', \'batch_size\', \'name\', \'dtype\', \'sparse\', \'tensor\', \'ragged\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\', \'False\'], "
}
member_method {
name: "add"

View File

@ -86,6 +86,6 @@ tf_module {
}
member_method {
name: "Input"
argspec: "args=[\'shape\', \'batch_size\', \'name\', \'dtype\', \'sparse\', \'tensor\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\'], "
argspec: "args=[\'shape\', \'batch_size\', \'name\', \'dtype\', \'sparse\', \'tensor\', \'ragged\'], varargs=None, keywords=kwargs, defaults=[\'None\', \'None\', \'None\', \'None\', \'False\', \'None\', \'False\'], "
}
}