Lecture4,5,6:Neural Networks

2018-08-20  本文已影响0人  dapulser

Lecture4,5,6是把神经网络的一些东西分开讲了,个人在笔记的顺序上没有按照原文来,我内容重新编排后整理融合成了这一篇文章

Lecture4,5,6:Neural Networks

1. 神经网络抽象

1.1 抽象生物神经元

    我们在高中就学过神经元是什么样的了。就是他前面有突触,突触可以传递递质,兴奋性递质到达的时候,神经元电位上升,抑制性递质到达,电位下降。总的来说就是接受前面的很多输入,经过一定处理后输出。这是不是跟之前的 Linear Classification一样?也就是说,我们可以将人脑单个神经元的操作视为一个线性分类器。记住,不同突触的能力不一样,有的很厉害,一个突触就可以影响整个神经元的电位,因此,我们还需要一个权重。还有一点,我们知道神经元还有一个“兴奋”的过程,在电位达到一定程度的时候神经元会达到兴奋状态,向后发送脉冲,没达到时是不会发送脉冲的,为了模拟这一点,我们设定一个“激活函数”。总的来说,一个神经元的运行可以理解为,执行操作
output = activation(W\cdot input)

1.2 人工神经网络(Artificial Neural Network)

    什么叫做神经网络?很多个神经元在一起连接成网就叫神经网络【滑稽.jpg】。最简单的神经网络可以看成是这样的。



我们将不同神经元之间分层,这样做有方便计算的考虑。在后面的反向传播中我们会提到这一点。

2. 神经网络基本计算

2.1 前向传播 Forward Propogation

    其实很容易发现,神经网络的前向计算方式跟线性分类器其实是一样的,实际上,线性分类器可以看作是一个只有输入和输出的神经网络。
不过由于激活函数的存在对每一层需要进行两次运算即:
a = W\cdot x_{input} + b
x_{output} = activation(a)

2.2 反向传播 Backward Propogation

    如果我没记错的话,在前面一片文章里,我就提到了一句求导好像就没有下文了,这里比较详细的说说。我们仍然需要简化问题……毕竟……太复杂算得太麻烦,而如果平时只使用框架也不需要频繁进行BP推导。

    我们先从最简单的开始。我们假设我们的网络输出只有一个数字。也就是说,网络是下面这样的,其中W是一个n维行向量,b是一个数字,x是一个n维列向量,y是正确预测结果。我们先看下这是种什么情况。
向前传播是x \to z = W\cdot x + b \to a = \sigma(z),\hat{y} = a\to L=(\hat{y} - y)^2
那么我们从最右边开始倒推我们首先计算\frac{dL}{d\hat{y}} = 2\hat{y} - 2y
接着是\frac{d\hat{y}}{dz} = \hat{y}(1 - \hat{y})
其中z展开之后是z = w_1x_1 + w_2x_2 + …… + b
因此有\frac{dz}{dw} = x , \frac{dz}{db} = 1
最后相乘就得到了参数更新所需的梯度。到现在为止还是简单的链式法则。

    这个还比较简单,我们下面看看当输出有很多个的时候的反向传播。我们先前进一小步,把上面的W改成一个m\times n的矩阵,这样b就是一个列向量了,x是输入还不变,还是n维列向量,当然相对应的y,也变成向量了,m维。
向前传播还是x \to z = W\cdot x + b \to a = \sigma(z),\hat{y} = a\to L=(\hat{y} - y)^2
现在我们开始从右面倒推,下面的表达式不是严格的数学表达式,理解意思即可
首先还是d\hat{y} =da = 2\hat{y} - 2y注意,这个式子里面的都是向量
接着是{dz} = d\hat{y} (\hat{y}(1 - \hat{y}))这个向量相乘,是逐元素相乘
db =dz
我们观察发现z_i = \sum_j W_{i,j} x_j于是有 dW_{i,j} = dz_ix_j因此dW = dz\times x^T
现在我们已经完成了多输入多输出的反向推导。这不就是神经网络中的一层吗?我们接下来给出更通用的形式。

2.3 神经网络中一层的计算

2.3.1 参数

    我们用l表示现在的层数,a^{l - 1}表示这一层从上一层接受的输入向量,W表示这一层的权重,b表示这一层加入的线性偏置,z为这一层的中间变量,a^l表示这一层的输出值。

2.3.2 Forward Propogation(整个网络的前向传播也称“推理”inference)

Input:a^{l - 1},Output:a^{l},Cache:z^l

z^l = W\times a^{l - 1}
a^l = avtivation(z^l)

2.3.3 Backward Propogation

Input:da^{l + 1},Output:da^{l},Cache:dW^l,db^l

dz^l = da^{l + 1} * activation'(z^{l})
dW^l = dz^l\times a^{l - 1}
db^l = dz^l
da^{l} = (W^l)^T \times z^l
注意:

2.3.4 trian

    接下来根据我们获得的dW,db来更新参数就可以了,更新方式并不止一种,在本文3.5节有介绍。

2.4 激活函数 Activation Function

    激活函数在之前不太好提,就是因为反向传播说的不很详细,不好讲激活函数的优缺点,下面是几种激活函数的介绍。

2.4.1 Sigmoid函数

\sigma (x) = \frac{1}{1 + e^{-x}}

2.4.2 Tanh函数

tanh(x) = 2\sigma(2x) - 1
和sigmoid一样,它也存在饱和问题,但是和sigmoid不同的是,它的输出是零中心的。因此,在实际操作中,tanh比sigmoid更受欢迎。注意,tanh是一个简单放大的sigmoid.

2.4.3 ReLU函数

ReLU(x) = max(0,x)
线性整流函数(Rectified Linear Unit, ReLU),又称修正线性单元

