博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(六) 6.2 Neurons Networks Backpropagation Algorithm
阅读量:5769 次
发布时间:2019-06-18

本文共 2304 字,大约阅读时间需要 7 分钟。

今天得主题是BP算法。大规模的神经网络可以使用batch gradient descent算法求解,也可以使用 stochastic gradient descent 算法,求解的关键问题在于求得每层中每个参数的偏导数,BP算法正是用来求解网络中参数的偏导数问题的。

先上一张吊炸天的图,可以看到BP的工作原理:

下面来看BP算法,用m个训练样本集合\textstyle \{ (x^{(1)}, y^{(1)}), \ldots, (x^{(m)}, y^{(m)}) \}来train一个神经网络,对于该模型,首先需要定义一个代价函数,常见的代价函数有以下几种:

1)0-1损失函数:(0-1 loss function)

2)平方损失函数:(quadratic loss function)

3)绝对值损失函数:(absolute loss function)

4)负log损失函数(log loss function)

损失函数的意义在于,假设函数(hypothesis function,即模型)的输出与数据标签的值月接近,损失函数越小。反之损失函数越大,这样减小损失函数的值,来求得最优的参数即可,最后将最优的参数带入带假设函数中,即可求得最终的最优的模型。

在Neurons Network中,对于一个样本(x,y),其损失函数可表示为  

  \begin{align} J(W,b; x,y) = \frac{1}{2} \left\| h_{W,b}(x) - y \right\|^2. \end{align}

上式这种形式是平方损失函数(注意若采用交叉熵损失则与此损失形式不一样),对于所有的m个样本,对于所有训练数据,总的损失函数为:

  \begin{align} J(W,b) &= \left[ \frac{1}{m} \sum_{i=1}^m J(W,b;x^{(i)},y^{(i)}) \right]                        + \frac{\lambda}{2} \sum_{l=1}^{n_l-1} \; \sum_{i=1}^{s_l} \; \sum_{j=1}^{s_{l+1}} \left( W^{(l)}_{ji} \right)^2  \\ &= \left[ \frac{1}{m} \sum_{i=1}^m \left( \frac{1}{2} \left\| h_{W,b}(x^{(i)}) - y^{(i)} \right\|^2 \right) \right]                        + \frac{\lambda}{2} \sum_{l=1}^{n_l-1} \; \sum_{i=1}^{s_l} \; \sum_{j=1}^{s_{l+1}} \left( W^{(l)}_{ji} \right)^2 \end{align}

上式中第一项为均方误差项,第二项为正则化项,用来限制权重W的大小,防止over-fitting,也即贝叶斯学派所说的给参数引入一个高斯先验的MAP(极大化后验)方法。\textstyle \lambda为正则项的参数,用来控制两项的相对重要性, 比如若\textstyle \lambda很大时,参数W,b必须很小才能使得最终的损失函数J(W,b) 很小。

常见的分类或者回归问题,都可以用这个损失函数,注意分类时标签y是离散值,回归时对于sigmod函数y为(0,1)之间的连续值。对于tanh为(-1,1)之间的值。

BP算法的目标就是求得一组最优的W、b ,使得损失函数 \textstyle J(W,b)的值最小

首先将每个参数 \textstyle W^{(l)}_{ij} 和 \textstyle b^{(l)}_i初始化为一个很小的随机值(比如说,使用正态分布 \textstyle {Normal}(0,\epsilon^2) 生成的随机值,其中 \textstyle \epsilon 设置为 \textstyle 0.01 ),然后使用批梯度下降算法来优化\textstyle W^{(l)}_{ij} 和 \textstyle b^{(l)}_i的值,因为\textstyle J(W, b) 是非凸函数,即存在不止一个极值点,梯度下降算法很可能会收敛到局部极值处,但通常效果很不错(在浅层网络中,比如说三层),需要强调的是要将参数随机初始化,而不是全部置0,如果所有参数都用相同的值作为初始值,那么所有隐藏层单元最终会得到与输入值有关的、相同的函数(也就是说,对于所有hidden unit \textstyle i\textstyle W^{(1)}_{ij}都会取相同的值,那么对于任何输入 \textstyle x 都会有:\textstyle a^{(2)}_1 = a^{(2)}_2 = a^{(2)}_3 = \ldots ),随机初始化会消除这种对称效果。

