从零开始学习深度学习库-2:反向传播

欢迎来到本系列的第二篇文章,我们将从头开始构建一个深度学习库。
本博客系列的代码可以在这个Github仓库中找到。

上一篇文章

在上一篇文章中(链接见这里),我们实现了线性层和常见的激活函数,并成功构建了神经网络的前向传递。
到目前为止,我们的模型只能进行预测,但没有能力训练和纠正其预测。这正是我们今天要讲解的内容,通过实现一个称为反向传播的过程。

反向传播的工作概述

当神经网络进行训练时,它会被给定一个包含输入及其对应输出的数据集。
网络会根据数据集的输入产生预测,并计算其预测与数据集中给出的真实输出之间的偏差(这称为损失)。
训练神经网络的目标是最小化这个损失。
在计算出损失后,网络的权重和偏差会以某种方式进行调整,以减少损失值。记住在上一篇文章中提到,权重和偏差是我们可调节的网络参数,用于计算网络输出。
这个过程会重复数次,希望每次重复时损失都会减少。每次重复被称为一个时代(epoch)。

损失函数

虽然有许多不同的损失函数,但在这篇文章中,我们只会关注均方误差函数。未来的文章中会讨论更多损失函数。
损失函数接收网络的原始错误(通过预测输出 - 实际输出计算得出)并产生一个关于错误严重程度的度量。
均方误差(MSE)接收错误向量,并返回向量中所有平方值的平均值。
例如…

Network output: [1, 2, 3]
Actual outputs: [3, 2, 1]
Error: [-2, 0, 2]
Loss: 2.6666666 ( ( (-2)**2 + (0)**2 + (2)**2 ) / 3 )

在计算平均值之前先对误差进行平方的原因是为了使误差向量中的负值与正值同等对待(因为负数的平方是正数)。
下面是我们的Python类,用于实现均方误差(MSE)…

#loss.pyimport numpy as npclass MSE:def __call__(self, pred, y):self.error = pred - yreturn np.mean(self.error ** 2)

反向传播

反向传播是网络的训练过程。
神经网络训练的目标是最小化损失。
这可以被视为一个优化问题,其解决方案在很大程度上依赖于微积分——更具体地说,是微分。

计算梯度

反向传播的第一步是找到网络中所有权重和偏差相对于损失函数的梯度。
我们用一个例子来演示…
我们的小型示例网络由以下部分组成:

  1. 线性层
  2. Sigmoid层
    所以整个网络的输出计算如下:
  • x - 网络输入
  • w - 线性层的权重
  • b - 线性层的偏差
  • a - 线性层输出
  • pred - 网络输出 / Sigmoid输出

在这里插入图片描述
在这里插入图片描述
现在让我们计算损失

  • y - 对于输入 x 的期望输出
    在这里插入图片描述
    在这里插入图片描述
    现在我们需要找到相对于损失的权重/偏差的梯度。
    在这里插入图片描述
    在这里插入图片描述
    这一步使用了链式法则。
    我们现在已经计算出了参数相对于损失的梯度。
    计算某一层相对于损失的权重/偏差梯度的一般规则是:
  1. 对每一层的输出相对于其输入进行微分(从最后一层开始,直到达到你想要调整参数的那一层)
  2. 将所有这些结果相乘,称之为 grad
  3. 一旦到达所需的层,对其输出相对于其权重进行微分(称这个为 w_grad),并对其偏差进行微分(称这个为 b_grad)。
  4. w_gradgrad 相乘以获得相对于该层权重的损失梯度。对 b_grad 做同样的操作,以获得相对于该层偏差的损失梯度。

有了这个思路,下面是我们所有层和MSE的代码。

