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

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.nhfor j in range(self.nh):error = 0for 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**2class BPNeuralNet:'''建立三层反向传播神经网络'''def __init__(self, ni, nh, no) -> None:self.ni = ni + 1self.nh = nhself.no = no# 激活神经网络的所有节点self.ai = [1.0]* self.niself.ah = [1.0]*self.nhself.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.0for 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 = 0for 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.nhfor j in range(self.nh):error = 0for 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] = changeerror = 0.0for k in range(len(targets)):error = error + 0.5*(targets[k]-self.ao[k])**2return errordef 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.0for 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.nhfor j in range(self.nh):error = 0for 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.hqwc.cn/news/166652.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

强力解决使用node版本管理工具 NVM 出现的问题(找不到 node,或者找不到 npm)

强力解决使用node版本管理工具 NVM 出现的问题(找不到 node,或者找不到 npm) node与npm版本对应关系 nvm是好用的Nodejs版本管理工具, 通过它可以方便地在本地调换Node版本。 2020-05-28 Node当前长期稳定版12.17.0,…

机器学习模板代码(期末考试复习)自用存档

机器学习复习代码 利用sklearn实现knn import numpy as np import pandas as pd from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import GridSearchCVdef model_selection(x_train, y_train):## 第一个是网格搜索## p是选择查找方式:1是欧…

YOLO目标检测——海洋目标检测数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用:海洋监管、海洋资源开发、海洋科学研究数据集说明:海洋目标检测数据集,真实场景的高质量图片数据,数据场景丰富,含有“金属”、“未知”、“橡胶”、“平台”、“塑料”、“木材”、“布”、“纸张”、“…

《QT从基础到进阶·十六》QT实现客户端和服务端的简单交互

QT版本:5.15.2 VS版本:2019 客户端程序主要包含三块:连接服务器,发送消息,关闭客户端 服务端程序主要包含三块:打开消息监听,接收消息并反馈,关闭服务端 1、先打开服务端监听功能 …

GIS入门,xyz地图瓦片是什么,xyz数据格式详解,如何发布离线XYZ瓦片到nginx或者tomcat中

XYZ介绍 XYZ瓦片是一种在线地图数据格式,由goole公司开发。 与其他瓦片地图类似,XYZ瓦片将地图数据分解为一系列小的图像块,以提高地图显示效率和性能。 XYZ瓦片提供了一种开放的地图平台,使开发者可以轻松地将地图集成到自己的应用程序中。同时,它还提供了高分辨率图像和…

如何在时间循环里最优决策——时间旅行者的最优决策

文章目录 每日一句正能量前言时间旅行和平行宇宙强化学习策略梯度算法代码案例推荐阅读赠书活动 每日一句正能量 做一个决定,并不难,难的是付诸行动,并且坚持到底。 前言 时间循环是一类热门的影视题材,其设定常常如下&#xff1…

Spring Data JPA 项目配置与QueryDSL集成

一、说明 Spring Data JPA通过Spring Initializer创建时勾选相关依赖即可引入,QueryDSL需要单独引入。Spring JPA针对QueryDSL有比较好的兼容性,可以实现优雅的SQL构建。 二、设置JPA默认配置(yaml格式) spring:jpa:hibernate:…

2023.11.8 hadoop学习-概述,hdfs dfs的shell命令

目录 1.分布式和集群 2.Hadoop框架 3.版本更新 4.hadoop架构详解 5.页面访问端口 6.Hadoop-HDFS HDFS架构 HDFS副本 7.SHELL命令 8.启动hive服务 1.分布式和集群 分布式: 多台服务器协同配合完成同一个大任务(每个服务器都只完成大任务拆分出来的单独1个子任务)集 群:…

HTTP-HTTPS区别详解

一、HTTP协议 1. GET和POST的请求的区别 Post 和 Get 是 HTTP 请求的两种方法,其区别如下: 应用场景: GET 请求是一个幂等的请求,一般 Get 请求用于对服务器资源不会产生影响的场景,比如说请求一个网页的资源。而 Po…

[模版总结] - 树的基本算法1 - 遍历

树结构定义 一种非线性存储结构,具有存储“一对多”关系的数据元素集合 种类 General Tree TrieB/B 树二叉树 满/完满/完全二叉树 完美BT : 除了叶子结点外所有节点都有两个字节点,每一层都完满填充完全BT: 除最后一层以外其他每一层都完美…

VUE获取当前日期的周日和周六

<template><div><div click"handleLast()">上一周</div><div click"handleNext()">下一周</div><el-calendarref"monChild"v-model"value":first-day-of-week"7":range"[sta…

二十、W5100S/W5500+RP2040树莓派Pico<MQTT连接阿里云控制板载LED>

文章目录 1. 前言2. 简介2.1 初步了解阿里云物联网平台创建产品步骤2.2 阿里云物模型讲解 3 WIZnet以太网芯片4 示例概述4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 结果演示 5 注意事项6 相关链接 1. 前言 物联网平台提供安全可靠的设备连接通信能力&#xf…