342|3

75

帖子

0

TA的资源

一粒金砂(高级)

楼主
 

《深度学习的数学——使用Python语言》10.全连接网络的反向传播 [复制链接]

 

基于书中第十章,本节中,我们将深入学习反向传播的原理,并通过MNIST手写数字识别任务,结合PyTorch代码实现,手动编写反向传播逻辑,从而加深对于反向传播内部机制的理解。

神经网络与反向传播的基本概念

神经网络是一种由多层神经元组成的计算模型,每一层神经元通过权重和偏置连接起来。神经网络的学习过程可以分为两个阶段:前向传播反向传播

  • 前向传播:输入数据从输入层经过隐藏层,最终到达输出层,计算网络的预测结果。

  • 反向传播:根据预测结果与真实标签之间的误差,从输出层逐层向前计算梯度,并更新网络的权重和偏置。

反向传播的核心是链式法则,它通过将误差从输出层传递回输入层,计算每一层参数的梯度,从而指导参数的更新。

MNIST手写数字识别任务

MNIST是一个经典的手写数字识别数据集,包含60,000张训练图像和10,000张测试图像,每张图像是一个28x28的灰度图,标签为0到9的数字。我们的目标是构建一个神经网络,能够正确识别这些手写数字。

反向传播的数学原理

为了更好地理解反向传播,我们需要从数学角度分析它的工作原理。假设我们有一个简单的两层神经网络,输入层大小为784(28x28),隐藏层大小为128,输出层大小为10(对应10个数字类别)。

前向传播

前向传播的计算过程如下:

  1. 输入层到隐藏层:

其中,X是输入数据,W1是输入层到隐藏层的权重矩阵,b1是偏置,σ是激活函数(如ReLU)。

  1. 隐藏层到输出层:

其中,W2是隐藏层到输出层的权重矩阵,b2是偏置,softmax函数用于将输出转换为概率分布。

损失函数

我们使用交叉熵损失函数来衡量预测结果与真实标签之间的差异:

其中,yi是真实标签的one-hot编码,a2i是输出层的预测概率。

反向传播

反向传播的目标是计算损失函数对每一层参数的梯度,并更新参数。具体步骤如下:

  1. 计算输出层的误差:

  1. 计算隐藏层的误差:

其中,σ′是激活函数的导数。

  1. 计算梯度并更新参数:

  1. 使用梯度下降法更新参数:

其中,η是学习率。

代码实现

下面我们通过PyTorch实现一个简单的两层神经网络,并手动编写反向传播逻辑,同时利用CUDA加速训练过程。

  • import torch
  • from torch.utils.data import DataLoader
  • from torchvision import datasets, transforms
  • # 检查CUDA是否可用
  • device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  • print(f'Using device: {device}')
  • # 定义数据预处理
  • transform = transforms.Compose([
  • transforms.ToTensor(),
  • transforms.Normalize((0.5,), (0.5,))
  • ])
  • # 加载MNIST数据集
  • train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
  • test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
  • train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
  • test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
  • # 定义神经网络模型
  • class SimpleNN:
  • def __init__(self, input_size, hidden_size, output_size):
  • # 初始化参数
  • self.W1 = torch.randn(input_size, hidden_size, device=device) * 0.01
  • self.b1 = torch.zeros(1, hidden_size, device=device)
  • self.W2 = torch.randn(hidden_size, output_size, device=device) * 0.01
  • self.b2 = torch.zeros(1, output_size, device=device)
  • def forward(self, X):
  • # 前向传播
  • self.z1 = torch.matmul(X, self.W1) + self.b1
  • self.a1 = torch.relu(self.z1)
  • self.z2 = torch.matmul(self.a1, self.W2) + self.b2
  • self.a2 = torch.softmax(self.z2, dim=1)
  • return self.a2
  • def backward(self, X, y, output, learning_rate):
  • # 反向传播
  • m = X.shape[0]
  • # 输出层误差
  • dz2 = output - y
  • dW2 = torch.matmul(self.a1.T, dz2) / m
  • db2 = torch.sum(dz2, dim=0, keepdim=True) / m
  • # 隐藏层误差
  • dz1 = torch.matmul(dz2, self.W2.T) * (self.a1 > 0).float()
  • dW1 = torch.matmul(X.T, dz1) / m
  • db1 = torch.sum(dz1, dim=0, keepdim=True) / m
  • # 更新参数
  • self.W2 -= learning_rate * dW2
  • self.b2 -= learning_rate * db2
  • self.W1 -= learning_rate * dW1
  • self.b1 -= learning_rate * db1
  • def train(self, train_loader, epochs, learning_rate):
  • for epoch in range(epochs):
  • for images, labels in train_loader:
  • # 将图像展平并移动到GPU
  • images = images.view(-1, 28*28).to(device)
  • # 将标签转换为one-hot编码并移动到GPU
  • y = torch.zeros(labels.size(0), 10, device=device)
  • y[torch.arange(labels.size(0)), labels] = 1
  • # 前向传播
  • output = self.forward(images)
  • # 反向传播
  • self.backward(images, y, output, learning_rate)
  • if (epoch+1) % 5 == 0:
  • print(f'Epoch [{epoch+1}/{epochs}]')
  • def evaluate(self, test_loader):
  • correct = 0
  • total = 0
  • for images, labels in test_loader:
  • images = images.view(-1, 28*28).to(device)
  • outputs = self.forward(images)
  • _, predicted = torch.max(outputs.data, 1)
  • total += labels.size(0)
  • correct += (predicted.cpu() == labels).sum().item()
  • print(f'Test Accuracy: {100 * correct / total:.2f}%')
  • # 初始化模型
  • input_size = 28 * 28
  • hidden_size = 128
  • output_size = 10
  • model = SimpleNN(input_size, hidden_size, output_size)
  • # 训练模型
  • model.train(train_loader, epochs=20, learning_rate=0.1)
  • # 测试模型
  • model.evaluate(test_loader)

