tensorflow: tf.keras multi input models don't work when using tf.data.Dataset
Please go to Stack Overflow for help and support:
https://stackoverflow.com/questions/tagged/tensorflow
If you open a GitHub issue, here is our policy:
- It must be a bug, a feature request, or a significant problem with documentation (for small docs fixes please send a PR instead).
- The form below must be filled out.
- It shouldn’t be a TensorBoard issue. Those go here.
Here’s why we have that policy: TensorFlow developers respond to issues. We want to focus on work that benefits the whole community, e.g., fixing bugs and adding features. Support only helps individuals. GitHub also notifies thousands of people when issues are filed. We want them to see you communicating an interesting problem, rather than being redirected to Stack Overflow.
System information
- Have I written custom code (as opposed to using a stock example script provided in TensorFlow): yes
- OS Platform and Distribution (e.g., Linux Ubuntu 16.04): macOS 10.13.5 and Debian GNU/Linux 9 (stretch)
- TensorFlow installed from (source or binary): binary
- TensorFlow version (use command below): v1.9.0-rc2-359-g95cfd8b3d9 1.10.0-dev20180711 also reproduces on v1.9.0
- Python version: 3.6.5 and 3.5.3
- Bazel version (if compiling from source):
- GCC/Compiler version (if compiling from source):
- CUDA/cuDNN version: None
- GPU model and memory: None
- Exact command to reproduce: see below
You can collect some of this information using our environment capture script:
https://github.com/tensorflow/tensorflow/tree/master/tools/tf_env_collect.sh
You can obtain the TensorFlow version with
python -c “import tensorflow as tf; print(tf.GIT_VERSION, tf.VERSION)”
Describe the problem
tf.keras
multi input models don’t work when used together with tf.data.Dataset
due to input broken validation checks. This problem reproduces both on tf@1.9.0 and the latest nightly.
@fchollet Do you have any ideas what’s going on here, or am I missing something obvious?
Source code / logs
Multi input model
Consider the following toy model:
import numpy as np
import tensorflow as tf
from tensorflow import keras
data_a = np.array([300, 455, 350, 560, 700, 800, 200, 250], dtype=np.float32)
labels = np.array([455, 350, 560, 700, 800, 200, 250, 300], dtype=np.float32)
data_b = np.array([200, 255, 350, 470, 600, 300, 344, 322], dtype=np.float32)
data_a = np.reshape(data_a, (8, 1, 1))
data_b = np.reshape(data_b, (8, 1, 1))
x = keras.layers.Input(shape=(1, 1), name='input_x')
y = keras.layers.Input(shape=(1, 1), name='input_y')
admi = keras.layers.LSTM(40, return_sequences=False)(x)
pla = keras.layers.LSTM(40, return_sequences=False)(y)
out = keras.layers.concatenate([admi, pla], axis=-1)
output = keras.layers.Dense(1, activation='sigmoid')(out)
model = keras.models.Model(inputs=[x, y], outputs=output)
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
Using numpy
data
When fitting using numpy
data this works as expected when passing a list or dictionary of inputs:
model.fit([data_a, data_b], labels, batch_size=2, epochs=10)
model.fit({'input_x': data_a, 'input_y': data_b}, labels, batch_size=2, epochs=10)
Using tf.data.Dataset.from_tensor_slices
dictionary
When trying the same with a tf.data.Dataset
the following fails due to incorrect input validation:
dataset = tf.data.Dataset.from_tensor_slices(({'input_x': data_a, 'input_y': data_b}, labels)).batch(2).repeat()
model.fit(dataset, epochs=10, steps_per_epoch=4)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-6-d35bacd274cc> in <module>()
1 dataset = tf.data.Dataset.from_tensor_slices(({'input_x': data_a, 'input_y': data_b}, labels)).batch(2).repeat()
----> 2 model.fit(dataset, epochs=10, steps_per_epoch=4)
/usr/local/lib/python3.6/site-packages/tensorflow/python/keras/engine/training.py in fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, **kwargs)
1276 steps_name='steps_per_epoch',
1277 steps=steps_per_epoch,
-> 1278 validation_split=validation_split)
1279
1280 # Prepare validation data.
/usr/local/lib/python3.6/site-packages/tensorflow/python/keras/engine/training.py in _standardize_user_data(self, x, y, sample_weight, class_weight, batch_size, check_steps, steps_name, steps, validation_split)
915 feed_output_shapes,
916 check_batch_axis=False, # Don't enforce the batch size.
--> 917 exception_prefix='target')
918
919 # Generate sample-wise weight values given the `sample_weight` and
/usr/local/lib/python3.6/site-packages/tensorflow/python/keras/engine/training_utils.py in standardize_input_data(data, names, shapes, check_batch_axis, exception_prefix)
180 ': expected ' + names[i] + ' to have ' +
181 str(len(shape)) + ' dimensions, but got array '
--> 182 'with shape ' + str(data_shape))
183 if not check_batch_axis:
184 data_shape = data_shape[1:]
ValueError: Error when checking target: expected dense to have 2 dimensions, but got array with shape (None,)
Using tf.data.Dataset.from_generator
dictionary
However using the same network together with tf.data.Dataset.from_generator
works. Probably because less validation is done:
def generator():
while True:
for i in np.random.permutation(8):
yield {'input_x': data_a[i], 'input_y': data_b[i]}, labels[i]
dataset = tf.data.Dataset.from_generator(generator, ({'input_x': tf.float32, 'input_y': tf.float32}, tf.float32)).batch(2)
model.fit(dataset, epochs=10, steps_per_epoch=4)
Using tf.data.Dataset
tuple
Passing the multi-input as a tuple to the model both datasets generated with from_tensor_slices
and from_generator
fail:
dataset = tf.data.Dataset.from_tensor_slices(((data_a, data_b), labels)).batch(2).repeat()
model.fit(dataset, epochs=10, steps_per_epoch=4)
def generator():
while True:
for i in np.random.permutation(8):
yield (data_a[i], data_b[i]), labels[i]
dataset = tf.data.Dataset.from_generator(generator, ((tf.float32, tf.float32), tf.float32)).batch(2)
model.fit(dataset, epochs=10, steps_per_epoch=4)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-7-512a95f0c2a7> in <module>()
1 dataset = tf.data.Dataset.from_tensor_slices(((data_a, data_b), labels)).batch(2).repeat()
----> 2 model.fit(dataset, epochs=10, steps_per_epoch=4)
/usr/local/lib/python3.6/site-packages/tensorflow/python/keras/engine/training.py in fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, **kwargs)
1276 steps_name='steps_per_epoch',
1277 steps=steps_per_epoch,
-> 1278 validation_split=validation_split)
1279
1280 # Prepare validation data.
/usr/local/lib/python3.6/site-packages/tensorflow/python/keras/engine/training.py in _standardize_user_data(self, x, y, sample_weight, class_weight, batch_size, check_steps, steps_name, steps, validation_split)
876 feed_input_shapes,
877 check_batch_axis=False, # Don't enforce the batch size.
--> 878 exception_prefix='input')
879
880 if y is not None:
/usr/local/lib/python3.6/site-packages/tensorflow/python/keras/engine/training_utils.py in standardize_input_data(data, names, shapes, check_batch_axis, exception_prefix)
141 data = data.values if data.__class__.__name__ == 'DataFrame' else data
142 data = [data]
--> 143 data = [standardize_single_array(x) for x in data]
144
145 if len(data) != len(names):
/usr/local/lib/python3.6/site-packages/tensorflow/python/keras/engine/training_utils.py in <listcomp>(.0)
141 data = data.values if data.__class__.__name__ == 'DataFrame' else data
142 data = [data]
--> 143 data = [standardize_single_array(x) for x in data]
144
145 if len(data) != len(names):
/usr/local/lib/python3.6/site-packages/tensorflow/python/keras/engine/training_utils.py in standardize_single_array(x)
79 elif tensor_util.is_tensor(x):
80 return x
---> 81 elif x.ndim == 1:
82 x = np.expand_dims(x, 1)
83 return x
AttributeError: 'tuple' object has no attribute 'ndim'
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 67
- Comments: 33 (8 by maintainers)
I think I discovered the problem fro my situation. The problem was I am using the standalone
Keras
. Not the one imported fromtendorflow.
So the new features of feeding theiterator
directly tomodel.fit()
is valid only when you are usingtf.Keras
not the standalone Keras.@JanRuettinger @ricoms Sorry for the delayed response.
I drafted up a toy example using MNIST in order to train a model with two inputs and two outputs. The model is simply two identical models fused together, which takes in two copies of the MNIST data (two inputs) and outputs a prediction for each (two outputs). You can adapt this to more complex models and input pipelines.
Note: This is still using
tf-nightly-gpu
version1.12.0-dev20180918
. I assume this will work in tensorflow 1.12 and above.Update: As @jashshopin mentions below, the
dataset
object can be passed directly tomodel.fit()
if you have no need for an iterator.Hi, @was84san. As you mentioned, I am using
tf.kera
. But the problem still exists. Do you have any idea? Thanks!I am not finding documentation for feeding models with multiple inputs with different dimensions with tf.data.
The above exchange leaves me still struggling for an understanding on feeding such models. May I asked for clarification?
I can use the generator directly, but my goal is to move the generator to a full tf.data pipleline, but I am missing something fundamental to get started.
This works, but does not use tf.data:
toy_model.fit(x=toy_generator_list(), steps_per_epoch=3, epochs=2)
The following as close to a solution I have gotten to, but it fails
Generates error
ValueError: The two structures don't have the same sequence length. Input structure has length 2, while the shallow structure has length 3.
I know that my request smells like “a request for help”, it is, but please interpret it as a request for improved documentation. Stack overflow does not have anything on multiple inputs of different shapes.
btw:
@ Igeiger, I tried to pass multiple inputs as a list of tf.dataset api to model fit directly, like this
model.fit ( [dataset1_iterator, dataset2_iterator] , .....)
then I got this error
And this is with tensorflow 1.12, so how you can pass multiple input using tf.dataset api with model fit not with model.fit_generator? `
@jashshopin Thanks for pointing that out, apparently you can pass the zipped
dataset
directly intomodel.fit()
. The example should still work for those who might want to use a one-shot iterator or initializable iterator as well.@gabrielibagon Could you post a snippet how you got a nested dataset iterator with multiple inputs working?
Thx, my problem solved! Just have changed
import keras
import tensorflow as tf
toimport tensorflow as tf
from tensorflow import keras
This still seems broken to me (in tensorflow 2.0.0-rc0). See this snippet:
which gives
I can confirm this works in tensorflow 2.0.0-rc0. Multiple input and output, even without all the zipping:
@johngrabner The problem in your code is that your
output_type
andoutput_shape
definitions differ. Changing theoutput_type
to((tensorflow.float32, tensorflow.float32), tensorflow.float32)
should to the trick.For the sake of completeness, here is a minimal example of a dataset that expects two inputs (shapes (1,32) and (1,128)):
@lgeiger what about using dictionaries as targets? https://github.com/tensorflow/tensorflow/issues/25299#issue-404556539