CS231n笔记(5)神经网络(1)

版权声明:本文为博主原创文章,如未特别声明,均默认使用CC BY-SA 3.0许可。 https://blog.csdn.net/Geek_of_CSDN/article/details/82383744

其实还是全连接的神经网络(玄学模型),也算是基础吧,但是这里的基础打好了对后面学习CNN的时候会有一点帮助。

快速简介

和之前提到的线性分类器里面 s = W x 来进行计算不同视觉类别的评分不同,神经网络是 s = W 2 max ( 0 , W 1 x ) ,其中 W 1 的作用是将图像转化为一个特定维度的过渡向量(这里过度向量具体含义是什么目前貌似没有解释,只能说引进这个 W 1 产生这么个过渡向量然后对图像进行分类的方法行得通)。这里的关键地方其实是 max 这个非线性函数,因为少了这个 max 的话那么就会导致 W 1 W 2 其实是可以合并在一起的(直接进行矩阵相乘运算就可以合并了,所以非线性的 max 才是神经网络和线性分类器的不同)。参数 W 1 W 2 后面会通过随机梯度下降来进行学习,梯度在反向传播过程中会通过链式法则来求导计算出。

如果是三层的神经网络的话就可以变成 s = W 3 max ( 0 , W 2 max ( 0 , W 1 x ) ) ,里面的 W 1 W 2 W 3 就是要进行学习的参数。中间隐藏层的尺寸就是网络的超参数(后面讲怎么设置这个超参数)。

单个神经元的模型

神经网络就是根据这个模型搭建起来的。

生物动机与连接

这张图就可以表达出所有需要知道的内容:

这里写图片描述

左边是生物神经元,右边是数学模型。原来的笔记里面解释得已经很好了,所以这里直接引用:

图表的左边展示了一个生物学的神经元,右边展示了一个常用的数学模型。每个神经元都从它的树突获得输入信号,然后沿着它唯一的轴突(axon)产生输出信号。轴突在末端会逐渐分枝,通过突触和其他神经元的树突相连。

在神经元的计算模型中,沿着轴突传播的信号(比如 x 0 )将基于突触的突触强度(比如 w 0 ),与其他神经元的树突进行乘法交互(比如 w 0 x 0 )。其观点是,突触的强度(也就是权重 w ),是可学习的且可以控制一个神经元对于另一个神经元的影响强度(还可以控制影响方向:使其兴奋(正权重)或使其抑制(负权重))。在基本模型中,树突将信号传递到细胞体,信号在细胞体中相加。如果最终之和高于某个阈值,那么神经元将会激活,向其轴突输出一个峰值信号。在计算模型中,我们假设峰值信号的准确时间点不重要,是激活信号的频率在交流信息。基于这个速率编码的观点,将神经元的激活率建模为激活函数(activation function) f ,它表达了轴突上激活信号的频率。由于历史原因,激活函数常常选择使用sigmoid函数 σ ,该函数输入实数值(求和后的信号强度),然后将输入值压缩到 0 1 之间。

神经元前向传播实例代码:

class Neuron(object):
  # ... 
  def forward(inputs):
    """ 假设输入和权重是1-D的numpy数组,偏差是一个数字 """
    cell_body_sum = np.sum(inputs * self.weights) + self.bias
    firing_rate = 1.0 / (1.0 + math.exp(-cell_body_sum)) # sigmoid激活函数
    return firing_rate

其实就是公式 s = 1 1 + e ( W x i + b ) 。注意这里用到了sigmoid,如果激活函数不同的话那里要改下(其实就是将firing_rate那里改下就行了,这里的程序保存了过程中产生的中间变量cell_body_sum,所以直接改firing_rate成对应的激活函数就可以了。如果没有用一个变量来保存中间的计算过程的话那么会麻烦一些。)。

扫描二维码关注公众号,回复: 3074147 查看本文章

注意上面对于生物神经元建立模型只是粗糙模型,不准确而且是过于简化了的。

作为线性分类器的单个神经元

可以在神经元的输出端设置一个合适的损失函数,来让单个神经元变成一个线性分类器。

