CS231n 之 训练神经网络 PART I

这一篇将着重介绍神经网络的训练细节,如何高效的训练神经网络。

一、追溯历史

可能女孩子都喜欢爱回忆过去,每次一讲到神经网络的历史,就特别激动,昨天在twitter上关注了Yann LeCun,内心更是激动不已,仿佛能感受到他在1998年提出CNN时的那种划时代的震撼力。AI真是一个充满挑战又激动人心的领域。就像学良老师所说,我们一直相信未来!
那么,长话短说,我们简单聊一聊a bit of history:

  • 追溯到1957年,Frank Rosenblatt发明被称之为感知机的机器,就像神经网络的硬件实现,由电路和电子元件连接:
    在这里插入图片描述
    激活函数使用的是二进制阶梯函数:
    在这里插入图片描述

由式子可以激活函数不能进行微分运算,也不能反向传播。同时,Frank Rosenblatt提出了学习法则,通过设置权值,得到好的训练结果:
在这里插入图片描述
在1957那个年代,没有损失函数的概念,没有反向传播的概念。

  • Widrow and Hoff在1960年对感知机进行改进,提出适应性神经元,将感知机整合成一个多层次的感知机网络,由下图可知,同样是由电子元件和电路组成的硬件器件,没有反向传播概念,同样也是有学习法则来评定神经网络的性能好坏。

在这里插入图片描述

  • 在1986年,神经网络有了突破性的进展,源于由Rumelhart等人第一次提出的反向传播的概念(Back-propagation),摒弃了多层次感知机特定的学习法则,并提出了损失函数的公式以及梯度下降的概念,但是对于大型的神经网络,训练效果并不是很好。

  • 在1986~2006年,神经网络的研究并没有什么实质性进展,直至2006年,神经网络开始复兴,由Hinton and Salakhutdinov 发表的论文,他们在深度学习中采用了一个无监督预训练方案,使用RBM(限制波尔兹曼机)不在单通道中对所有层使用反向传播算法,而是在第一层建立一个无监督的学习目标,第二层,第三层,第四层…….对数据集进行预训练,也就是逐层进行训练,然后将所有层整合起来进行反向传播。
    在这里插入图片描述

  • 在2010~2012年人们开始普遍注意到神经网络,最开始是应用在语音识别方面,是由微软提出将神经网络运用在GMM+HMM语音识别模型中,用来改善语音识别的效果。在2012年,神经网络开始应用到图像识别领域,变得愈加火热。分析原因,大概是因为有很多好的方式用来实现激活函数的初始化以及GPU硬件的发展,更重要的是数据作为驱动力的时代已经来临!

在这里插入图片描述

神经网络的历史就说到这里,下面我们来详解神经网络的训练细节。

二、激活函数(Activation Function)

在这里插入图片描述
也就是图中的f,在上一篇中斯坦福cs231n学习笔记(6)——神经网络初步简单的介绍了几种激活函数,下面将从各个函数的优缺点和特点详细的介绍每一种激活函数。

  • Sigmoid

在这里插入图片描述
在这里插入图片描述
sigmoid函数在过去的一段历史中非常受欢迎,又被称为挤压函数,取值为[0,1]之间的实数。同时,sigmoid存在3个问题:

【1一个饱和的神经元(Saturated neurons),也就是这个神经元的输出要么非常接近1要么非常接近0。这类神经元会导致在反向传播算法中出现梯度趋0的问题,这个问题叫做梯度弥散(消失)。重点解释这意味着什么,先看一下sigmoid函数的运算链路:
在这里插入图片描述
输入一个x,会输出一个sigmoid结果,然后进行反向传播,运用链式法则,最终得到dl/dx,可以看到,链式法则就是将这两项相乘。那我们想象一下,当sigmoid单数的输入是-10,0,10时会发生什么?
当x取-10或10时,梯度会非常小。在sigmoid图像上可以看到,在取值-10的地方,因为这点的斜率为0,梯度几乎为0;在取值为10的地方也几乎是0。那么问题来了,在输出是1或者0的时候,那么你的梯度将会趋于0,当局部梯度是一个很小的数,在进行反向传播的时候将会停止。因此可以想象我们有一个很大的sigmoid神经网络,而且很多数据都落在饱和区域,输出值不是0就是1,那么梯度将无法对网络进行反向传播,因为梯度的传播将很快停止。当很多数据落在饱和区域,梯度将只会当你的数据落在安全区域时才会变化,这一安全区域我们被称为sigmoid函数的激活区域。

