Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >反向传播神经网络极简入门

反向传播神经网络极简入门

作者头像
机器学习AI算法工程
发布于 2018-03-13 02:22:18
发布于 2018-03-13 02:22:18
1.2K00
代码可运行
举报
运行总次数:0
代码可运行

我一直在找一份简明的神经网络入门,然而在中文圈里并没有找到。直到我看到了这份162行的Python实现,以及对应的油管视频之后,我才觉得这就是我需要的极简入门资料。这份极简入门笔记不需要突触的图片做装饰,也不需要赘述神经网络的发展历史;要推导有推导,要代码有代码,关键是,它们还对得上。对于欠缺的背景知识,利用斯坦福大学的神经网络wiki进行了补全。

单个神经元

神经网络是多个“神经元”(感知机)的带权级联,神经网络算法可以提供非线性的复杂模型,它有两个参数:权值矩阵{Wl}和偏置向量{bl},不同于感知机的单一向量形式,{Wl}是复数个矩阵,{bl}是复数个向量,其中的元素分别属于单个层,而每个层的组成单元,就是神经元。

神经元

神经网络是由多个“神经元”(感知机)组成的,每个神经元图示如下:

这其实就是一个单层感知机,其输入是由

和+1组成的向量,其输出为

,其中f是一个激活函数,模拟的是生物神经元在接受一定的刺激之后产生兴奋信号,否则刺激不够的话,神经元保持抑制状态这种现象。这种由一个阀值决定两个极端的函数有点像示性函数,然而这里采用的是Sigmoid函数,其优点是连续可导。

Sigmoid函数

常用的Sigmoid有两种——

单极性Sigmoid函数

或者写成

其图像如下

双极性Sigmoid函数

或者写成

把第一个式子分子分母同时除以ez,令x=-2z就得到第二个式子了,换汤不换药。

其图像如下

从它们两个的值域来看,两者名称里的极性应该指的是正负号。从导数来看,它们的导数都非常便于计算:

对于

,对于tanh,有

视频作者Ryan还担心观众微积分学的不好,细心地给出了1/(1+e^-x)求导的过程:

一旦知道了f(z),就可以直接求f'(z),所以说很方便。

本Python实现使用的就是1/(1+e^-x)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def sigmoid(x):
    """
    sigmoid 函数,1/(1+e^-x)
    :param x:
    :return:
    """
    return 1.0/(1.0+math.exp(-x))
 
 
def dsigmoid(y):
    """
    sigmoid 函数的导数
    :param y:
    :return:
    """
    return y * (1 - y)

也可以使用双曲正切函数tanh

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def sigmoid(x):
    """
    sigmoid 函数,tanh 
    :param x:
    :return:
    """
    return math.tanh(x)

其导数对应于:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def dsigmoid(y):
    """
    sigmoid 函数的导数
    :param y:
    :return:
    """
    return 1.0 - y ** 2

神经网络模型

神经网络就是多个神经元的级联,上一级神经元的输出是下一级神经元的输入,而且信号在两级的两个神经元之间传播的时候需要乘上这两个神经元对应的权值。例如,下图就是一个简单的神经网络:

其中,一共有一个输入层,一个隐藏层和一个输出层。输入层有3个输入节点,标注为+1的那个节点是偏置节点,偏置节点不接受输入,输出总是+1。

定义上标为层的标号,下标为节点的标号,则本神经网络模型的参数是:

,其中

是第l层的第j个节点与第l+1层第i个节点之间的连接参数(或称权值);

表示第l层第i个偏置节点。这些符号在接下来的前向传播将要用到。

前向传播

虽然标题是《(误差)后向传播神经网络入门》,但这并不意味着可以跳过前向传播的学习。因为如果后向传播对应训练的话,那么前向传播就对应预测(分类),并且训练的时候计算误差也要用到预测的输出值来计算误差。

定义

为第l层第i个节点的激活值(输出值)。当l=1时,

。前向传播的目的就是在给定模型参数

的情况下,计算l=2,3,4…层的输出值,直到最后一层就得到最终的输出值。具体怎么算呢,以上图的神经网络模型为例:

这没什么稀奇的,核心思想是这一层的输出乘上相应的权值加上偏置量代入激活函数等于下一层的输入,一句大白话,所谓中文伪码。

另外,追求好看的话可以把括号里面那个老长老长的加权和定义为一个参数:

表示第l层第i个节点的输入加权和,比如

。那么该节点的输出可以写作

于是就得到一个好看的形式:

在这个好看的形式下,前向传播可以简明扼要地表示为:

在Python实现中,对应如下方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def runNN(self, inputs):
        """
        前向传播进行分类
        :param inputs:输入
        :return:类别
        """
        if len(inputs) != self.ni - 1:
            print 'incorrect number of inputs'
 
        for i in range(self.ni - 1):
            self.ai[i] = inputs[i]
 
        for j in range(self.nh):
            sum = 0.0
            for i in range(self.ni):
                sum += ( self.ai[i] * self.wi[i][j] )
            self.ah[j] = sigmoid(sum)
 
        for k in range(self.no):
            sum = 0.0
            for j in range(self.nh):
                sum += ( self.ah[j] * self.wo[j][k] )
            self.ao[k] = sigmoid(sum)
 
        return self.ao

其中,ai、ah、ao分别是输入层、隐藏层、输出层,而wi、wo则分别是输入层到隐藏层、隐藏层到输出层的权值矩阵。在本Python实现中,将偏置量一并放入了矩阵,这样进行线性代数运算就会方便一些。

后向传播

后向传播指的是在训练的时候,根据最终输出的误差来调整倒数第二层、倒数第三层……第一层的参数的过程。

符号定义

在Ryan的讲义中,符号定义与斯坦福前向传播讲义相似但略有不同:

:第l层第j个节点的输入。

:从第l-1层第i个节点到第l层第j个节点的权值。

:Sigmoid函数。

:第l层第j个节点的偏置。

:第l层第j个节点的输出。

:输出层第j个节点的目标值(Target value)。

输出层权值调整

给定训练集

和模型输出

(这里没有上标l是因为这里在讨论输出层,l是固定的),输出层的输出误差(或称损失函数吧)定义为:

其实就是所有实例对应的误差的平方和的一半,训练的目标就是最小化该误差。怎么最小化呢?看损失函数对参数的导数

呗。

将E的定义代入该导数:

无关变量拿出来:

看到这里大概明白为什么非要把误差定义为误差平方和的一半了吧,就是为了好看,数学家都是外貌协会的。

=

(输出层的输出等于输入代入Sigmoid函数)这个关系代入有:

对Sigmoid求导有:

要开始耍小把戏了,由于输出层第k个节点的输入

等于上一层第j个节点的输出

,而上一层的输出

是与到输出层的权值变量无关的,所以对

求权值变量

的偏导数直接等于其本身,也就是说:

=

=

然后将上面用过的

=

代进去就得到最终的:

为了表述方便将上式记作:

其中:

隐藏层权值调整

依然采用类似的方法求导,只不过求的是关于隐藏层和前一层的权值参数的偏导数:

老样子:

还是老样子:

还是把Sigmoid弄进去:

=

代进去,并且将导数部分拆开:

又要耍把戏了,输出层的输入等于上一层的输出乘以相应的权值,亦即

=

,于是得到:

把最后面的导数挪到前面去,接下来要对它动刀了:

再次利用

=

,这对j也成立,代进去:

再次利用

=

,j换成i,k换成j也成立,代进去:

利用刚才定义的

,最终得到:

其中:

我们还可以仿照

的定义来定义一个

,得到:

其中

偏置的调整

因为没有任何节点的输出流向偏置节点,所以偏置节点不存在上层节点到它所对应的权值参数,也就是说不存在关于权值变量的偏导数。虽然没有流入,但是偏置节点依然有输出(总是+1),该输出到下一层某个节点的时候还是会有权值的,对这个权值依然需要更新。

我们可以直接对偏置求导,发现:

原视频中说∂O/∂θ=1,这是不对的,作者也在讲义中修正了这个错误,∂O/∂θ=O(1–O)。

然后再求

,后面的导数等于

,代进去有

其中,

后向传播算法步骤

  • 随机初始化参数,对输入利用前向传播计算输出。
  • 对每个输出节点按照下式计算delta:
  • 对每个隐藏节点按照下式计算delta:
  • 计算梯度

,并更新权值参数和偏置参数:

。这里的

是学习率,影响训练速度。

