tvars = tf.trainable_variables() # 得到所有训练的变量,这些变量才会有梯度
all_grads = tf.gradients(self.loss, tvars) # 求得loss对所有变量的梯度
grads, _ = tf.clip_by_global_norm(all_grads, grad_clip) # 将所有梯度乘以比例
train_op = tf.train.AdamOptimizer(learning_rate) # 优化器
self.optimizer = train_op.apply_gradients(zip(grads, tvars)) # 这个优化器计算的所有变量都进行梯度裁剪
Gradient Clipping的引入是为了处理gradient explosion或者gradients vanishing的问题。当在一次迭代中权重的更新过于迅猛的话,很容易导致loss divergence。Gradient Clipping的直观作用就是让权重的更新限制在一个合适的范围。
具体的细节是
1.在solver中先设置一个clip_gradient
2.在前向传播与反向传播之后,我们会得到每个权重的梯度diff,这时不像通常那样直接使用这些梯度进行权重更新,而是先求所有权重梯度的平方和sumsq_diff,如果sumsq_diff > clip_gradient,则求缩放因子scale_factor = clip_gradient / sumsq_diff。这个scale_factor在(0,1)之间。如果权重梯度的平方和sumsq_diff越大,那缩放因子将越小。
3.最后将所有的权重梯度乘以这个缩放因子,这时得到的梯度才是最后的梯度信息。
这样就保证了在一次迭代更新中,所有权重的梯度的平方和在一个设定范围以内,这个范围就是clip_gradient.
还有另一个裁剪函数
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
gvs = optimizer.compute_gradients(loss)
capped_gvs = [(tf.clip_by_value(grad, -1., 1.), var) for grad, var in gvs]
train_op = optimizer.apply_gradients(capped_gvs)
引用
tf.clip_by_global_norm理解 写的非常好