正则化
机器学习的问题中,过拟合是一个很常见的问题。过拟合指的是只能拟
合训练数据,但不能很好地拟合不包含在训练数据中的其他数据的状态。机
器学习的目标是提高泛化能力,即便是没有包含在训练数据里的未观测数据,
也希望模型可以进行正确的识别。我们可以制作复杂的、表现力强的模型,但是相应地,抑制过拟合的技巧也很重要。
过拟合
发生过拟合的原因,主要有以下两个。
- 模型拥有大量参数、表现力强。
- 训练数据少。
这里,我们故意满足这两个条件,制造过拟合现象。为此,要从
MNIST数据集原本的60000 个训练数据中只选定300 个,并且,为了增加网
络的复杂度,使用7 层网络(每层有100 个神经元,激活函数为ReLU)。
下面是用于实验的部分代码(对应文件在ch06/overfit_weight_decay.py
中)。首先是用于读入数据的代码。
(x_train,t_train),(x_test,t_test)=load_mnist(normalize=True)# 为了再现过拟合,减少学习数据x_train=x_train[:300]t_train=t_train[:300]接着是进行训练的代码。和之前的代码一样,按epoch 分别算出所有训
练数据和所有测试数据的识别精度。
network=MultiLayerNet(input_size=784,hidden_size_list=[100,100,100,100,100,100],output_size=10)optimizer=SGD(lr=0.01)# 用学习率为0.01的SGD更新参数max_epochs=201train_size=x_train.shape[0]batch_size=100train_loss_list=[]train_acc_list=[]test_acc_list=[]iter_per_epoch=max(train_size/batch_size,1)epoch_cnt=0foriinrange(1000000000):batch_mask=np.random.choice(train_size,batch_size)x_batch=x_train[batch_mask]t_batch=t_train[batch_mask]grads=network.gradient(x_batch,t_batch)optimizer.update(network.params,grads)ifi%iter_per_epoch==0:train_acc=network.accuracy(x_train,t_train)test_acc=network.accuracy(x_test,t_test)train_acc_list.append(train_acc)test_acc_list.append(test_acc)epoch_cnt+=1ifepoch_cnt>=max_epochs:breaktrain_acc_list和test_acc_list中以epoch 为单位(看完了所有训练数据
的单位)保存识别精度。现在,我们将这些列表(train_acc_list、test_acc_
list)绘成图,结果如图6-20 所示。
过了100 个epoch 左右后,用训练数据测量到的识别精度几乎都为
100%。但是,对于测试数据,离100%的识别精度还有较大的差距。如此大
的识别精度差距,是只拟合了训练数据的结果。从图中可知,模型对训练时
没有使用的一般数据(测试数据)拟合得不是很好。
权值衰减
权值衰减是一直以来经常被使用的一种抑制过拟合的方法。该方法通过
在学习的过程中对大的权重进行惩罚,来抑制过拟合。很多过拟合原本就是
因为权重参数取值过大才发生的。
复习一下,神经网络的学习目的是减小损失函数的值。这时,例如为
损失函数加上权重的平方范数(L2 范数)。这样一来,就可以抑制权重变大。
用符号表示的话,如果将权重记为WWW,L2 范数的权值衰减就是$
\frac{1}{2} \lambda W^2
,然后将这个,然 后将这个,然后将这个
\frac{1}{2} \lambda W^2
$加到损失函数上。这里,λ是控制正则化强度的超参数。λ
设置得越大,对大的权重施加的惩罚就越重。此外, $
\frac{1}{2} \lambda W^2
开头的是用于将开头的是用于 将开头的是用于将
\frac{1}{2} \lambda W^2
的求导结果变成的求导结果变成的求导结果变成λW$的调整用常量。
对于所有权重,权值衰减方法都会为损失函数加$
\frac{1}{2} \lambda W^2
。因此,在求权重梯度的计算中,要为之前的误差反向传播法的结果加上正则化项的导数。因此,在求权 重梯度的计算中,要为之前的误差反向传播法的结果加上正则化项的导数。因此,在求权重梯度的计算中,要为之前的误差反向传播法的结果加上正则化项的导数λW$。
L2 范数相当于各个元素的平方和。用数学式表示的话,假设有权重
W=(w1,w2,...,wn)W= (w_1, w_2, . . . , w_n)W=(w1,w2,...,wn),则L2L2L2范数可用$ \sqrt{w_1^2 + w_2^2 + \cdots + w_n^2}计算出来。除了计算 出来。除了计算出来。除了L2$ 范数,还有L1L1L1范数、L∞L_∞L∞范数等。L1L_1L1范数是各个元
素的绝对值之和,相当于∣w1∣+∣w2∣+...+∣wn∣|w_1| + |w_2| + . . . + |w_n|∣w1∣+∣w2∣+...+∣wn∣。L∞L_∞L∞范数也称为
Max 范数,相当于各个元素的绝对值中最大的那一个。L2L_2L2范数、L1L_1L1
范数、L∞L_∞L∞范数都可以用作正则化项,它们各有各的特点,不过这里
我们要实现的是比较常用的L2L_2L2范数。
现在我们来进行实验。对于刚刚进行的实验,应用λ = 0.1 的权值衰减,
结果如图6-21 所示(对应权值衰减的网络在common/multi_layer_net.py 中,
用于实验的代码在ch06/overfit_weight_decay.py中)。
如图6-21 所示,虽然训练数据的识别精度和测试数据的识别精度之间有
差距,但是与没有使用权值衰减的图6-20 的结果相比,差距变小了。这说明
过拟合受到了抑制。此外,还要注意,训练数据的识别精度没有达到100%(1.0)
Dropout
作为抑制过拟合的方法,前面我们介绍了为损失函数加上权重的L2 范
数的权值衰减方法。该方法可以简单地实现,在某种程度上能够抑制过拟合。
但是,如果网络的模型变得很复杂,只用权值衰减就难以应对了。在这种情
况下,我们经常会使用Dropout 方法。
Dropout 是一种在学习的过程中随机删除神经元的方法。训练时,随机
选出隐藏层的神经元,然后将其删除。被删除的神经元不再进行信号的传递,
如图6-22 所示。训练时,每传递一次数据,就会随机选择要删除的神经元。
然后,测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,
要乘上训练时的删除比例后再输出。
下面我们来实现Dropout。这里的实现重视易理解性。不过,因为训练
时如果进行恰当的计算的话,正向传播时单纯地传递数据就可以了(不用乘
以删除比例),所以深度学习的框架中进行了这样的实现。关于高效的实现,
可以参考Chainer 中实现的Dropout。
classDropout:def__init__(self,dropout_ratio=0.5):self.dropout_ratio=dropout_ratio self.mask=Nonedefforward(self,x,train_flg=True):iftrain_flg:self.mask=np.random.rand(*x.shape)>self.dropout_ratioreturnx*self.maskelse:returnx*(1.0-self.dropout_ratio)defbackward(self,dout):returndout*self.mask这里的要点是,每次正向传播时,self.mask中都会以False的形式保
存要删除的神经元。self.mask会随机生成和x形状相同的数组,并将值比
dropout_ratio大的元素设为True。反向传播时的行为和ReLU相同。也就是说,
正向传播时传递了信号的神经元,反向传播时按原样传递信号;正向传播时没有传递信号的神经元,反向传播时信号将停在那里。
现在,我们使用MNIST数据集进行验证,以确认Dropout的效果。源代
码在ch06/overfit_dropout.py中。另外,源代码中使用了Trainer类来简化实现。
Dropout的实验和前面的实验一样,使用7 层网络(每层有100 个神经元,
激活函数为ReLU),一个使用Dropout,另一个不使用Dropout,实验的结
果如图6-23 所示。
图6-23 中,通过使用Dropout,训练数据和测试数据的识别精度的差距
变小了。并且,训练数据也没有到达100%的识别精度。像这样,通过使用
Dropout,即便是表现力强的网络,也可以抑制过拟合。