二分类softmax分类器:可以把 \sigmoid ( i ω i x i + b ) 看作分类器将输入判定为某个特定的类别的概率为 P ( y i = 1 | x i ; ω ) ,那么其他类别概率就是 P ( y i = 0 | x i ; ω ) = 1 P ( y i = 1 | x i ; ω ) (因为总的概率和必须是1,不然就是出错了)。这样理解的话可以将分类器的输出看成交叉熵损失(不理解的话看参考里面的交叉熵代价函数(损失函数)及其求导推导,那里列出了交叉熵损失函数的公式)。然后将这个分类器最优化成二分类的softmax分类器(就是逻辑回归)。因为sigmoid输出固定在0~1之间,所以分类器将输入判定为某个类别的基准是神经元的输出是否大于0.5。

二分类SVM分类器:可以在神经元输出增加一个最大边界损失函数(max-margin hinge loss),将其训练成一个二分类支持向量机。

理解正则化:SVM/Softmax例子中,正则化损失逐渐遗忘,因为它的效果是让所有突触权重 ω 在参数更新过程中逐渐向0变化。

常用激活函数

这里写图片描述

上图左边的是sigmoid,将实数压缩到[0, 1],右边的是tanh,将实数压缩到[-1, 1]。

sigmoid:公式是 σ ( x ) = 1 1 + e x 。sigmoid曾经得到了大量的使用,因为它对神经元激活率有着良好的解释:0代表完全不激活,1代表完全饱和的激活。但是现在sigmoid已经不怎么用了,因它有两个主要缺点:

  1. sigmoid饱和会使梯度消失:当神经元的激活在接近0或者1处事会饱和,在这些区域梯度几乎为0.因为反响传播的时候这个局部梯度会与整个损失函数关于该门单位输出的梯度相乘,所以如果局部梯度非常小,那么相乘的结果也会接近0,这样就几乎没有信号通过神经元传到权重了(更新不了权重)。同时如果初始化出来的权重矩阵中权重特别大,那么大多数神经元会饱和,那么网络就几乎不会学习。
  2. sigmoid的输出不是0中心:这会导致神经网络后面层中的神经元得到的数据不是0中心的。这回影响梯度下降,因为如果所有输入神经元的数据都是正数(例如在 f = ω T x + b 中x的每个元素都大于0),那么关于 ω 的梯度在反向传播的过程中要么全部是正数要么全部是负数(由 f 决定)。这会导致梯度下降权重更新时出现z字型的下降。不过当整个批量的数据的梯度被加起来后,对于权重的更新将会有不同的正负,所以在一定程度上减轻了这个问题(所以在批量设置成只有1个样本被输入到神经网络中训练而且激活函数还是sigmoid的时候很容易出现这种z字型权重更新)。这个问题相对于上面那个只是小问题。

tanh:tanh就是上面图里面右边的函数。和sigmoid一样存在梯度消失、饱和的问题,但是因为输出是以零点为中心对称的,所以在实际操作中tanh比sigmoid更加受欢迎。注意tanh其实是一个简单放大的sigmoid神经元, t a n h ( x ) = 2 σ ( 2 x ) 1

这里写图片描述

ReLU:这个函数最近变得很流行,它的公式是 f ( x ) = max ( 0 , x ) 。优缺点:

  1. 优点:相较于sigmoid和tanhReLU对随机梯度下降的收敛有巨大的加速作用。据称这是由它的限行,非饱和公式导致的
  2. 优点:sigmoid和tanh神经元含有指数运算等耗费计算资源的操作,而ReLU可以简单地通过对一个矩阵进行阈值计算得到
  3. 缺点:训练的时候ReLU比较脆弱,可能会“死掉”(事实证明就算是机器也有脑神经元死亡的时候)例如当一个很大的梯度流过ReLU的神经元的时候可能会导致梯度被更新到一种特别的状态,这种状态下神经元将无法被其他任何数据点再次激活。如果这种情况发生,那么从此所有流过这个神经元的梯度都会变成0。这个ReLU单元在训练中将不可逆转地死亡,这就会导致部分数据丢失。(据译文说如果学习率设置太高会发现网络中40%的神经元都会死掉。。。)合理设置学习率可以降低这种事情的发生概率。

