web-dev-qa-db-fra.com

Keras 'model.fit_generator () `se comporte différemment de` model.fit () `

J'ai un énorme jeu de données à fournir à Keras sous la forme d'un générateur car il ne tient pas dans la mémoire. Cependant, avec fit_generator , je ne peux pas reproduire les résultats obtenus lors de la formation habituelle avec model.fit. De plus, chaque époque dure beaucoup plus longtemps.

J'ai implémenté un exemple minimal. Peut-être que quelqu'un peut me montrer où est le problème.

import random
import numpy

from keras.layers import Dense
from keras.models import Sequential

random.seed(23465298)
numpy.random.seed(23465298)

no_features = 5
no_examples = 1000


def get_model():
    network = Sequential()
    network.add(Dense(8, input_dim=no_features, activation='relu'))
    network.add(Dense(1, activation='sigmoid'))
    network.compile(loss='binary_crossentropy', optimizer='adam')
    return network


def get_data():
    example_input = [[float(f_i == e_i % no_features) for f_i in range(no_features)] for e_i in range(no_examples)]
    example_target = [[float(t_i % 2)] for t_i in range(no_examples)]
    return example_input, example_target


def data_gen(all_inputs, all_targets, batch_size=10):
    input_batch = numpy.zeros((batch_size, no_features))
    target_batch = numpy.zeros((batch_size, 1))
    while True:
        for example_index, each_example in enumerate(Zip(all_inputs, all_targets)):
            each_input, each_target = each_example
            wrapped = example_index % batch_size
            input_batch[wrapped] = each_input
            target_batch[wrapped] = each_target
            if wrapped == batch_size - 1:
                yield input_batch, target_batch


if __== "__main__":
    input_data, target_data = get_data()
    g = data_gen(input_data, target_data, batch_size=10)
    model = get_model()
    model.fit(input_data, target_data, epochs=15, batch_size=10)  # 15 * (1000 / 10) * 10
    # model.fit_generator(g, no_examples // 10, epochs=15)        # 15 * (1000 / 10) * 10

Sur mon ordinateur, model.fit termine toujours la 10ème époque avec une perte de 0.6939 et après ca. 2-3 secondes.

Cependant, la méthode model.fit_generator est beaucoup plus longue et termine la dernière époque avec une perte différente _ (0.6931).

Je ne comprends pas en général pourquoi les résultats des deux approches diffèrent. Cela pourrait ne pas sembler très différent, mais je dois être sûr que les mêmes données avec le même réseau produisent le même résultat, indépendamment de la formation conventionnelle ou de l’utilisation du générateur.

Mise à jour: @Alex R. a fourni une réponse à une partie du problème initial (certains problèmes de performances ainsi que l'évolution des résultats à chaque exécution). Comme le problème fondamental demeure, j'ai simplement ajusté la question et le titre en conséquence .

6
wehnsdaefflae

Je ne comprends pas comment la perte peut être instable avec des lots de plus grande taille, car il devrait y avoir moins de fluctuations pour les lots plus importants. Cependant, en regardant la documentation de Keras , la routine fit() ressemble à:

fit(self, x, y, batch_size=32, epochs=10, verbose=1, callbacks=None, validation_split=0.0, 
    validation_data=None, shuffle=True, class_weight=None, sample_weight=None, 
    initial_Epoch=0)

qui a par défaut batch_size=32 et epochs=10. A quoi la fit_generator() ressemble à:

fit_generator(self, generator, steps_per_Epoch, epochs=1, verbose=1,
              callbacks=None, validation_data=None, validation_steps=None, 
              class_weight=None, max_queue_size=10, workers=1,
              use_multiprocessing=False, initial_Epoch=0)

Plus précisément, les "step_per_Epoch" sont définis par:

steps_per_Epoch: Nombre total d’étapes (lots d’échantillons) à générer du générateur avant de déclarer une époque terminée et de démarrer le prochaine époque. Il devrait généralement être égal au nombre de uniques échantillons de votre ensemble de données divisé par la taille du lot.

Donc, pour commencer, il semble que votre fit_generator prélève beaucoup plus d'échantillons que votre routine fit (). Voir ici pour plus de détails.

4
Alex R.

Taille des lots

  • Dans fit, vous utilisez la taille de lot standard = 32. 
  • Dans fit_generator, vous utilisez une taille de lot = 10. 

Keras exécute probablement les mises à jour de poids après chaque lot. Par conséquent, si vous utilisez des lots de tailles différentes, vous pouvez obtenir des gradients différents entre les deux méthodes. Et une fois le poids mis à jour, les deux modèles ne se reverront plus jamais.

Essayez d’utiliser fit avec batch_size=10 ou d’utiliser un générateur avec batch_size=32


Seed problème?

Créez-vous un nouveau modèle avec get_model() pour chaque cas?

Si tel est le cas, les poids dans les deux modèles sont différents et vous obtiendrez naturellement des résultats différents pour les deux modèles. (Ok, vous avez créé une graine, mais si vous utilisez tensorflow, vous faites peut-être face à ce problème )

Sur le long terme, ils vont en quelque sorte converger. La différence entre les deux ne semble pas beaucoup. 


Vérification des données

Si vous n'êtes pas sûr que votre générateur génère les mêmes données que prévu, effectuez une simple boucle dessus et imprimez/comparez/vérifiez les données qu'il génère:

for i in range(numberOfBatches):
    x,y = g.next() #or next(g)
    #print or compare x,y here. 

1
Daniel Möller

Assurez-vous de mélanger vos lots dans votre générateur.

Cette discussion vous suggère d'activer la lecture aléatoire dans votre itérateur: https://github.com/keras-team/keras/issues/2389 . J'ai eu le même problème et cela l'a résolu.

1
Cerno

J'espère que je ne suis pas en retard à la fête. La chose la plus importante que j'ajouterais:

Dans Keras, utiliser fit() convient aux jeux de données plus petits pouvant être chargés en mémoire. Mais dans la pratique, dans la plupart des cas d'utilisation pratique, presque tous les jeux de données sont volumineux et ne peuvent pas être chargés en mémoire en une fois.

Pour les grands ensembles de données, nous devons utiliser fit_generator().

0
prosti

En ce qui concerne la perte, cela est probablement dû à la différence de taille de lot qui a déjà été discutée. 

En ce qui concerne la différence de temps de formation, model.fit_generator() vous permet de spécifier le nombre de "travailleurs". Ce paramètre fait référence au nombre d'instances de votre modèle en cours de formation dans différentes zones de votre jeu de données en même temps. Si l'architecture de votre ordinateur est correctement optimisée, vous devriez pouvoir modifier le paramètre workers à 4 ou 8 et voir une réduction importante du temps de formation. 

0
Lee James