后向传播算法实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def backPropagate(self, targets, N, M):
        """
        后向传播算法
        :param targets: 实例的类别 
        :param N: 本次学习率
        :param M: 上次学习率
        :return: 最终的误差平方和的一半
        """
        # http://www.youtube.com/watch?v=aVId8KMsdUU&feature=BFa&list=LLldMCkmXl4j9_v0HeKdNcRA
 
        # 计算输出层 deltas
        # dE/dw[j][k] = (t[k] - ao[k]) * s'( SUM( w[j][k]*ah[j] ) ) * ah[j]
        output_deltas = [0.0] * self.no
        for k in range(self.no):
            error = targets[k] - self.ao[k]
            output_deltas[k] = error * dsigmoid(self.ao[k])
 
        # 更新输出层权值
        for j in range(self.nh):
            for k in range(self.no):
                # output_deltas[k] * self.ah[j] 才是 dError/dweight[j][k]
                change = output_deltas[k] * self.ah[j]
                self.wo[j][k] += N * change + M * self.co[j][k]
                self.co[j][k] = change
 
        # 计算隐藏层 deltas
        hidden_deltas = [0.0] * self.nh
        for j in range(self.nh):
            error = 0.0
            for k in range(self.no):
                error += output_deltas[k] * self.wo[j][k]
            hidden_deltas[j] = error * dsigmoid(self.ah[j])
 
        # 更新输入层权值
        for i in range(self.ni):
            for j in range(self.nh):
                change = hidden_deltas[j] * self.ai[i]
                # print 'activation',self.ai[i],'synapse',i,j,'change',change
                self.wi[i][j] += N * change + M * self.ci[i][j]
                self.ci[i][j] = change
 
        # 计算误差平方和
        # 1/2 是为了好看,**2 是平方
        error = 0.0
        for k in range(len(targets)):
            error = 0.5 * (targets[k] - self.ao[k]) ** 2
        return error

注意不同于上文的单一学习率

,这里有两个学习率N和M。N相当于上文的

,而M则是在用上次训练的梯度更新权值时的学习率。这种同时考虑最近两次迭代得到的梯度的方法,可以看做是对单一学习率的改进。

另外,这里并没有出现任何更新偏置的操作,为什么?

因为这里的偏置是单独作为一个偏置节点放到输入层里的,它的值(输出,没有输入)固定为1,它的权值已经自动包含在上述权值调整中了。

如果将偏置作为分别绑定到所有神经元的许多值,那么则需要进行偏置调整,而不需要权值调整(此时没有偏置节点)。

哪个方便,当然是前者了,这也导致了大部分神经网络实现都采用前一种做法。

完整的实现

已开源到了Github上:https://github.com/hankcs/neural_net

这一模块的原作者是Neil Schemenauer,我做了些注释。

直接运行bpnn.py即可得到输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Combined error 0.171204877501
Combined error 0.190866985872
Combined error 0.126126875154
Combined error 0.0658488960415
Combined error 0.0353249077599
Combined error 0.0214428399072
Combined error 0.0144886807614
Combined error 0.0105787745309
Combined error 0.00816264126944
Combined error 0.00655731212209
Combined error 0.00542964723539
Combined error 0.00460235328667
Combined error 0.00397407912435
Combined error 0.00348339081276
Combined error 0.00309120476889
Combined error 0.00277163178862
Combined error 0.00250692771135
Combined error 0.00228457151714
Combined error 0.00209550313514
Combined error 0.00193302192499
Inputs: [0, 0] --> [0.9982333356008245] 	Target [1]
Inputs: [0, 1] --> [0.9647325217906978] 	Target [1]
Inputs: [1, 0] --> [0.9627966274767186] 	Target [1]
Inputs: [1, 1] --> [0.05966109502803293] 	Target [0]

IBM利用Neil Schemenauer的这一模块(旧版)做了一个识别代码语言的例子,我将其更新到新版,已经整合到了项目中。

要运行测试的话,执行命令

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
code_recognizer.py testdata.200

即可得到输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ERROR_CUTOFF = 0.01
INPUTS = 20
ITERATIONS = 1000
MOMENTUM = 0.1
TESTSIZE = 500
OUTPUTS = 3
TRAINSIZE = 500
LEARNRATE = 0.5
HIDDEN = 8
Targets: [1, 0, 0] -- Errors: (0.000 OK)   (0.001 OK)   (0.000 OK)   -- SUCCESS!