批梯度下降算法中,每一次迭代都按照如下公式对参数 \textstyle W 和\textstyle b 进行更新:

  \begin{align} W_{ij}^{(l)} &= W_{ij}^{(l)} - \alpha \frac{\partial}{\partial W_{ij}^{(l)}} J(W,b) \\ b_{i}^{(l)} &= b_{i}^{(l)} - \alpha \frac{\partial}{\partial b_{i}^{(l)}} J(W,b) \end{align}

其中J(W,b)包含了所有的样本,\textstyle \alpha 是学习速率,对于多层神经网络,如何计算每一层参数的偏导数是关键问题,BP算法正使用来计算每一项的偏导数的。

首先来看对于单个样例,参数\textstyle W^{(l)}_{ij} 和 \textstyle b^{(l)}_i 的偏导数分别为 \textstyle \frac{\partial}{\partial W_{ij}^{(l)}} J(W,b; x, y) 和 \textstyle \frac{\partial}{\partial b_{i}^{(l)}} J(W,b; x, y)

有了单个样例的偏导数后,根据,就可以很好求出损失函数 \textstyle J(W,b) 的偏导数:

  \begin{align} \frac{\partial}{\partial W_{ij}^{(l)}} J(W,b) &= \left[ \frac{1}{m} \sum_{i=1}^m \frac{\partial}{\partial W_{ij}^{(l)}} J(W,b; x^{(i)}, y^{(i)}) \right] + \lambda W_{ij}^{(l)} \\ \frac{\partial}{\partial b_{i}^{(l)}} J(W,b) &= \frac{1}{m}\sum_{i=1}^m \frac{\partial}{\partial b_{i}^{(l)}} J(W,b; x^{(i)}, y^{(i)}) \end{align}

 \textstyle \lambda 并不作用于bais unit b,所以第二个式子中没有第二项。

先看如下的式子,l+1层的输入等于l层的加权输出求和,即

课件hidden layer的输入z为参数的方程,为了求解对每个样本中参数\textstyle W^{(l)}_{ij} 和 \textstyle b^{(l)}_i 的偏导数,可以用根据链式求导法则有:

 

我们把上边的第一项称为残差,有了以上链式求导的思想,为了求得各个参数的偏导数,我们需要求得每一层的每个单元的残差。下面反向传播算法的思路:

1)给定 \textstyle (x,y),我们首先进行“前向传导”,计算出网络中所有的激活值,包括 \textstyle h_{W,b}(x) 的输出值

2)对第 \textstyle l 层的每个节点 \textstyle i,计算出其“残差” \textstyle \delta^{(l)}_i,该残差表明节点对最终输出值的残差产生多少影响

3)对于最终的输出节点,直接算出网络产生的激活值与实际值之间的差距,将这个差距定义为 \textstyle \delta^{(n_l)}_i 

4)对于隐藏单元,将第 \textstyle l+1 层节点的残差的加权平均值计算 \textstyle \delta^{(l)}_i,这些节点以 \textstyle a^{(l)}_i 作为输入到 \textstyle l+1 层

下面将给出反向传导算法的细节:

1)进行前馈传导计算,利用前向传导公式,得到 \textstyle L_2, L_3, \ldots 直到输出层 \textstyle L_{n_l} 的激活值。

2)对于第 \textstyle n_l 层(输出层)的每个输出单元 \textstyle i,我们根据以下公式计算残差:

  \begin{align} \delta^{(n_l)}_i = \frac{\partial}{\partial z^{(n_l)}_i} \;\;         \frac{1}{2} \left\|y - h_{W,b}(x)\right\|^2 = - (y_i - a^{(n_l)}_i) \cdot f'(z^{(n_l)}_i) \end{align}