#loss.pyimport numpy as npclass MSE:def __call__(self, pred, y):self.error = pred - yreturn np.mean(self.error ** 2)def backward(self):return 2 * (1 / self.error.shape[-1]) * self.error 
#layers.pyimport numpy as npclass Activation:def __init__(self):passclass Layer:def __init__(self):passclass Model: def __init__(self, layers):self.layers = layersdef __call__(self, x):output = xfor layer in self.layers:output = layer(output)return outputclass Linear(Layer):def __init__(self, units):self.units = unitsself.initialized = Falsedef __call__(self, x):self.input = xif not self.initialized:self.w = np.random.rand(self.input.shape[-1], self.units)self.b = np.random.rand(self.units)self.initialized = Truereturn self.input @ self.w + self.bdef backward(self, grad):self.w_gradient = self.input.T @ gradself.b_gradient = np.sum(grad, axis=0)return grad @ self.w.Tclass Sigmoid(Activation):def __call__(self, x):self.output = 1 / (1 + np.exp(-x))return self.outputdef backward(self, grad):return grad * (self.output * (1 - self.output))class Relu(Activation):def __call__(self, x):self.output = np.maximum(0, x)   return self.outputdef backward(self, grad):return grad * np.clip(self.output, 0, 1)class Softmax(Activation):def __call__(self, x):exps = np.exp(x - np.max(x))self.output = exps / np.sum(exps, axis=1, keepdims=True)return self.outputdef backward(self, grad):m, n = self.output.shapep = self.outputtensor1 = np.einsum('ij,ik->ijk', p, p)tensor2 = np.einsum('ij,jk->ijk', p, np.eye(n, n))dSoftmax = tensor2 - tensor1dz = np.einsum('ijk,ik->ij', dSoftmax, grad) return dzclass Tanh(Activation):def __call__(self, x):self.output = np.tanh(x)return self.outputdef backward(self, grad):return grad * (1 - self.output ** 2)

每个类中的 backward 方法是一个函数,用于对层的输出相对于其输入进行微分。
随意查阅每个激活函数的导数,这将使代码更加易于理解。
线性层的 backward 函数不同,因为它不仅计算了输出相对于输入的梯度,还计算了相对于其参数的梯度。

注意
矩阵乘法的微分规则如下,其中 xy 是被相乘的矩阵。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用随机梯度下降优化参数

优化网络参数的方法有很多,但在这篇文章中,我们将介绍最基本的方法,即随机梯度下降(SGD)。
SGD非常简单。它将每个参数计算出的梯度乘以一个指定的学习率。然后,相应的参数减去这个结果。
使用学习率的原因是为了控制网络学习的速度。
最佳学习率值在少量的时代内最小化成本。
过小的学习率也能最小化成本,但需要经过多个时代,因此会花费时间。
过大的学习率会使损失逼近一个非最小值,因此网络无法正确训练。
下面是MSE的代码。

#optim.pyimport layers
import tqdm
#Tqdm是一个进度条,所以我们可以看到代码运行的进度class SGD:def __init__(self, lr = 0.01):self.lr = lrdef __call__(self, model, loss):grad = loss.backward()for layer in tqdm.tqdm(model.layers[::-1]):grad = layer.backward(grad) #计算图层参数梯度if isinstance(layer, layers.Layer):layer.w -= layer.w_gradient * self.lrlayer.b -= layer.b_gradient * self.lr

随着网络训练所需的一切都已准备就绪,我们可以在我们的模型中添加一个训练函数。

#layers.pyimport numpy as np
import loss
import optim
np.random.seed(0)#...class Model: def __init__(self, layers):self.layers = layersdef __call__(self, x):output = xfor layer in self.layers:output = layer(output)return outputdef train(self, x, y, optim = optim.SGD(), loss=loss.MSE(), epochs=10):for epoch in range(1, epochs + 1):pred = self.__call__(x)l = loss(pred, y)optim(self, loss)print (f"epoch {epoch} loss {l}")#...

测试一下!

我们将构建并训练一个神经网络,使其能够作为异或门(XOR gate)。
异或门接收两个输入。输入可以是 0 或 1(代表假或真)。
如果两个输入相同,则门输出 0。
如果两个输入不同,则门输出 1。

#main.pyimport layers
import loss
import optim
import numpy as npx = np.array([[0, 1], [0, 0], [1, 1], [0, 1]])
y = np.array([[1],[0],[0], [1]]) net = layers.Model([layers.Linear(8),layers.Relu(),layers.Linear(4),layers.Sigmoid(),layers.Linear(1),layers.Sigmoid()
])net.train(x, y, optim=optim.SGD(lr=0.6), loss=loss.MSE(), epochs=400)print (net(x))

`` Output ... epoch 390 loss 0.0011290060124405485 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 391 loss 0.0011240809175767955 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 392 loss 0.0011191976855805586 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 393 loss 0.0011143557916784605 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 394 loss 0.0011095547197546522 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 395 loss 0.00110479396217416 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 396 loss 0.0011000730196106248 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 397 loss 0.0010953914008780786 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 398 loss 0.0010907486227668803 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 399 loss 0.0010861442098835058 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s] epoch 400 loss 0.0010815776944942087 [[0.96955654] [0.03727081] [0.03264158] [0.96955654]] ``

正如您所见,结果非常好,与真实输出相差不远(0.001 的损失非常低)。
我们还可以调整模型,使其适用于其他激活函数。

