神经网络训练细节系列笔记:
这一篇将介绍如何在训练中调整学习速率,以及对超参数优化的问题。
简单描述下神经网络的训练的几个步骤:
Step 1 :数据预处理
在神经网络训练细节(数据预处理、权重初始化)这篇博客中有详细的介绍数据预处理的几种方式。
Step 2:选择你要训练的神经网络的结构
以上图为例是一个两层的神经网络,我们选择32*32*3的CIFAR-10图片作为输入数据,10个分类,隐藏层中有50个神经元。
如果我们想要保证预测结果正确就是神经网络正常工作,第一件事就是初始化一个两层的神经网络,神经网络的权重和偏置值都是最简单的初始值:
def init_two_layer_model(input_size,hidden_size,output_size):
model={}
model['W1'] = 0.0001 * np.random.randn(input_size, hidden_size)
model['b1'] = np.zeros(hidden_size)
model['W1'] = 0.0001 * np.random.randn(hidden_size, output_size)
model['b1'] = np.zeros(output_size)
return model
Step 3:检查loss的合理性
因为这个网络非常小,我们可以使用这种简单的初始化创建一个简单的样咧:
在这里,我们做了一个尝试,取消了正则化,以确保损失是正确的。在之前的博客中有提过,比如我有10个类别和CIFAR-10,同时使用softmax分类器,期待的损失值是-log(1/10),因为这是损失值的表达式。正确结果应该是2.3,所以输入一个值,然后得到的损失值是2.3。这样我们就知道神经网络给了一个基于10个类别的弥散性分布(diffuse distribution)。因为神经网络什么都不知道,它只是刚被初始化而已。
接下来要检查的是启动正则化的loss,我们预期的loss值是上升的:
由结果可知,是正确的,那么这一项就检查完了。
Step 4:完整性检查
接着,我们要进行的是一个非常好的完整性检查。取一些数据,比如取20个训练样本和20个标签,保证我们的模型基于这一小部分数据训练。
由图可以看出,训练到最后的cost降为0,对于这一小部分数据可以得到100%的准确度,并且说明反向传播、学习速率和w值的更新都正常,因此对于这一小部分的数据达到过饱和,效果不错。如果做不到过饱和,说明你的实现过程有问题。
接着,开始考虑扩大训练集,然后找到合适的学习速率,这项工作是一定要做的,不能凭肉眼看出来,只能通过求出合适的学习速率。首先,先选一个小的学习速率,eg:1e-6,然后实验结果可以发现损失值非常小非常小的往下减少,所以这个学习速率可能太小了,基本上没有改变,也有可能是其他原因造成的,因为loss有问题,可能有一百万种原因导致。
这是一个很有意思的例子,由图中的结果可以看出,loss值基本没有下降,但是准确度(train蓝色框子)却从一开始的10%激增到了20%,Why?
是因为在刚开始训练时,这个损失会有一些小变动,但到训练到最后,你的损失值还是很大的,也就说,经过神经网络的训练,损失值变小的很慢,这个时候我们可能就需要提高我们的学习速率,这是一种很常见的情形。
刚才是因为学习速率低,导致loss值降不下来,现在来看另一个极端,把学习速率改成一百万:
损失函数原地爆炸,boom shakalaka! 一百万应该是太大了。
我们尝试把学习速率限制在一个小区间,使得损失函数在训练中很快的变得越来越小直至收敛:[1e-3…1e-5]
在这一部分中,我们会用一个粗糙到细化的思想,为我们的网络找到最好的超参数。
Step 1 :我们需要有个大概的参数区间;
Step 2 :对学习速率的区间做一个粗略的研究,然后在原有的区间内选出表现较好的一个小区间;
我们一遍一遍重复这个步骤,让我们选的区间越来越窄,最后选出一个表现最好的参数。在代码中,这个任务在一开始就被分解成了一个循环中的很多步,在每一个循环中,对正则化系数和学习速率取样,然后进行训练并得到结果。
下图执行Step 1,有一个大概的参数区间:
得到的结果就是这些正确率所对应的超参数,红框圈出来的正确率已经高达40%到50%,从这个结果可以看出,学习速率和正则化系数在什么范围内的工作会相对比较好。另外提一句,当我们优化正则化系数和学习速率时,最好是去从对数空间中取样,因为这两个变量在进行反向传播算法的时候是相乘的。在我们的例子中学习速率的取样是在10的n次方,n在[-3,-6]这个区间中取样,正则化系数是10的m次方,m在[-5,5]这个区间中取样。而我们不希望是普通的0,1,100之类的数,因为这些取样中的大部分都会在一个效果很差额区域内,并且学习速率是一个乘法交互。
继续,进行Step 2,搜索最好的参数:
进行一遍一遍的循环操作……
但我们还是要注意一下,有时候会得到像这样的结果,如上图中的第三个红框,53%已经算是一个很好的结果了,得到的学习速率差不多是1e-3,正好是上一轮优化中找到的学习速率,也就是说这一轮优化中得到的值和上一轮一样,这并不是一个好消息,因为有可能这个学习速率并不是最好的,所以我们需要确定它是不是全局最好的(或者是一个局部最好值),所以要继续调整取值范围;另一方面,对于正则化参数,我们能观察到1e-4工作得很好,所以相对上一轮在一个更好的范围上。
另外,我们需要注意的是,这些学习速率和正则化参数值都是在一个区间内随机取样,有时候还会使用一种叫做网格搜索的方式:
网格搜索的区别在于,不再像前面提的那样在一定范围内随机取样,而是使用一个确定的补偿,在正则化系数和学习速率上每一步取一个值,所以用两个循环,分别负责这两个参数取值。实际上,这是一个很差的思路,不如随机取样效果好。因为用一个确定区间取值,并不确定是否会取到整个区域内最好的值。一般情况下,随机取样能带来更好的优化结果。
所以,我们经常优化选择的超参数就是学习速率
更新方式(后面会说),还有正则化参数等。形象的表示下神经网络的调参类比于调音师…..haha
我们用工作集群来举个例子,在这个集群中有很多个机器,我们可以把训练任务分配给这些机器,给机器下达指令让他们训练,并返回训练结果。不同的机器训练时使用相同的损失函数,不同的机器使用不同的超参数做训练,然后我们通过观察损失函数图像,来确定哪些超参数样本工作的更好。通过不断循环地随机在区间中取样这一流程,最后能得到一组性能很好的参数。
再来聊一聊,在优化过程中损失函数的作用,损失函数可以通过很多种不同的方式表现出来,但是需要学会看懂这些不同的形式所代表的意思:
举个例子:
这个函数图像并不像我们习惯的那样指数化,看起来有点接近于线性函数,这个函数的走势对比上面的学习速率图可以看出,可能我们选择的学习速率太低了。当然这只是个猜测,并不一定是学习速率,应该说是在这种情况下,可以试一试更高的学习速率。比如我们还会看到一些有趣的图像:
一开始会有一个很高的平稳值,在某一个时间点,它决定开始优化了,于是就开始下降….这种情况的发生,是因为初始值选的不好,一个错误的初始值会让梯度在一开始就接近零,所以在一开始损失函数基本没有下降,直到运动到了某个点的梯度有明显变化了才开始下降。
还有一些很奇葩的结果:
这个是训练一个强化学习agent,强化学习的问题是:你并没有一个稳定的数据集,在强化学习中,客户端是在不停的与环境进行数据交互的,如果学习方针变化了或者观察数据集的不同地方的话,会得到和以前不一样的数据分布。损失函数会突然变高,因为客户端从环境中接收到跟前面完全不一样的数据。
最后,还有一点是值得注意的,在训练的过程中,不要只记得看损失函数,准确率是很重要的,因为准确率可以解释很多东西,由下图可以看出在训练集中的准确率明显变得越来越高,而在验证集中的准确率一直在一个很低的状态,这个明显的缺口就能提示我们,在这个例子中很可能出现了过拟合的现象。虽然也不确定,但至少下一步要做的是尝试提高正则化系数。