反向传播算法推导过程(看一篇就够了)

2023-05-16

反向传播BackPropagation算法简称BP,算是神经网络的基础了。

在神经网络中,正向传播用于模型的训练,模型中的参数不一定达到最佳效果,需要进行“反向传播”进行权重等参数的修正。此外,神经网络每层的每个神经元都可以根据误差信号修正每层的权重反向传播只需应用链式求导法则即可求出:

这是典型的三层神经网络的基本构成,Layer L1是输入层,Layer L2是隐含层,Layer L3是隐含层,现在我们手里有一堆数据 {x1,x2,x3,...,xn},输出也是一堆数据 {y1,y2,y3,...,yn},现在要他们在隐含层做某种变换,使得数据输入进去后得到你期望的输出。如果你希望输出和原始输入一样,那么就是最常见的自编码模型(Auto-Encoder)。为什么要输入输出都一样呢?有什么用呢?其实应用挺广的,在图像识别,文本分类等等都会用到。

如果你的输出和原始输入不一样,那么就是很常见的人工神经网络了,相当于让原始数据通过一个映射来得到我们想要的输出数据,也就是我们今天要讲的话题。 

假设网络层结构如下:

第一层是输入层,包含两个神经元 i1,i2,和截距项 b1;第二层是隐含层,包含两个神经元 h1,h2 和截距项 b2,第三层是输出 o1,o2,每条线上标的 wi 是层与层之间连接的权重,激活函数我们默认为sigmoid函数

先对以上参数赋值:

其中,输入数据  i1=0.05,i2=0.10;

   输出数据 o1=0.01,  o2=0.99;

   初始权重  w1=0.15,  w2=0.20,  w3=0.25,  w4=0.30;

         w5=0.40,  w6=0.45,  w7=0.50,  w8=0.55 。

目标:给出输入数据 i1, i2 (0.05和0.10),使输出尽可能与原始输出 o1, o2 (0.01和0.99)接近。

第 1 步   前向传播

  1. 输入层---->隐含层:

  计算神经元h1的输入加权和:

由于这里的计算需要使用sigmoid函数,补充一点:

上式即为sigmoid函数的表达式,从下图可以看出当 x 趋近于负无穷时,y 趋近于0;当 x 趋近于正无穷时,y 趋近于1;当 x = 0 时, y =1/2。

 神经元h1的输出 o1 (此处用到激活函数为sigmoid函数): 

同理,可计算出神经元h2的输出o2:

2. 隐含层---->输出层:

 计算输出层神经元o1和o2的值:

    这样前向传播的过程就结束了,我们得到输出值为[0.75136079 , 0.772928465],与实际值[0.01 , 0.99]相差还很远,现在我们对误差进行反向传播,更新权值,重新计算输出。

第 2 步  反向传播

1.计算总误差

总误差:(square error)

但是有两个输出,所以分别计算o1和o2的误差,总误差为两者之和:

 导数(梯度)已经计算出来了,下面就是反向传播与参数更新过程

 同理,可更新w2,w3,w4的权值:

      这样误差反向传播法就完成了,最后我们再把更新的权值重新计算,不停地迭代,在这个例子中第一次迭代之后,总误差E(total)由0.298371109下降至0.291027924。迭代10000次后,总误差为0.000035085,输出为[0.015912196,0.984065734](原输入为[0.01,0.99]),证明效果还是不错的。

最后附python代码:

#coding:utf-8
import random
import math

#
#   参数解释:
#   "pd_" :偏导的前缀
#   "d_" :导数的前缀
#   "w_ho" :隐含层到输出层的权重系数索引
#   "w_ih" :输入层到隐含层的权重系数的索引

