昨天,我们通过改进代价函数来改进了学习效率。今天,我们来探究一下其他的方法。
1)规范化
(1)为什么要规范化?
这里我们先举一个例子,就用我们刚刚学过的神经网络。这里我们只使用mnist数据集中的前1000个数据。这里我们使用交叉熵代价函数,设置学习速率为0.5,小批量数据大小为10将神经网络训练400个周期。代码如下:
import mnist_loader
import network2
training_data,validation_data,test_data=mnist_loader.load_data_wrapper()
net=network2.Network([784,30,10],cost=network2.CrossEntropyCost)
net.large_weight_initializer()
net.SGD(training_data[:1000],400,10,0.5,evaluation_data=test_data,monitor_evaluation_accuracy=True,monitor_training_cost=True)
我们通过overfitting.py函数得到如下两个图像:
我们可以看到一个奇怪的现象:在280轮之后,虽然代价函数值在训练集上不断变小,但是神经网络在测试集的准确率并没有随之上升。这种训练结果不能很好泛化到测试集的现象就叫做过拟合(overfitting)。这也是我们为什么要规范化的原因。
(2)L2规范化
L2规范化其思路是:在代价函数后面再加一项作为调整。如下图:
其中的 λ 是规范化参数(其大小与训练数据大小有关),n就是训练集合大小,这里要注意规范化项里面并不包含偏置。C0可以是交叉熵函数和二次代价函数等。如下:
那么这个的原理是什么呢?
我们知道梯度下降算法需要求出代价函数对w和b的偏导数,这里我们对公式(87)求偏导数:
我们可以知道,∂C/∂w和∂C/∂b可以通过BP算法求得。那么唯一的变化就是,权重w的学习规则多减去了一个ηλ/n倍的w。如下:
我们可以看到这里的权重比原来小了,所以又叫权重衰减(weight decay)。
同理随即梯度下降算法用L2规范化得到如下:
同理,x也是代表是在小批量数据上进行的求和。现在我们试验一下规范化的效果,代码如下:
import mnist_loader
import network2
training_data,validation_data,test_data=mnist_loader.load_data_wrapper()
net=network2.Network([784,30,10],cost=network2.CrossEntropyCost)
net.large_weight_initializer()
net.SGD(training_data,30,10,0.5,evaluation_data=test_data,lmbda=5.0,True,monitor_training_cost=True)
最后,我们得到分类准确率提高到了96.5%,这个相对之前的结果获得巨大的提升,可见规范化是很有效果的。如下:
(3)L1规范化
除了L2规范化还有其他的规范化方法,这里再给出一种L1规范化方法,如下:
这里我们将w取绝对值,再求和。接下来推导一下w的更新方程。
对上式求导得:
再将公式(20)带入得到:
我们可以看到这里增添的项是一个常量,也就是通过常量来改变权重。而L2规范化是通过等比例改变w来改变。
这里有一点要注意的是,w=0的时候,由sgn(w)的函数图像得,∂C/∂w偏导数未定义。所以,我们需要在写程序的时候,当w=0的时候,令∂C/∂w=0。
2)权重初始化
(1)为什么要初始化权重
比如说,我们现在有个神经网络,有1000个输入神经元。该网络的第一个隐藏层的权重是用归一化的高斯分布初始化的,每个w服从N(0,1)。我们现在只讨论该部分内容:
我们将输入神经元一半(500)置为1,另一半置为0。根据我们之前的输出公式:
其中包含500个输入权重项,另外500个因为输入神经元为0,所以权重项置为0。还有一个偏置项b。所以根据正态分布公式得到,z服从N(0,√500+1)。其图像如下:
根据这个概率分布图像,我们可以知道|z|在绝大部分情况下都是大于1的。在这种情况下,我们的σ(z)的会接近1或者0。如下图:
那么这样的话,我们的隐藏神经元就会饱和,也就是说其变化率太低了。这样也就导致了权重的改变仅仅会改变激活值很小的值,前馈的速率变得很低,BP更新也会很慢(四个方程)。
(2)解决方法
既然图像变得很“矮胖”,那么我们把它变得“瘦高”就可以啦。我们可以让权重服从N(0,1/√n)分布,这里的n代表n个输入神经元。那么这里的z服从的是
N(0,500*1/1000+1),即N(0,3/2)。其图像如下:
我们可以看到,|z|的绝大部分都小于1。这样就避免了神经元饱和的问题。我们来试验一下,这里我们调用的是network2.py程序。代码如下:
import mnist_loader
import network2
training_data,validation_data,test_data=mnist_loader.load_data_wrapper()
net=network2.Network([784,30,10],cost=network2.CrossEntropyCost)
net.SGD(training_data,30,10,0.1,evaluation_data=validation_data,lmbda=5.0,monitor_training_cost=True)
这里我们把学习率降低到0.1,更低的学习率会让速度优势的图像更明显。在第一种方式的分类准确率在87%的时候,第二种的分类准确率已经达到了93%。事实上,合适的初始化不仅能提高速度,还能提高准确率。
以上数据图像分别通过overfitting.py和weight_initialization.py程序得到的。回复后台“改进效率的其他方法”从github获得完整程序。
希望有志同道合的小伙伴关注我的公众平台,欢迎您的批评指正,共同交流进步。