推倒:

  \begin{align} \delta^{(n_l)}_i &= \frac{\partial}{\partial z^{n_l}_i}J(W,b;x,y)  = \frac{\partial}{\partial z^{n_l}_i}\frac{1}{2} \left\|y - h_{W,b}(x)\right\|^2 \\  &= \frac{\partial}{\partial z^{n_l}_i}\frac{1}{2} \sum_{j=1}^{S_{n_l}} (y_j-a_j^{(n_l)})^2  = \frac{\partial}{\partial z^{n_l}_i}\frac{1}{2} \sum_{j=1}^{S_{n_l}} (y_j-f(z_j^{(n_l)}))^2 \\  &= - (y_i - f(z_i^{(n_l)})) \cdot f'(z^{(n_l)}_i)  = - (y_i - a^{(n_l)}_i) \cdot f'(z^{(n_l)}_i) \end{align}

3)对 \textstyle l = n_l-1, n_l-2, n_l-3, \ldots, 2 的各个层,第 \textstyle l 层的第 \textstyle i 个节点的残差计算方法如下:

  \delta^{(l)}_i = \left( \sum_{j=1}^{s_{l+1}} W^{(l)}_{ji} \delta^{(l+1)}_j \right) f'(z^{(l)}_i)

有了最后一层的层差,可以计算前一层的残差:

  \begin{align} \delta^{(n_l-1)}_i &=\frac{\partial}{\partial z^{n_l-1}_i}J(W,b;x,y)  = \frac{\partial}{\partial z^{n_l-1}_i}\frac{1}{2} \left\|y - h_{W,b}(x)\right\|^2   = \frac{\partial}{\partial z^{n_l-1}_i}\frac{1}{2} \sum_{j=1}^{S_{n_l}}(y_j-a_j^{(n_l)})^2 \\ &= \frac{1}{2} \sum_{j=1}^{S_{n_l}}\frac{\partial}{\partial z^{n_l-1}_i}(y_j-a_j^{(n_l)})^2  = \frac{1}{2} \sum_{j=1}^{S_{n_l}}\frac{\partial}{\partial z^{n_l-1}_i}(y_j-f(z_j^{(n_l)}))^2 \\ &= \sum_{j=1}^{S_{n_l}}-(y_j-f(z_j^{(n_l)})) \cdot \frac{\partial}{\partial z_i^{(n_l-1)}}f(z_j^{(n_l)})  = \sum_{j=1}^{S_{n_l}}-(y_j-f(z_j^{(n_l)})) \cdot  f'(z_j^{(n_l)}) \cdot \frac{\partial z_j^{(n_l)}}{\partial z_i^{(n_l-1)}} \\ &= \sum_{j=1}^{S_{n_l}} \delta_j^{(n_l)} \cdot \frac{\partial z_j^{(n_l)}}{\partial z_i^{n_l-1}}  = \sum_{j=1}^{S_{n_l}} \left(\delta_j^{(n_l)} \cdot \frac{\partial}{\partial z_i^{n_l-1}}\sum_{k=1}^{S_{n_l-1}}f(z_k^{n_l-1}) \cdot W_{jk}^{n_l-1}\right) \\ &= \sum_{j=1}^{S_{n_l}} \delta_j^{(n_l)} \cdot  W_{ji}^{n_l-1} \cdot f'(z_i^{n_l-1})  = \left(\sum_{j=1}^{S_{n_l}}W_{ji}^{n_l-1}\delta_j^{(n_l)}\right)f'(z_i^{n_l-1}) \end{align}

4)将上式中的 \textstyle n_l-1 与 \textstyle n_l 的关系替换为 \textstyle l 与 \textstyle l+1 的关系,就可以得到:

\delta^{(l)}_i = \left( \sum_{j=1}^{s_{l+1}} W^{(l)}_{ji} \delta^{(l+1)}_j \right) f'(z^{(l)}_i)

5)根据链式求导法则,计算方法如下:

\begin{align} \frac{\partial}{\partial W_{ij}^{(l)}} J(W,b; x, y) &= a^{(l)}_j \delta_i^{(l+1)} \\ \frac{\partial}{\partial b_{i}^{(l)}} J(W,b; x, y) &= \delta_i^{(l+1)}. \end{align}
其中,第二项的计算公式如下:
根据
,有:
 

概括一下整个算法:

1)进行前馈传导计算,利用前向传导公式,得到 \textstyle L_2, L_3, \ldots直到输出层 \textstyle L_{n_l} 的激活值。

2)对输出层(第 \textstyle n_l 层),计算:

  \begin{align} \delta^{(n_l)} = - (y - a^{(n_l)}) \bullet f'(z^{(n_l)}) \end{align}

3)对于 \textstyle l = n_l-1, n_l-2, n_l-3, \ldots, 2 的各层,计算:

  \begin{align} \delta^{(l)} = \left((W^{(l)})^T \delta^{(l+1)}\right) \bullet f'(z^{(l)}) \end{align}

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

  \begin{align} \nabla_{W^{(l)}} J(W,b;x,y) &= \delta^{(l+1)} (a^{(l)})^T, \\ \nabla_{b^{(l)}} J(W,b;x,y) &= \delta^{(l+1)}. \end{align}

指的注意的是在以上的第2步和第3步中,我们需要为每一个 单元\textstyle i 值计算其 \textstyle f'(z^{(l)}_i)。假设 \textstyle f(z) 是sigmoid函数,f'(z)=f(z)*(1-f(z)),并且我们已经在前向传导运算中得到了 \textstyle a^{(l)}_i。那么,使用我们早先推导出的 \textstyle f'(z)表达式,就可以计算得到 \textstyle f'(z^{(l)}_i) = a^{(l)}_i (1- a^{(l)}_i)

经过以上步骤,已经可以求出每个参数的偏导数,下一步就是更新参数,即使得参数沿梯度方向下降,下面给出梯度下降算法伪代码:

\textstyle \Delta W^{(l)} 是一个与矩阵 \textstyle W^{(l)} 维度相同的矩阵,\textstyle \Delta b^{(l)} 是一个与 \textstyle b^{(l)} 维度相同的向量。注意这里“\textstyle \Delta W^{(l)}”是一个矩阵,而不是“\textstyle \Delta 与 \textstyle W^{(l)} 相乘”。下面,我们实现批量梯度下降法中的一次迭代:

不断更新W,b的值,直到W,b不再变化为止,即网络达到收敛。

 

有机会再补上代码#24!!

 

转载于:https://www.cnblogs.com/ooon/p/5284676.html

你可能感兴趣的文章
ES7/8流行特性
查看>>
最近两个月中我是如何开始学习 AI 的
查看>>
Android存储方式之SharedPreference
查看>>
laravel-mix下webpack打包es6报错UglifyJs + Unexpected token punc 解决
查看>>
JavaScript参数按值传递的理解
查看>>
2017-08-18 前端日报
查看>>
H5 是 HTML5 吗?
查看>>
策略模式
查看>>
1.Django中错误:django.core.exceptions.ImproperlyConfigured
查看>>
Go语言的栈空间管理
查看>>
高并发 - 基础
查看>>
SVG之Patterns & Gradient
查看>>
vue导航条+下拉菜单
查看>>
前端周刊 - 收藏集 - 掘金
查看>>
laravel 用户发送邮件重置密码
查看>>
PDF插件VintaSoftPDF.NET Plug-in v5.5新增PDF验证功能
查看>>
jQuery+PHP实现砸金蛋抽奖
查看>>
玩转Node.js单元测试
查看>>
Pointwise 17.3 R4 安装
查看>>
NodeJS 工程师必备的 8 个工具
查看>>