D2DL笔记_6
模型选择、过拟合和欠拟合
模型选择:训练集、验证集
用训练集训练模型得到相关权重参数,可以用验证集初步验证参数后再进行训练调整,但是测试集只有最后才能用来输出模型的结果,过程中不允许测试集数据加入训练。
这样主要是为了得到真实结果,评估模型的泛化性能,即模型不能只在训练集上有很好的结果,它要在拿出去应用的时候都能有好的表现。
拿一个预测猫狗的例子来解释过拟合与欠拟合:
过拟合:将训练集中的一些噪声或非必要信息学习成模型的特征,比如我只需要模型能够分辨是不是狗,由于数据集中只有哈士奇和阿拉斯加,导致模型认为只有黑白毛色的大型犬才是狗,这就是拟合“过了”。
欠拟合:同理,也就是训练集数据太少或是训练模型过于简单,导致模型没学到猫和狗的足够的特征,也就是没拟合到位,把猫认为是狗。
一般来说,数据集过小或模型过简单,会造成欠拟合,对不大的数据集使用很深的模型(或者跑很多个epoch)就会造成过拟合。
所以,首先就是要求模型要足够大足够深,然后争取使用更大的训练集,想比欠拟合而言,在此基础上的一定程度的过拟合是可以接受的。
这就涉及到模型的复杂度和数据的复杂度问题,后面会说如何控制模型的复杂度(实际上就是调参,调整超参数,完全看手感)。
这一节就不加代码实现内容了,解释起来就是上面那些东西。
权重衰退
权重衰退是最常见也是最传统的处理过拟合的方法。
解决过拟合一般来说有两个方向的思路,一个是降低模型的体量,或者说容量,常见方法是减少模型的层数,训练的epoch数,或者减少参数。
另一种方向就是缩小权重的取值范围,把权重能够拟合的数值限制在一个较小的范围内,从而在一定程度上防止模型拟合一些比较夸张的噪声,这种方法就是权重衰退。
权重衰退的方法:
使用均方范数作为硬性限制:直接使用均方范数限制权重w的取值范围
使用均方范数作为硬性限制:在原来损失函数的基础上加上一个均方误差形式的罚项(penalty),通过罚项(因为是均方范数形式,也叫正则项)的系数来控制惩罚力度的大小,这也是常说的正则化,可以让模型拟合的曲线更平滑。
这样的话,在求梯度的时候,权重w就会减去一个学习率和罚项系数的乘积,每一次梯度更新的时候,权重相比没有罚项的时候会少更新一点,就是所谓的权重衰退。
权重衰退的实现:
人工生成一个数据集,数据如下:
首先调用包,只取20个训练数据,因为训练数据越小,模型越复杂的情况下,就会更容易过拟合(跟测试集大小没关系,测试集只是期末考试而已,不参加模型的训练)。
初始化模型参数:
定义L2范数罚项,为了方便后面对超参数(罚项系数)进行修改,所以没有把系数加进来:
训练函数:
试验一下不同的lambda(罚项系数)有什么不同的效果:
λ = 0:
可以看到过拟合很严重,模型在训练的时候疯狂拟合,损失也一直有下降,但是测试的时候损失基本不动,说明模型后面拟合到的那些特征对测试集来说没有任何帮助。
λ = 3:
可以看到train和test之间的差距还是蛮大的,说明过拟合还是存在,但是此时训练损失在下降(学到了更多的特征)的时候,测试集的损失也是有下降的,说明新拟合到的特征在测试集中也具备,也就提高了模型的泛化性能。而进一步学到了50个epoch左右,训练的损失已经不再降低了,说明这个时候的罚项已经使得权重w的取值范围接近了0,此时模型没有剩余的空间去拟合了,过拟合也就被控制住了(但因为数据集小而无法避免),如果再训练更多的epoch的话(或者更大的λ,但并不是越大越好),训练的效果还能更好。
基本在λ = 15的时候,过拟合方面的表现达到最佳,再提高λ,不会再显著增强模型的性能。
丢弃法(Dropout)
在训练数据中加入随机噪音进行扰动再来训练,从而强化训练过程使模型对测试中的扰动鲁棒,相当于在训练中加入一种正则,因为同是限制权重范围避免过拟合的方法,所以在推理预测的过程中,是不加入dropout的。
此外,在加入噪音后,要保持数据的期望与原始数据相同。通常将dropout作用在隐藏全连接层的输出上。
丢弃法对每个元素进行如下扰动:
这就意味这采取一定的概率p来将某些元素置0,做到所谓的“丢弃”,从而控制模型的复杂度,所以,其中丢弃的概率p是一个超参数。
数值稳定性
梯度爆炸、梯度消失:
梯度爆炸的问题:
1.值超出值域,即浮点数的范围;2.对学习率敏感,学习率大了,就意味着更大的参数,更大的梯度,梯度就会爆炸,学习率太小,训练速度就会很慢。
梯度消失的问题:
根据一些激活函数,比如sigmoid函数,函数输入过大或过小时,梯度就会变得很小,n个层累乘时值就会趋近于0,此时无论如何修改学习率,训练都没有结果,学不到特征且训练速度慢,梯度消失也会限制神经网络的深度。
解决办法:
限制梯度值在一定合理的范围内;
将乘法变为加法,消除累乘带来的数值过大或过小,如ResNet和LSTM;
归一化,梯度归一化,梯度裁剪,将很小的梯度值扩大,将很大的梯度值缩小;
合理的初始权重和激活函数的选用。
让每层输出的方差都是同一个常数:
对每一层的多个输出,都保持均值为零,方差为同一特定常数,这样无论网络多深,都可以使结果在一个比较合理的范围内。
权重初始化:
在合理值区间里随机初始参数,因为训练刚开始的时候权重特别容易不稳定,变化过大意味着过大的梯度,可能导致权重变大,给后续学习增加难度,所以先给一个权重取值的范围,初始化一下,但是也不能在接近最优解的区间内初始化,因为最优解附近比较平滑,梯度很小,容易梯度消失。
要每一层的结果均值都为零,方差都为同一常数,需要满足一定的条件(需要概率论推导)。基于这一点选择激活函数,通过tanh或者ReLU,抑或是调整之后的sigmoid,即4×sigmoid-2(泰勒展开后可以明显看出调整后的激活函数经过原点且在邻域内近似y=x),也满足条件,可以使用。
TIPS:D2DL的第一部分到这里基本就结束了,基础理论和一般的传统方法主要就是之前笔记中的这些,其他内容如有遇到就在第二部分再做补充记录,下一部分主要就是pyTorch的应用内容,涉及到python和pyTorch各种模块的使用。