11-08 周三 图解机器学习之实现逻辑异或,理解输出层误差和隐藏层误差项和动量因子

news2024/11/24 10:05:12
11-08 周三 图解机器学习之实现逻辑异或,理解输出层误差和隐藏层误差项
时间版本修改人描述
2023年11月8日14:36:36V0.1宋全恒新建文档

简介

 最近笔者完成了《图解机器学习》这本书的阅读,由于最近深度学习网络大行其是,所以也想要好好的弄清楚神经网络的工作原理。比如说训练、比如说验证,比如说权重更新,之前也曾经写过两个博客来描述感知机和BP算法示意。

  • 10-09 周一 图解机器学习之深度学习感知机学习
  • 11-06 周一 神经网络之前向传播和反向传播代码实战

 反向传播这个博客里主要通过一个样本,来不断的更新参数,但实际的神经网络结构是不会像博客中name简单的,因此还是需要给出一个计算公式的。在阅读图解机器学习P169页,如下代码时,自己没有看懂:

        # 计算输出层误差
        for k in range(self.no):
            error = targets[k] - self.ao[k]
            output_deltas[k] = dsigmoid(self.ao[k]) * error
            
        # 计算隐藏层的误差
        hidden_deltas = [0.0]*self.nh
        for j in range(self.nh):
            error = 0
            for k in range(self.no):
                error = error + output_deltas[k] * self.wo[j][k]
            hidden_deltas[j] = dsigmoid(self.ah[j]) * error
            
        # 更新输出层权重
        for j in range(self.nh):
            for k in range(self.no):
                change = output_deltas[k]*self.ah[j]
                self.wo[j][k] = self.wo[j][k] + N*change + M * self.co[j][k]

 上述在计算过程中求出了输出层误差和隐藏层误差项。如何理解这个代码片段呢?

完整代码

import math
import random
import string
random.seed(0)

# 生成区间[a, b)内的随机数
def rand(a, b):
    return (b - a) *random.random() + a


# 生成I*J大小的矩阵, 默认零矩阵
def makeMatrix(I, J, fill=0.0):
    m = []
    for i in range(I):
        m.append([fill]*J)
    return m

# 函数 sigmoid, 采用tanh函数, 比起标准的1/(1+exp(-x))更好
def sigmoid(x):
    return math.tanh(x)

# 函数sigmoid的派生函数 tanh(x)' = 1 - tanh(x)^2
def dsigmoid(x):
    return 1.0 - x**2


class BPNeuralNet:
    '''建立三层反向传播神经网络'''
    def __init__(self, ni, nh, no) -> None:
        self.ni = ni + 1
        self.nh = nh
        self.no = no
        
        # 激活神经网络的所有节点
        self.ai = [1.0]* self.ni
        self.ah = [1.0]*self.nh
        self.ao = [1.0]* self.no
        
        # 建立权重矩阵
        self.wi = makeMatrix(self.ni, self.nh)
        self.wo = makeMatrix(self.nh, self.no)
        
        # 设为随机值
        for i in range(self.ni):
            for j in range(self.nh):
                self.wi[i][j] = rand(-0.2, 0.2)
                
        for i in range(self.nh):
            for j in range(self.no):
                self.wo[i][j] = rand(-2.0, 2.0)
                
                
        # 建立动量因子
        self.ci = makeMatrix(self.ni, self.nh)
        self.co = makeMatrix(self.nh, self.no)
        
    # 前向传播,得到预计的输出。
    # 各个神经元的输出分别位于self.ah 和self.ao
    # inputs 代表一个样本
    def fp(self, inputs):
        if len(inputs) != self.ni -1:
            raise ValueError('与输入层节点数不符错误!')
        
        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 j in range(self.no):
            sum = 0
            for i in range(self.nh):
                sum += self.ah[i]*self.wo[i][j]
                
            self.ao[j] = sigmoid(sum)
        return self.ao[:]
    
    # N 学习速率 learning factor
    # M 动量因子 momentum factor
    # 基本思路是直接求出每个神经元的误差
    def back_propagate(self, targets, N, M):
        '''反向传播'''
        if len(targets) != self.no:
            raise ValueError("与输出层节点数不符!")
        
        output_deltas = [0.0] * self.no
        
        
        # 计算输出层误差
        for k in range(self.no):
            error = targets[k] - self.ao[k]
            output_deltas[k] = dsigmoid(self.ao[k]) * error
            
        # 计算隐藏层的误差
        hidden_deltas = [0.0]*self.nh
        for j in range(self.nh):
            error = 0
            for k in range(self.no):
                error = error + output_deltas[k] * self.wo[j][k]
            hidden_deltas[j] = dsigmoid(self.ah[j]) * error
            
        # 更新输出层权重
        for j in range(self.nh):
            for k in range(self.no):
                change = output_deltas[k]*self.ah[j]
                self.wo[j][k] = self.wo[j][k] + N*change + M * self.co[j][k]
                self.co[j][k] = change
        
        # 更新输入层权重
        for i in range(self.ni):
            for j in range(self.nh):
                change=hidden_deltas[j]*self.ai[i]
                self.wi[i][j] += N * change + M * self.ci[i][j]
                self.ci[i][j] = change
        
        error = 0.0
        for k in range(len(targets)):
            error = error + 0.5*(targets[k]-self.ao[k])**2
        return error
    

    def test(self, patterns):
        for p in patterns:
            print(p[0], '->', self.fp(p[0]))
            
    
    def weights(self):
        print('输入层权重')
        for i in range(self.ni):
            print(self.wi[i])
            
        print()
        print("输出层权重")
        for j in range(self.nh):
            print(self.wo[j])
    
    def train(self, patterns, iterations=100000, N =0.5, M=0.1):
        for i in range(iterations):
            error = 0.0
            for p in patterns:
                inputs = p[0]
                targets = p[1]
                self.fp(inputs)
                error = error + self.back_propagate(targets, N, M)
                
            if i % 100 ==0:
                print('计算误差的值是: %-.5f'%error)
                    