【2另一个问题是,sigmoid函数的输出不是关于远点中心对称的。假设我们有一个多层的sigmoid神经网络,如果你的输入x都是正数,然后这些不关于原点对称的输出值都集中在0和1之间,那么在反向传播中w的梯度会传播到网络的某一处,那么权值的变化是要么全正要么全负。解释下:当梯度从上层传播下来,w的梯度都是用x乘以f的梯度,因此如果神经元输出的梯度是正的,那么所有w的梯度就会是正的,反之亦然。在这个例子中,我们会得到两种权值,权值范围分别位于图中一三象限:
在这里插入图片描述
当输入一个值时,w的梯度要么都是正的要么都是负的,当我们想要输入一三象限区域以外的点时,我们将会得到这种并不理想的曲折路线(zig zag path),图中红色曲折路线。虽然这个理由很简单,但根据经验得出,如果我们训练的数据并不关于原点中心对称,收敛速度会非常之慢。
**(3)**最后一个问题就是,sigmoid函数的表达书中关于exp()的计算是很耗时的,这可能是一个小细节,但当你去训练很大的卷积网络,耗时的痛苦无法想象。

  • tanh(x)
    在这里插入图片描述
    tanh(x)函数是由Yann LeCun在1991年提出的,最终的输出是[-1,1]之间的实数,好比两个sigmoid函数叠在一起,由图像可以看出tanh是关于原点对称的,在这方面的做的很nice。但是tanh(x)与sigmoid函数有相同的缺点,就是梯度仍然会出现的饱和的问题,导致梯度无法传播。

  • ReLU(Rectified Linear Unit修正线性单元)
    在这里插入图片描述
    ReLU是Krizhevsky等人在2012年关于一篇卷积神经网络的论文中提出的一种非线性函数max(0,x)。在使用ReLU激活函数之后,神经网络收敛速度非常快,达到了之前的六倍速。简短的讲一下原因,首先因为ReLU不会饱和,至少在输入为正的时候,在正的区域内不会产生梯度弥散的问题,梯度不会突然为0,当神经元在很小的有边界的区域被激活时,才会出现梯度消失的问题。实际上,这些神经元只在反向传播的过程中才会被激活,不论正确与否,至少在一半的区域内(正区域内)反向传播不会是0。Also,ReLU有一些缺点:
    (1)输出结果不是关于原点对称的;
    (2)有一个很烦恼的事,当输入的神经元x<0时,梯度是怎样的?当ReLU神经元没有被激活时,会发生什么情况?答案是梯度会消散。因此,非激活的输入值无法进行反向传播,权值也不会更新,可以说,这个神经网络什么都没有做。
    【Hint:这里说的梯度是指泛化的梯度,指原函数是必须可微的。】
    在使用ReLU激活函数的时候,可能会发生这样的情形,如果这个神经元什么都不输出,那么说明没有任何的梯度,假如梯度弥散了,就不会有任何更新。
    那么问题来了:初始化ReLU神经元时,输入数据集之后,我们可能会得到dead ReLU神经元,如果神经元在数据集的外面,那么dead ReLU将永远不会激活,参数也不会更新。
    在这里插入图片描述
    以上问题的发生,通常发生在一下两种情况:
    (1)在初始化过程中,如果你非常不幸的将权重设置成不能使神经元激活的数值,神经元将不会训练Data集;
    (2)在训练过程中,若学习速率太高,想象一下这些神经元在一定范围内波动,将会发生数据多样性的丢失,在这种情况下,神经元不会被激活,数据多样性的丢失也不会逆转。
    在实践中,我们会看到这种情况,当训练很大的且激活函数为ReLU的神经网络,在训练过程中,看起来一切运行正常,但需要停止训练,因为当我们将整个训练集输入网络,查看所有神经元的统计状况时,我们会发现有10%或20%的神经元已经死了(不会被训练集中任何数据激活),为了解决初始化中dead ReLU问题,人们把偏置值b设置成很小的证书0.01,而不是0,这使未经初始化的ReLU更有可能输出正值,从而使参数更新,减少神经元在训练过程中不被激活的可能性。虽然这种做法会起一定作用,但是很多人还是不认可这种做法。因此,有了Leaky ReLU,PReLU,ELU等等的出现,那我们继续…

  • Leaky ReLU
    在这里插入图片描述
    Leaky ReLU很好的解决了dead ReLU的问题,因为Leaky ReLU保留了第三象限的曲线,分段线性并保留了ReLU的效率。以前ReLU在第三象限的区域内的梯度会消失,而Leaky ReLU给这个区间一个很小的负或正的斜率,而不会有神经元死了的问题。

  • PReLU(参数校正器)
    在这里插入图片描述
    PReLU对Leaky ReLU进行升级,由表达式可以看出,多了一个α参数,那么计算图中的每一个神经元都会有一个α参数,就像偏置值b一样,α参数可以由反向传播学习此参数。同时只有让αx和x不相等,计算过程才有意义。

  • ELU(Exponential Linear Units )
    在这里插入图片描述
    ELU是由Clevert等人在2015年提出的,使用非常普遍, 有着ReLU所有的优点,并且不会出现dead ReLU,并且有着接近0的平均输出。但是因为表达式中有exp(),所以有一定的计算成本。
    In short,小心使用ReLU,ReLU是一个很好的选择!

  • Maxout “Neuron”(Maxout神经元)
    Maxout “Neuron”是由Goodfellow等人在2013年提出的一种很有特点的神经元,它的激活函数、计算的变量、计算方式和普通的神经元完全不同,并有两组权重。先得到两个超平面,再进行最大值计算。激活函数是Generalizes ReLU和Leaky ReLU,没有ReLU函数的缺点,不会出现神经元失活(dead),仍然是分段线性和高效率。
    在这里插入图片描述
    在使用Maxout神经元时,会有2倍的参数问题。这个方法不是很理想。