Leaky ReLU:这个函数是为了解决“ReLU死亡”的问题的。Leaky ReLU在 x < 0 时会给出一个很像的负数梯度值,例如0.01,所以对应的函数公式是: f ( x ) = 1 ( x < 0 ) ( α x ) + 1 ( x >= 0 ) ( x ) 。公式里面出现的 α 是一个小的常量。据说这个激活函数表现很不错,但是效果不稳定。还有一种新的激活函数是PReLU,这种激活函数会将负区间上的斜率当作每个神经元中的一个参数。但是目前没有证据表明这个激活函数在不同的任务中都能对训练有益。

Maxout:这是一种对ReLU和Leaky ReLU的归纳,对应的公式是: max ( ω 1 T x + b 1 , ω 2 T x + b 2 ) 。ReLU和Leaky ReLU都是这个公式的特殊情况(例如ReLU就是在这个公式里面的 ω 1 , b 1 = 0 的时候对应的情况)。这样就可以让Maxout神经元拥有ReLU单元的所有有点(先行操作和不饱和),而没有缺点(没那么容易死),但是相比ReLU的参数数量多了一倍(充值参数来变强),会让整体模型的参数数量激增。

注意:可以在同一个网络中混合使用不同类型的神经元,但是没什么人这么干就是了(摊手)。

怎么选择:用ReLU,注意设置好学习率就行了,如果不放心就监视神经网络中死亡的神经元比例。如果神经元死亡比例比较高就是是Leaky ReLU或者Maxout,不要再用sigmoid了。可以试试tanh,但是效果应该不如ReLU或Maxout。

神经网络结构

组织层结构

这里写图片描述

上面图片里面的就是用了全连接层的神经网络了(那些一圈圈的就是神经元)。神经网络就是神经元的集合,神经元的连接是以一种类似于无环图的方式连接的,就是说神经元的连接不会构成环状,而是像上面那样一边是输入,另外一边对应着输出。网络中一些神经元的输出是另外一些神经元的输入。在网络中不允许循环输入,因为这样会导致前向传播陷入无限循环。通常神经网络中的神经元按照层来进行组织,而不是像生物神经元聚合成大小不一的团状。普通神经网络中最普遍的层结构是全连接层(fully -connected layer)。全连接层里面所有的神经元与其前后两层的神经元是全部一一对应相连的(这样说可能比较难理解,就是单个神经元都会和前后两层的所有神经元一一对应连接),但是在同一个层中的神经元之间没有连接。

命名规则:N层神经网络里面的N不包括输入层,所以单层神经网络就是没有隐层的,输入直接映射到输出的神经网络。所以有些研究者会说逻辑回归或者SVM只是单层神经网络的一个特例。研究者也会用人工神经网络(Artificial Neural Network)或者多层感知器(Multi-Layer Perceptrons)来指代神经网络。有些不喜欢把神经网络比作人类大脑的研究者会用单元(unit)而不是神经元作为术语。

输出层:输出层通常不会有激活函数(或者可以理解成有一个线性相等的激活函数)。这是因为最后的输出层大多用于表示分类评分值,所以可以是任意的实数,或者某种实数值得目标数。

确定网络尺寸:度量神经网络尺寸的标准有两个:1. 神经元的个数;2. 参数个数

前向传播计算举例

不断重复的矩阵乘法与激活函数交织,将神经网络组织成层状的一个主要原因就是这种结构让神经网络算法可以通过矩阵向量操作来变得简单和高效(所以通常都是用显卡来训练神经网络算法)。

用上面那个3层神经网络举例,输入是[3x1]的向量。一个层所有连接的强度可以存在一个单独的矩阵中。比如第一个隐层的权重W1是[4x3],所有单元的偏置储存在b1中,尺寸[4x1]。这样,每个神经元的权重都在W1的一个行中,于是矩阵乘法np.dot(W1, x)就能计算该层中所有神经元的激活数据。类似的,W2将会是[4x4]矩阵,存储着第二个隐层的连接,W3是[1x4]的矩阵,用于输出层。完整的3层神经网络的前向传播就是简单的3次矩阵乘法,其中交织着激活函数的应用。