def trainprog():
    # BP神经网络学习逻辑异或
    pat = [
        [[0, 0], [0]],
        [[0, 1], [1]],
        [[1, 0], [1]],
        [[1, 1], [0]]
    ]
    # 创建一个神经网络,输入层两个节点, 输出层两个节点,输出层一个节点:
    net = BPNeuralNet(2, 3, 1)
    net.train(pat)
    
    # 测试训练的成果
    net.test(pat)
    
if __name__ == '__main__':
    trainprog()

 上述代码在理解上并不复杂,主要是通过三层神经网络来拟合逻辑异或运算。采用的是个案更新的策略来更新权重参数。

权重更新

基础知识

 多层前馈神经网络。

 一个示例: 奶酪是否喜爱。

 为此我们构建一个神经网络:

激活函数

  激活函数

  • 指数函数
  • sigmoid
  • 逻辑回归

 校正因子的概念如下:

 权重更新的策略有多种:

  • 个案更新 case-based 更容易得到准确的结果。
  • 批量更新 batch 优点就是比较快,加速。

迭代终止条件

 迭代终止条件:

  • 当权重和偏置差异与上一次非常小
  • 误差达到之前设置的阈值
  • 运行次数

存疑代码

        # 计算输出层误差
        for k in range(self.no):
            error = targets[k] - self.ao[k]
            output_deltas[k] = dsigmoid(self.ao[k]) * error
            
        # 计算隐藏层的误差
        hidden_deltas = [0.0]*self.nh
        for j in range(self.nh):
            error = 0
            for k in range(self.no):
                error = error + output_deltas[k] * self.wo[j][k]
            hidden_deltas[j] = dsigmoid(self.ah[j]) * error
            
        # 更新输出层权重
        for j in range(self.nh):
            for k in range(self.no):
                change = output_deltas[k]*self.ah[j]
                self.wo[j][k] = self.wo[j][k] + N*change + M * self.co[j][k]
                self.co[j][k] = change
        
        # 更新输入层权重
        for i in range(self.ni):
            for j in range(self.nh):
                change=hidden_deltas[j]*self.ai[i]
                self.wi[i][j] += N * change + M * self.ci[i][j]
                self.ci[i][j] = change

 上述分别计算出了输出层的误差项和输出层的误差项。按照上述代码理解,前两个for循环用于计算误差项,后两个循环用来更新权重,顺序从后向前,这也是反向传播得名的由来。关键是为什么输出层的误差是这么得来的呢?

 参考 BP神经网络-第6集 反向传播误差,调整全部权重,这对于理解是非常关键的。

 我们以同样的方式,就可以得到每个神经元的误差。如下图

 可以采用矩阵相乘的方法

 权重通过矩阵乘表示。

