我们在上一节讲解了深度学习中常用的几种正则化手段,包括提前终止、添加噪声、参数共享和Dropout。并且说明了与我们常见的L1、L2正则化的等价性。我们在这篇文章里将会使用keras面对具体的任务添加不同的正则化手段。
我们采用boston房价这一经典回归问题的数据集,因为它包含404个训练样例,102个测试样本,特征维数为13。对于深度学习而言,这样的数据量和特征都少得可怜,用一个简单的神经网络可能就会发生过拟合,这正是我们需要的。
boston房价数据的特征具有不同的单位,比如有的特征使人均犯罪率,而有的特征是到就业中心的加权距离,特征之间单位的不一致,不利于后续对特征的统一处理,同时也是为了加快收敛,我们先对数据做标准化处理。
from keras.datasets import boston_housing
from import StandardScaler
(x_train, y_train), (x_test, y_test) = bo()
scale=StandardScaler().fit(x_train)
x_train=(x_train)
x_test=(x_test)
在这里我们使用了StandardScaler类进行预处理,因为它可以保存好标准化训练集得到的均值和标准差,在转化测试集时也要使用它,因为我们假设训练数据和测试数据具有相同的分布,对测试数据不建议单独标准化。
接下来我们搭建一个较为复杂的网络用于回归问题:
import numpy as np
from keras import models
from keras.layers import Dense
from keras import optimizers
def normal_model(a,b):
model=models.Sequential()
model.add(Dense(1024,activation=a,input_shape=(13,)))
model.add(Dense(512,activation=a))
model.add(Dense(256,activation=a))
model.add(Dense(128,activation=a))
model.add(Dense(512,activation=a))
model.add(Dense(256,activation=a))
model.add(Dense(128,activation=a))
model.add(Dense(1,activation='linear'))
model.compile(optimizer=o(),\
loss=b,metrics=['mse'])
return(model)
model_1=normal_model('sigmoid','mse')
his=model_1.fit(x_train,y_train,\
batch_size=32,validation_data=(x_test,y_test),\
verbose=1,epochs=10)
w=
需要特别注意,因为特征有13维,所以我们需要设置13维的输入,因为是回归问题,我们可以将输出单元设置为线性,并且只有一个;将训练结果保存在变量中,画图来观察loss随着epochs的变化,因为训练集的loss一定是随着迭代的增加减小直至趋于收敛,所以我们只看验证集上的loss:
import ma as plt
import seaborn as sns
(style='whitegrid')
(range(10),w['val_loss'],'-',label='validation loss')
('Loss')
('epochs')
()
如图,验证集上的loss随着epochs在波动,经过剧烈的上升后又剧烈的下降。
对于这样的图我们似乎就无法判断是否发生了过拟合,因为loss忽高忽低,看起来只是在正常的波动,所以我们最好将这样的曲线变得平滑一些,我们可以采用时间序列模型经常采用的指数平滑法:
def smooth_curve(points,factors=0.9):
smoothed_points= []
for point in points:
if smoothed_points:
previous=smoothed_points[-1]
(previous*factors+point*(1-factors))
else:
(point)
return(smoothed_points)
......
(range(10),smooth_curve(w['val_loss']),'o',label='validation loss')
......
如图,我们不仅做了平滑处理,而且使用点代替了折线,可以看出,在刚开始训练的时候,或者在第一个epochs之后,loss就已经开始上升,代表着严重的过拟合。
在训练过程中,我们设置了batch为32,所以在重复过程中会有差别。我们当然可以考虑提前终止,只是提前终止在技术上是非常简单的事情,所以此处不做提前终止的示例。
根据我们的理论知识,我们采取相应的正则化手段去削减模型容量的时候,需要观察模型的经过几次迭代才会产生过拟合,如果正则化手段可以将过拟合所需要的迭代次数增加,那么就意味着有效的,因为在训练过程中,训练的Loss总会下降对应着参数值的变化,过拟合在多久会发生就会成为模型容量的度量。必须要注意,这一过程,我们需要保持模型的基本结构不变,优化算法的学习率不变。
我们首先尝试常见的正则化手段,在keras中,添加惩罚项的方法非常简单,它直接添加在层里面,表示这一层的权重矩阵,偏置,激活函数的阈值这三类参数构成的惩罚项均可以被添加到损失函数中,而其他没有设置正则化的层的参数不是添加到损失函数中。简而言之,我们可以实现针对某一层的正则化:
from keras import regularizers
......
model.add(Dense(1024,\
kernel_regularizer=regularizers.l2),\
activation=a,input_shape=(13,)))
......
上述代码的意思是,我们对模型的第一层的权重矩阵进行了正则化,将惩罚系数设置为0.1,这已经是一个相当大的系数,我们继续观察Loss的变化:
如图,模型在前6个epochs在微弱的波动,从第六个开始就产生过拟合,说明我们的正则化手段是有效的,它一定程度上削减了模型的容量,将过拟合的步数增加到了第六个epochs。
除此之外,我们还可以添加噪声,在keras中,噪声可以是单独的一个层,分为加性噪声和乘性噪声,可以分别将噪声加在输入层和权重上,我们可以将模型的开始部位改为:
from keras.layers import GaussianNoise
......
model.add(GaussianNoise),input_shape=(13,)))
model.add(Dense(1024,activation=a))
......
这样就将标准差为0.9的高斯噪声加入了输入中,就得到了:
如图,模型的Loss在前4个epochs在保持下降,从第4个开始就产生过拟合,说明添加噪声是有效的。
最后我们来尝试使用dropout方法,在keras中dropout也是可以当作一个单独的层,我们可以在输入层和中间层分别添加Dropout层,分别代表有些特征不会使用,有些神经元不会使用:
from keras.layers import Dropout
......
model.add(Dropou, input_shape=(13,)))
model.add(Dense(1024,activation=a))
model.add(Dropou))
......
这代表着我们在输入层随机丢弃了比例为0.2的特征,在第一层丢弃了0.5的神经元,结果如下:
如图,模型的Loss在前5个epochs在保持下降,从第5个开始就产生过拟合,说明Dropout方法是有效的。
在实际操作中,我们完全可以将这些方法融合在一起,即添加噪声,也添加和正则化,还添加dropout,在训练过程中,进行提前终止。
读芯君开扒
课堂TIPS
• 在keras中,如果我们想添加提前终止,最便利的方法是利用回调函数EarlyStopping,它自动检测模型的loss或者性能度量,当在几步之内都没有上升的时候就停止训练,并将最优的模型保存下来。
• 如果我们相对网络的参数做一些额外的约束,例如非负,或者是想和一个预训练模型进行参数绑定,我们可以使用keras的contraints。
• 当数据量较少时,我们应该做交叉验证,这样得到的结果会更加准确,本文只做简单示例,未使用交叉验证。
作者:唐僧不用海飞丝
如需转载,请后台留言,遵守转载规范