55 votes

Comment combiner correctement les API de jeux de données de TensorFlow et Keras?

Keras' fit_generator() modèle méthode attend un générateur qui produit des n-uplets de la forme (entrée, objectifs), où les deux éléments sont des tableaux NumPy. La documentation semble impliquer que si je enroulez simplement un Dataset itérateur dans un générateur, et assurez-vous de convertir les Tenseurs de tableaux NumPy, je devrais être bon pour aller. Ce code, cependant, me donne 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

Assez étrangement, l'ajout d'une ligne contenant next(datagen) directement après l'endroit où j'initialise datagen provoque le code fonctionne très bien, avec pas d'erreurs.

Pourquoi mon code ne fonctionne pas? Pourquoi est-il commencer à travailler lorsque j'ajoute que la ligne de mon code? Est-il un moyen plus efficace d'utiliser TensorFlow Dataset de l'API avec Keras qui n'implique pas de conversion de Tenseurs de tableaux NumPy et de retour?

61voto

Dat Nguyen Points 527

Mise À Jour Le 09 Juin 2018

  • À partir de Tensorflow 1.9, on peut passer tf.data.Dataset objet directement dans keras.Model.fit() et il serait agissent de façon similaire à l' fit_generator.
  • Un exemple complet peut être trouvé sur ce gist.
# 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 objet iterable tf.data.Dataset.
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

Vieille Solution:

En plus de @Yu-Yang de la réponse, vous pouvez également modifier tf.data.Dataset pour devenir un générateur pour l' 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)

41voto

Yu-Yang Points 10051

En effet, il existe un moyen plus efficace d'utiliser Dataset sans avoir à convertir les tenseurs dans les tableaux numpy. Cependant, il n'est pas (encore?) sur la documentation officielle. À partir de la note de version, c'est une fonctionnalité introduite dans Keras 2.0.7. Vous pourriez avoir à installer keras>=2.0.7 pour 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:

  1. L'approvisionnement de l' tensor argument de la Input de la couche. Keras va lire les valeurs de ce tenseur, et de l'utiliser comme entrée pour ajuster le modèle.
  2. L'approvisionnement de l' target_tensors argument Model.compile().
  3. N'oubliez pas de convertir à la fois x et y, en float32. Dans des conditions normales d'utilisation, Keras permettra de faire la conversion pour vous. Mais maintenant, vous aurez à faire vous-même.
  4. La taille des lots est spécifié lors de la construction de Dataset. Utiliser steps_per_epoch et epochs pour déterminer le moment de l'arrêt de 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 sont lues à partir de tenseurs.

3voto

geometrikal Points 551

Les autres réponses sont bonnes, cependant, il est important de noter que l'utilisation d' from_tensor_slices directement avec de grands tableaux numpy peut rapidement remplir votre mémoire que, autant que je me souvienne, les valeurs sont copiées dans le graphique que l' tf.constants. Dans mon expérience, cela provoquera un échec silencieux, où la formation démarre mais pas d'amélioration etc perte se produit.

Une meilleure façon est d'utiliser des espaces réservés. E. g. voici mon code pour créer un générateur d'images et de leur onehot objectifs:

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

2voto

MiniQuark Points 8927

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)
 

0voto

siby Points 178

Voici une solution si vous créez un jeu de données TensorFlow à l'aide de 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() n'ont pas d'informations de forme. Donc, cela ne fonctionne pas avec tuple . Quelqu'un at-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)
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X