class NeuralNetwork:
    LEARNING_RATE = 0.5

    def __init__(self, num_inputs, num_hidden, num_outputs, hidden_layer_weights = None, hidden_layer_bias = None, output_layer_weights = None, output_layer_bias = None):
        self.num_inputs = num_inputs

        self.hidden_layer = NeuronLayer(num_hidden, hidden_layer_bias)
        self.output_layer = NeuronLayer(num_outputs, output_layer_bias)

        self.init_weights_from_inputs_to_hidden_layer_neurons(hidden_layer_weights)
        self.init_weights_from_hidden_layer_neurons_to_output_layer_neurons(output_layer_weights)

    def init_weights_from_inputs_to_hidden_layer_neurons(self, hidden_layer_weights):
        weight_num = 0
        for h in range(len(self.hidden_layer.neurons)):
            for i in range(self.num_inputs):
                if not hidden_layer_weights:
                    self.hidden_layer.neurons[h].weights.append(random.random())
                else:
                    self.hidden_layer.neurons[h].weights.append(hidden_layer_weights[weight_num])
                weight_num += 1

    def init_weights_from_hidden_layer_neurons_to_output_layer_neurons(self, output_layer_weights):
        weight_num = 0
        for o in range(len(self.output_layer.neurons)):
            for h in range(len(self.hidden_layer.neurons)):
                if not output_layer_weights:
                    self.output_layer.neurons[o].weights.append(random.random())
                else:
                    self.output_layer.neurons[o].weights.append(output_layer_weights[weight_num])
                weight_num += 1

    def inspect(self):
        print('------')
        print('* Inputs: {}'.format(self.num_inputs))
        print('------')
        print('Hidden Layer')
        self.hidden_layer.inspect()
        print('------')
        print('* Output Layer')
        self.output_layer.inspect()
        print('------')

    def feed_forward(self, inputs):
        hidden_layer_outputs = self.hidden_layer.feed_forward(inputs)
        return self.output_layer.feed_forward(hidden_layer_outputs)

    def train(self, training_inputs, training_outputs):
        self.feed_forward(training_inputs)

        # 1. 输出神经元的值
        pd_errors_wrt_output_neuron_total_net_input = [0] * len(self.output_layer.neurons)
        for o in range(len(self.output_layer.neurons)):

            # ∂E/∂zⱼ
            pd_errors_wrt_output_neuron_total_net_input[o] = self.output_layer.neurons[o].calculate_pd_error_wrt_total_net_input(training_outputs[o])

        # 2. 隐含层神经元的值
        pd_errors_wrt_hidden_neuron_total_net_input = [0] * len(self.hidden_layer.neurons)
        for h in range(len(self.hidden_layer.neurons)):

            # dE/dyⱼ = Σ ∂E/∂zⱼ * ∂z/∂yⱼ = Σ ∂E/∂zⱼ * wᵢⱼ
            d_error_wrt_hidden_neuron_output = 0
            for o in range(len(self.output_layer.neurons)):
                d_error_wrt_hidden_neuron_output += pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].weights[h]

            # ∂E/∂zⱼ = dE/dyⱼ * ∂zⱼ/∂
            pd_errors_wrt_hidden_neuron_total_net_input[h] = d_error_wrt_hidden_neuron_output * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_input()

        # 3. 更新输出层权重系数
        for o in range(len(self.output_layer.neurons)):
            for w_ho in range(len(self.output_layer.neurons[o].weights)):

                # ∂Eⱼ/∂wᵢⱼ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢⱼ
                pd_error_wrt_weight = pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].calculate_pd_total_net_input_wrt_weight(w_ho)

                # Δw = α * ∂Eⱼ/∂wᵢ
                self.output_layer.neurons[o].weights[w_ho] -= self.LEARNING_RATE * pd_error_wrt_weight

        # 4. 更新隐含层的权重系数
        for h in range(len(self.hidden_layer.neurons)):
            for w_ih in range(len(self.hidden_layer.neurons[h].weights)):

                # ∂Eⱼ/∂wᵢ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢ
                pd_error_wrt_weight = pd_errors_wrt_hidden_neuron_total_net_input[h] * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_weight(w_ih)

                # Δw = α * ∂Eⱼ/∂wᵢ
                self.hidden_layer.neurons[h].weights[w_ih] -= self.LEARNING_RATE * pd_error_wrt_weight

    def calculate_total_error(self, training_sets):
        total_error = 0
        for t in range(len(training_sets)):
            training_inputs, training_outputs = training_sets[t]
            self.feed_forward(training_inputs)
            for o in range(len(training_outputs)):
                total_error += self.output_layer.neurons[o].calculate_error(training_outputs[o])
        return total_error

