Comment définir le taux d'apprentissage adaptatif pour GradientDescentOptimizer?

104

J'utilise TensorFlow pour former un réseau neuronal. Voici comment j'initialise le GradientDescentOptimizer:

init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)

mse        = tf.reduce_mean(tf.square(out - out_))
train_step = tf.train.GradientDescentOptimizer(0.3).minimize(mse)

Le problème ici est que je ne sais pas comment définir une règle de mise à jour pour le taux d'apprentissage ou une valeur de décroissance pour cela.

Comment puis-je utiliser un taux d'apprentissage adaptatif ici?

Afficher un nom
la source
3
C'est une bonne habitude d'initialiser toutes les variables après avoir spécifié votre optimiseur car certains optimiseurs comme AdamOptimizer utilisent ses propres variables qui doivent également être initialisées. Sinon, vous risquez d'obtenir une erreur qui ressemble à ceci:FailedPreconditionError (see above for traceback): Attempting to use uninitialized value beta2_power
JYun
J'obtiens l'erreur mentionnée ci-dessus, lorsque j'essaie de définir un nouveau taux d'apprentissage dans Tensorflow par tf.train.GradientDescentOptimizer(new_lr).minimize(loss). Il semble que la définition d'un nouveau taux d'apprentissage nécessite d'initialiser le modèle avec les variables déjà entraînées. Mais je ne peux pas comprendre comment faire cela.
Siladittya

Réponses:

193

Tout d'abord, tf.train.GradientDescentOptimizerest conçu pour utiliser un taux d'apprentissage constant pour toutes les variables dans toutes les étapes. TensorFlow fournit également des optimiseurs adaptatifs prêts à l'emploi, y compris le tf.train.AdagradOptimizeret le tf.train.AdamOptimizer, et ceux-ci peuvent être utilisés comme remplacements instantanés.

Cependant, si vous souhaitez contrôler le taux d'apprentissage avec une descente de gradient autrement vanille, vous pouvez profiter du fait que l' learning_rateargument du tf.train.GradientDescentOptimizerconstructeur peut être un Tensorobjet. Cela vous permet de calculer une valeur différente pour le taux d'apprentissage à chaque étape, par exemple:

learning_rate = tf.placeholder(tf.float32, shape=[])
# ...
train_step = tf.train.GradientDescentOptimizer(
    learning_rate=learning_rate).minimize(mse)

sess = tf.Session()

# Feed different values for learning rate to each training step.
sess.run(train_step, feed_dict={learning_rate: 0.1})
sess.run(train_step, feed_dict={learning_rate: 0.1})
sess.run(train_step, feed_dict={learning_rate: 0.01})
sess.run(train_step, feed_dict={learning_rate: 0.01})

Vous pouvez également créer un scalaire tf.Variablecontenant le taux d'apprentissage et l'attribuer à chaque fois que vous souhaitez modifier le taux d'apprentissage.