在SimpleNN的init部分,W1 和 W2 分别是输入层到隐藏层和隐藏层到输出层的权重矩阵。我们使用 torch.randn 生成服从标准正态分布的随机数,并乘以 0.01 来缩小初始值的范围。b1 和 b2 是偏置,初始化为零。所有参数都被放置在指定的设备(如GPU)上。

在forward()中,实现了前向传播的过程。首先,输入数据 X 与权重矩阵 W1 进行矩阵乘法,再加上偏置 b1,得到隐藏层的加权输入 z1。然后,通过ReLU激活函数对 z1 进行非线性变换,得到隐藏层的激活值 a1。之后,隐藏层的激活值 a1 与权重矩阵 W2 进行矩阵乘法,再加上偏置 b2,得到输出层的加权输入 z2。最后,通过softmax函数将 z2 转换为概率分布 a2,表示每个类别的预测概率。

在反向传播中,通过链式法则,误差从输出层逐层向前传递,计算每一层的梯度。首先,计算输出层的误差 dz2,即预测值 output 与真实标签 y 的差值。然后,计算权重 W2 的梯度 dW2,通过将隐藏层的激活值 a1 的转置与误差 dz2 相乘,并除以样本数 m。偏置 b2 的梯度 db2 是误差 dz2 的均值。之后,计算隐藏层的误差 dz1,通过将输出层的误差 dz2 与权重矩阵 W2 的转置相乘,再乘以ReLU激活函数的导数(即 a1 > 0 的布尔值转换为浮点数)。然后,计算权重 W1 的梯度 dW1,通过将输入数据 X 的转置与误差 dz1 相乘,并除以样本数 m。偏置 b1 的梯度 db1 是误差 dz1 的均值。最后,使用梯度下降法更新参数。权重和偏置分别减去学习率与对应梯度的乘积。

运行结果如下,可以看到通过上述代码,我们能够得到较高的准确率。

深入思考

反向传播是神经网络训练的核心算法,但它并非完美无缺。在实际应用中,我们可能会遇到以下问题:

  1. 梯度消失:在深层网络中,梯度可能会逐渐变小,导致靠近输入层的参数几乎无法更新。使用ReLU激活函数和批量归一化可以有效缓解这一问题。

  2. 过拟合:神经网络容易过拟合训练数据。可以通过正则化(如L2正则化)和Dropout来减少过拟合。

  3. 计算效率:反向传播的计算复杂度较高,尤其是在大规模数据集和深层网络中。使用GPU加速和分布式计算可以显著提高训练速度。

总结

通过MNIST手写数字识别任务,我们从理论和代码两个层面深入探讨了反向传播的原理和实现。不仅学习了反向传播的数学原理,还通过手动编写反向传播逻辑,更好地理解了其内部机制。

查看本帖全部内容,请登录或者注册

最新回复

反向传播是一步一步反算,还是直接和反馈似的    详情 回复 发表于 2025-2-1 08:20
点赞 关注(1)
 
 

回复
举报

1

帖子

0

TA的资源

一粒金砂(初级)

沙发
 

非常有用,非常好,感谢博主所分享出来的经典的知识内容,有所帮助的知识点

 
 
 

回复

6737

帖子

10

TA的资源

版主

板凳
 

反向传播是一步一步反算,还是直接和反馈似的 

点评

反向传播是从输出层开始,一步一步往回计算误差,并调整每一层的参数。它利用了链式法则,每一步都依赖前一步的结果,是逐步反算的。  详情 回复 发表于 2025-2-1 12:28
个人签名

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 
 
 

回复

75

帖子

0

TA的资源

一粒金砂(高级)

4
 
秦天qintian0303 发表于 2025-2-1 08:20 反向传播是一步一步反算,还是直接和反馈似的 

反向传播是从输出层开始,一步一步往回计算误差,并调整每一层的参数。它利用了链式法则,每一步都依赖前一步的结果,是逐步反算的。

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
【回帖赢京东卡】说说无线技术那些事儿
即日起—2月28日,参与回帖讨论都有机会赢取【30元京东卡】

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表