class NeuronLayer:
    def __init__(self, num_neurons, bias):

        # 同一层的神经元共享一个截距项b
        self.bias = bias if bias else random.random()

        self.neurons = []
        for i in range(num_neurons):
            self.neurons.append(Neuron(self.bias))

    def inspect(self):
        print('Neurons:', len(self.neurons))
        for n in range(len(self.neurons)):
            print(' Neuron', n)
            for w in range(len(self.neurons[n].weights)):
                print('  Weight:', self.neurons[n].weights[w])
            print('  Bias:', self.bias)

    def feed_forward(self, inputs):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.calculate_output(inputs))
        return outputs

    def get_outputs(self):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.output)
        return outputs

class Neuron:
    def __init__(self, bias):
        self.bias = bias
        self.weights = []

    def calculate_output(self, inputs):
        self.inputs = inputs
        self.output = self.squash(self.calculate_total_net_input())
        return self.output

    def calculate_total_net_input(self):
        total = 0
        for i in range(len(self.inputs)):
            total += self.inputs[i] * self.weights[i]
        return total + self.bias

    # 激活函数sigmoid
    def squash(self, total_net_input):
        return 1 / (1 + math.exp(-total_net_input))


    def calculate_pd_error_wrt_total_net_input(self, target_output):
        return self.calculate_pd_error_wrt_output(target_output) * self.calculate_pd_total_net_input_wrt_input();

    # 每一个神经元的误差是由平方差公式计算的
    def calculate_error(self, target_output):
        return 0.5 * (target_output - self.output) ** 2


    def calculate_pd_error_wrt_output(self, target_output):
        return -(target_output - self.output)


    def calculate_pd_total_net_input_wrt_input(self):
        return self.output * (1 - self.output)


    def calculate_pd_total_net_input_wrt_weight(self, index):
        return self.inputs[index]


# 文中的例子:

nn = NeuralNetwork(2, 2, 2, hidden_layer_weights=[0.15, 0.2, 0.25, 0.3], hidden_layer_bias=0.35, output_layer_weights=[0.4, 0.45, 0.5, 0.55], output_layer_bias=0.6)
for i in range(10000):
    nn.train([0.05, 0.1], [0.01, 0.09])
    print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.09]]]), 9))


#另外一个例子,可以把上面的例子注释掉再运行一下:

# training_sets = [
#     [[0, 0], [0]],
#     [[0, 1], [1]],
#     [[1, 0], [1]],
#     [[1, 1], [0]]
# ]

# nn = NeuralNetwork(len(training_sets[0][0]), 5, len(training_sets[0][1]))
# for i in range(10000):
#     training_inputs, training_outputs = random.choice(training_sets)
#     nn.train(training_inputs, training_outputs)
#     print(i, nn.calculate_total_error(training_sets))

参考文章:

一文弄懂神经网络中的反向传播法——BackPropagation - Charlotte77 - 博客园 

Backpropagation

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