gpt辅助理解

 自己还是无法理解,但感觉输出层的误差项与选用的损失函数密切相关,因此,笔者询问了GPT,得到了如下的结果:

  • 为什么要乘以激活函数的导数?
  • 交叉熵损失函数的输出层误差项
  • 均方差 输出层误差项:

 由此,我们可以得到如下的图示:

在计算h1节点的误差项时,输出层两个误差项以w7 和 w8进行作用,进而可以得到h1神经元的误差项:

errorh1=w7*e1 + w8 * e2。 依次可以得到h1, h2, h3神经元的误差项损失。

动量因子

 代码片段,其实整体贯彻了P166 图解机器学习图13.10,

 相互映照,也可以通过代码来理解上述的过程:

 代码参见 11-06 周一 神经网络之前向传播和反向传播代码实战

总结

 这部分的代码片段比之前的全部手动计算权重更新的过程复杂一些,因为抽象出了输出层误差项和隐藏层误差项,代码的抽象知识更加复杂了,但 BP神经网络-第6集 反向传播误差,调整全部权重则直接给出了误差项的矩阵乘表示,而这种方式,应该也是机器学习库中默认使用的方式吧。总之,这篇文章试图解释《图解机器学习》中第13章深度学习网络的代码,弄清楚其中权重更新的方式,包括为什么使用动量因子进行更新这种优化技术。希望读者能够读懂,进而在自己的工程实践中使用深度学习解决自己的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1184912.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows环境下编译OLLVM源码(VS2022)

windows环境下编译OLLVM 13.x VisualStudio配置下载OLLVM13.xollvm的使用 网上关于windows环境编译ollvm信息比较杂乱,在此编译成功的基础上做一下总结! VisualStudio配置 1,正常配置C桌面环境 2,在单个组件中选择用于Windows得C…

Go基础知识全面总结

文章目录 go基本数据类型bool类型数值型字符字符串 数据类型的转换运算符和表达式1. 算数运算符2.关系运算符3. 逻辑运算符4. 位运算符5. 赋值运算符6. 其他运算符运算符优先级转义符 go基本数据类型 bool类型 布尔型的值只可以是常量 true 或者 false。⼀个简单的例⼦&#…

Windows系统C++语言环境下通过SDK进行动作捕捉数据传输

NOKOV度量动作捕捉系统可以与市面上主流的操作系统和编程语言实现通信。可以在Windows系统C语言环境下通过SDK进行动作捕捉数据传输。 一、形影软件设置 1、实时模式和后处理模式都可以通过SDK传输数据。以后处理模式为例。将模式切换到后处理模式 2、加载一个刚体数据 3、打…

2.7V 到 5.5V、串行输入、电压输出、16 位数模转换器MS5541/MS5542

MS5541/MS5542 是一款单通道、 16 位、串行输入、电压 输出的数模转换器,采用 2.7V 至 5.5V 单电源供电,输出范围 为 0V 至 V REF 。在输出范围内保证单调性,在温度范围为 -40 C 至 85 C 能够提供 1LSB INL 的 14 位精度。…

2023年重水(氧化氘)市场规模:现状及未来发展趋

重水是水的一种,又称氘化水,它的摩尔质量比一般水要重。普通的水是由两个只具有质子的氢原子和一个氧16原子所组成,但在重水分子内的两个氢同位素氘,比一般氢原子有各多一个中子,因此造成重水分子的质量比一般水要重。…

双wan路由器介绍( 多wan口路由器用途,双wan网速叠加快吗)

​ 文章同款:https://www.key-iot.com/iotlist/sr500-15.html 对于工业领域来说,网络连接的可靠性和稳定性至关重要。双WAN口工业级路由器SR500是一款出色的解决方案,旨在提供强大的多线路冗余和负载均衡功能,以满足工业环境中的…

使用PHP实现对称加密和解密过程,真的是太简单了!

🚀 个人主页 极客小俊 ✍🏻 作者简介:web开发者、设计师、技术分享博主 🐋 希望大家多多支持一下, 我们一起进步!😄 🏅 如果文章对你有帮助的话,欢迎评论 💬点赞&#x1…