Tip:有着不同的激活函数的神经元之所以优化过程不同,不单单的与损失函数有关,还与梯度反向传播的动态过程有关,我们应动态的思考问题,才能把各类参数调整得游刃有余。

三、Data Preprocessing(数据预处理)

在这里插入图片描述
如图是原始数据,数据矩阵X有三种常见的数据预处理形式,其中我们假定X的大小为[N×D](N是数据的数量,D是它们的维数)。

  • 归一化处理(normalized data)
    在这里插入图片描述
    如上图所示,归一化处理是指每个维度都通过标准偏差来进行缩放或者确保每个维度最大值和最小值在-1到1之间等等,红线表示数据的范围,中间长度不等,右边长度相等。在图像处理中,归一化并不常用。
  • 零中心化处理
    在这里插入图片描述
    零中心化处理是指减去每个维度的平均值,进而使得数据是以零为中心的。数据云集中在原点的周围。在图像处理中,零中心化处理是一种常用的数据预处理的方式。

PCA and Whitening(PCA算法和白化处理)
PCA和白化是数据预处理的另一种方式。在这个过程中,应用PCA算法将协方差矩阵变成对角矩阵,或者对数据进行白化处理,那意味着在PCA处理后对数据进行压缩,使协方差矩阵变成单位矩阵,这种预处理方式在机器学习中经常用到。

以上三种数据预处理的方法在图像处理中并不常用,在图像处理中常见的是均值中心化处理,以32323的CIFAR图像为例,有两种方式实现:

减去均值图像
经过计算后得到一个32323的均值图像,用每张图像减去均值图像进行中心化处理,从而获得更好的训练效果。
减去单通道均值
这种数据预处理方式常常用在VGGNet中,在红绿蓝三色通道中分别计算,分别得到3个数值。这种预处理方式非常常用,因为我们只需要关心三个数值,而不用考虑上一方法中均值图像中的那个均值图像数组。

四、Weight Initialization(权重初始化)

w(权重)的初始化问题也是早期神经网络发展不好的原因,是人们不重视的一个very very very重要的参数。首先,如果不进行初始化,那么在w=0的情况下所有的神经元都是一样的,在反向传播时的梯度都是一样的,这个网络是不能工作的,所有的神经元进行相同的计算,这不是一个很好的方式。因此,人们用很小的随机数值代替,随机数值是从标准差为0.01的高斯公式中随机抽取,这是w初始化的一种方式。
在这里插入图片描述
但是这种初始化方式是存在问题的,应用层数少的神经网络还可以,但是随着层数的增加,这种简单的初始化方法是无效的。下面将详细讲解什么方法无效以及Why,How。我们从代码、可视化数据来分析w的初始化过程。

D = np.random.randn[(1000,500)]
hidden_layer_sizes = [500]*10
nornlinearities = ['tanh']*len(hidden_layer_sizes)

我们要做的是用高斯分布来初始化权值w,然后进行网络传播,所以定义一个for循环,循环进行传播,即在一些相同尺寸的隐藏层中进行一系列计算。

act = {'relu':lambda x:np.maximum(0,x), 'tanh':lambda x:np.tanh(x)}
Hs = {}
for i in xrange(len(hidden_layer_sizes)):
     X = D if i == 0 else Hs[i-1]
     fan_in = X.shape[1]
     fan_out = hidden_layer_sizes[i]
     W = np.random.randn(fan_in,fan_out) * 0.01
     H = np.dot(X,W)
     H = act[nonlinearities[i]](H)
     Hs[i] = H

So,我们希望更加了解隐藏层中的神经元会如何激活,因此我们着重关注均差和标准差,同时,我们将均差和标准差用柱状图绘制出来,让所有的数据通过网络,然后观察第五、第六或第七层上的权值,用柱状图显示出来。

print 'input layer had mean %f and std %f' % (np.mean(D), np.std(D))
layer_means = [np.mean(H) for i,H in Hs.iteritems()]
layer_stds = [np.std(H) for i,H in Hs.iteritems()]
for i,H in Hs.itemritems():
      print ' hidden layer %d had mean %f and std %f' % (i+1, layer_means[i], layer_stds[i])
plt.figure()
plt.subplot(121)
plt.plot(Hs.keys(), layer_means, 'ob-')
plt.title('layer mean')
plt.subplot(122)
plt.plot(Hs.keys(), layer_means, 'or-')
plt.title('layer std')

plt.figure()
for i,H in Hs.iteritems():
      plt.subplot(1,len(Hs),i+1)
      plt.hist(H.ravel(),30,range=(-1,1))

