La méthode du modèle fit_generator()
de Keras attend un générateur qui produit des n-uplets de la forme (entrée, cibles), où les deux éléments sont des tableaux NumPy. La documentation semble impliquer que si j'emballe simplement un Dataset
iterator dans un générateur et m'assure de convertir les tableaux Tensors en tableaux NumPy, je devrais être bon aller. Ce code me donne cependant une erreur:
import numpy as np
import os
import keras.backend as K
from keras.layers import Dense, Input
from keras.models import Model
import tensorflow as tf
from tensorflow.contrib.data import Dataset
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
with tf.Session() as sess:
def create_data_generator():
dat1 = np.arange(4).reshape(-1, 1)
ds1 = Dataset.from_tensor_slices(dat1).repeat()
dat2 = np.arange(5, 9).reshape(-1, 1)
ds2 = Dataset.from_tensor_slices(dat2).repeat()
ds = Dataset.Zip((ds1, ds2)).batch(4)
iterator = ds.make_one_shot_iterator()
while True:
next_val = iterator.get_next()
yield sess.run(next_val)
datagen = create_data_generator()
input_vals = Input(shape=(1,))
output = Dense(1, activation='relu')(input_vals)
model = Model(inputs=input_vals, outputs=output)
model.compile('rmsprop', 'mean_squared_error')
model.fit_generator(datagen, steps_per_Epoch=1, epochs=5,
verbose=2, max_queue_size=2)
Voici l'erreur que je reçois:
Using TensorFlow backend.
Epoch 1/5
Exception in thread Thread-1:
Traceback (most recent call last):
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 270, in __init__
fetch, allow_tensor=True, allow_operation=True))
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 2708, in as_graph_element
return self._as_graph_element_locked(obj, allow_tensor, allow_operation)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 2787, in _as_graph_element_locked
raise ValueError("Tensor %s is not an element of this graph." % obj)
ValueError: Tensor Tensor("IteratorGetNext:0", shape=(?, 1), dtype=int64) is not an element of this graph.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/jsaporta/anaconda3/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/home/jsaporta/anaconda3/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/keras/utils/data_utils.py", line 568, in data_generator_task
generator_output = next(self._generator)
File "./datagen_test.py", line 25, in create_data_generator
yield sess.run(next_val)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 895, in run
run_metadata_ptr)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 1109, in _run
self._graph, fetches, feed_dict_tensor, feed_handles=feed_handles)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 413, in __init__
self._fetch_mapper = _FetchMapper.for_fetch(fetches)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 233, in for_fetch
return _ListFetchMapper(fetch)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 340, in __init__
self._mappers = [_FetchMapper.for_fetch(fetch) for fetch in fetches]
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 340, in <listcomp>
self._mappers = [_FetchMapper.for_fetch(fetch) for fetch in fetches]
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 241, in for_fetch
return _ElementFetchMapper(fetches, contraction_fn)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 277, in __init__
'Tensor. (%s)' % (fetch, str(e)))
ValueError: Fetch argument <tf.Tensor 'IteratorGetNext:0' shape=(?, 1) dtype=int64> cannot be interpreted as a Tensor. (Tensor Tensor("IteratorGetNext:0", shape=(?, 1), dtype=int64) is not an element of this graph.)
Traceback (most recent call last):
File "./datagen_test.py", line 34, in <module>
verbose=2, max_queue_size=2)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/keras/legacy/interfaces.py", line 87, in wrapper
return func(*args, **kwargs)
File "/home/jsaporta/anaconda3/lib/python3.6/site-packages/keras/engine/training.py", line 2011, in fit_generator
generator_output = next(output_generator)
StopIteration
Curieusement, ajouter une ligne contenant next(datagen)
directement après l’initialisation de datagen
entraîne une exécution parfaite du code, sans erreur.
Pourquoi mon code d'origine ne fonctionne pas? Pourquoi ça commence à fonctionner quand j'ajoute cette ligne à mon code? Existe-t-il un moyen plus efficace d’utiliser l’API de jeu de données de TensorFlow avec Keras qui n’implique pas la conversion de Tensors en tableaux NumPy et vice-versa?
Il existe en effet un moyen plus efficace d'utiliser Dataset
sans avoir à convertir les tenseurs en tableaux numpy. Cependant, ce n'est pas (encore?) Sur la documentation officielle. De la note de publication, c'est une fonctionnalité introduite dans Keras 2.0.7. Vous devrez peut-être installer keras> = 2.0.7 pour pouvoir l'utiliser.
x = np.arange(4).reshape(-1, 1).astype('float32')
ds_x = Dataset.from_tensor_slices(x).repeat().batch(4)
it_x = ds_x.make_one_shot_iterator()
y = np.arange(5, 9).reshape(-1, 1).astype('float32')
ds_y = Dataset.from_tensor_slices(y).repeat().batch(4)
it_y = ds_y.make_one_shot_iterator()
input_vals = Input(tensor=it_x.get_next())
output = Dense(1, activation='relu')(input_vals)
model = Model(inputs=input_vals, outputs=output)
model.compile('rmsprop', 'mse', target_tensors=[it_y.get_next()])
model.fit(steps_per_Epoch=1, epochs=5, verbose=2)
Plusieurs différences:
tensor
à la couche Input
. Keras lira les valeurs de ce tenseur et l'utilisera comme entrée pour s'adapter au modèle.target_tensors
À Model.compile()
.float32
. Dans des conditions normales d'utilisation, Keras procédera à cette conversion pour vous. Mais maintenant, vous devrez le faire vous-même.Dataset
. Utilisez steps_per_Epoch
Et epochs
pour contrôler quand arrêter l'ajustement du modèle.En bref, utilisez Input(tensor=...)
, model.compile(target_tensors=...)
et model.fit(x=None, y=None, ...)
si vos données doivent être lues à partir de tenseurs.
tf.data.Dataset
À la fonction keras.Model.fit()
et d'agir de la même manière que fit_generator
.# Load mnist training data
(x_train, y_train), _ = tf.keras.datasets.mnist.load_data()
training_set = tfdata_generator(x_train, y_train,is_training=True)
model = # your keras model here
model.fit(
training_set.make_one_shot_iterator(),
steps_per_Epoch=len(x_train) // 128,
epochs=5,
verbose = 1)
tfdata_generator
Est une fonction qui retourne un tf.data.Dataset
Itératif.def tfdata_generator(images, labels, is_training, batch_size=128):
'''Construct a data generator using `tf.Dataset`. '''
def map_fn(image, label):
'''Preprocess raw data to trainable input. '''
x = tf.reshape(tf.cast(image, tf.float32), (28, 28, 1))
y = tf.one_hot(tf.cast(label, tf.uint8), _NUM_CLASSES)
return x, y
dataset = tf.data.Dataset.from_tensor_slices((images, labels))
if is_training:
dataset = dataset.shuffle(1000) # depends on sample size
dataset = dataset.map(map_fn)
dataset = dataset.batch(batch_size)
dataset = dataset.repeat()
dataset = dataset.prefetch(tf.contrib.data.AUTOTUNE)
return dataset
En plus de la réponse de @ Yu-Yang, vous pouvez également modifier tf.data.Dataset
Pour qu'il devienne un générateur pour fit_generator
Comme suit
from tensorflow.contrib.learn.python.learn.datasets import mnist
data = mnist.load_mnist()
model = # your Keras model
model.fit_generator(generator = tfdata_generator(data.train.images, data.train.labels),
steps_per_Epoch=200,
workers = 0 , # This is important
verbose = 1)
def tfdata_generator(images, labels, batch_size=128, shuffle=True,):
def map_func(image, label):
'''A transformation function'''
x_train = tf.reshape(tf.cast(image, tf.float32), image_shape)
y_train = tf.one_hot(tf.cast(label, tf.uint8), num_classes)
return [x_train, y_train]
dataset = tf.data.Dataset.from_tensor_slices((images, labels))
dataset = dataset.map(map_func)
dataset = dataset.shuffle().batch(batch_size).repeat()
iterator = dataset.make_one_shot_iterator()
next_batch = iterator.get_next()
while True:
yield K.get_session().run(next_batch)
Les autres réponses sont bonnes, mais il est important de noter que l'utilisation de from_tensor_slices
directement avec de grands tableaux numpy peut rapidement remplir votre mémoire car, IIRC, les valeurs sont copiées dans le graphique sous forme de tf.constants
. D'après mon expérience, cela provoquera un échec silencieux où la formation débutera éventuellement sans que la perte, etc., ne s'améliore.
Un meilleur moyen consiste à utiliser des espaces réservés. Par exemple. voici mon code pour créer un générateur d'images et de cibles onehot:
def create_generator_tf_dataset(self, images, onehots, batch_size):
# Get shapes
img_size = images.shape
img_size = (None, img_size[1], img_size[2], img_size[3])
onehot_size = onehots.shape
onehot_size = (None, onehot_size[1])
# Placeholders
images_tensor = tf.placeholder(tf.float32, shape=img_size)
onehots_tensor = tf.placeholder(tf.float32, shape=onehot_size)
# Dataset
dataset = tf.data.Dataset.from_tensor_slices((images_tensor, onehots_tensor))
# Map function (e.g. augmentation)
if map_fn is not None:
dataset = dataset.map(lambda x, y: (map_fn(x), y), num_parallel_calls=tf.data.experimental.AUTOTUNE)
# Combined shuffle and infinite repeat
dataset = dataset.apply(
tf.data.experimental.shuffle_and_repeat(len(images), None))
dataset = dataset.batch(batch_size)
dataset = dataset.prefetch(1)
# Make the iterator
iterator = dataset.make_initializable_iterator()
init_op = iterator.initializer
next_val = iterator.get_next()
with K.get_session().as_default() as sess:
sess.run(init_op, feed_dict={images_tensor: images, onehots_tensor: onehots})
while True:
inputs, labels = sess.run(next_val)
yield inputs, labels
Les solutions de @Yu_Yang et de Dat-Nguyen fonctionnent bien. Il est également possible de créer l'ensemble de validation du support de solution de @ Yu-Yang pendant la formation, en utilisant des itérateurs pouvant être alimentés et en transmettant le descripteur de l'ensemble de validation en tant que "données" de validation. C'est un peu compliqué mais ça marche.
Vous pouvez également convertir le modèle Keras en un estimateur. Ils prennent en charge les jeux de données suivants:
estimator = tf.keras.estimator.model_to_estimator(keras_model=model,
model_dir=model_dir)
input_name = model.layers[0].input.op.name
def input_fn(dataset):
dataset = dataset.map(lambda X,y: {input_name: X}, y)
return dataset.make_one_shot_iterator().get_next()
train_spec = tf.estimator.TrainSpec(
input_fn=lambda: input_fn(train_set), max_steps=100)
eval_spec = tf.estimator.EvalSpec(
input_fn=lambda: input_fn(test_set))
tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)
Une observation importante de mon expérience récente est d’utiliser tf.keras à la place des keras natives. Fonctionne pour moi tf> 1.12.
J'espère que cela peut aider les autres aussi.
Voici une solution si vous créez un jeu de données TensorFlow en utilisant la bibliothèque Pandas. Notez que ce code ne fonctionnera pas sans tf.reshape()
) car, pour une raison quelconque, les tenseurs provenant de tf.py_func()
ne dispose pas d'informations sur la forme. Cela ne fonctionne donc pas avec Tuple
. Quelqu'un a-t-il une solution de contournement?
def _get_input_data_for_dataset(file_name):
df_input=pd.read_csv(file_name.decode(),usecols=['Wind_MWh'])
X_data = df_input.as_matrix()
return X_data.astype('float32', copy=False)
X_dataset = tf.data.Dataset.from_tensor_slices(file_names)
X_dataset = X_dataset.flat_map(lambda file_name: tf.data.Dataset.from_tensor_slices(
tf.reshape(tf.py_func(_get_input_data_for_dataset,[file_name], tf.float32),[-1,1])))
X_dataset = X_dataset.batch(5)
X_iter = X_dataset.make_one_shot_iterator()
X_batch = X_iter.get_next()
input_X1 = Input(tensor= X_batch ,name='input_X1')
y1 = Dense(units=64, activation='relu',kernel_initializer=tf.keras.initializers.Constant(1),name='layer_FC1')(input_X1)