• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Tests for Keras composite tensor support."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21from absl.testing import parameterized
22
23import numpy as np
24import scipy.sparse
25
26from tensorflow.python import keras
27
28from tensorflow.python.data.ops import dataset_ops
29from tensorflow.python.eager import context
30from tensorflow.python.framework import dtypes
31from tensorflow.python.framework import ops
32from tensorflow.python.framework import sparse_tensor
33from tensorflow.python.framework import test_util
34from tensorflow.python.keras import keras_parameterized
35from tensorflow.python.keras import testing_utils
36from tensorflow.python.keras.engine import input_layer
37from tensorflow.python.keras.layers import core
38from tensorflow.python.keras.layers import Dense
39from tensorflow.python.keras.layers import Embedding
40from tensorflow.python.keras.layers import Layer
41from tensorflow.python.ops import array_ops
42from tensorflow.python.ops import math_ops
43from tensorflow.python.ops import sparse_ops
44from tensorflow.python.ops.ragged import ragged_factory_ops
45from tensorflow.python.ops.ragged import ragged_tensor
46from tensorflow.python.platform import test
47from tensorflow.python.util import nest
48
49
50# Define test-only Layer classes to validate passing Sparse and Ragged tensors
51# between layers.
52class ToDense(Layer):
53  """Create a dense (standard) tensor from the given input tensor."""
54
55  def __init__(self, default_value, **kwargs):
56    super(ToDense, self).__init__(**kwargs)
57    self._default_value = default_value
58
59  def call(self, inputs):
60    if isinstance(inputs, dict):  # Dicts are no longer flattened.
61      # Always a single element in these tests.
62      inputs = nest.flatten(inputs)[0]
63
64    if isinstance(inputs, ragged_tensor.RaggedTensor):
65      output = inputs.to_tensor(default_value=self._default_value)
66    elif isinstance(inputs, sparse_tensor.SparseTensor):
67      output = sparse_ops.sparse_tensor_to_dense(
68          inputs, default_value=self._default_value)
69    elif isinstance(inputs, ops.Tensor):
70      output = inputs
71    else:
72      raise TypeError("Unexpected tensor type %s" % type(inputs).__name__)
73
74    # Return a float so that we can compile models with this as the final layer.
75    return math_ops.cast(output, dtypes.float32)
76
77
78class ToRagged(Layer):
79  """Create a ragged tensor based on a given dense tensor."""
80
81  def __init__(self, padding, ragged_rank=1, **kwargs):
82    super(ToRagged, self).__init__(**kwargs)
83    self._padding = padding
84    self._ragged_rank = ragged_rank
85
86  def call(self, inputs):
87    return ragged_tensor.RaggedTensor.from_tensor(
88        inputs, padding=self._padding, ragged_rank=self._ragged_rank)
89
90
91class ToSparse(Layer):
92  """Create a sparse tensor based on a given dense tensor."""
93
94  def call(self, inputs):
95    indices = array_ops.where_v2(math_ops.not_equal(inputs, 0))
96    values = array_ops.gather_nd(inputs, indices)
97    shape = array_ops.shape(inputs, out_type=dtypes.int64)
98    return sparse_tensor.SparseTensor(indices, values, dense_shape=shape)
99
100
101class _SubclassModel(keras.Model):
102  """A Keras subclass model."""
103
104  def __init__(self, layers, i_layer=None):
105    super(_SubclassModel, self).__init__()
106    # Note that clone and build doesn't support lists of layers in subclassed
107    # models. Adding each layer directly here.
108    for i, layer in enumerate(layers):
109      setattr(self, self._layer_name_for_i(i), layer)
110    self.num_layers = len(layers)
111    if i_layer is not None:
112      self._set_inputs(i_layer)
113
114  def _layer_name_for_i(self, i):
115    return "layer{}".format(i)
116
117  def call(self, inputs, **kwargs):
118    x = inputs
119    for i in range(self.num_layers):
120      layer = getattr(self, self._layer_name_for_i(i))
121      x = layer(x)
122    return x
123
124
125def get_model_from_layers_with_input(layers,
126                                     input_shape=None,
127                                     input_dtype=None,
128                                     model_input=None):
129  """Builds a model from a sequence of layers."""
130  if model_input is not None and input_shape is not None:
131    raise ValueError("Cannot specify a model_input and an input shape.")
132
133  model_type = testing_utils.get_model_type()
134  if model_type == "subclass":
135    return _SubclassModel(layers, model_input)
136
137  if model_type == "sequential":
138    model = keras.models.Sequential()
139    if model_input is not None:
140      model.add(model_input)
141    elif input_shape is not None:
142      model.add(keras.Input(shape=input_shape, dtype=input_dtype))
143    for layer in layers:
144      model.add(layer)
145    return model
146
147  if model_type == "functional":
148    if model_input is not None:
149      inputs = model_input
150    else:
151      if not input_shape:
152        raise ValueError("Cannot create a functional model from layers with no "
153                         "input shape.")
154      inputs = keras.Input(shape=input_shape, dtype=input_dtype)
155    outputs = inputs
156    for layer in layers:
157      outputs = layer(outputs)
158    return keras.Model(inputs, outputs)
159
160  raise ValueError("Unknown model type {}".format(model_type))
161
162
163def get_test_mode_kwargs():
164  run_eagerly = testing_utils.should_run_eagerly()
165  return {
166      "run_eagerly": run_eagerly,
167  }
168
169
170@keras_parameterized.run_with_all_model_types
171@keras_parameterized.run_all_keras_modes
172class CompositeTensorInternalTest(keras_parameterized.TestCase):
173
174  def test_internal_ragged_tensors(self):
175    # Create a model that accepts an input, converts it to Ragged, and
176    # converts the ragged tensor back to a dense tensor.
177    layers = [ToRagged(padding=0), ToDense(default_value=-1)]
178    model = testing_utils.get_model_from_layers(layers, input_shape=(None,))
179
180    # Define some input data with additional padding.
181    input_data = np.array([[1, 0, 0], [2, 3, 0]])
182    expected_output = np.array([[1, -1], [2, 3]])
183    output = model.predict(input_data)
184    self.assertAllEqual(expected_output, output)
185
186  def test_internal_sparse_tensors(self):
187    # Create a model that accepts an input, converts it to Sparse, and
188    # converts the sparse tensor back to a dense tensor.
189    layers = [ToSparse(), ToDense(default_value=-1)]
190    model = testing_utils.get_model_from_layers(layers, input_shape=(None,))
191
192    # Define some input data with additional padding.
193    input_data = np.array([[1, 0, 0], [2, 3, 0]])
194    expected_output = np.array([[1, -1, -1], [2, 3, -1]])
195    output = model.predict(input_data)
196    self.assertAllEqual(expected_output, output)
197
198  def test_training_internal_ragged_tensors(self):
199    # Create a model that implements y=Mx. This is easy to learn and will
200    # demonstrate appropriate gradient passing. (We have to use RaggedTensors
201    # for this test, as ToSparse() doesn't support gradient propagation through
202    # the layer.) TODO(b/124796939): Investigate this.
203    layers = [core.Dense(2), ToRagged(padding=0), ToDense(default_value=-1)]
204    model = testing_utils.get_model_from_layers(layers, input_shape=(1,))
205
206    input_data = np.random.rand(1024, 1)
207    expected_data = np.concatenate((input_data * 3, input_data * .5), axis=-1)
208
209    model.compile(loss="mse", optimizer="adam", **get_test_mode_kwargs())
210    history = model.fit(input_data, expected_data, epochs=10, verbose=0)
211
212    # If the model trained, the loss stored at history[0] should be different
213    # than the one stored at history[-1].
214    self.assertNotEqual(history.history["loss"][-1], history.history["loss"][0])
215
216
217@keras_parameterized.run_with_all_model_types
218@keras_parameterized.run_all_keras_modes
219class CompositeTensorOutputTest(keras_parameterized.TestCase):
220
221  def test_ragged_tensor_outputs(self):
222    # Create a model that accepts an input, converts it to Ragged, and
223    # converts the ragged tensor back to a dense tensor.
224    layers = [ToRagged(padding=0)]
225    model = testing_utils.get_model_from_layers(layers, input_shape=(None,))
226    model._run_eagerly = testing_utils.should_run_eagerly()
227
228    # Define some input data with additional padding.
229    input_data = np.array([[1, 0, 0], [2, 3, 0]])
230    output = model.predict(input_data)
231
232    expected_values = [[1], [2, 3]]
233    self.assertAllEqual(expected_values, output)
234
235  def test_ragged_tensor_rebatched_outputs(self):
236    # Create a model that accepts an input, converts it to Ragged, and
237    # converts the ragged tensor back to a dense tensor.
238    layers = [ToRagged(padding=0)]
239    model = testing_utils.get_model_from_layers(layers, input_shape=(None,))
240    model._run_eagerly = testing_utils.should_run_eagerly()
241
242    # Define some input data with additional padding.
243    input_data = np.array([[1, 0, 0], [2, 3, 0], [4, 0, 0], [5, 6, 0]])
244    output = model.predict(input_data, batch_size=2)
245
246    expected_values = [[1], [2, 3], [4], [5, 6]]
247    self.assertAllEqual(expected_values, output)
248
249  def test_sparse_tensor_outputs(self):
250    # Create a model that accepts an input, converts it to Ragged, and
251    # converts the ragged tensor back to a dense tensor.
252    layers = [ToSparse()]
253    model = testing_utils.get_model_from_layers(layers, input_shape=(None,))
254    model._run_eagerly = testing_utils.should_run_eagerly()
255
256    # Define some input data with additional padding.
257    input_data = np.array([[1, 0, 0], [2, 3, 0]])
258    output = model.predict(input_data)
259
260    expected_indices = np.array([[0, 0], [1, 0], [1, 1]])
261    expected_values = np.array([1, 2, 3])
262    expected_dense_shape = np.array([2, 3])
263
264    self.assertAllEqual(output.indices, expected_indices)
265    self.assertAllEqual(output.values, expected_values)
266    self.assertAllEqual(output.dense_shape, expected_dense_shape)
267
268  def test_sparse_tensor_rebatched_outputs(self):
269    # Create a model that accepts an input, converts it to Ragged, and
270    # converts the ragged tensor back to a dense tensor.
271    layers = [ToSparse()]
272    model = testing_utils.get_model_from_layers(layers, input_shape=(None,))
273    model._run_eagerly = testing_utils.should_run_eagerly()
274
275    # Define some input data with additional padding.
276    input_data = np.array([[1, 0, 0], [2, 3, 0], [4, 0, 0], [5, 6, 0]])
277    output = model.predict(input_data, batch_size=2)
278
279    expected_indices = np.array([[0, 0], [1, 0], [1, 1], [2, 0], [3, 0], [3,
280                                                                          1]])
281    expected_values = np.array([1, 2, 3, 4, 5, 6])
282    expected_dense_shape = np.array([4, 3])
283
284    self.assertAllEqual(output.indices, expected_indices)
285    self.assertAllEqual(output.values, expected_values)
286    self.assertAllEqual(output.dense_shape, expected_dense_shape)
287
288
289def get_input_name(use_dict):
290  # Define the input name.
291  if not use_dict:
292    return None  # This is the same as not setting 'name'.
293  elif testing_utils.get_model_type() == "subclass":
294    return "input_1"  # Subclass models don"t support input names.
295  else:
296    return "test_input_name"
297
298
299def get_kwargs(use_dataset, action="predict"):
300  if use_dataset or not context.executing_eagerly():
301    if action == "fit":
302      return {"steps_per_epoch": 1}
303    return {"steps": 1}
304  else:
305    return {"batch_size": 2}
306
307
308def prepare_inputs(data, use_dict, use_dataset, action, input_name):
309  input_data, expected_output = data
310  batch_size = input_data.shape[0]
311  # Prepare the input data.
312  if use_dict:
313    input_data = {input_name: input_data}
314  if use_dataset:
315    if action == "predict":
316      input_data = dataset_ops.DatasetV2.from_tensor_slices(input_data).batch(
317          batch_size)
318    else:
319      input_data = dataset_ops.DatasetV2.from_tensor_slices(
320          (input_data, expected_output)).batch(batch_size)
321      expected_output = None
322  return (input_data, expected_output)
323
324
325@keras_parameterized.run_with_all_model_types
326@keras_parameterized.run_all_keras_modes
327@parameterized.named_parameters(
328    *testing_utils.generate_combinations_with_testcase_name(
329        use_dict=[True, False],
330        use_dataset=[True, False],
331        action=["predict", "evaluate", "fit"]))
332class SparseTensorInputTest(keras_parameterized.TestCase):
333
334  def test_sparse_tensors(self, use_dict, use_dataset, action):
335    data = [(sparse_tensor.SparseTensor([[0, 0, 0], [1, 0, 0], [1, 0, 1]],
336                                        [1, 2, 3], [2, 1, 3]),
337             np.array([[[1, -1, -1]], [[2, 3, -1]]])),
338            (sparse_tensor.SparseTensor(
339                [[0, 0, 0], [1, 0, 0], [1, 0, 1], [2, 0, 1]], [5, 6, 7, 8],
340                [3, 1, 4]),
341             np.array([[[5, -1, -1, -1]], [[6, 7, -1, -1]], [[-1, 8, -1,
342                                                              -1]]]))]
343    # Prepare the model to test.
344    input_name = get_input_name(use_dict)
345    model_input = input_layer.Input(
346        shape=(1, None), sparse=True, name=input_name, dtype=dtypes.int32)
347    layers = [ToDense(default_value=-1)]
348    model = get_model_from_layers_with_input(layers, model_input=model_input)
349    model.compile(
350        optimizer="sgd",
351        loss="mse",
352        metrics=["accuracy"],
353        **get_test_mode_kwargs())
354    kwargs = get_kwargs(use_dataset, action)
355
356    # Prepare the input data
357    for data_element in data:
358      input_data, expected_output = prepare_inputs(data_element, use_dict,
359                                                   use_dataset, action,
360                                                   input_name)
361      # Perform the action.
362      if action == "predict":
363        result = model.predict(input_data, **kwargs)
364        self.assertAllEqual(expected_output, result)
365      if action == "evaluate":
366        result = model.evaluate(input_data, expected_output, **kwargs)
367        self.assertAllEqual(1.0, result[-1])
368      if action == "fit":
369        # TODO(momernick): What's the best way of validating that fit happened?
370        _ = model.fit(input_data, expected_output, shuffle=False, **kwargs)
371
372
373@keras_parameterized.run_with_all_model_types
374@keras_parameterized.run_all_keras_modes
375class ScipySparseTensorInputTest(keras_parameterized.TestCase,
376                                 test_util.TensorFlowTestCase):
377
378  def test_sparse_scipy_predict_inputs_via_input_layer_args(self):
379    # Create a model that accepts a sparse input and converts the sparse tensor
380    # back to a dense tensor. Scipy sparse matrices are limited to 2D, so use
381    # a one-dimensional shape; note also that scipy's default dtype is int64.
382    model_input = input_layer.Input(shape=(3,), sparse=True, dtype=dtypes.int64)
383    layers = [ToDense(default_value=-1)]
384    model = get_model_from_layers_with_input(layers, model_input=model_input)
385
386    input_data = scipy.sparse.coo_matrix(([1, 2, 3], ([0, 1, 1], [0, 0, 1])),
387                                         shape=[2, 3])
388    expected_output = np.array([[1, -1, -1], [2, 3, -1]])
389    output = model.predict(input_data, steps=1)
390    self.assertAllEqual(expected_output, output)
391
392    input_data_2 = scipy.sparse.coo_matrix(
393        ([5, 6, 7, 8], ([0, 1, 1, 2], [0, 0, 1, 1])), shape=[3, 3])
394    expected_output_2 = np.array([[5, -1, -1], [6, 7, -1], [-1, 8, -1]])
395    output_2 = model.predict(input_data_2, steps=1)
396    self.assertAllEqual(expected_output_2, output_2)
397
398  def test_sparse_scipy_eval_inputs(self):
399    # Create a model that accepts a sparse input and converts the sparse tensor
400    # back to a dense tensor. Scipy sparse matrices are limited to 2D, so use
401    # a one-dimensional shape; note also that scipy's default dtype is int64.
402    model_input = input_layer.Input(shape=(3,), sparse=True, dtype=dtypes.int64)
403    layers = [ToDense(default_value=-1)]
404    model = get_model_from_layers_with_input(layers, model_input=model_input)
405    model.compile(
406        optimizer="sgd",
407        loss="mse",
408        metrics=["accuracy"])
409
410    input_data = scipy.sparse.coo_matrix(([1, 2, 3], ([0, 1, 1], [0, 0, 1])),
411                                         shape=[2, 3])
412    expected_output = np.array([[1, -1, -1], [2, 3, -1]])
413
414    output = model.evaluate(input_data, expected_output, steps=1)
415    self.assertAllEqual(1.0, output[-1])
416
417    input_data_2 = scipy.sparse.coo_matrix(
418        ([5, 6, 7, 8], ([0, 1, 1, 2], [0, 0, 1, 1])), shape=[3, 3])
419    expected_output_2 = np.array([[5, -1, -1], [6, 7, -1], [-1, 8, -1]])
420    output_2 = model.evaluate(input_data_2, expected_output_2, steps=1)
421    self.assertAllEqual(1.0, output_2[-1])
422
423  def test_sparse_scipy_predict_input_dicts_via_input_layer_args(self):
424    # Create a model that accepts a sparse input and converts the sparse tensor
425    # back to a dense tensor. Scipy sparse matrices are limited to 2D, so use
426    # a one-dimensional shape; note also that scipy's default dtype is int64.
427    if testing_utils.get_model_type() == "subclass":
428      input_name = "input_1"  # Subclass models don"t support input names.
429    else:
430      input_name = "test_input_name"
431    model_input = input_layer.Input(
432        shape=(3,), sparse=True, name=input_name, dtype=dtypes.int64)
433    layers = [ToDense(default_value=-1)]
434    model = get_model_from_layers_with_input(layers, model_input=model_input)
435
436    input_data = {
437        input_name:
438            scipy.sparse.coo_matrix(([1, 2, 3], ([0, 1, 1], [0, 0, 1])),
439                                    shape=[2, 3])
440    }
441    expected_output = np.array([[1, -1, -1], [2, 3, -1]])
442    output = model.predict(input_data, steps=1)
443    self.assertAllEqual(expected_output, output)
444
445    input_data_2 = {
446        input_name:
447            scipy.sparse.coo_matrix(
448                ([5, 6, 7, 8], ([0, 1, 1, 2], [0, 0, 1, 1])), shape=[3, 3])
449    }
450    expected_output_2 = np.array([[5, -1, -1], [6, 7, -1], [-1, 8, -1]])
451    output_2 = model.predict(input_data_2, steps=1)
452    self.assertAllEqual(expected_output_2, output_2)
453
454  def test_sparse_scipy_eval_input_dicts(self):
455    # Create a model that accepts a sparse input and converts the sparse tensor
456    # back to a dense tensor. Scipy sparse matrices are limited to 2D, so use
457    # a one-dimensional shape; note also that scipy's default dtype is int64.
458    if testing_utils.get_model_type() == "subclass":
459      input_name = "input_1"  # Subclass models don"t support input names.
460    else:
461      input_name = "test_input_name"
462    model_input = input_layer.Input(
463        shape=(3,), sparse=True, name=input_name, dtype=dtypes.int64)
464    layers = [ToDense(default_value=-1)]
465    model = get_model_from_layers_with_input(layers, model_input=model_input)
466    model.compile(
467        optimizer="sgd",
468        loss="mse",
469        metrics=["accuracy"])
470
471    input_data = {
472        input_name:
473            scipy.sparse.coo_matrix(([1, 2, 3], ([0, 1, 1], [0, 0, 1])),
474                                    shape=[2, 3])
475    }
476    expected_output = np.array([[1, -1, -1], [2, 3, -1]])
477    output = model.evaluate(input_data, expected_output, steps=1)
478    self.assertAllEqual(1.0, output[-1])
479
480    input_data_2 = {
481        input_name:
482            scipy.sparse.coo_matrix(
483                ([5, 6, 7, 8], ([0, 1, 1, 2], [0, 0, 1, 1])), shape=[3, 3])
484    }
485    expected_output_2 = np.array([[5, -1, -1], [6, 7, -1], [-1, 8, -1]])
486    output_2 = model.evaluate(input_data_2, expected_output_2, steps=1)
487    self.assertAllEqual(1.0, output_2[-1])
488
489
490@keras_parameterized.run_with_all_model_types
491@keras_parameterized.run_all_keras_modes
492@parameterized.named_parameters(
493    *testing_utils.generate_combinations_with_testcase_name(
494        use_dict=[True, False],
495        use_dataset=[True, False],
496        action=["predict", "evaluate", "fit"]))
497class RaggedTensorInputTest(keras_parameterized.TestCase,
498                            test_util.TensorFlowTestCase):
499
500  def test_ragged_input(self, use_dict, use_dataset, action):
501    data = [(ragged_factory_ops.constant([[[1]], [[2, 3]]]),
502             np.array([[[1, -1]], [[2, 3]]]))]
503
504    # Prepare the model to test.
505    input_name = get_input_name(use_dict)
506    model_input = input_layer.Input(
507        shape=(None, None), ragged=True, name=input_name, dtype=dtypes.int32,
508        batch_size=2)
509    self.assertIsInstance(model_input._type_spec,
510                          ragged_tensor.RaggedTensorSpec)
511    self.assertEqual(model_input.shape.as_list(), [2, None, None])
512    layers = [ToDense(default_value=-1)]
513    model = get_model_from_layers_with_input(layers, model_input=model_input)
514    model.compile(
515        optimizer="sgd",
516        loss="mse",
517        metrics=["accuracy"],
518        **get_test_mode_kwargs())
519
520    # Prepare the input data
521    for data_element in data:
522      input_data, expected_output = prepare_inputs(data_element, use_dict,
523                                                   use_dataset, action,
524                                                   input_name)
525      # Perform the action.
526      if action == "predict":
527        result = model.predict(input_data)
528        self.assertAllEqual(expected_output, result)
529      if action == "evaluate":
530        result = model.evaluate(input_data, expected_output)
531        self.assertAllEqual(1.0, result[-1])
532      if action == "fit":
533        # TODO(momernick): What's the best way of validating that fit happened?
534        _ = model.fit(input_data, expected_output, shuffle=False)
535
536
537@keras_parameterized.run_with_all_model_types
538@keras_parameterized.run_all_keras_modes
539@parameterized.named_parameters(
540    *testing_utils.generate_combinations_with_testcase_name(
541        use_dict=[True, False], use_dataset=[True, False]))
542class RaggedTensorInputValidationTest(keras_parameterized.TestCase,
543                                      test_util.TensorFlowTestCase):
544
545  def test_ragged_tensor_input_with_one_none_dimension(self, use_dict,
546                                                       use_dataset):
547    # Define some input data.
548    data = [(ragged_factory_ops.constant([[[1, 0]], [[2, 3]]], ragged_rank=1),
549             np.array([[[1, 0]], [[2, 3]]]))]
550
551    # Prepare the model to test.
552    input_shape = (None, 2)  # RaggedTensorInputTest uses (None, None).
553    input_name = get_input_name(use_dict)
554    model_input = input_layer.Input(
555        shape=input_shape, ragged=True, name=input_name, dtype=dtypes.int32)
556    layers = [ToDense(default_value=-1)]
557    model = get_model_from_layers_with_input(layers, model_input=model_input)
558    model.compile(
559        optimizer="sgd",
560        loss="mse",
561        metrics=["accuracy"],
562        **get_test_mode_kwargs())
563
564    for data_element in data:
565      input_data, expected_output = prepare_inputs(
566          data_element,
567          use_dict,
568          use_dataset,
569          action="predict",
570          input_name=input_name)
571      result = model.predict(input_data)
572      self.assertAllEqual(expected_output, result)
573
574  def test_ragged_tensor_input_with_no_none_dimension(self, use_dict,
575                                                      use_dataset):
576    # Define some input data.
577    data = [(ragged_factory_ops.constant([[[1, 0]], [[2, 3]]], ragged_rank=0),
578             np.array([[[1, 0]], [[2, 3]]]))]
579
580    # Prepare the model to test.
581    input_shape = (1, 2)  # RaggedTensorInputTest uses (None, None).
582    input_name = get_input_name(use_dict)
583    model_input = input_layer.Input(
584        shape=input_shape, ragged=True, name=input_name, dtype=dtypes.int32)
585    layers = [ToDense(default_value=-1)]
586    model = get_model_from_layers_with_input(layers, model_input=model_input)
587    model.compile(
588        optimizer="sgd",
589        loss="mse",
590        metrics=["accuracy"],
591        **get_test_mode_kwargs())
592    kwargs = get_kwargs(use_dataset)
593
594    for data_element in data:
595      input_data, expected_output = prepare_inputs(
596          data_element,
597          use_dict,
598          use_dataset,
599          action="predict",
600          input_name=input_name)
601      result = model.predict(input_data, **kwargs)
602      self.assertAllEqual(expected_output, result)
603
604
605@keras_parameterized.run_with_all_model_types()
606@keras_parameterized.run_all_keras_modes(always_skip_v1=True)
607class CompositeTensorModelPredictTest(keras_parameterized.TestCase):
608
609  def _normalize_shape(self, shape):
610    if not isinstance(shape, tuple):
611      shape = tuple(shape.as_list())
612    return shape
613
614  def test_sparse_tensor_model_predict(self):
615    # Create a model that accepts a sparse input and runs a "Dense" layer on it.
616    model_input = input_layer.Input(
617        shape=(3,), sparse=True, dtype=dtypes.float32)
618
619    self.assertEqual([None, 3], model_input.shape.as_list())
620
621    layers = [Dense(2)]
622    model = get_model_from_layers_with_input(layers, model_input=model_input)
623
624    sparse_input = sparse_tensor.SparseTensor(
625        # A two-row matrix
626        indices=[(0, 0), (0, 1), (0, 2), (5, 0), (5, 1), (5, 2)],
627        values=[1., 1., 1., 1., 1., 1.],
628        dense_shape=(6, 3))
629
630    shape = model(sparse_input).shape
631    self.assertEqual((6, 2), self._normalize_shape(shape))
632
633    shape = model.predict(sparse_input, steps=1).shape
634    self.assertEqual((6, 2), self._normalize_shape(shape))
635
636  def test_ragged_tensor_model_predict(self):
637    # Create a model that accepts a sparse input and runs a "Dense" layer on it.
638    model_input = input_layer.Input(shape=(None,), ragged=True)
639    self.assertEqual([None, None], model_input.shape.as_list())
640
641    layers = [Embedding(input_dim=7, output_dim=5)]
642    model = get_model_from_layers_with_input(layers, model_input=model_input)
643
644    ragged_input = ragged_factory_ops.constant([
645        [1, 2, 3, 4, 5],
646        [2, 4],
647    ])
648
649    shape = model(ragged_input).shape
650    self.assertEqual((2, None, 5), self._normalize_shape(shape))
651
652    shape = model.predict(ragged_input, steps=1).shape
653    self.assertEqual((2, None, 5), self._normalize_shape(shape))
654
655
656if __name__ == "__main__":
657  test.main()
658