#main.pyimport layers
import loss
import optim
import numpy as npx = np.array([[0, 1], [0, 0], [1, 1], [0, 1]])
y = np.array([[0, 1], [1, 0], [1, 0], [0, 1]]) net = layers.Model([layers.Linear(8),layers.Relu(),layers.Linear(4),layers.Sigmoid(),layers.Linear(2),layers.Softmax()
])net.train(x, y, optim=optim.SGD(lr=0.6), loss=loss.MSE(), epochs=400)print (net(x)) 
Output
epoch 390 loss 0.00045429759266240227
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s]
epoch 391 loss 0.0004524694487356741
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s]
epoch 392 loss 0.000450655387643655
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s]
epoch 393 loss 0.00044885525012255907
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<00:00, 6236.88it/s]
epoch 394 loss 0.00044706887927775473
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s]
epoch 395 loss 0.0004452961205401462
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<00:00, 5748.25it/s]
epoch 396 loss 0.0004435368216234964
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s]
epoch 397 loss 0.00044179083248269265
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s]
epoch 398 loss 0.00044005800527292425
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s]
epoch 399 loss 0.00043833819430972714
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:00<?, ?it/s]
epoch 400 loss 0.0004366312560299245
[[0.01846441 0.98153559][0.97508489 0.02491511][0.97909267 0.02090733][0.01846441 0.98153559]]

我们已经成功构建了一个有效的神经网络。这可以成功应用于更有用的事物,比如MNIST数据集,我们很快会在另一篇文章中使用它。
下一篇文章将介绍更多的损失函数和更多的优化函数。
感谢阅读!

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

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

相关文章

前端学习笔记 | WebAPIs(DOM+BOM)

一、作用和分类 1、基本概念 作用&#xff1a;使用JS去操作HTML和浏览器 分类&#xff1a;DOM&#xff08;文档对象模型&#xff09;和BOM&#xff08;浏览器对象模型&#xff09; html的标签JS的DOM对象 2、获取DOM对象-参数必须加引号 &#xff08;1&#xff09;选择匹配的第…

第10集《天台教观纲宗》

请大家打开讲义第十七页。我们讲到己二、结申正义。 己二、结申正义 《法华经》把我们修行人修行的相貌&#xff0c;比喻作一个车乘。车乘就是一种交通工具&#xff0c;它能够让我们从此岸超越到彼岸去。所以修行它是可以超越的&#xff0c;你今天比昨天超越了&#xff0c;就好…

【C语言步行梯】C语言实现三子棋游戏(含详细分析)

&#x1f3af;每日努力一点点&#xff0c;技术进步看得见 &#x1f3e0;专栏介绍&#xff1a;【C语言步行梯】专栏用于介绍C语言相关内容&#xff0c;每篇文章将通过图片代码片段网络相关题目的方式编写&#xff0c;欢迎订阅~~ 文章目录 需求分析具体实现主函数体菜单实现游戏实…

基于微信小程序的车位共享平台的设计与实现【附项目源码】分享

基于微信小程序的车位共享平台的设计与实现: 源码地址&#xff1a;https://download.csdn.net/download/qq_41810183/88842865 基于微信小程序的车位共享平台设计与实现需求文档 一、引言 随着城市化进程的加快&#xff0c;停车难问题日益凸显。为解决这一难题&#xff0c;我…

VB+ACCESS学籍管理系统-264-(代码+说明)

转载地址: http://www.3q2008.com/soft/search.asp?keyword264 设计要求&#xff1a; 第一&#xff1a;一篇论文&#xff08;5000到10000字&#xff09;不包括图表和程序代码。A4纸20页之内。 论文结构如下&#xff1a; 设计题目&#xff1a;学籍管理系统 附&#xff1a;程…

高级JAVA工程师解决生产环境JVM宕机Java进程挡掉操作系统内存异常实例讲解

高级JAVA工程师解决生产环境JVM宕机Java进程挡掉内存溢出实例讲解 一、事故描述 生产环境Java进程莫名挡掉&#xff0c;JVM宕机。监控平台报警。生产停了&#xff0c;老板急了&#xff0c;客户爆了&#xff0c;怎么迅速解决事故&#xff1f;每次出现生产事故&#xff0c;都是…

使用公式在Excel中指定列值的变化实现自动间隔着色(不是按照固定的行数)

如果你的文件很小&#xff0c;可以手工着色&#xff1b;但如果很大&#xff0c;就要借助公式来着色&#xff1b; 目的是什么&#xff0c;其中之一是&#xff1a;提升可读性。 一起往下看吧&#xff01;&#xff01; 如果你想要根据Excel某列中值的变化来间隔着色&#xff0c;…

