web-dev-qa-db-fra.com

Comment mettre à jour les paramètres du modèle avec les gradients accumulés?

J'utilise TensorFlow pour créer un modèle d'apprentissage en profondeur. Et nouveau chez TensorFlow.

Pour une raison quelconque, mon modèle a une taille de lot limitée, alors cette taille de lot limitée donnera au modèle une variance élevée.

Donc, je veux utiliser une astuce pour rendre la taille du lot plus grande. Mon idée est de stocker les gradients de chaque mini-lot, par exemple 64 mini-lots, puis de les additionner, puis d'utiliser les gradients moyens de ces 64 mini-lots de données d'apprentissage pour mettre à jour les paramètres du modèle.

Cela signifie que pour les 63 premiers mini-lots, ne mettez pas à jour les paramètres et, après le lot de 64 mini, mettez à jour les paramètres du modèle une seule fois.

Mais comme TensorFlow est basé sur des graphes, est-ce que quelqu'un sait comment implémenter cette fonctionnalité voulue?

Merci beaucoup.

13
weixsong

J'ai trouvé une solution ici: https://github.com/tensorflow/tensorflow/issues/3994#event-766328647

opt = tf.train.AdamOptimizer()
tvs = tf.trainable_variables()
accum_vars = [tf.Variable(tf.zeros_like(tv.initialized_value()), trainable=False) for tv in tvs]                                        
zero_ops = [tv.assign(tf.zeros_like(tv)) for tv in accum_vars]
gvs = opt.compute_gradients(rmse, tvs)
accum_ops = [accum_vars[i].assign_add(gv[0]) for i, gv in enumerate(gvs)]
train_step = opt.apply_gradients([(accum_vars[i], gv[1]) for i, gv in enumerate(gvs)])

Dans la boucle d'entraînement:

while True:
    sess.run(zero_ops)
    for i in xrange(n_minibatches):
        sess.run(accum_ops, feed_dict=dict(X: Xs[i], y: ys[i]))
    sess.run(train_step)

Mais ce code ne semble pas très propre et joli, est-ce que quelqu'un sait comment optimiser ce code?

8
weixsong

J'ai eu le même problème et je l'ai compris. 

Commencez par obtenir les gradients symboliques, puis définissez les gradients accumulés comme étant des variables. (Il semble que tf.global_variables_initializer() doit être exécuté avant de définir grads_accum. Sinon, j'ai des erreurs, je ne sais pas pourquoi.)

tvars = tf.trainable_variables()
optimizer = tf.train.GradientDescentOptimizer(lr)
grads = tf.gradients(cost, tvars)

# initialize
tf.local_variables_initializer().run()
tf.global_variables_initializer().run()

grads_accum = [tf.Variable(tf.zeros_like(v)) for v in grads] 
update_op = optimizer.apply_gradients(Zip(grads_accum, tvars)) 

Lors de la formation, vous pouvez accumuler des dégradés (enregistrés dans gradients_accum) à chaque lot et mettre à jour le modèle après avoir exécuté le 64 e lot:

feed_dict = dict()
for i, _grads in enumerate(gradients_accum):
    feed_dict[grads_accum[i]] = _grads
sess.run(fetches=[update_op], feed_dict=feed_dict) 

Vous pouvez vous référer à tensorflow/tensorflow/python/training/optimizer_test.py pour un exemple d'utilisation, en particulier cette fonction: testGradientsAsVariables()

J'espère que ça aide. 

2
Irene W.

Les solutions précédentes ne calculent pas la moyenne des gradients accumulés, ce qui peut entraîner une instabilité de la formation. J'ai modifié le code ci-dessus, ce qui devrait résoudre ce problème.

# Fetch a list of our network's trainable parameters.
trainable_vars = tf.trainable_variables()

# Create variables to store accumulated gradients
accumulators = [
    tf.Variable(
        tf.zeros_like(tv.initialized_value()),
        trainable=False
    ) for tv in trainable_vars
]

# Create a variable for counting the number of accumulations
accumulation_counter = tf.Variable(0.0, trainable=False)

# Compute gradients; grad_pairs contains (gradient, variable) pairs
grad_pairs = optimizer.compute_gradients(loss, trainable_vars)

# Create operations which add a variable's gradient to its accumulator.
accumulate_ops = [
    accumulator.assign_add(
        grad
    ) for (accumulator, (grad, var)) in Zip(accumulators, grad_pairs)
]

# The final accumulation operation is to increment the counter
accumulate_ops.append(accumulation_counter.assign_add(1.0))

# Update trainable variables by applying the accumulated gradients
# divided by the counter. Note: apply_gradients takes in a list of 
# (grad, var) pairs
train_step = optimizer.apply_gradients(
    [(accumulator / accumulation_counter, var) \
        for (accumulator, (grad, var)) in Zip(accumulators, grad_pairs)]
)

# Accumulators must be zeroed once the accumulated gradient is applied.
zero_ops = [
    accumulator.assign(
        tf.zeros_like(tv)
    ) for (accumulator, tv) in Zip(accumulators, trainable_vars)
]

# Add one last op for zeroing the counter
zero_ops.append(accumulation_counter.assign(0.0))

Ce code est utilisé de la même manière que celui fourni par @weixsong.

0
Ben Price