这篇文章主要介绍一些在深度学习模型训练时的一些小技巧。
warmup可以理解为预热、热身的意思,它的思想是一开始以较小的学习率去更新参数,然后逐步提高学习率。
这样的好处是:
有助于减缓模型在初始阶段对mini-batch的提前过拟合现象,保持分布的平稳;有助于保持模型深层的稳定性。 # 线性warmup # num_warmup_steps:warmup的步数 # init_lr:预设的学习率 global_step = tf.train.get_or_create_global_step() global_steps_int = tf.cast(global_step, tf.int32) warmup_steps_int = tf.constant(num_warmup_steps, dtype=tf.int32) global_steps_float = tf.cast(global_steps_int, tf.float32) warmup_steps_float = tf.cast(warmup_steps_int, tf.float32) warmup_percent_done = global_steps_float / warmup_steps_float warmup_learning_rate = init_lr * warmup_percent_done is_warmup = tf.cast(global_steps_int < warmup_steps_int, tf.float32) learning_rate = ( (1.0 - is_warmup) * learning_rate + is_warmup * warmup_learning_rate)为什么需要学习率递减呢?因为当我们的模型在接近最优点的时候,过大的学习率会导致一直在最优点附近动荡,难以找到最优点,甚至可能会逃离,这个时候就需要以较小的学习率才能找到最优点,换另一种说法:这个时候我们的网络已经趋于稳定了,过大的学习率会破坏网络的稳定性。
适用场景:通过上述的理解,当我们的模型在前期loss能够正常下降,但在后期难以收敛的情况下,就可以派上用场了。
global_step = tf.train.get_or_create_global_step() # learning_rate:初设的学习率 # num_train_steps:总的训练次数 # end_learning_rate:学习率最终降为多少 # power:决定递减的幅度 # cycle:当学习率递减为end_learning_rate时,是否开始新的一轮递减 learning_rate = tf.train.polynomial_decay( learning_rate, global_step, num_train_steps, end_learning_rate=0.0, power=1.0, cycle=False)根据官方API的解释,递减的公式为:
global_step = min(global_step, decay_steps) decayed_learning_rate = (learning_rate - end_learning_rate) * (1 - global_step / decay_steps) ^ (power) + end_learning_rate这里的decay_steps就是总的递减步数,一般是训练的总步数即num_train_steps。
看到这里, 相信有人就有疑问了:warmup和学习率递减,这不是自相矛盾吗?
确实,它们两者是完全相反的思路,但是它们有各自不同的适用场景。
并且,在实际情况中,两者是可以结合一起使用的,例如大名鼎鼎的Bert模型就是这么干的。但是,warmup一般在前10%-20%的训练过程中,所以在前期对学习率影响较大的还是warmup,即专注于warmup,后期则是专注于lr_decar。
这样即可以缓解初设阶段对局部数据的过拟合,也能缓解后期的动荡,提高网络的稳定性,增加模型收敛的可能性
适用场景:当你的梯度很容易出现爆炸的情况下,这个时候就可以考虑使用梯度裁剪了。
# 获取所有待更新参数 tvars = tf.trainable_variables() # 计算所有参数的梯度 # loss:your loss grads = tf.gradients(loss, tvars) # 对梯度进行裁剪 (grads, _) = tf.clip_by_global_norm(grads, clip_norm=1.0) optimizer = tf.train.GradientDescentOptimizer(0.1) # 进行梯度下降 train_op = optimizer.apply_gradients( zip(grads, tvars))根据官方API的解释,梯度裁剪的公式为:
grads[i] = grads[i] * clip_norm / max(global_norm, clip_norm) global_norm = sqrt(sum([l2norm(t)**2 for t in grads])) l2norm(t) = sqrt(sum(t ** 2))