easyrecovery破解版百度云(含Mac/Win版)以及EasyRecovery可以恢复哪些设备

软件介绍 当不小心将回收站的文件删除了怎么办&#xff1f;想找回但是不知道怎么找回需要的数据文件&#xff1f;别担心今天小编就为大家介绍一款非常专业的电脑数据文件恢复工具&#xff0c;easyrecovery14是由Ontrack专为电脑用户推出的一款专业的数据恢复软件&…

景联文科技:提供行业垂直大模型训练数据

近年来&#xff0c;以大模型为代表的人工智能技术已成为国家科技实力竞争的焦点。其中垂直大模型作为重要方向&#xff0c;在相关政策引导及市场需求的驱动下&#xff0c;已展现出较强的发展活力。 行业垂直大模型是针对特定行业的需求和场景进行深度定制的。这意味着模型在训练…

蓝桥杯专题 bfs习题详解

1.离开中山路 #include<iostream> #include<cstring> #include<queue> #include<algorithm> #include<string> using namespace std; int x1,x2,y1,y2; int n,n1,m1; const int N1010;typedef pair<int,int> PII; queue<PII> q;int …

GIS软件应用(二)

任务&#xff1a; 1. 正确划分渔网并裁剪出研究区域 2. 渔网与poi数据正确空间链接并统计网格内类别POI数量 步骤&#xff1a; 将南京市边界进行投影变换&#xff0c;具体看我的这篇文章&#xff1a;GIS软件应用&#xff08;一&#xff09;-CSDN博客 选择ArcToolbox中的 Cr…

3.Linux/UNIX平台Python的下载、安装和配置环境变量——《跟老吕学Python编程》

3.Linux/UNIX平台Python的下载、安装和配置环境变量——《跟老吕学Python编程》 一、下载Linux/UNIX版Python1.Python官网2.Linux/UNIX版Python下载网址 二、在Linux/UNIX安装Python1.在Ubuntu Linux安装Python1.1 检查Python版本1.2 高级包管理工具1.3 添加存储库1.4 更新软件…

HTML静态网页成品作业(HTML+CSS)——电影肖申克的救赎介绍设计制作(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

【Flink SQL】Flink SQL 基础概念:SQL 动态表 连续查询

Flink SQL 基础概念&#xff1a;SQL 动态表 & 连续查询 1.SQL 应用于流处理的思路2.流批处理的异同点及将 SQL 应用于流处理核心解决的问题3.SQL 流处理的输入&#xff1a;输入流映射为 SQL 动态输入表4.SQL 流处理的计算&#xff1a;实时处理底层技术 - SQL 连续查询5.SQL…

Netty架构详解

文章目录 概述整体结构Netty的核心组件逻辑架构BootStrap & ServerBootStrapChannelPipelineFuture、回调和 ChannelHandler选择器、事件和 EventLoopChannelHandler的各种ChannelInitializer类图 Protocol Support 协议支持层Transport Service 传输服务层Core 核心层模块…

Windows C++ 使用WinAPI实现RPC

demo下载地址&#xff1a;https://download.csdn.net/download/2403_83063732/88958730 1、创建IDL文件以及acf文件&#xff08;创建helloworld.idl helloworld.acf&#xff09; 其中IDL文件&#xff1a; import "oaidl.idl"; import "ocidl.idl"; [ …

【LeetCode热题100】73. 矩阵置零(矩阵)

一.题目要求 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 二.题目难度 中等 三.输入样例 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0…

2024考研计算机考研复试-每日重点(第二十期)

公众号“准研计算机复试”&#xff0c;超全大佬复试资料&#xff0c;保姆级复试&#xff0c;80%的题目都是上岸大佬提供的。 研宝们&#xff0c;App更新啦&#xff01; 计算机组成原理&#xff1a; 10.☆什么是数据存储的大端模式和小端模式&#xff1f; 大端模式&#xff1a;数…

github拉取项目找不到前端代码

今天从github上拉取了一个项目&#xff0c;使用docker部署在了服务器上&#xff0c; 代码正常运行&#xff0c;但是想在下载的项目中找前端代码遇到了问题&#xff0c;项目结构中的template模板只有一个页面&#xff0c;于是查看nginx配置文件 查看index.html 引入了一些js文件…

使用Julia及R语言生成正态分布的随机数字并写入CSV文件

在操作之前需要先下载Julia的Distributions包&#xff0c;这个包用于进行相关概率分布的函数调用。 在输入 ] 进入Julia包管理模式后输入&#xff1a; add Distributions 这里我使用我们自己实验室的实测数据 &#xff0c;平均值0.67&#xff0c;方差0.11&#xff0c;数据分…