值得一提的是,这里的HIDDEN = 8指的是隐藏层的节点个数,不是层数,层数多了就变成DeepLearning了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2015-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大数据挖掘DT数据分析 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【数据挖掘】神经网络 后向传播算法 ( 神经网络分类 | 适用场景 | 优缺点 | 多路前馈神经网络 | 后向传播算法步骤 | 初始化权 | 向前传播输入 )
① 感知器 : 感知器 对应有监督的学习方法 , 给出已知的训练集 , 学习过程中指导模型的训练 ;
韩曙亮
2023/03/27
4810
【数据挖掘】神经网络 后向传播算法 向前传播输入 案例计算分析 ( 网络拓扑 | 输入层计算 | 隐藏层计算 | 输出层计算 )
② 连接方式 : 该网络结构中的连接方式是全连接方式 , 即每个节点都连接全部的相邻层的节点 ; ( 与之对应的是局部连接 )
韩曙亮
2023/03/27
7480
【数据挖掘】神经网络 后向传播算法 向前传播输入 案例计算分析 ( 网络拓扑 | 输入层计算 | 隐藏层计算 | 输出层计算 )
神经网络–反向传播详细推导过程
为了描述神经网络,我们先从最简单的神经网络讲起,这个神经网络仅由一个“神经元”构成,以下即是这个“神经元”的图示:
全栈程序员站长
2022/08/23
7890
神经网络–反向传播详细推导过程
从零推导神经网络之入门篇
从零推导神经网络之入门篇 什么是神经网络? 可以先看逼乎这个问题下的答案:(https://www.zhihu.com/question/22553761)。 在有了基本概念后(没有也ojbk),我们往下走。 为了实现上述分类功能,我们可以采用如下的一种模型: 假设输入向量为,我们让输出为y=1或y=0(二分类问题) 以垃圾邮件识别为例,输入向量即为每个词汇出现的次数(频率),让输出y=1为垃圾邮件,为普通邮件。 【Tips:这种分类模型可以使用感知器或者逻辑回归(感兴趣的可以看历史文章了解更多)】
企鹅号小编
2018/01/22
5470
从零推导神经网络之入门篇
神经网络反向传播算法
反向传播算法(Back Propagation,简称BP)是一种用于训练神经网络的算法。
@小森
2024/05/03
1040
神经网络反向传播算法
深度学习与CV教程(4) | 神经网络与反向传播
本系列为 斯坦福CS231n 《深度学习与计算机视觉(Deep Learning for Computer Vision)》的全套学习笔记,对应的课程视频可以在 这里 查看。更多资料获取方式见文末。
ShowMeAI
2022/05/30
8010
深度学习与CV教程(4) | 神经网络与反向传播
一文弄懂神经网络中的反向传播法——BackPropagation
本博客所有内容以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,并且是非商业用途,谢谢! 最近在看深度学习的东西,一开始看的吴恩达的UFLDL教程,有中文版就直接看了,后来发现有些地方总是不是很明确,又去看英文版,然后又找了些资料看,才发现,中文版的译者在翻译的时候会对省略的公式推导过程进行补充,但是补充的又是错的,难怪觉得有问题。反向传播法其实是神经网络的基础了,但是很多人在学的时候总是会遇到一些问题,或者看到大篇的公式觉得好像很难就退缩了,其实不难,就是一个链式求导法则反复用
Charlotte77
2018/01/09
1.5K0
一文弄懂神经网络中的反向传播法——BackPropagation
NLP教程(3) | 神经网络与反向传播
教程地址:http://www.showmeai.tech/tutorials/36
ShowMeAI
2022/05/04
8150
NLP教程(3) | 神经网络与反向传播
数据科学 IPython 笔记本 四、Keras(上)
深度学习允许由多层组成的计算模型,来学习具有多个抽象级别的数据表示。这些方法极大地改进了语音识别,视觉对象识别,物体检测,以及药物发现和基因组学等许多其他领域的最新技术。
ApacheCN_飞龙
2022/05/07
1.7K0
数据科学 IPython 笔记本 四、Keras(上)
小白也能看懂的BP反向传播算法之Surpass Backpropagation
上篇文章小白也能看懂的BP反向传播算法之Further into Backpropagation中,我们小试牛刀,将反向传播算法运用到了一个两层的神经网络结构中!然后往往实际中的神经网络拥有3层甚至更多层的结构,我们接下来就已一个三层的神经网络结构为例,分析如何运用动态规划来优化反向传播时微分的计算!
desperate633
2018/08/23
8600
机器学习——神经网络代价函数、反向传播、梯度检验、随机初始化
机器学习(十二) ——神经网络代价函数、反向传播、梯度检验、随机初始化 (原创内容,转载请注明来源,谢谢) 一、代价函数 同其他算法一样,为了获得最优化的神经网络,也要定义代价函数。 神经网络的输出的结果有两类,一类是只有和1,称为二分分类(Binary classification),另一种有多个结果,称为多分类。其中,多个结果时,表示方式和平时不太一样。例如,y的结果范围在0~5,则表示y=2,用的是矩阵y=[0 1 0 0 0]T来表示,如下图: 代价函数可以类比logistic回归的代价函数,l
企鹅号小编
2018/02/07
1K0
机器学习——神经网络代价函数、反向传播、梯度检验、随机初始化
零基础入门深度学习 | 第三章:神经网络和反向传播算法
无论即将到来的是大数据时代还是人工智能时代,亦或是传统行业使用人工智能在云上处理大数据的时代,作为一个有理想有追求的程序员,不懂深度学习这个超热的技术,会不会感觉马上就out了? 现在救命稻草来了,《零基础入门深度学习》系列文章旨在讲帮助爱编程的你从零基础达到入门级水平。零基础意味着你不需要太多的数学知识,只要会写程序就行了,没错,这是专门为程序员写的文章。虽然文中会有很多公式你也许看不懂,但同时也会有更多的代码,程序员的你一定能看懂的(我周围是一群狂热的Clean Code程序员,所以我写的代码也不会
用户1332428
2018/03/09
3.7K0
零基础入门深度学习 | 第三章:神经网络和反向传播算法
神经网络算法——反向传播 Back Propagation
本文将从反向传播的本质、反向传播的原理、反向传播的案例三个方面,详细介绍反向传播(Back Propagation)。
JOYCE_Leo16
2024/03/19
4K0
神经网络算法——反向传播 Back Propagation
BP神经网络原理及实现
经典的BP神经网络通常由三层组成: 输入层, 隐含层与输出层.通常输入层神经元的个数与特征数相关,输出层的个数与类别数相同, 隐含层的层数与神经元数均可以自定义.
全栈程序员站长
2022/09/13
1.2K0
BP神经网络原理及实现
用Python从零开始构建反向传播算法
在本教程中,你将探索如何使用Python从零开始构建反向传播算法。
ArrayZoneYour
2018/02/13
4.9K9
用Python从零开始构建反向传播算法
误差反向传播算法浅解
反向传播(英语:Backpropagation,缩写为BP)是“误差反向传播”的简称。由于多层前馈神经网络的训练经常采用误差反向传播算法,人们也常把多层前馈神经网络称为BP网络。
用户9624935
2022/04/02
2.2K0
误差反向传播算法浅解
一文弄懂神经网络中的反向传播法——BackPropagation
https://www.cnblogs.com/charlotte77/p/5629865.html
用户1148525
2019/07/01
1.5K0
一文弄懂神经网络中的反向传播法——BackPropagation
神经网络学习笔记1——BP神经网络原理到编程实现(matlab,python)[通俗易懂]
不好意思拖了这么久才整理,弄完考试的事情就在研究老师给安排的新任务,一时间还有点摸不到头脑,就直接把百度网盘链接放在视频下面了但是最近才发现那个链接发出来了看不到,所以现在有时间了就来重新整理一下!
全栈程序员站长
2022/09/09
1.4K0
神经网络学习笔记1——BP神经网络原理到编程实现(matlab,python)[通俗易懂]
AI从入门到放弃:BP神经网络算法推导及代码实现笔记
作者 | @Aloys (腾讯员工,后台工程师) 本文授权转自腾讯的知乎专栏 ▌一. 前言: 作为AI入门小白,参考了一些文章,想记点笔记加深印象,发出来是给有需求的童鞋学习共勉,大神轻拍! 【毒鸡汤】:算法这东西,读完之后的状态多半是 --> “我是谁,我在哪?” 没事的,吭哧吭哧学总能学会,毕竟还有千千万万个算法等着你。 本文货很干,堪比沙哈拉大沙漠,自己挑的文章,含着泪也要读完! ▌二. 科普: 生物上的神经元就是接收四面八方的刺激(输入),然后做出反应(输出),给它一点☀️就灿烂。 仿生嘛,于是
用户1737318
2018/07/20
1.1K0
深度学习之神经网络(反向传播算法)(三)
神经网络最开始是受生物神经系统的启发,为了模拟生物神经系统而出现的。大脑最基本的计算单元是神经元,人类的神经系统中大概有86亿的神经元,它们之间通过1014-1015的突触相连接。每个神经元从它的树突(dendrites)接受输入信号,沿着唯一的轴突(axon)产生输出信号,而轴突通过分支(branches of axon),通过突触(synapses)连接到其他神经元的树突,神经元之间就这通过这样的连接,进行传递。如下图。
李小白是一只喵
2020/04/24
1.1K0
推荐阅读
相关推荐
【数据挖掘】神经网络 后向传播算法 ( 神经网络分类 | 适用场景 | 优缺点 | 多路前馈神经网络 | 后向传播算法步骤 | 初始化权 | 向前传播输入 )
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验