那么,我们采用标准差为0.01的高斯公式的策略初始化w:
在这里插入图片描述
由图片中,我们可以看到,一开始输入的均值是0,标准差是1,在前向传播的过程中,我们观察经过10层网络之后的情况,我们使用的是tanh函数,tanh函数关于原点对称的,所以均值会归于0左右,再来看看方差,一开始是1,在接下来的层中,变成0.2,0.04,直线下降至0,所以神经元的方差会降到0。看柱状图,第一张是合理的,我们的数据处于-1到1之间,随后这些数据分布开始“坍塌”,最终只分布在0上,在10层网络上使用这种初始化方法,最终所有以tanh为激活函数的神经元,输出都是0,这是为什么?
A:输入X是小量数值,那么w的梯度也是小量数值,w*x的激活函数计算出的结果对梯度的叠加是无影响的,在反向传播过程中,通过链式法则不断的乘w,最终得到的梯度非常小,几乎为0,这时就出现了梯度弥散(消失)的问题。使用标准差为0.01的高斯分布策略进行w初始化,我们会发现梯度的量级不断缩小,所以我们可以尝试另一种方式来优化w初始化,与其将数值规整到-1和1之间,我们可以尝试一些其他的尺度来初始化w矩阵,使用1.0来代替0.01。那么我们来看下此时的输出情况:
在这里插入图片描述
由图像可以看出,发生了另一个奇特的事情,因为我们“走过头”了。由于数值设置的过高,所有的tanh函数输出不是1就是-1,这意味着所有传递的数值都过于饱和了。因为w过大,正向传播后的数值就会变得非常大。那么在反向传播中计算的梯度就会趋向0,然后使得我们的网络无法工作,即使训练时间加长,损失函数也根本不会变,因为所有的神经元都饱和了,无法进行反向传播,权值得不到更新。基于此,我们没有必要在1.0和0.01之间进行手工测试。

2010年,有一篇是由Glorot等人写的论文,我们称之为Xavier初始化,他们关注了神经元的方差表达式,对每个神经元的输入进行开根号,如果有很多的输入,最终会得到较小的权值,从直观上来看,这么做是有意义的。
在这里插入图片描述
在这里插入图片描述
因为加权和中被算入的量更多了,而且希望它们之间的相关性降低。如果输入很少,那么我们需要较大的权值,因为其中仅有一小部分希望它们的方差是1。所以,这种思路的核心就是只关注单个神经元,不包括激活函数,仅仅是线性的神经元。如果我们输入的是高斯分布的数据,并且希望线性神经元方差是1,那么我们应该用这种方法对w进行初始化。从图像上看,数值的分布更有意义了 ,从-1到1之间的tanh单元的直方图,数值都在作用区域内,没有出现饱和的情况,这种方式效果也不是非常好,从一个直方图可以看出,出现了一个下凹,其原因是这篇论文并未考虑tanh的非线性。tanh函数的非线性,将数据的方差改变,这将影响数据方差的分布。So,这是一个靠谱的初始化方式,可以应用到神经网络中来,比起设置0.01要好得多。但是,虽然这种方法在tanh为激活函数的网络中奏效,但是换了ReLU,ReLU并不奏效,反而方差下降的更快,ReLU神经元分布越来越差,并趋于0,进而越来越多的神经元未被激活。

在这里插入图片描述
在这里插入图片描述
从ReLU的数据分布中,我们可以看到,在第一层中,能看出一些分布,但是在之后的几层中,分布越来越窄,并趋于0,也就说在这种初始化方法下,越来越多的神经元没有激活。所以这种初始化方式应用在ReLU网络中,并没有什么好的结果。回头看一下已tanh函数为激活函数的神经元,Glorot等人当年并没有讨论非线性和ReLU神经元的情况,所以在计算加权和后,让结果经过一个ReLU的激活函数,由ReLU神经元的数据分布可以看出,一半的贡献被置0了,从直观上来看,输出的分布是方差的一半。所以应用ReLU在高斯分布中应该额外考虑因数2,最终结果就会收敛;若没有引入因数2,激活函数输出的分布就会以指数倍收缩。如果你在实际中使用ReLU神经元,那么这里提到的这种初始化方法才是合理的。

In a word,w初始化问题在实际中是需要好好斟酌的,由于这是一种策略性的东西,所以才使得神经网络走这么远。可能人们认为调参不会很难,这也使得研究一种合理的初始化方法一直是一个活跃的研究领域,这里给大家提供一些论文,大量的论文介绍不同的初始化方法。它们没有一个标准的初始化公式,使用何种初始化也是数据驱动的。
在这里插入图片描述

作者:胡大炮的妖孽人生
来源:CSDN
原文:https://blog.csdn.net/huplion/article/details/79122027
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值