Skip to main content

神经网络(容易被忽视的基础知识)

神经网络(容易被忽视的基础知识)




神经网络(容易被忽视的基础知识)

以下大多笔记主要来自cs231n和其他深度学习资料和一些我自己的补充,首先CS231n一个非常不错的deep learning课,值得一看,奉上链接,然后,cs231n本身是有笔记的-链接,最后,知乎上的一些大神对这个笔记进行了翻译---链接。在这里,我主要是将一些重要的笔记,我认为是比较容易被我们忽视的,加上查阅其他博文,做一个笔记和心得总结!
这次主要讲解的内容有:
  • 基本神经元
  • 作为线性分类器的单个神经元
  • 为什么要是深度神经网而不是”肥胖“神经网络?
  • 为什么在人工神经网络中的神经元需要激活函数?
  • 一些主要的激活函数
  • 神经网络中的偏置有什么意义?
  • 初始化神经网络的参数可以全为0吗,其他普通线性分类器呢?
  • 交叉熵损失存在的意义和与平方差损失相比有什么好处?

神经元

下面图表的左边展示了一个生物学的神经元,右边展示了一个常用的数学模型。乍一看还是有点相似的,事实上也是,人工神经网络中的神经元也有受到生物神经元的启发。总结要点:
  • 在生物的神经元(也就是左图)中,树突将信号传递到细胞体,信号在细胞体中相加。如果最终之和高于某个阈值,那么神经元将会激活,向其轴突输出一个峰值信号,注意这里输出是一个脉冲信号!
  • 在数学计算模型(也就是右图)中,首先将输入进行加权求和加上偏执,得到待激励值,然后将该值作为输入,输入到激活函数中,最后输出的是一个激励后的值,这里的激活函数可以看成对生物中神经元的激活率建模。由于历史原因,激活函数常常选择使用sigmoid函数 \sigma当然还有很多其他激活函数,下面再仔细聊!
  • 需要注意:1.一个神经元可以看成包含两个部分,一个是对输入的加权求和加上偏置,一个是激活函数对求和后的激活或者抑制。2.注意生物中的神经元要复杂的多,其中一个是生物中的输出是一个脉冲,而现在大多数的数学模型神经输出的就是一个值,当然现在也有一些脉冲人工神经网络,可以自行了解!





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

  • 比如基础的逻辑回归,结合上面的神经元知识,可以发现,逻辑回归就是激活函数是sigmoid的单层简单神经网络。也就是说,只要在神经元的输出端有一个合适的损失函数,就能让单个神经元变成一个线性分类器。因此说,那些线性的分类器本身就是一个单层神经网络
  • 但注意,对于非线性的模型:SVM和神经网络走了两条不同的道路:神经网络通过多个隐层的方法来实现非线性的函数,有一些理论支持(比如说带隐层的神经网络可以模拟任何函数),但是目前而言还不是非常完备;SVM则采用了kernel trick的方法,这个在理论上面比较完备(RKHS,简单地说就是一个泛函的线性空间)。两者各有好坏,神经网络最近的好处是网络设计可以很灵活,有很多的trick&tip,很多理论都不清不楚的;SVM的理论的确漂亮,但是kernel设计不是那么容易,所以最近也就没有那么热了。

为什么要是深度神经网而不是”肥胖“(宽度)神经网络?






“肥胖”网络的隐藏层数较少,如上左图。虽然有研究表明,浅而肥的网络也可以拟合任何的函数,但它需要非常的“肥胖”,可能一层就要成千上万个神经元。而这直接导致的后果是参数的数量增加到很多很多。
也有实验表明,也就是上图的实验,我们可以清楚的看出,当准确率差不多的时候,参数的数量却相差数倍。这也说明我们一般用深层的神经网络而不是浅层“肥胖”的网络。
注意:说神经网络多少层数的时候一般不包括输入层。 在神经网络中的激活主要讲的是梯度的更新的激活

为什么在人工神经网络中的神经元需要激活函数?






上图可看做普通的线性分类器,也就是线性回归方程。这个比较基础,效果如右图。当然有时候我们发现这样的线性分类器不符合我们要求时,我们很自然的想到那我们就加多一层,这样可以拟合更加复杂的函数,如下图a:




图a
图b

但同时当我们动笔算下, 就会发现, 这样一个神经网络组合起来,输出的时候无论如何都还是一个线性方程。如上图b右边,就只能这样分类。(那也太蠢了吧)。下图表示一层加如激活函数的情况!





一层很多时候是远远不够的,前面讲过,简单的线性分类器就可以看成是一层的神经网络,比如上图,激活函数是signmoid,那就可以看成是二分类的逻辑回归!
下面扩展到多层,如下图1,2:




图1
图2

图1是一个简单的MLP(全链接神经网络),图2的右边课简单表示左图的可视化,那么对比之前的无激活函数的图,很明显是更加的非线性,拟合能力也会更强,同时可以想到,当层数更多,其能力也会越来越强!
简单来说:就是使得神经网络具有的拟合非线性函数的能力,使得其具有强大的表达能力!
简单扩展,神经网络的万能近似定理:一个前馈神经网络如果具有线性层和至少一层具有"挤压"性质的激活函数(如signmoid等),给定网络足够数量的隐藏单元,它可以以任意精度来近似任何从一个有限维空间到另一个有限维空间的borel可测函数。
要相符上面的定理,也就是想拟合任意函数,一个必须点是“要有带有“挤压”性质的激活函数”。这里的“挤压”性质是因为早期对神经网络的研究用的是sigmoid类函数,所以对其数学性质的研究也主要基于这一类性质:将输入数值范围挤压到一定的输出数值范围。(后来发现,其他性质的激活函数也可以使得网络具有普适近似器的性质,如ReLU 。

一些主要的激活函数

sigmoid

优点:有较好的解释性
缺点:1.Sigmoid函数饱和使梯度消失。sigmoid神经元有一个不好的特性,就是当神经元的激活在接近0或1处时会饱和:在这些区域,梯度几乎为0。2.输出不是零中心的,这一情况将影响梯度下降的运作,因为如果输入神经元的数据总是正数,那么关于w的梯度在反向传播的过程中,将会要么全部是正数,要么全部是负数,这样梯度下降权重更新时出现z字型的下降。这样收敛会变得异常的慢。(这也是为什么要一直保持为数据的0中心化)—–但这个问题比较小3.exp()在深度神经网络时候相比其他运算就比较慢





Tanh非线性函数

优点:1.它的输出是零中心的。因此,在实际操作中,tanh非线性函数比sigmoid非线性函数更受欢迎。
缺点:1.和Sigmoid函数一样,饱和使梯度消失。计算慢





ReLU

优点:1.ReLU对于随机梯度下降的收敛有巨大的加速作用( Krizhevsky 等的论文alexnet指出有6倍之多)。据称这是由它的线性,非饱和的公式导致的;2.注意:现在大部分的DNN用的激活函数就是ReLu
缺点:1.当x是小于0的时候,那么从此所以流过这个神经元的梯度将都变成0;这个时候这个ReLU单元在训练中将死亡(也就是参数无法更新),这也导致了数据多样化的丢失(因为数据一旦使得梯度为0,也就说明这些数据已不起作用)。





Leaky ReLU

优点:1.非饱和的公式;2.Leaky ReLU是为解决“ReLU死亡”问题的尝试
缺点:1.有些研究者的论文指出这个激活函数表现很不错,但是其效果并不是很稳定
Kaiming He等人在2015年发布的论文Delving Deep into Rectifiers中介绍了一种新方法PReLU,把负区间上的斜率当做每个神经元中的一个参数。然而该激活函数在在不同任务中均有益处的一致性并没有特别清晰。





ELU

指数线性单元(Exponential Linear Units, ELU) ELU的公式为:





ELU.png 函数曲线如下:





Maxout

  • Maxout是对ReLU和leaky ReLU的一般化归纳
优点:1.拥有ReLU单元的所有优点(线性操作和不饱和),而没有它的缺点(死亡的ReLU单元)
缺点 :1.每个神经元的参数数量增加了一倍,这就导致整体参数的数量激增。难训练,容易过拟合

怎么用激活函数

“那么该用那种呢?”用ReLU非线性函数。注意设置好学习率,(如果学习率设置得太高,可能会发现网络中40%的神经元都会死掉(在整个训练集中这些神经元都不会被激活)。通过合理设置学习率,这种情况的发生概率会降低。),解决方案:或许可以监控你的网络中死亡的神经元占的比例。如果单元死亡问题困扰你,就试试Leaky ReLU或者Maxout,不要再用sigmoid了。也可以试试tanh,但是其效果应该不如ReLU或者Maxout。

神经网络中的偏置有什么意义?

最基础的神经元感知器如下:





这个例子中输入为x_1, x_2, x_3 三个变量, 输出为0或1. 当三个变量的加权和\sum_i w_ix_i大于某个阈值的时候, 输出为1, 反之为0.
output = \left\{ \begin{array}{ll} 0 & \textrm{if $\sum_iw_ix_i < $threshold }\\ 1 & \textrm{if $\sum_iw_ix_i \geq $threshold } \end{array} \right.
注意这里的阈值, 它度量了神经元产生正(负)激励的难易程度.也就是说,在上文的模型中, 阈值越大, 产生正激励的难度越大.
而为了更好的计算和表达,我们可以把其中的b和 \sum_i w_ix_i  移到同一边,同时给他取了一个不错的名字,叫做偏置Bias,而w叫做权重weight!
output = \left\{ \begin{array}{ll} 0 & \textrm{if $\sum_iw_ix_i +b< 0$}\\ 1 & \textrm{if $\sum_iw_ix_i +b\geq 0$ } \end{array} \right.
也就是说: 偏置的大小度量了神经元产生正(负)激励的难易程度.
这是最简单的感知器神经元. 而现代神经网络中常用的Sigmoid, tanh或是ReLU都是在\textbf{w} \cdot \textbf{x} + b的基础上加上一个激活函数, Bias也就是b的意义是一样的。
同时对于偏置需要注意的点是:偏置是不需要正则化的,并且正则化偏置的话会导致欠拟合。我们从意义上去理解话,若对偏置正则化(惩罚),会导致激活变得更加简单,偏差就会上升,学习的能力就会下降!

初始化神经网络的参数可以全为0吗,其他普通线性分类器呢?

在讲初始化前,先简单复习下反向传播算法可表示为以下几个步骤:
  1. 进行前馈传导计算,利用前向传导公式,得到 L_{2},L_{3} ,直到输出层 L_{nl} 的激活值。
  2. 对输出层(第 nl 层),计算:





3. 对于 l=nl-1,nl-2,nl-3,...,2 的各层,计算:





4. 计算最终需要的偏导数值:





5. 对 f 举个例子,若表示sigmoid函数,就可以计算得到





从上往下,其中y代表正确label, a^{nl} 代表最后一层的输出, z^{nl} 表达的是最后一层的加权求和值, f  代表的是激活函数,δ代表的是要反向传播回去的误差, l 代表第l层,w,b表示权重和偏置。
我们前面说过:线性分类器逻辑回归模型可以算作为一个简单的单层神经网络。为了更好的说明问题,假设一个场景,在十类分类的数字手写识别中,我们将本来二分类的逻辑回归推到多分类的softmax,也就是说,神经元的激活函数是softmax。也就是下图,然后分析:





  • 输入层(不算是一层):28×28=784个输入单元。也就是n=784
  • 输出层:10个输出单元, 激活函数为softmax,也就是m=10
  • 它由两组参数组成: W和b, 前者是一个10×784维的权值矩阵, 后者是长度为10的bias 列向量.现在我们假设把它们全部初始化为0:
  • 第一次正向计算过程中, 简单算出,输出层的所有输出为0.5.
  • 反向时,根据前面复习的反向传播算法,首先计算δ,假如 y^{i} =1, 即输入x对应的数字为i, 那么除了第i个神经元的δ是不同的,其他的值都是一样, 又于计算权重w梯度的公式得知,注意此时的 a^{l} 就是输入值x,又由于输入向量x的元素之间的不同, 所以即使δ是一样的,但最终得到的10个梯度值有很大概率是不同且非0的, 所以第一次BP过程可以取得成效并将一些w和所有的b变成非0值.
  • 由于w值已经变得不相同了,这个时候前向传播和后向传播也就会正常的进行, 后面的训练显然也可以顺利进行.
得出结论,没有隐层时, 可以将所有的参数初始化为0.
如果有隐层呢?假如还是用sigmoid激活函数呢。来个更简单MLP,





  • 第一次正向计算过程中, 简单算出,输出层的所有输出为神经元4,5,6输出的值(初始化w,b为0,激活函数是sigmoid)都是一样的,都是0.5
  • 第一个反向传播过程,由权重的更新公式,算出即 W_{6,4},W_{6,5} (神经元6和4,5之间的权重)是一样的,同样算出 W_{4,1},W_{4,2},W_{4,3},W_{5,1},W_{5,2},W_{5,3} 都一样,但非0,由sgd算法赋值。
  • 然后第二次正向时,算出神经元4,5的输出一样(因为4,5都是接受1,2,3神经元的输入,且权重相同),但和神经元6的输出不一样。
  • 然后第二次反向时,根据公式,得出 W_{4,1},W_{5,1}一样,W_{4,2},W_{5,2}一样,W_{4,3},W_{5,3} 一样。原因在于计算 W_{4,1},W_{5,1}用的是一样的输出,这个也可以从公式得出!
  • 最后结论,一直循环,发现学习得到的模型中, 所有的隐单元都是相同的(在这里就是神经元4,5永远 W_{4,1},W_{5,1}一样,W_{4,2},W_{5,2}一样,W_{4,3},W_{5,3} 一样). 学习失败.
对于隐层使用了其他函数比如ReLU: f(net)=max(0,net)其结果也是一样的: 除了输入层的输出值为x本身, 不为0, 其余的所有层的输出都为0. BP时所有梯度也都为0, 包括输出层. 这意味着所有的非输入神经元都是无意义的. 学习失败.对于卷积神经网络,循环神经网络也是一样的,故一般会用其他初始化方式。
最后最后的结论是, 一般只在训练SLP/逻辑回归模型时才使用0初始化所有参数., 更实用的结论是, 深度模型都不会使用0初始化所有参数.

交叉熵存在的意义和与平方差相比有什么好处?

一个非常常见的,非常漂亮的成本函数是“交叉熵”(cross-entropy)。交叉熵产生于信息论里面的信息压缩编码技术,但是它后来演变成为从博弈论到机器学习等其他领域里的重要技术手段。它的定义如下:





其中,y 是我们预测的概率分布, y’ 是实际的分布
1.交叉熵是正的,2.当所有输入x的输出都能接近期望输出y的话,交叉熵的值将会接近 0。这两个特征在直觉上我们都会觉得它适合做代价函数。事实上,我们的均方代价函数也同时满足这两个特征。然而....

为什么在神经网络中用交叉熵代替二次代价函数?

还是从反向传播,参数更新的角度理解更新参数的公式如下:





a 是 神经元的输出,其中 a = σ(z), z = wx + b,可知,偏导数受激活函数的导数影响,假设这里是传统的sigmoid激活函数(在之前很多时候都是)。那么sigmoid 的导数在输出接近 0 和 1 的时候 是非常小的,这会导致一些实例在刚开始训练时学习得非常慢!
但是如果换成是交叉熵loss,其更新权重公式最后推导得出如下:





由以上公式可知,权重学习的速度受到 σ(z) − y 影响,更大的误差,就有更快的学习速度,还避免了二次代价函数方程中因 σ′(z) 导致的学习缓慢。
但是现在也有很多其他的激活函数并不会产生饱和状态,那么均方差也有其存在价值。

Comments

Popular posts from this blog

tf.slice用法

函数:tf.slice(inputs, begin, size, name) 作用:从列表、数组、张量等对象中抽取一部分数据 begin和size是两个多维列表,他们共同决定了要抽取的数据的开始和结束位置 begin表示从inputs的哪几个维度上的哪个元素开始抽取 ,begin基于下标0开始 size表示在inputs的各个维度上抽取的元素个数 若begin[]或size[]中出现-1,表示抽取对应维度上的所有元素 测试:如下是一个三维Tensor数据表 import tensorflow as tf import numpy as np #这个代码演示测试tf.slice函数 t=[[[1,1,1],[2,2,2]],[[3,3,3],[4,4,4]],[[5,5,5],[6,6,6]]] s1=tf.slice(t,[1,0,0],[1,1,3]) #begin=[1,0,0],表明从第1维的[0,0]开始切片,也就是从[3,3,3]开始 #size=[1,1,3]表明切片后是1维,切片的尺寸为1行3列。 s2=tf.slice(t,[0,1,1],[2,1,1]) #begin=[0,1,1],表明从第0维的[1,1]开始切片,也就是从[2,2,2]的第二个2开始 #size=[2,1,1]表明切片后是2维,切片的尺寸为1行1列。 sess=tf.Session() print(sess.run(s1)) #输出结果为 [[[3 3 3]]] ,一行三列 print(sess.run(s2)) #输出结果: [[[2]] [[4]]] #测试维度: print(sess.run(s1).shape) print(sess.run(s2).shape) #结果如下 (1, 1, 3) (2, 1, 1) 下面的立体图说明切片的实际方法: (size的大小不能大于切片刀下方表格数据的尺寸,维度数不能大于被切片数据的维度) 以s2为例

TF random_normal和Python random.normal的用法

先看TF tf.random_normal()函数用于从服从指定正太分布的数值中取出指定个数的值。 tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None) 再看NP np.random.normal()给出均值为loc,标准差为scale的高斯随机数(场). numpy.random.normal(loc=0.0, scale=1.0, size=None) 所谓正态分布==高斯分布,所以二者一回事 mean==loc==均值(或称期待值) stddev==scale==标准差 shape==size==输出形状,二者在处理这个参数时候(a,b)==[a,b],其中,np的normal对参数格式要求更灵活一些。 比如创建随机数的一行两列数组: np.random.normal([2])==np.random.normal((2))==np.random.normal(0,1,2)注意最后一种用法必须带上前面两个参数,否则传递参数时候会把2当作均值传递 而tf的random_normal对shape的要求不能是数字,必须为[]或()格式 再探究一下形状参数的规律,看下面代码 a=np.random.normal(0,1,(2,1)) b=np.random.normal(0,1,(1,2)) c=np.random.normal(0,1,(2)) print(a) print(a.shape) print(b) print(b.shape) print(c) print(c.shape) 程序输出如下 [[ 1.11843359] [-0.69801129]] (2, 1) [[-0.87110089 -0.46089345]] (1, 2) [ 0.88227522 -0.26728542] (2,) a,b是二维的矩阵,c是一维的矩阵(严格说一维的不是矩阵)

tf.transpose矩阵的转置

二维矩阵的转置很简单,元素的i,j坐标交换就行。多为矩阵的转换复杂,遵循什么规律呢? 先看看代码: import tensorflow as tf import numpy as np print("二维矩阵的转置\n") t=[[1,2,3],[4,5,6]] t1=tf.transpose(t) t2=tf.transpose(t,[0,1])#[0,1]是正常的坐标轴排列顺序,运行结果不转置 t3=tf.transpose(t,[1,0]) sess=tf.Session() print("Default Transepose is:\n",sess.run(t1),"\n[0,1] Tranpose is:\n",sess.run(t2),"\n[1,0] Tranpose is:\n",sess.run(t3)) print("三维矩阵的转置") t=[[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]#这个三维矩阵有两张表格,每个表格都是2行3列 print("先测试不带perm参数的默认转置和参数顺序为不转置的正常坐标轴顺序[0,1,2]转置结果") t1=tf.transpose(t) t2=tf.transpose(t,[0,1,2]) print(sess.run(t1)) print(sess.run(t2)) 代码输出如下: 二维矩阵的转置 Default Transepose is: [[1 4] [2 5] [3 6]] [0,1] Tranpose is: [[1 2 3] [4 5 6]] [1,0] Tranpose is: [[1 4] [2 5] [3 6]] 三维矩阵的转置 先测试不带perm参数的默认转置和参数顺序为不转置的正常坐标轴顺序[0,1,2]转置结果 [[[ 1 7] [ 4 10]] [[ 2 8] [ 5 11]] [[ 3 9] [ 6 12]]] 这里分析一下元素的坐标在转换前和转换后的变化: 1:(0,0,0)==>(