[yarn]yarn异常

一、运行一下算圆周率的测试代码,看下报错 cd /home/data_warehouse/module/hadoop-3.1.3/share/hadoop/mapreduce hadoop jar hadoop-mapreduce-examples-3.1.3.jar pi 1000 1000 后面2个数字参数的含义: 第1个1000指的是要运行1000次map任务 …

【Element】隐藏 el-table 展开行的箭头

需求 点击行展开行,隐藏箭头 方法 首先需求是点击行显示展开行 row-click"rowClick"const rowClick (row: any, column: any, event: any) > {console.log(row, column, event)if (multipleTable.value) {multipleTable.value.toggleRowExpansio…

存储虚拟化讲解

目录 存储虚拟化的分类 按照虚拟化发生的位置分类 基于主机的虚拟化 基于存储设备的虚拟化 基于网络的虚拟化 按照虚拟化实现方式分类 带内虚拟化 带外虚拟化 按照虚拟化的对象分类 虚拟机磁盘类型 按照磁盘的特性分类 按照磁盘的安全性分类 什么是虚拟化 存储虚拟…

许战海战略文库 | 从“物美价廉”到“技术出海”:中国车企新能源时代如何征服全球市场?

引言:从燃油车到新能源汽车,中国车企的海外战略正在经历一次深刻的转变。面临技术、产业链和文化等多重挑战,他们如何调整步伐,捕捉新的机遇,同时避免潜在风险,将决定其在全球市场的未来地位。 在燃油车时代,中国车企凭借“物美价廉”赢得了一…

基于SSM的“大学生艺术节”管理系统的设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

Powerpoint不小心被覆盖?PPT误删文件如何恢复?

PowerPoint不小心删除了,这可能是众多学生和工作人员最头痛的事情了。PPT被覆盖或误删可能意味着几个小时的努力付之东流。那么PPT覆盖的文档要如何救回来呢?小编将会在本篇文章中为大家分享几个解决方案,使PPT文档覆盖还原操作成为可能&…

Go:如何在GoLand中引用github.com中的第三方包

本篇博客主要介绍如何在GoLand中引入github.com中的第三方包。具体步骤如下: 正文 (1) 先在GoLand中打开go的工作区目录(即环境变量$GOPATH设置的变量)。如图: 关于工作区目录中的三个子目录: bin: 保存已编译的二进制可执行程序;pkg: 保…

OpenAI首届开发者大会多项更新汇总

OpenAI Dev Day 提供了多项更新,总结如下: GPT 4-Turbo - 现在可以通过API使用GPT 4-Turbo。- 提供了更长的128k令牌上下文,之前为32k。- 相比GPT-4,成本降低了50%以上。- 知识更新至2023年4月,之前为2021年9月。- 性…

JAVA 版小程序商城免费搭建 多商家入驻 直播带货 商城系统 B2B2C 商城源码之 B2B2C产品概述

1. 涉及平台 平台管理、商家端(PC端、手机端)、买家平台(H5/公众号、小程序、APP端(IOS/Android)、微服务平台(业务服务) 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

快速搭建UmiJS4.0项目及常见问题解决方案

yarn create umi选择项目类型 ○ Pick Umi App Template │ Simple App选择创建工具 ○ Pick Npm Client │ yarn选择源 ○ Pick Npm Registry │ taobao启用 Prettier(可选) yarn umi g √ Pick generator type Enable Prettier -- Setup Pr…

Cesium 相机设置

1.setView 直接跳转到目的地 // 设置相机位置 const position Cesium.Cartesian3.fromDegrees(113, 31, 20000); // setView通过定义相机目的地(方向),直接跳转到目的地 viewer.camera.setView({ destination: position, // 位置设置 orientation: { //…

【C/PTA——循环结构3】

C/PTA——循环结构3 7-1 二分法求多项式单根1.题目要求2.代码实现 7-2 循环-十进制转化1.题目要求2.代码实现 7-3 梅森数1.题目要求2.代码实现 7-4 单词长度1.题目要求2.代码实现 7-5 21循环-求和31.题目要求2.代码实现 7-6 21循环-金字塔1.题目要求2.代码实现 7-7 循环-杨辉三…