super(Model, self).__init__(*args, **kwargs)
# Create a cache for iterator get_next op.
self._iterator_get_next = weakref.WeakKeyDictionary()
+ # Create a cache for dataset - uninitialized iterators
+ self._dataset_iterator_cache = weakref.WeakKeyDictionary()
def compile(self,
optimizer,
(in case the model has multiple inputs).
- A dict mapping input names to the corresponding array/tensors,
if the model has named inputs.
- - A `tf.data` dataset iterator.
+ - A `tf.data` dataset or a dataset iterator.
y: Target data. Like the input data `x`,
it could be either Numpy array(s) or TensorFlow tensor(s).
It should be consistent with `x` (you cannot have Numpy inputs and
- tensor targets, or inversely). If `x` is a dataset iterator,
- `y` should not be specified
+ tensor targets, or inversely). If `x` is a dataset or a
+ dataset iterator, `y` should not be specified
(since targets will be obtained from the iterator).
sample_weight: An optional sample-weight array passed by the user to
weight the importance of each sample in `x`.
RuntimeError: If the model was never compiled.
"""
if isinstance(x, dataset_ops.Dataset):
- raise ValueError('You passed a `Dataset` instance to your model (%s), '
- 'which is not supported. Instead, pass an `Iterator`, '
- 'which you can obtain e.g. via '
- '`dataset.make_one_shot_iterator()` (the exact method '
- 'to use will depend on your specific dataset).' % x)
+ if context.executing_eagerly():
+ x = x.make_one_shot_iterator()
+ else:
+ if x in self._dataset_iterator_cache:
+ x = self._dataset_iterator_cache[x]
+ else:
+ iterator = x.make_initializable_iterator()
+ self._dataset_iterator_cache[x] = iterator
+ x = iterator
+ K.get_session().run(x.initializer)
# Validates `steps` argument based on x's type.
if check_steps:
is_x_eager_iterator = isinstance(x, iterator_ops.EagerIterator)
is_x_iterator = isinstance(x, iterator_ops.Iterator)
- # Validate user inputs when data is given as a dataset iterator.
+ # Validate user inputs when data is given as a dataset or dataset iterator.
if is_x_iterator or is_x_eager_iterator:
training_utils.validate_iterator_input(x, y, sample_weight,
validation_split)
(in case the model has multiple inputs).
- A dict mapping input names to the corresponding array/tensors,
if the model has named inputs.
- - A `tf.data` dataset iterator.
+ - A `tf.data` dataset or a dataset iterator.
y: Target data. Like the input data `x`,
it could be either Numpy array(s) or TensorFlow tensor(s).
It should be consistent with `x` (you cannot have Numpy inputs and
- tensor targets, or inversely). If `x` is a dataset iterator,
- `y` should not be specified
+ tensor targets, or inversely). If `x` is a dataset or dataset
+ iterator, `y` should not be specified
(since targets will be obtained from the iterator).
batch_size: Integer or `None`.
Number of samples per gradient update.
If unspecified, `batch_size` will default to 32.
Do not specify the `batch_size` if your data is in the
- form of symbolic tensors or dataset iterators (since they generate
- batches).
+ form of symbolic tensors, datasets, or dataset iterators
+ (since they generate batches).
epochs: Integer. Number of epochs to train the model.
An epoch is an iteration over the entire `x` and `y`
data provided.
on this data at the end of each epoch.
The validation data is selected from the last samples
in the `x` and `y` data provided, before shuffling. This argument is
- not supported when `x` is a dataset iterator.
+ not supported when `x` is a dataset or a dataset iterator.
validation_data: Data on which to evaluate
the loss and any model metrics at the end of each epoch.
The model will not be trained on this data.
`validation_data` could be:
- tuple `(x_val, y_val)` of Numpy arrays or tensors
- tuple `(x_val, y_val, val_sample_weights)` of Numpy arrays
- - dataset iterator
+ - dataset or a dataset iterator
shuffle: Boolean (whether to shuffle the training data
before each epoch) or str (for 'batch').
'batch' is a special option for dealing with the
to apply a different weight to every timestep of every sample.
In this case you should make sure to specify
`sample_weight_mode="temporal"` in `compile()`. This argument is not
- supported when `x` is a dataset iterator.
+ supported when `x` is a dataset or a dataset iterator.
initial_epoch: Integer.
Epoch at which to start training
(useful for resuming a previous training run).
# Prepare validation data.
if validation_data:
if (isinstance(validation_data, iterator_ops.Iterator) or
- isinstance(validation_data, iterator_ops.EagerIterator)):
+ isinstance(validation_data, iterator_ops.EagerIterator) or
+ isinstance(validation_data, dataset_ops.Dataset)):
val_x = validation_data
val_y = None
val_sample_weight = None
'When passing a `validation_data` argument, '
'it must contain either 2 items (x_val, y_val), '
'or 3 items (x_val, y_val, val_sample_weights), '
- 'or alternatively it could be a dataset iterator. However we '
- 'received `validation_data=%s`' % validation_data)
+ 'or alternatively it could be a dataset or a '
+ 'dataset or a dataset iterator. '
+ 'However we received `validation_data=%s`' % validation_data)
# Validate and standardize validation data.
val_x, val_y, val_sample_weights = self._standardize_user_data(
(in case the model has multiple inputs).
- A dict mapping input names to the corresponding array/tensors,
if the model has named inputs.
- - A `tf.data` dataset iterator.
+ - A `tf.data` dataset or a dataset iterator.
y: Target data. Like the input data `x`,
it could be either Numpy array(s) or TensorFlow tensor(s).
It should be consistent with `x` (you cannot have Numpy inputs and
- tensor targets, or inversely). If `x` is a dataset iterator,
- `y` should not be specified
- (since targets will be obtained from the iterator).
+ tensor targets, or inversely).
+ If `x` is a dataset or a dataset iterator, `y` should not be specified
+ (since targets will be obtained from the iterator/dataset).
batch_size: Integer or `None`.
Number of samples per gradient update.
If unspecified, `batch_size` will default to 32.
Do not specify the `batch_size` is your data is in the
- form of symbolic tensors or dataset iterators (since they generate
- batches).
+ form of symbolic tensors, datasets, or dataset iterators
+ (since they generate batches).
verbose: 0 or 1. Verbosity mode.
0 = silent, 1 = progress bar.
sample_weight: Optional Numpy array of weights for
to apply a different weight to every timestep of every sample.
In this case you should make sure to specify
`sample_weight_mode="temporal"` in `compile()`. This argument is not
- supported when `x` is a dataset iterator.
+ supported when `x` is a dataset or a dataset iterator.
steps: Integer or `None`.
Total number of steps (batches of samples)
before declaring the evaluation round finished.
(in case the model has multiple inputs).
- A TensorFlow tensor, or a list of tensors
(in case the model has multiple inputs).
- - A `tf.data` dataset iterator.
+ - A `tf.data` dataset or a dataset iterator.
batch_size: Integer or `None`.
Number of samples per gradient update.
If unspecified, `batch_size` will default to 32.
Do not specify the `batch_size` is your data is in the
- form of symbolic tensors or dataset iterators (since they generate
- batches).
+ form of symbolic tensors, dataset, or dataset iterators
+ (since they generate batches).
verbose: Verbosity mode, 0 or 1.
steps: Total number of steps (batches of samples)
before declaring the prediction round finished.
(in case the model has multiple inputs).
- A dict mapping input names to the corresponding array/tensors,
if the model has named inputs.
- - A `tf.data` dataset iterator.
+ - A `tf.data` dataset or a dataset iterator.
y: Target data. Like the input data `x`,
it could be either Numpy array(s) or TensorFlow tensor(s).
It should be consistent with `x` (you cannot have Numpy inputs and
- tensor targets, or inversely). If `x` is a dataset iterator,
- `y` should not be specified
+ tensor targets, or inversely). If `x` is a dataset or a
+ dataset iterator, `y` should not be specified
(since targets will be obtained from the iterator).
sample_weight: Optional array of the same length as x, containing
weights to apply to the model's loss for each sample.
to apply a different weight to every timestep of every sample.
In this case you should make sure to specify
sample_weight_mode="temporal" in compile(). This argument is not
- supported when `x` is a dataset iterator.
-
+ supported when `x` is a dataset or a dataset iterator.
class_weight: Optional dictionary mapping
class indices (integers) to
a weight (float) to apply to the model's loss for the samples
(in case the model has multiple inputs).
- A dict mapping input names to the corresponding array/tensors,
if the model has named inputs.
- - A `tf.data` dataset iterator.
+ - A `tf.data` dataset or a dataset iterator.
y: Target data. Like the input data `x`,
it could be either Numpy array(s) or TensorFlow tensor(s).
It should be consistent with `x` (you cannot have Numpy inputs and
- tensor targets, or inversely). If `x` is a dataset iterator,
- `y` should not be specified
+ tensor targets, or inversely). If `x` is a dataset or a
+ dataset iterator, `y` should not be specified
(since targets will be obtained from the iterator).
sample_weight: Optional array of the same length as x, containing
weights to apply to the model's loss for each sample.
to apply a different weight to every timestep of every sample.
In this case you should make sure to specify
sample_weight_mode="temporal" in compile(). This argument is not
- supported when `x` is a dataset iterator.
+ supported when `x` is a dataset or a dataset iterator.
Returns:
Scalar test loss (if the model has a single output and no metrics)
(in case the model has multiple inputs).
- A TensorFlow tensor, or a list of tensors
(in case the model has multiple inputs).
- - A `tf.data` dataset iterator.
+ - A `tf.data` dataset or a dataset iterator.
Returns:
Numpy array(s) of predictions.
# Test with validation split
with self.assertRaisesRegexp(
ValueError, '`validation_split` argument is not supported '
- 'when input `x` is a dataset iterator'):
+ 'when input `x` is a dataset or a dataset iterator'):
model.fit(iterator,
epochs=1, steps_per_epoch=2, verbose=0,
validation_split=0.5, validation_steps=2)
sample_weight = np.random.random((10,))
with self.assertRaisesRegexp(
ValueError, '`sample_weight` argument is not supported '
- 'when input `x` is a dataset iterator'):
+ 'when input `x` is a dataset or a dataset iterator'):
model.fit(
iterator,
epochs=1,
# Test invalid usage
with self.assertRaisesRegexp(ValueError,
- 'Instead, pass an `Iterator`'):
- model.fit(dataset,
- epochs=1, steps_per_epoch=2, verbose=0)
- with self.assertRaisesRegexp(ValueError,
'you should not specify a target'):
model.fit(iterator, iterator,
epochs=1, steps_per_epoch=2, verbose=0)
'dataset iterator ran out of data')
+class TestTrainingWithDataset(test.TestCase):
+
+ def test_calling_model_on_same_dataset(self):
+ with self.test_session():
+ x = keras.layers.Input(shape=(3,), name='input')
+ y = keras.layers.Dense(4, name='dense')(x)
+ model = keras.Model(x, y)
+
+ optimizer = RMSPropOptimizer(learning_rate=0.001)
+ loss = 'mse'
+ metrics = ['mae']
+ model.compile(optimizer, loss, metrics=metrics)
+
+ inputs = np.zeros((10, 3), dtype=np.float32)
+ targets = np.zeros((10, 4), dtype=np.float32)
+ dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets))
+ dataset = dataset.repeat(100)
+ dataset = dataset.batch(10)
+
+ # Call fit with validation data
+ model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0,
+ validation_data=dataset, validation_steps=2)
+ # Finalize the graph to make sure new ops aren't added when calling on the
+ # same dataset
+ ops.get_default_graph().finalize()
+ model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0,
+ validation_data=dataset, validation_steps=2)
+
+ @tf_test_util.run_in_graph_and_eager_modes()
+ def test_training_and_eval_methods_on_dataset(self):
+ with self.test_session():
+ x = keras.layers.Input(shape=(3,), name='input')
+ y = keras.layers.Dense(4, name='dense')(x)
+ model = keras.Model(x, y)
+
+ optimizer = RMSPropOptimizer(learning_rate=0.001)
+ loss = 'mse'
+ metrics = ['mae']
+ model.compile(optimizer, loss, metrics=metrics)
+
+ inputs = np.zeros((10, 3), dtype=np.float32)
+ targets = np.zeros((10, 4), dtype=np.float32)
+ dataset = dataset_ops.Dataset.from_tensor_slices((inputs, targets))
+ dataset = dataset.repeat(100)
+ dataset = dataset.batch(10)
+
+ model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=1)
+ model.evaluate(dataset, steps=2, verbose=1)
+ model.predict(dataset, steps=2)
+ model.train_on_batch(dataset)
+ model.predict_on_batch(dataset)
+
+ # Test with validation data
+ model.fit(dataset, epochs=1, steps_per_epoch=2, verbose=0,
+ validation_data=dataset, validation_steps=2)
+
+ # Test with validation split
+ with self.assertRaisesRegexp(
+ ValueError, '`validation_split` argument is not supported '
+ 'when input `x` is a dataset or a dataset iterator'):
+ model.fit(dataset,
+ epochs=1, steps_per_epoch=2, verbose=0,
+ validation_split=0.5, validation_steps=2)
+
+ # Test with sample weight.
+ sample_weight = np.random.random((10,))
+ with self.assertRaisesRegexp(
+ ValueError, '`sample_weight` argument is not supported '
+ 'when input `x` is a dataset or a dataset iterator'):
+ model.fit(
+ dataset,
+ epochs=1,
+ steps_per_epoch=2,
+ verbose=0,
+ sample_weight=sample_weight)
+
+ # Test invalid usage
+ with self.assertRaisesRegexp(ValueError,
+ 'you should not specify a target'):
+ model.fit(dataset, dataset,
+ epochs=1, steps_per_epoch=2, verbose=0)
+
+ with self.assertRaisesRegexp(
+ ValueError, 'you should specify the `steps_per_epoch` argument'):
+ model.fit(dataset, epochs=1, verbose=0)
+ with self.assertRaisesRegexp(ValueError,
+ 'you should specify the `steps` argument'):
+ model.evaluate(dataset, verbose=0)
+ with self.assertRaisesRegexp(ValueError,
+ 'you should specify the `steps` argument'):
+ model.predict(dataset, verbose=0)
+
+
if __name__ == '__main__':
test.main()