2.4.4 Leaky ReLU

\begin{equation} f(x)=\left\{ \begin{aligned} \alpha x (x<0) \\ 0 (x=0) \\ x (x>0) \\ \end{aligned} \right. \end{equation}
Leaky ReLU是为解决“ReLU死亡”问题的尝试。其中\alpha为一个比较小的数比如0.01.

3. 训练神经网络

3.1 数据预处理

    在数据预处理中我们有三个常用记号:数据矩阵X,数据大小[N \times D],其中N代表样本数量,D代表数据维度

3.1.1 均值减法(Mean subtraction)

    均值减法(Mean subtraction) 是预处理最常用的形式。它对数据中每个独立特征减去平均值,从几何上可以理解为在每个维度上都将数据云的中心都迁移到原点。

3.1.2 归一化(Normalization)

    归一化(Normalization) 是指将数据的所有维度都归一化,使其数值范围都近似相等。有两种常用方法可以实现归一化。第一种是先对数据做零中心化(zero-centered)(其实就是上面的均值减法)处理,然后每个维度都除以其标准差。第二种方法是对每个维度都做归一化,使得每个维度的最大和最小值是1和-1。但是这个预处理操作只有在确信不同的输入特征有不同的数值范围(或计量单位)时才有意义。在图像处理中,由于像素的数值范围几乎是一致的(都在0-255之间),所以进行这个额外的预处理步骤并不是很必要。


可视化结果

3.2 权值初始化

3.2.1 一种错误的方案:全零初始化

   如果我们的权重是全零的,那么他们的输出也相同,梯度也相同,什么都是一样的,这样用很多神经元就没有意义了。所以,全零初始化是一种错误的初始化。

3.2.2 随机初始化

   我们使用很多神经元的目的就是为了让他们各自不一样,以此来学习出不同的特征,加大训练的准确度,那怎么着就不一样了呢?干脆随机初始化吧。就这样。但是这样还会有一个不很容易注意到的小问题,在实践中会遇到,我们生成的随机数越多,他们的方差就越大,以常用的Python中的numpy为例,numpy默认生成的是高斯分布,他的均值是指定好的,数字越多,方差自然越大,我们可以这样做

w = np.random.randn(n) / sqrt(n)

这样做会使得生成的方差都为1,使得神经网络在一开始的时候每一层生成相似的结果。实践证明,这可以提高收敛速度。

3.3 批量归一化 Batch Normalization

   我们在数据预处理中介绍了归一化,Batch Normalization说简单了就是在神经网络内部进行归一化处理,比如说



在实践中,使用了批量归一化的网络对于不好的初始值有更强的鲁棒性。

3.4 正则化

   我们在之前介绍过L2正则化,现在,介绍另一种正则化Dropout。
   Dropout就是在训练的时候随机废掉一些节点,相当于把网络搞小了。用图片来表示就是这样的:


   核心思路:在训练过程中,随机失活可以被认为是对完整的神经网络抽样出一些子集,每次基于输入数据只更新子网络的参数(然而,数量巨大的子网络们并不是相互独立的,因为它们都共享参数)。在测试过程中不使用随机失活,可以理解为是对数量巨大的子网络们做了模型集成(model ensemble),以此来计算出一个平均的预测。
   需要注意的是,我们在训练的时候会规定一个参数

   在这里引入了一个初始化为0的变量v和一个超参数mu。说得不恰当一点,这个变量(mu)在最优化的过程中被看做动量(一般值设为0.9),但其物理意义与摩擦系数更一致。这个变量有效地抑制了速度,降低了系统的动能,不然质点在山底永远不会停下来。通过交叉验证,这个参数通常设为[0.5,0.9,0.95,0.99]中的一个。和学习率随着时间退火(下文有)类似,动量随时间变化有时能略微改善优化的效果,其中动量在学习过程的后阶段会上升。一个典型的设置是刚开始将动量设为0.5而在后面的多个训练轮次中慢慢提升到0.99。

3.5.3 Nesterov动量更新

   Nesterov动量更新说实话我没怎么看明白他到底想讲什么意思……有时间看论文原文再回来填坑吧。
v_{pre} = v
v = mu * v ‐ learningrate * dx
x =x ‐mu * v_{pre} + (1 + mu) * v

3.5.4 学习率退火

   我们在之前已经提到过,学习率过大会导致在训练后期网络难以收敛,在最优解附近跳来跳去,为了解决这个问题,我们将使学习率不断下降,常用的有两种。

3.5.5 逐参数适应学习率方法

我们前面讨论的方案都是对全局学习率进行操控,但是,我们知道,神经网络每一点都是不同的,如果我们对每个参数都用不同的学习率怎么样呢?
下面为了方便(懒得打公式,下面的式子写成写程序都简单,写公式麻烦的一比)
下面的结论都是没有推导的(如果我看视频没走神的话),想看可以找原论文看

3.5.5.1 Adagrad
cache += dx**2
x += ‐ learning_rate * dx / (np.sqrt(cache) + eps)

其中eqs一般设置为[0.000000001,0.0001]防止除0。

3.5.5.2 RMSprop
cache = decay_rate * cache + (1 ‐ decay_rate) * dx**2
x += ‐ learning_rate * dx / (np.sqrt(cache) + eps)

decay_rate是一个超参数,常用的值是[0.9,0.99,0.999]。

3.5.5.3 Adam
m = beta1*m + (1‐beta1)*dx
v = beta2*v + (1‐beta2)*(dx**2)
x += ‐ learning_rate * m / (np.sqrt(v) + eps)

RMSProp的动量版【滑稽.jpg】推荐的参数值eps=0.00000001, beta1=0.9, beta2=0.999

上一篇下一篇

猜你喜欢

热点阅读