从 0 开始机器学习 - 神经网络反向 BP 算法!
最近一个月项目好忙,终于挤出时间把这篇 BP 算法基本思想写完了,公式的推导放到下一篇讲吧。
一、神经网络的代价函数
神经网络可以看做是复杂逻辑回归的组合,因此与其类似,我们训练神经网络也要定义代价函数,之后再使用梯度下降法来最小化代价函数,以此来训练最优的权重矩阵。
1.1 从逻辑回归出发
我们从经典的逻辑回归代价函数引出,先来复习下:
\]
逻辑回归代价函数计算每个样本的输入与输出的误差,然后累加起来除以样本数,再加上正则化项,这个我之前的博客已经写过了:
这里补充一点对单变量逻辑回归代价函数的理解,虽然这一行代价公式很长:
\]
但是其实可以把它简单的理解为输出与输入的方差,虽然形式上差别很大,但是可以帮助我们理解上面这个公式到底在计算什么,就是计算输出与输入的方差,这样理解就可以:
\]
1.2 一步步写出神经网络代价函数
前面讲的简单逻辑回归的只有一个输出变量,但是在神经网络中输出层可以有多个神经元,所以可以有很多种的输出,比如 K 分类问题,神经元的输出是一个 K 维的向量:
因此我们需要对每个维度计算预测输出与真实标签值的误差,即对 K 个维度的误差做一次求和:
\]
然后累加训练集的 m 个样本:
\]
再加上所有权重矩阵元素的正则化项,注意 \(i, j\) 都是从 1 开始的,因为每一层的 \(\theta_0\) 是偏置单元,不需要对其进行正则化:
\]
- 最内层求和:循环一个权重矩阵所有的行,行数是 \(S_l + 1\) 层激活单元数
- 中间层求和:循环一个权重矩阵所有的列,列数是 \(S_l\) 层激活单元数
- 最外层求和:循环所有的权重矩阵
这就得到了输出层为 K 个单元神经网络最终的代价函数:
\]
有了代价函数后,就可以通过反向传播算法来训练一个神经网络啦!
二、神经网络反向 BP(Back Propagation) 算法
2.1 BP 算法简介
之前写神经网络基础的时候,跟大家分享了如何用训练好的神经网络来预测手写字符:从 0 开始机器学习 - 神经网络识别手写字符!,只不过当时我们没有训练网络,而是使用已经训练好的神经网络的权重矩阵来进行前馈预测,那么我们如何自己训练神经网络呢?
这就需要学习反向 BP 算法,这个算法可以帮助我们求出神经网络权重矩阵中每个元素的偏导数,进而利用梯度下降法来最小化上面的代价函数,你可以联想简单的线性回归算法:从 0 开始机器学习 - 一文入门多维特征梯度下降法!,也是先求每个参数的偏导数,然后在梯度下降算法中使用求出的偏导数来迭代下降。
因此训练神经网络的关键就是:如何求出每个权重系数的偏导数?,反向 BP 就可以解决这个问题!这里强烈建议你学习的时候完全搞懂 BP 算法的原理,最好自己独立推导一遍公式,因为你以后学习深度学习那些复杂的网络,不管是哪种,最终都要使用反向 BP 来训练,这个 BP 算法是最核心的东西,面试也逃不过的,所以既然要学,就要学懂,不然就是在浪费时间。
2.2 BP 算法基本原理
我先用个例子简单介绍下 BP 算法的基本原理和步骤,公式的推导放到下一节,反向 BP 算法顾名思义,与前馈预测方向相反:
- 计算最后一层输出与实际标签值的误差,反向传播到倒数第二层
- 计算倒数第二层的传播误差,反向传播到倒数第三层
- 以此类推,一层一层地求出各层的误差
- 直到第二层结束,因为第一层是输入特征,不是我们计算的,所以不需要求误差
以下面这个 4 层的神经网络为例:
假如我们的训练集只有 1 个样本 \((x^{(1)}, y^{(1)})\),每层所有激活单元的输出用 \(a^{(i)}\) 向量表示,每层所有激活单元的误差用 \(\delta^{(i)}\) 向量表示,来看下反向传播的计算步骤(公式的原理下一节讲):
- 输出层的误差为预测值减去真实值:\(\delta^{(4)} = a^{(4)} - y^{(1)}\)
- 倒数第二层的误差为:\(\delta^{(3)} = (W^{(3)})^T \delta^{(4)} * g'(z^{(3)})\)
- 倒数第三层的误差为:\(\delta^{(2)} = (W^{(2)})^T \delta^{(3)} * g'(z^{(2)})\)
- 第一层是输入变量,不需要计算误差
有了每层所有激活单元的误差后,就可以计算代价函数对每个权重参数的偏导数,即每个激活单元的输出乘以对应的误差,这里不考虑正则化:
\]
解释下这个偏导数的计算:
- \(l\) 表示目前计算的是第几层
- \(j\) 表示当前层中正在计算的激活单元下标(\(j\) 作为列)
- \(i\) 表示下一层误差单元的下标(\(i\) 作为行)
这个计算过程是对一个样本进行的,网络的输入是一个特征向量,所以每层计算的误差也是向量,但是我们的网络输入是特征矩阵的话,就不能用一个个向量来表示误差了,而是应该也将误差向量组成误差矩阵,因为特征矩阵就是多个样本,每个样本都做一个反向传播,就会计算误差,所以我们每次都把一个样本计算的误差累加到误差矩阵中:
\]
然后,我们需要除以样本总数 \(m\),因为上面的误差是累加了所有 \(m\) 个训练样本得到的,并且我们还需要考虑加上正则化防止过拟合,注意对偏置单元不需要正则化,这点已经提过好多次了:
- 非偏置单元正则化后的偏导数 \(j \neq 0\):
\]
- 偏置单元正则化后的偏导数 \(j = 0\):
\]
最后计算的所有偏导数就放在误差矩阵中:
\]
这样我们就求出了每个权重参数的偏导数,再回想之前的梯度下降法,我们有了偏导数计算方法后,直接送到梯度下降法中进行迭代就可以最小化代价函数了,比如我在 Python 中把上面的逻辑写成一个正则化梯度计算的函数 regularized_gradient
,然后再用 scipy.optimize
等优化库直接最小化文章开头提出的神经网络代价函数,以此来使用反向 BP 算法训练一个神经网络:
import scipy.optimize as opt
res = opt.minimize(fun = 神经网络代价函数,
x0 = init_theta,
args = (X, y, 1),
method = 'TNC',
jac = regularized_gradient,
options = {'maxiter': 400})
所以神经网络反向 BP 算法关键就是理解每个权重参数偏导数的计算步骤和方法!关于偏导数计算公式的详细推导过程,我打算在下一篇文章中单独分享,本次就不带大家一步步推导了,否则内容太多,先把基本步骤搞清楚,后面推导公式就容易理解。
2.3 反向 BP 算法的直观理解
之前学习前馈预测时,我们知道一个激活单元是输入是上一层所有激活单元的输出与权重的加权和(包含偏置),计算方向从左到右,计算的是每个激活单元的输出,看图:
其实反向 BP 算法也是做类似的计算,一个激活单元误差的输入是后一层所有误差与权重的加权和(可能不包含偏置),只不过这里计算的反向是从右向左,计算的是每个激活单元的误差,对比看图:
你只需要把单个神经元的前馈预测和反向 BP 的计算步骤搞清楚就可以基本理解反向 BP 的基本过程,因为所有的参数都是这样做的。
三、神经网络编程细节
3.1 随机初始化
每种优化算法都需要初始化参数,之前的线性回归初始化参数为 0 是没问题的,但是如果把神经网络的初始参数都设置为 0,就会有问题,因为第二层的输入是要用到权重与激活单元输出的乘积:
- 如果权重都是 0,则每层网络的输出都是 0
- 如果权重都是相同的常数 \(a\),则每层网络的输出也都相同,只是不为 0
所以为了在神经网络中避免以上的问题,我们采用随机初始化,把所有的参数初始化为 \([-\epsilon, \epsilon]\) 之间的随机值,比如初始化一个 10 X 11 的权重参数矩阵:
\]
3.2 矩阵 <-> 向量
注意上面优化库的输入 X0 = init_theta
是一个向量,而我们的神经网络每 2 层之间就有一个权重矩阵,所以为了把权重矩阵作为优化库的输入,我们必须要把所有的权重参数都组合到一个向量中,也就是实现一个把矩阵组合到向量的功能,但是优化库的输出也是一个包含所有权重参数的向量,我们拿到向量后还需要把它转换为每 2 层之间的权重矩阵,这样才能进行前馈预测:
- 训练前:初始多个权重矩阵 -> 一个初始向量
- 训练后:一个最优向量 -> 多个最优权重矩阵
3.3 梯度校验
梯度校验是用来检验我们的 BP 算法计算的偏导数是否和真实的偏导数存在较大误差,计算以下 2 个偏导数向量的误差:
- 反向 BP 算法计算的偏导数
- 利用导数定义计算的偏导数
对于单个参数,在一点 \(\theta\) 处的导数可由 \([\theta - \epsilon, \theta + \epsilon]\) 表示,这也是导数定义的一种:
\]
如图:
但是我们的神经网络代价函数有很多参数,当我们把参数矩阵转为向量后,可以对向量里的每个参数进行梯度检验,只需要分别用定义求偏导数即可,比如检验 \(\theta_1\):
\]
以此类推,检验 \(\theta_n\):
\]
求出导数定义的偏导数后,与 BP 算法计算的偏导数计算误差,在误差范围内认为 BP 算法计算的偏导数(D_vec)是正确的,梯度检验的伪代码如下:
for i = 1 : n
theta_plus = theta
theta_plus[i] = theta_plus + epsilon
theta_minu = theta
theta_minu[i] = theta_minu - epsilon
grad = (J(theta_plus) - J(theta_minu)) / (2 * epsilon)
end
check 误差: grad 是否约等于 D_vec
注意一点:梯度检验通常速度很慢,在训练神经网络前先别进行检验!
今天就到这,溜了溜了,下篇文章见:)
从 0 开始机器学习 - 神经网络反向 BP 算法!的更多相关文章
- 人工神经网络反向传播算法(BP算法)证明推导
为了搞明白这个没少在网上搜,但是结果不尽人意,最后找到了一篇很好很详细的证明过程,摘抄整理为 latex 如下. (原文:https://blog.csdn.net/weixin_41718085/a ...
- [DL学习笔记]从人工神经网络到卷积神经网络_1_神经网络和BP算法
前言:这只是我的一个学习笔记,里边肯定有不少错误,还希望有大神能帮帮找找,由于是从小白的视角来看问题的,所以对于初学者或多或少会有点帮助吧. 1:人工全连接神经网络和BP算法 <1>:人工 ...
- 神经网络反向传播算法&&卷积神经网络
听一遍课程之后,我并不太明白这个算法的奇妙之处?? 为啥? 神经网络反向传播算法 神经网络的训练依靠反向传播算法,最开始输入层输入特征向量,网络层计算获得输出,输出层发现输出和正确的类号不一样,这时就 ...
- 神经网络与机器学习 笔记—反向传播算法(BP)
先看下面信号流图,L=2和M0=M1=M2=M3=3的情况,上面是前向通过,下面部分是反向通过. 1.初始化.假设没有先验知识可用,可以以一个一致分布来随机的挑选突触权值和阈值,这个分布选择为均值等于 ...
- 【机器学习】反向传播算法 BP
知识回顾 1:首先引入一些便于稍后讨论的新标记方法: 假设神经网络的训练样本有m个,每个包含一组输入x和一组输出信号y,L表示神经网络的层数,S表示每层输入的神经元的个数,SL代表最后一层中处理的单元 ...
- 神经网络中 BP 算法的原理与 Python 实现源码解析
最近这段时间系统性的学习了 BP 算法后写下了这篇学习笔记,因为能力有限,若有明显错误,还请指正. 什么是梯度下降和链式求导法则 假设我们有一个函数 J(w),如下图所示. 梯度下降示意图 现在,我们 ...
- 100天搞定机器学习|day38 反向传播算法推导
往期回顾 100天搞定机器学习|(Day1-36) 100天搞定机器学习|Day37无公式理解反向传播算法之精髓 上集我们学习了反向传播算法的原理,今天我们深入讲解其中的微积分理论,展示在机器学习中, ...
- 神经网络的BP算法
正向传播: W下脚标定义根据用户自己的习惯 反向传播算法 1.误差由本层传到上层相关联的结点,权重分配 2.上层某个结点的总误差 2.误差最小化与权重变量有关,最小梯度法. 权重因子更新 偏导数求解, ...
- 神经网络和BP算法推导
注意:绘画太难了,因为他们画,本文中的所有插图来自基本算法饺子机类.请勿转载 1.习模型: 事实上,基本上全部的基本机器学习模型都能够概括为下面的特征:依据某个函数,将输入计算并输出. 图形化表示为下 ...
随机推荐
- Kubectl exec 的工作原理解读
对于经常和 Kubernetes 打交道的 YAML 工程师来说,最常用的命令就是 kubectl exec 了,通过它可以直接在容器内执行命令来调试应用程序.如果你不满足于只是用用而已,想了解 ku ...
- net core获取appsetting.json的另外一种思路(全局,实时变化无需重启项目)
最近在写net core的项目,在非controller和service里面需要用到appsetting.json文件里面的一些配置,查资料大概有几种思路: 注入,然后config.GetSectio ...
- [Flutter] 音频播放插件 audioplayers 的一个路径坑
pubsepc.yaml 文件 flutter: assets: video/video-01.mp4 video/video-02.mp4 audio/bg.mp3 以上关于视频的文件的配置,都能正 ...
- [PHP自动化-进阶]003.CURL处理Https请求访问
引言:继前文<模拟登录并采集数据>,<模拟登录带有验证码的网站>,大家对CURL基本上已经有了认识,这一讲简单的说一下请求Https. 在很多的站点,如TalkingData, ...
- [注]一将功成万骨枯!App的七种死法
一将功成万骨枯,这种事在有泡沫的行业总是会发生的.移动互联网尤甚.从<愤怒的小鸟>到<植物大战僵尸>.<捕鱼达人>.<唱吧>.<陌陌>……一 ...
- 【转】shell的反引号、单引号、双引号的作用
Linux Shell中有三种引号,分别为双引号(" ").单引号(' ')以及反引号(` `). 其中双引号对字符串中出现的$.''.`和\进行替换:单引号不进行替换,将字符串中 ...
- JAVA自学笔记(2)
Java跳跃级知识储备 1.Mathod新篇章 1.0进制转化(方法中的参数其实就是局部变量,在方法中定义的变量要赋初值) import java.util.Scanner; public class ...
- 【译】Gartner CWPP市场指南
https://www.gartner.com/doc/reprints?id=1-1YSHGBQ8&ct=200416&st=sb?utm_source=marketo&ut ...
- discuz mlv3.x命令注入
本次漏洞是由于Discuz! ML对于cookie字段的不恰当处理造成的cookie字段中的language参数未经过滤,直接被拼接希尔缓存文件中,而缓存文件随后被加载,造成代码执行. 共有60出利用 ...
- 从 React 架构开始讲解 useState、useEffect 编程设计
随着前端开发复杂度增加,原生开发模式显得越来越笨重,前端框架也层出不穷. MVC 和 MVVM MVC MVC是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计 ...