从零开始学习深度学习库-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.hqwc.cn/news/537296.html

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

相关文章

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

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

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

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

IT廉连看——Uniapp——模板语法

IT廉连看——Uniapp——模板语法 众所周知&#xff0c;Uniapp是使用vue.js框架开发出来的&#xff0c;所以说它可以使用vue中的语法和指令来开发我们的项目。如果没有学习过vue的话开发起来会比较吃力&#xff0c;所以这节课就带大家学习几个常用的vue知识。如果有学习过vue&a…

【五、接口自动化测试】

大家好&#xff0c;我是山茶&#xff0c;一个探索AI 测试的程序员 在网上看到了许多关于post与get之间区别的帖子&#xff0c;也有很多帖子是直接粘贴复制的&#xff0c;甚至连标题、符号都没改&#xff0c;甚至还有很多争议 一、post、get 关于post与get之间区别&#xff0c;…

BigDL-LLM 安装指南——在iGPU集成显卡下使用BigDL-LLM大模型库加速LLM

文章目录 iGPU是什么&#xff1f;一、环境准备1.1 Visual Studio 2022 Community 安装1.2 安装或更新最新版本的GPU驱动程序1.3 安装英特尔oneAPI工具包2024.0版本1.4 安装Anaconda 二、BigDL -LLM 安装2.1 创建虚拟环境2.2 激活虚拟环境2.3 安装bigdl-llm[xpu] 三、运行环境配…

nacos做注册注册中心go语言实战教程(服务的注册与获取)

背景 随着访问量的逐渐增大&#xff0c;单体应用结构渐渐不满足需求&#xff0c;在微服务出现之后引用被拆分为一个个的服务&#xff0c;服务之间可以互相访问。初期服务之间的调用只要知道服务地址和端口即可&#xff0c;而服务会出现增减、故障、升级等变化导致端口和ip也变…

法规解读 | 坚持总体国家安全观,新修订的《保守国家秘密法》今年5月1日起施行!

2024年2月27日&#xff0c;第十四届全国人大常委会第八次会议表决通过新修订的《中华人民共和国保守国家秘密法》&#xff08;以下简称保密法&#xff09;&#xff0c;自2024年5月1日起施行。 本次保密法修订坚持总体国家安全观&#xff0c;统筹发展与安全。 一方面吸收了一些工…

雅特力AT32A403开发板评测 05 0.96寸 IIC接口 OLED模块显示

雅特力AT32A403开发板评测 05 0.96寸 IIC接口 OLED模块显示 1.软硬件平台 软硬件平台 AT32A403A Board开发板 MDK-ARM Keil 0.96寸 IIC接口 OLED显示模块 2.IIC总线 处理器和芯片间的通信可以形象的比喻成两个人讲话&#xff1a;1、你说的别人得能听懂&#xff1a;双方…

OPPO后端二面,凉了!

这篇文章的问题来源于一个读者之前分享的 OPPO 后端凉经&#xff0c;我对比较典型的一些问题进行了分类并给出了详细的参考答案。希望能对正在参加面试的朋友们能够有点帮助&#xff01; Java String 为什么是不可变的? public final class String implements java.io.Seri…

4.MAC平台Python的下载、安装(含Python2.7+Python3.12双版本环境变量配置)——《跟老吕学Python编程》

4.MAC平台Python的下载、安装&#xff08;含Python2.7Python3.12双版本环境变量配置&#xff09;——《跟老吕学Python编程》&#xff09;——跟老吕学Python编程 一、下载MAC版Python1.Python官网2.MAC版Python下载网址 二、在MAC安装Python1.在MAC安装Python2.阅读Python重要…

JVM之调优(一)

背景&#xff1a;生产环境由于堆内存较大&#xff0c;fullgc 垃圾回收导致程序卡顿问题&#xff08;假死&#xff09; 目录 一、程序卡顿导致的影响 前端页面空白后端数据重复 二、解决方法 降低堆内存大小使用合适的垃圾回收器&#xff08;可以尝试&#xff0c;还未进行测试…

ROS 语音交互(三) tts

目录 一、模型选择 二、流程 三、核心代码展示 一、模型选择 科大讯飞超拟人识别 二、流程 超拟⼈合成协议 | 讯飞开放平台文档中心 (xfyun.cn) 三、核心代码展示 # coding: utf-8 import _thread as thread import os import time import base64import base64 import …