本文介绍在tensorflow
库中,用于动态调整神经网络的学习率的一种方法——指数衰减ExponentialDecay()
策略的参数含义及其具体用法。
在进行神经网络训练时,我们经常需要用到动态变化的学习率,其中指数衰减ExponentialDecay()
策略是我们常用的一种策略。在tensorflow
库中,其完整的用法是tf.keras.optimizers.schedules.ExponentialDecay()
,其中的具体参数如下所示。
tf.keras.optimizers.schedules.ExponentialDecay(
initial_learning_rate, decay_steps, decay_rate, staircase=False, name=None
)
首先,我们需要知道,在用了ExponentialDecay()
策略后,程序将动态调整神经网络训练过程中的学习率,且这一调整是与我们当前训练的step
有关的。具体关于step
的解释,大家可以参考文章epoch、batch、step、iteration等神经网络参数是什么意思?,本文就不再赘述。
如以下代码所示,使用ExponentialDecay()
策略后,程序将依据如下的规律,基于当前训练的step
,以及我们自行设定的几个参数,从而计算得到当前的学习率。其中,函数的返回值就是当前的学习率。
def decayed_learning_rate(step):
return initial_learning_rate * decay_rate ^ (step / decay_steps)
其中,initial_learning_rate * decay_rate ^ (step / decay_steps)
就是当前学习率的计算公式。这里的initial_learning_rate
、decay_rate
以及decay_steps
,就是我们前面提到的ExponentialDecay()
函数的前3
个参数。其中,initial_learning_rate
是我们的初始学习率,decay_rate
是学习率下降的速率,而decay_steps
则是学习率下降的位置(具体含义我们稍后介绍)。此外,ExponentialDecay()
策略还有两个参数,staircase
表示我们在计算(step / decay_steps)
时,是对结果向下取整还是取小数,默认为False
,即取小数结果(具体含义我们稍后介绍);最后一个name
参数,只是对当前这一学习率下降的策略加以命名,一般用不上这个参数,我们就不再介绍了。
由此,我们可以初步知道,ExponentialDecay()
函数的前4
个参数都是用来计算当前的学习率的;且结合我们前面的公式initial_learning_rate * decay_rate ^ (step / decay_steps)
,我们可以知道,随着当前的step
不断增加,decay_rate ^ (step / decay_steps)
是降低的。
接下来,我们直接带入具体的数据,来看一下这几个参数的具体作用。
如下图所示,我们这里有一个训练数据集,其中共有193608
个样本。
同时,我设置了神经网络的batch size
为2048
,那么基于前述提及的文章epoch、batch、step、iteration等神经网络参数是什么意思?,可知在1
个epoch
中,我们对这193608
个样本加以训练,共需要的batch
数目为193608 / 2048
,也就是94.54
,向上取整为95
,相当于需要95
个step
。此外,我设置initial_learning_rate
、decay_rate
以及decay_steps
分别为0.1
、0.95
以及95
,且设置staircase
为True
。如下图所示。
此时,我们就可以对每一个参数的具体含义与作用加以介绍了。首先,我们开始训练神经网络模型,即step
开始从0
逐步增加;但是由于我的staircase
为True
,因此只要指数(step / decay_steps)
是小于1
的,那么都视作0
(因为当前参数设置是对结果向下取整);而由于除了0
以外任何数的0
次方都是1
,因此此时的公式initial_learning_rate * decay_rate ^ (step / decay_steps)
始终等于initial_learning_rate
,也就是一直保持0.1
;只有当step
到达我们设置的decay_steps
之后,指数(step / decay_steps)
才可以成为1
,使得decay_rate
终于产生了效果。而在这里,由于我故意设置decay_steps
为95
,因此按道理只要经过1
个epoch
之后,学习率就会下降——因为前面我们计算过了,在1
个epoch
中需要95
个step
。那么此时,学习率就变为了0.1 * 0.95
。
接下来,我们运行上述代码,训练6
个epoch
,来验证一下学习率的变化是否如同我们的设想。
下图为TensorBoard
中,学习率随着epoch
的变化。这里需要注意,我这里截图的时候开了曲线图的平滑选项,因此应该以浅色的线为准。
上面的图因为不太全,所以或许看不出什么;我们直接将学习率变化情况导出,如下图所示。
其中,图中的step
实际上表示的是epoch
,大家这里理解即可。可以看到,在epoch
为0
时(也就是进行第一个epoch
时),学习率一直为0.1
;而进行到第二个epoch
时——此时我们训练过程的step
就应该是从95
开始,但还不到190
,因此(step / decay_steps)
始终为1
,学习率就是0.1 * 0.95 = 0.095
了(因为数据格式问题,精度稍有差距);随后,进行到第三个epoch
时——此时我们训练过程的step
就应该是从190
开始,但还不到285
,因此(step / decay_steps)
始终为2
,学习率就已经是0.1 * 0.95 * 0.95 = 0.09025
了。
由此可知,假如我将decay_steps
扩大10
倍,使得其为950
,那么在前10
个epoch
时,学习率都不会发生改变,而从第11
个epoch
开始,学习率才会开始衰减。
这里我的参数staircase
设置为True
,因此会出现上述结果;相反的,如果设置为False
,那么计算(step / decay_steps)
时,是对结果取小数,换句话说只要step
发生变化,那么当前对应的学习率也会发生变化,只不过变化的幅度会稍小一些。
由此看到,上述学习率的变化,是符合我们的预期的。当然,上图中最后两个epoch
对应的学习率没有发生变化,这个具体原因我暂时也没搞清楚;不过学习率下降作为一种策略,我们通过上述代码,还是达到了动态调整学习率的需求的。
至此,大功告成。