# 一个3层神经网络的前向传播:
f = lambda x: 1.0/(1.0 + np.exp(-x)) # 激活函数(用的sigmoid)
x = np.random.randn(3, 1) # 含3个数字的随机输入向量(3x1)
h1 = f(np.dot(W1, x) + b1) # 计算第一个隐层的激活数据(4x1)
h2 = f(np.dot(W2, h1) + b2) # 计算第二个隐层的激活数据(4x1)
out = np.dot(W3, h2) + b3 # 神经元输出(1x1)

在上面的代码中,W1,W2,W3,b1,b2,b3都是网络中可以学习的参数。注意x并不是一个单独的列向量,而可以是一个批量的训练数据(其中每个输入样本将会是x中的一列),所有的样本将会被并行化的高效计算出来。注意神经网络最后一层通常是没有激活函数的(例如,在分类任务中它给出一个实数值的分类评分)。

全连接层的前向传播一般就是先进行一个矩阵乘法,然后加上偏置并运用激活函数。

表达能力

拥有至少一个隐层的神经网络是一个通用的近似器,已经可以很好地近似任何连续函数,但是实际操作中效果相对较差,而增加更多层将神经网络做深就可以让效果更好,输出函数更加平滑,而且能够让魔心对数据的统计特性有很好的你和。同时相比于一个隐层的模型,神经网络能通过最优化算法(例如前面提到的梯度下降)能比较容易地学到这个函数。就实践经验而言,深度神经网络比单层网络效果好。

但是神经网络的层数多到一定程度后就不会对输出有太大的好处。而卷积神经网络则反过来,深度成了一个极端重要的因素。对这种现象的一种解释是:因为图像拥有层次化结构,所以多层处理对这种数据有直观意义。

设置层的数量和尺寸

增加层的数量和尺寸会导致网络的容量上升,就可以表达更多复杂的函数,这样的优点是可以分类更加复杂的数据,但是可能会出现对训练数据的过拟合。过拟合(Overfitting)是网络对数据中的噪声有很强的拟合能力,而没有重视数据间的潜在基本关系(假设这种关系存在)。就好像是直接背答案,结果把错误的答案也背下来了,但是没有发现各个答案(训练数据)之间的关系。层的数量比较少的时候泛化(generalization)能力也相对较强。

但是这不是说当训练数据不复杂的时候用小一点的网络更好,因为防止神经网络过拟合有很多方法(L2正则化、dropout和输入噪音等,通常正则化是控制神经网络过拟合的好方法)。在实践中用这些方法来控制过拟合比减少网络神经元数量要好。而且小网络更难使用梯度下降等局部方法来进行训练,因为虽然小型网络的损失函数的局部极小值更少,但是比较容易收敛到这些局部最小值,而且这些最小值通常会让模型表现得很差、损失值很高。而大网络有更多局部极小值,但是这些局部极小值对应的模型表现更好,损失更小。因为神经网络是非凸的,所以很难从数学上研究这些特性,但是还是有文章尝试对这些目标函数进行理解(例如The Loss Surfaces of Multilayer Networks)。

实际中如果训练的是小网络,那么最终的损失值可能会不稳定,有时会运气好收敛到一个好地方,另一些情况就可能收敛到不好的地方。如果训练的是大网络那么可以发现许多不同的解决方法,但是最终损失值得变化会小一些,而且对于随机初始化参数好坏的以来也会小很多。

所以不要因为害怕过拟合而使用小网络,应该尽可能使用大网络,然后通过使用正则化技巧来控制过拟合。

参考

交叉熵代价函数(损失函数)及其求导推导:提到这篇文章只是因为里面有交叉熵损失函数的公式,不过博文本身确实值得一看
Neural Networks Part 1: Setting up the Architecture:万恶之源
神经网络笔记1(上):中文版万恶之源,本博文大部分内容都是来自这篇译文的!

猜你喜欢

转载自blog.csdn.net/Geek_of_CSDN/article/details/82383744