mrry
la source
Très bonne réponse. La même technique peut-elle être utilisée pour le découpage en dégradé? tf.clip_by_normn'accepte pas de tenseur pour la norme de clip, alors que diriez-vous de faire [(tf.minimum(gv[0], ct), gv[1]) for gv in optimizer.compute_gradients(cost, vars)], oùct = tf.placeholder('float32', shape=[])
richizy
Cela devrait fonctionner, oui. (Bien que regardant tf.clip_by_norm, la seule chose qui l'empêche d'accepter un tenseur comme entrée est le constant_op.constant(1.0 / clip_norm). Remplacer cette expression par la math_ops.inv(clip_norm)ferait fonctionner avec une entrée d'espace réservé (ou tout autre tenseur).)
mrry
@mrry J'ai fait ce que vous avez dit et certains comment la vitesse d'entraînement est beaucoup plus lente. Est-ce prévu s'il vous plaît?
tnq177 le
89

Tensorflow fournit une op pour appliquer automatiquement une décroissance exponentielle à un tenseur de taux d'apprentissage: tf.train.exponential_decay. Pour un exemple d'utilisation, voir cette ligne dans l'exemple de modèle convolutif MNIST . Ensuite, utilisez la suggestion de @ mrry ci-dessus pour fournir cette variable comme paramètre learning_rate à l'optimiseur de votre choix.

L'extrait clé à regarder est:

# Optimizer: set up a variable that's incremented once per batch and
# controls the learning rate decay.
batch = tf.Variable(0)

learning_rate = tf.train.exponential_decay(
  0.01,                # Base learning rate.
  batch * BATCH_SIZE,  # Current index into the dataset.
  train_size,          # Decay step.
  0.95,                # Decay rate.
  staircase=True)
# Use simple momentum for the optimization.
optimizer = tf.train.MomentumOptimizer(learning_rate,
                                     0.9).minimize(loss,
                                                   global_step=batch)

Notez le global_step=batchparamètre à minimiser. Cela indique à l'optimiseur d'incrémenter utilement le paramètre 'batch' pour vous chaque fois qu'il s'entraîne.

dga
la source
3
Habituellement, la variable que vous appelez batchest appelée global_stepet il existe plusieurs fonctions pratiques, une pour la créer tf.train.create_global_step()(qui crée simplement un entier tf.Variableet l'ajoute à la tf.GraphKeys.GLOBAL_STEPcollection) et tf.train.get_global_step().
Lenar Hoyt
86

L'algorithme de descente de gradient utilise le taux d'apprentissage constant que vous pouvez fournir lors de l'initialisation . Vous pouvez passer différents taux d'apprentissage de la manière indiquée par Mrry.

Mais au lieu de cela, vous pouvez également utiliser des optimiseurs plus avancés qui ont un taux de convergence plus rapide et s'adaptent à la situation.

Voici une brève explication basée sur ma compréhension:

  • l'élan aide SGD à naviguer dans les directions pertinentes et adoucit les oscillations dans le non pertinent. Il ajoute simplement une fraction de la direction de l'étape précédente à une étape actuelle. Cela permet une amplification de la vitesse dans la bonne direction et adoucit l'oscillation dans les mauvaises directions. Cette fraction se situe généralement dans la plage (0, 1). Il est également logique d'utiliser l'élan adaptatif. Au début de l'apprentissage, un grand élan ne fera qu'entraver votre progression, il est donc logique d'utiliser quelque chose comme 0,01 et une fois que tous les gradients élevés ont disparu, vous pouvez utiliser un élan plus grand. Il y a un problème avec l'élan: lorsque nous sommes très proches de l'objectif, notre élan dans la plupart des cas est très élevé et il ne sait pas qu'il devrait ralentir. Cela peut le faire rater ou osciller autour des minima
  • Le gradient accéléré nesterov surmonte ce problème en commençant à ralentir tôt. Dans l'élan, nous calculons d'abord le gradient, puis faisons un saut dans cette direction amplifié par l'élan que nous avions précédemment. NAG fait la même chose mais dans un autre ordre: dans un premier temps, nous faisons un grand saut en fonction de nos informations stockées, puis nous calculons le gradient et effectuons une petite correction. Ce changement apparemment hors de propos donne des accélérations pratiques significatives.
  • AdaGrad ou gradient adaptatif permet au taux d'apprentissage de s'adapter en fonction des paramètres. Il effectue des mises à jour plus importantes pour les paramètres peu fréquents et des mises à jour plus petites pour les paramètres fréquents. Pour cette raison, il convient parfaitement aux données rares (PNL ou reconnaissance d'image). Un autre avantage est qu'il élimine fondamentalement la nécessité d'ajuster le taux d'apprentissage. Chaque paramètre a son propre taux d'apprentissage et en raison des particularités de l'algorithme, le taux d'apprentissage diminue de manière monotone. Cela pose le plus gros problème: à un moment donné, le taux d'apprentissage est si faible que le système cesse d'apprendre
  • AdaDelta résout le problème de la diminution monotone du taux d'apprentissage dans AdaGrad. Dans AdaGrad, le taux d'apprentissage a été calculé approximativement comme un divisé par la somme des racines carrées. À chaque étape, vous ajoutez une autre racine carrée à la somme, ce qui entraîne une diminution constante du dénominateur. Dans AdaDelta, au lieu de sommer toutes les racines carrées passées, il utilise une fenêtre glissante qui permet à la somme de diminuer. RMSprop est très similaire à AdaDelta
  • Adam ou élan adaptatif est un algorithme similaire à AdaDelta. Mais en plus de stocker les taux d'apprentissage pour chacun des paramètres, il stocke également les changements d'élan pour chacun d'eux séparément

    A quelques visualisations : entrez la description de l'image ici entrez la description de l'image ici

Salvador Dali
la source
2
Pour comparer les différents optimiseurs dans TensorFlow, jetez un œil au cahier ipython suivant: github.com/vsmolyakov/experiments_with_python/blob/master/chp03/… for
Vadim Smolyakov
Les optimiseurs plus avancés ne doivent pas être pris "à la place" mais en plus, voir stats.stackexchange.com/questions/200063/…
Dima Lituiev
@DimaLituiev pouvez-vous utiliser deux optimiseurs en même temps? Si non, vous utilisez l'optimiseur1 au lieu de l'optimiseur2.
Salvador Dali
1
ce n'est pas ce que je dis, et ce n'était pas la question ici. Vous suggérez d'utiliser des optimiseurs avancés au lieu d'un taux d'apprentissage adaptatif. Je dis que vous préférez utiliser des optimiseurs avancés en plus du taux d'apprentissage adaptatif
Dima Lituiev
7

À partir de la documentation officielle de tensorflow

global_step = tf.Variable(0, trainable=False)
starter_learning_rate = 0.1
learning_rate = tf.train.exponential_decay(starter_learning_rate, global_step,
                                       100000, 0.96, staircase=True)

# Passing global_step to minimize() will increment it at each step.
learning_step = (
tf.train.GradientDescentOptimizer(learning_rate)
.minimize(...my loss..., global_step=global_step))
Prakash Vanapalli
la source
0

Si vous souhaitez définir des taux d'apprentissage spécifiques pour des intervalles d'époques comme 0 < a < b < c < .... Ensuite, vous pouvez définir votre taux d'apprentissage comme un tenseur conditionnel, conditionnel à l'étape globale, et le fournir normalement à l'optimiseur.

Vous pouvez y parvenir avec un tas d' tf.condinstructions imbriquées , mais il est plus facile de construire le tenseur de manière récursive:

def make_learning_rate_tensor(reduction_steps, learning_rates, global_step):
    assert len(reduction_steps) + 1 == len(learning_rates)
    if len(reduction_steps) == 1:
        return tf.cond(
            global_step < reduction_steps[0],
            lambda: learning_rates[0],
            lambda: learning_rates[1]
        )
    else:
        return tf.cond(
            global_step < reduction_steps[0],
            lambda: learning_rates[0],
            lambda: make_learning_rate_tensor(
                reduction_steps[1:],
                learning_rates[1:],
                global_step,)
            )

Ensuite, pour l'utiliser, vous devez savoir combien d'étapes d'entraînement il y a dans une seule époque, afin que nous puissions utiliser l'étape globale pour basculer au bon moment, et enfin définir les époques et les taux d'apprentissage que vous souhaitez. Donc, si je veux les taux d'apprentissage [0.1, 0.01, 0.001, 0.0001]pendant les intervalles d'époque de [0, 19], [20, 59], [60, 99], [100, \infty]respectivement, je ferais:

global_step = tf.train.get_or_create_global_step()
learning_rates = [0.1, 0.01, 0.001, 0.0001]
steps_per_epoch = 225
epochs_to_switch_at = [20, 60, 100]
epochs_to_switch_at = [x*steps_per_epoch for x in epochs_to_switch_at ]
learning_rate = make_learning_rate_tensor(epochs_to_switch_at , learning_rates, global_step)
Ben
la source