反向传播算法推导过程(看一篇就够了) 的相关文章

  • maven项目无法解析插件

    发现问题 使用IDEA创建Maven项目时 xff0c 报错无法解析插件 org apache maven plugins maven clean plugin 这里使用的是IDEA捆绑的Maven插件 解决方案 查看Maven配置 打开用
  • WPF ItemContainerGenerator.ContainerFromItem返回Null

    解决办法 xff1a 1 设置DataGrid 属性VirtualizingStackPanel IsVirtualizing 61 34 False 34 2 动态设置 xff1a 如果后台代码返回null xff0c 例如 xff0c
  • 实现两个数据库之间的数据同步

    不同服务器数据库之间的数据操作 创建链接服务器 exec sp addlinkedserver 39 ITSV 39 39 39 39 SQLOLEDB 39 39 远程服务器名或ip地址 39 exec sp addlinkedsrvlo
  • ABAP SY-系统值

    网址 xff1a http www fu he net sap 4817 html Description SY SUBRC xff1a 语句执行后的返回值 xff0c 0表示成功 SY DATUM xff1a 当前服务器日期 SY UZE
  • ORA -04098 触发器无效且未通过重新验证

    ORACLE 菜鸟 xff0c 犯了一个低级错误 xff0c 用PowerDesigner的SQL Preview创建表的时候没有创建sequence xff0c 导致新增数据报此错误 xff0c 折腾半天才反应过来 xff01 于是打开P
  • C# ConfigurationManager.ConnectionStrings[""].ConnectionString初始值设定引发异常

    开发的时候遇到这样一个问题 xff1a 在同一个C 项目中有两个Server xff0c 每个Server中有一个web config配置文件 xff0c 两个Server公用一个数据库服务类 xff08 SqlHelper cs xff0
  • SQL SERVER请验证实例名称是否正确并且SQL Server已配置为允许远程连接

    解决办法 xff1a 1 对象资源管理器中右键 方面 进入服务器配置页面 2 选择进入 外围应用配置器 3 将RemoteDacEnbled的属性设为Ture 4 确定 如果还是不行 xff0c 继续如下处理 xff1a 1 进入SQL S
  • A new value is to be assigned to the field 'L_BOX'

    转载 xff1a https www cnblogs com aurora cj p 9353144 html DUMP A new value is to be assigned to the field 34 lt L BOX gt 3
  • 七大Linux桌面介绍:Unity、KDE、GNOME等等

    对于Linux桌面环境来说 xff0c 因为具备着各种独特的设计风格 功能配备以及自身特性 从具体硬件平台上 xff0c 只有通过实际情况才可以判断一款桌面环境究竟能否适合用户的需求 这里就来为大家推荐七款顶级Linux桌面环境选项 一 U
  • Android 10.0 kenel和frameworks中修改ram运行内存的功能实现

    1 前言 nbsp 在10 0的系统rom产品开发定制中 在对一些产品开发中的配置需求方面 在产品后续订单中 产品提出要提高硬件配置 但是硬件方面已经定板 项目时间比较仓促 所以 来不及对硬件重新定制 就需要软件方面在ram运行内存的容量大
  • fft算法总结

    前言 电赛期间总结的FFT算法 一 fft是什么 xff1f FFT xff08 Fast Fourier Transformation xff09 是离散傅氏变换 xff08 DFT xff09 的快速算法 即为快速傅氏变换 它是根据离散
  • 火狐安全软件Huohong

    你还在为垃圾软件 xff0c 恶意弹窗 xff0c 病毒骚扰等而烦扰吗 xff1f 让简约高效的火狐安全软件来帮助你吧 xff01 火绒互联网安全软件 轻巧 高效 超强防御的安全防护软件 功能强悍 xff0c 体量轻巧 xff0c 既干净又
  • 利用scp从Linux上传送文件到windows

    一 下载Git 软件 下载链接 xff1a https git scm com downloads 二 打开Git Bash here 三 输入一下scp命令 红色部分是linux部分 xff1a 用户名 64 ip地址 xff1a 传输文
  • 我又会了json,啊啊啊啊!!!

    这个博主对json的创建与解析很详细哦 博主文章链接 xff1a 文章链接
  • vscode查找快捷键

    全局查找 xff1a ctrl 43 p文件内部查找 xff1a ctrl 43 f
  • 51单片机100倒计时

    目标 xff1a 基于51单片机的定时器在数码管上显示100s倒计时 xff0c 计时结束后led灯全亮 上面是我的开发版的原理图 xff0c 晶振是11 0592MHZ 找不到STC89C5xRC h的可以用AT89 xff0c 把头文件
  • Linux下的crontab定时执行任务命令详解

    在LINUX中 xff0c 周期执行的任务一般由cron这个守护进程来处理 ps ef grep cron cron读取一个或多个配置文件 xff0c 这些配置文件中包含了命令行及其调用时间 cron的配置文件称为 crontab xff0
  • golang密码加密解密

    简介 最近正在迁移自己的小项目 xff0c 项目之前是基于Laravel5 5开发的 整个用户登陆也是基于框架的 Auth 包认证的 其中用户密码这块也是用到了PHP内置的函数password hash xff0c 用它进行密码加密 而且
  • Android9.0 iptables用Netd实现删除子链功能的实现

    1 前言 在9 0的系统rom定制化开发中 在system中netd网络这块的产品需要中 会要求设置屏蔽ip地址之内的功能 liunx中iptables命令也是比较重要的 接下来就来在INetd这块实现删除创建子链的相关功能 2 nbsp
  • golang错误处理

    Go语言主要的设计准则是 xff1a 简洁 明白 xff0c 简洁是指语法和C类似 xff0c 相当的简单 xff0c 明白是指任何语句都是很明显的 xff0c 不含有任何隐含的东西 xff0c 在错误处理方案的设计中也贯彻了这一思想 我们

