Как обновить параметры модели с накопленными градиентами?
Я использую TensorFlow для создания модели глубокого обучения. И новый для TensorFlow.
По какой-то причине моя модель имеет ограниченный размер партии, тогда этот ограниченный размер партии сделает модель высокой дисперсией.
Итак, я хочу использовать некоторый трюк, чтобы увеличить размер партии. Моя идея состоит в том, чтобы хранить градиенты каждой мини-партии, например, 64 мини-пакета, а затем суммировать градиенты вместе, использовать средние градиенты этих 64 мини-партий обучающих данных для обновления параметров модели.
Это означает, что для первых 63 мини-пакетов не обновляйте параметры, а после 64-разрядной партии обновите параметры модели только один раз.
Но поскольку TensorFlow основан на графике, кто-нибудь знает, как реализовать эту функцию?
Большое спасибо.
Ответы
Ответ 1
Я нашел решение здесь: 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)])
В цикле обучения:
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)
Но этот код кажется не очень чистым и красивым, кто-нибудь знает, как оптимизировать этот код?
Ответ 2
У меня была такая же проблема, и я только что понял.
Сначала получите символические градиенты, затем определите накопленные градиенты как переменные. (Похоже, что tf.global_variables_initializer()
необходимо запустить до определения grads_accum
. В противном случае я получил ошибки, не знаю почему.)
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))
В процессе обучения вы можете накапливать градиенты (сохраненные в gradients_accum
) в каждом пакете и обновлять модель после запуска 64-го пакета:
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)
Вы можете обратиться к tenorflow/tenorflow/python/training/optimizer_test.py для примера использования, в частности, этой функции: testGradientsAsVariables()
.
Надеюсь, поможет.
Ответ 3
Предыдущие решения не рассчитывают среднее значение накопленных градиентов, что может привести к нестабильности в обучении. Я изменил приведенный выше код, который должен решить эту проблему.
# Fetch a list of our network 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 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))
Этот код используется так же, как и @weixsong.
Ответ 4
Метод, который вы опубликовали, похоже, потерпит неудачу, если я не передам feed_dict снова в sess.run(train_step). Я не знаю, зачем требовать feed_dict, но возможно, что снова запустят все добавления аккумулятора с повторением последнего примера. Вот что я должен был сделать в моем случае:
self.session.run(zero_ops)
for i in range(0, mini_batch):
self.session.run(accum_ops, feed_dict={self.ph_X: imgs_feed[np.newaxis, i, :, :, :], self.ph_Y: flow_labels[np.newaxis, i, :, :, :], self.keep_prob: self.dropout})
self.session.run(norm_acums, feed_dict={self.ph_X: imgs_feed[np.newaxis, i, :, :, :], self.ph_Y: flow_labels[np.newaxis, i, :, :, :], self.keep_prob: self.dropout})
self.session.run(train_op, feed_dict={self.ph_X: imgs_feed[np.newaxis, i, :, :, :], self.ph_Y: flow_labels[np.newaxis, i, :, :, :], self.keep_prob: self.dropout})
И для нормализации градиента я понимаю, что это только разделить накопленный градиент на размер пакета, поэтому я только добавляю новый оператор
norm_accums = [accum_op/float(batchsize) for accum_op in accum_ops]
У кого-то была такая же проблема feed_dict?
* ОБНОВЛЕНИЕ Как я предположил, что это неправильно, он снова запускает весь граф с последним примером в пакете. Этот маленький тест кода, который
import numpy as np
import tensorflow as tf
ph = tf.placeholder(dtype=tf.float32, shape=[])
var_accum = tf.get_variable("acum", shape=[],
initializer=tf.zeros_initializer())
acum = tf.assign_add(var_accum, ph)
divide = acum/5.0
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for i in range(5):
sess.run(acum, feed_dict={ph: 2.0})
c = sess.run([divide], feed_dict={ph: 2.0})
#10/5 = 2
print(c)
#but it gives 2.4, that is 12/5, so sums one more time
Я разобрался, как это решить. Итак, тензор потока имеет условные операции. Я кладу накопление в одну ветку и последнее накопление с нормализацией и обновлением в другую ветку. Мой код беспорядок, но для быстрой проверки я говорю, что я приведу небольшой код в качестве примера использования.
import numpy as np
import tensorflow as tf
ph = tf.placeholder(dtype=tf.float32, shape=[])
#placeholder for conditional braching in the graph
condph = tf.placeholder(dtype=tf.bool, shape=[])
var_accum = tf.get_variable("acum", shape=[], initializer=tf.zeros_initializer())
accum_op = tf.assign_add(var_accum, ph)
#function when condition of condph is True
def truefn():
return accum_op
#function when condtion of condph is False
def falsefn():
div = accum_op/5.0
return div
#return the conditional operation
cond = tf.cond(condph, truefn, falsefn)
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for i in range(4):
#run only accumulation
sess.run(cond, feed_dict={ph: 2.0, condph: True})
#run acumulation and divition
c = sess.run(cond, feed_dict={ph: 2.0, condph: False})
print(c)
#now gives 2
* ВАЖНОЕ ПРИМЕЧАНИЕ: забудьте, что все не работает. Оптимизаторы сбрасывают неудачу.
Ответ 5
Вы можете использовать Pytorch вместо Tensorflow, так как он позволяет пользователю накапливать градиенты во время обучения.