我正在使用TensorFlow构建深度学习模型。 TensorFlow新手。
由于某些原因,我的模型的批量大小有限,那么这种有限的批量大小将使模型具有很大的差异。
所以,我想用一些技巧来增加批量。我的想法是存储每个小批量的渐变,例如64个小批量,然后将渐变相加,使用这64个迷你批次的训练数据的平均渐变来更新模型的参数。
这意味着对于前63个迷你批次,不要更新参数,在64迷你批次之后,只更新模型的参数一次。
但是由于TensorFlow是基于图形的,有人知道如何实现这个想要的功能吗?
非常感谢。
答案 0 :(得分:9)
我在这里找到了一个解决方案: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)
但是这段代码看起来不是很干净漂亮,有没有人知道如何优化这些代码?
答案 1 :(得分:2)
我遇到了同样的问题并且弄明白了。
首先获取符号渐变,然后将累积的渐变定义为tf.Variables。 (似乎必须在定义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)
您可以参考tensorflow/tensorflow/python/training/optimizer_test.py作为示例用法,尤其是此功能:testGradientsAsVariables()
。
希望它有所帮助。
答案 2 :(得分:1)
以前的解决方案不计算累积梯度的平均值,这可能会导致训练不稳定。我已经修改了上面的代码,应该可以解决这个问题。
# 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))
此代码的使用方式与@weixsong提供的方式相同。
答案 3 :(得分:1)
如果我不再次在sess.run(train_step)中提供feed_dict,则您发布的方法似乎会失败。我不知道为什么需要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})
为了归一化梯度,我知道这只是将累积的gradietn除以batchsize,所以我只添加了一个新运算符
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
*重要说明:忘记所有无效的内容。优化器会失败。
答案 4 :(得分:0)
您可以使用Pytorch代替Tensorflow,因为它允许用户在训练过程中累积梯度
答案 5 :(得分:0)
Tensorflow 2.0兼容答案:与上面提到的weixsong的答案以及Tensorflow Website中提供的解释一致,下面提到的是Tensorflow 2.0版中的累积梯度代码:
def train(epochs):
for epoch in range(epochs):
for (batch, (images, labels)) in enumerate(dataset):
with tf.GradientTape() as tape:
logits = mnist_model(images, training=True)
tvs = mnist_model.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]
loss_value = loss_object(labels, logits)
loss_history.append(loss_value.numpy().mean())
grads = tape.gradient(loss_value, tvs)
#print(grads[0].shape)
#print(accum_vars[0].shape)
accum_ops = [accum_vars[i].assign_add(grad) for i, grad in enumerate(grads)]
optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables))
print ('Epoch {} finished'.format(epoch))
# call the above function
train(epochs = 3)
完整代码可在此Github Gist中找到。