随机推荐

  • golang使用gdb

    GDB调试简介 GDB是FSF 自由软件基金会 发布的一个强大的类UNIX系统下的程序调试工具 使用GDB可以做如下事情 xff1a 启动程序 xff0c 可以按照开发者的自定义要求运行程序 可让被调试的程序在开发者设定的调置的断点处停住
  • Go怎么写测试用例

    如何编写测试用例 由于go test命令只能在一个相应的目录下执行所有文件 xff0c 所以我们接下来新建一个项目目录gotest 这样我们所有的代码和测试代码都在这个目录下 接下来我们在该目录下面创建两个文件 xff1a gotest g
  • golang应用日志

    seelog介绍 seelog是用Go语言实现的一个日志系统 xff0c 它提供了一些简单的函数来实现复杂的日志分配 过滤和格式化 主要有如下特性 xff1a XML的动态配置 xff0c 可以不用重新编译程序而动态的加载配置信息 支持热更
  • golang网站错误处理

    我们的Web应用一旦上线之后 xff0c 那么各种错误出现的概率都有 xff0c Web应用日常运行中可能出现多种错误 xff0c 具体如下所示 xff1a 数据库错误 xff1a 指与访问数据库服务器或数据相关的错误 例如 xff0c 以
  • golang应用部署

    程序开发完毕之后 xff0c 我们现在要部署Web应用程序了 xff0c 但是我们如何来部署这些应用程序呢 xff1f 因为Go程序编译之后是一个可执行文件 xff0c 编写过C程序的读者一定知道采用daemon就可以完美的实现程序后台持续
  • golang备份和恢复

    我们经常会遇到生产服务器的网络断了 硬盘坏了 操作系统崩溃 或者数据库不可用了等各种异常情况 xff0c 所以维护人员需要对生产服务器上的应用和数据做好异地灾备 xff0c 冷备热备的准备 在接下来的介绍中 xff0c 讲解了如何备份应用
  • golang自定义路由器设计

    HTTP路由组件负责将HTTP请求交到对应的函数处理 或者是一个struct的方法 xff0c 如前面小节所描述的结构图 xff0c 路由在框架中相当于一个事件处理器 xff0c 而这个事件包括 xff1a 用户请求的路径 path 例如
  • golang中的Session支持

    session集成 beego中主要有以下的全局变量来控制session处理 xff1a related to session SessionOn bool 是否开启session模块 xff0c 默认不开启 SessionProvider
  • golang表单及验证支持

    在Web开发中对于这样的一个流程可能很眼熟 xff1a 打开一个网页显示出表单 用户填写并提交了表单 如果用户提交了一些无效的信息 xff0c 或者可能漏掉了一个必填项 xff0c 表单将会连同用户的数据和错误问题的描述信息返回 用户再次填
  • sublime搭建C/C++编译环境

    代码一 xff1a 34 cmd 34 34 g 43 43 34 34 file 34 34 std 61 c 43 43 11 34 34 o 34 34 file path file base name 34 34 amp 34 34
  • golang用户认证

    在开发Web应用过程中 xff0c 用户认证是开发者经常遇到的问题 xff0c 用户登录 注册 登出等操作 xff0c 而一般认证也分为三个方面的认证 HTTP Basic和 HTTP Digest认证第三方集成认证 xff1a QQ 微博
  • golang多语言支持

    专注后台开发相关技术 xff0c 广度深度并存 xff0c 干货情怀同在 微信搜索 盼盼编程 关注这个不一样的程序员 强烈推荐人工智能学习网站 beego中设置全局变量如下 xff1a Translation i18n IL Lang st
  • golang中的pprof支持

    专注后台开发相关技术 xff0c 广度深度并存 xff0c 干货情怀同在 微信搜索 盼盼编程 关注这个不一样的程序员 强烈推荐人工智能学习网站 Go语言有一个非常棒的设计就是标准库里面带有代码的性能监控工具 xff0c 在两个地方有包 xf
  • 大厂动态规划面试汇总,提升内功

    注 xff1a 本文是BAT真题收录很值得大家花心思看完 xff0c 看完会有收获 前言 算法是面试大公司必考的项目 xff0c 所以面试前准备好算法至关重要 xff0c 今天整理的常见的动态规划题目 xff0c 希望可以帮到大家 要想学习
  • 进程知识点,只需这一篇

    前言 你的进程 xff0c 为啥挂了 xff1f 进程挂了 xff0c 这个问题大家并不陌生 学完这篇 xff0c 你会对进程有一定了解 后面碰到进程挂的情况 xff0c 你很快能找到对应解决思路 进程在操作系统中 xff0c 是一个很重要
  • 算法:多数元素,多种解法

    前言 xff1a 以前做数学题的时候 xff0c 老师说 xff1a 你们学习多种解题方法 遇到类似不同的问题 xff0c 你都会了 xff0c 这样能提高解题能力 如果你写出多种解法 xff0c 面试官会对你刮目相看 下面一题 xff0c
  • 栈和队列互相实现,一文弄懂它们的关系

    前言 栈和队列是比较基础的数据结构 无论在工作中 xff0c 还是在面试中 xff0c 栈和队列都用的比较多 在计算机的世界 xff0c 你会看到队列和栈 xff0c 无处不在 栈 xff1a 一个先进后出的数据结构 队列 xff1a 一个
  • linux下调试core dump方式汇总,工作必备技能

    缘起 调试 xff0c 是开发流程中一个非常重要的环节 每个程序员都应 xff0c 具备调试代码的能力 xff0c 尤其对于从事 Linux 下的开发的读者 从事 linux 下后台开发 xff0c 有时候会遇到程序突然崩溃的情况 xff0
  • TCP 连接的前世今生

    前言 大家好 xff01 我是盼盼 xff01 之前写了几篇关于算法和 linux 命令的文章 xff0c 今天来学习下 xff0c 网络协议相关的知识 不管你是客户端 xff0c 还是服务端开发 xff0c 网络协议这块都是要学习和了解的
  • 反向传播算法推导过程(看一篇就够了)

    反向传播BackPropagation算法简称BP xff0c 算是神经网络的基础了 在神经网络中 xff0c 正向传播用于模型的训练 xff0c 模型中的参数不一定达到最佳效果 xff0c 需要进行 反向传播 进行权重等参数的修正 此外