总结:学习了 PyTorch 中的基本概念和常用功能,张量(Tensor)的操作、自动微分(Autograd)、正向传播、反向传播。通过了解认识LeNet 模型,定义神经网络类,熟悉卷积神经网络的基本结构和训练过程,卷积层和全连接层的创建和运用。在训练网络中,通过反向传播计算梯度,利用优化器根据计算出的梯度更新网络中的参数。--2025/02/12
目录
1. 实验环境
1.1. 导入库
1.2. 版本
2. 基本概念
2.1. Tensor 张量
2.2. Scalar 标量
2.3. Autograd 自动微分
2.3.1. 启动自动微分
2.3.2. 反向传播
2.3.3. 梯度累加和清零
3. LeNet 神经网络
3.1. 基本概念
3.1.1. torch.nn
3.1.2. nn.Module 类
3.1.3. 卷积层 (nn.Conv2d)
3.1.4. 全连接层 (nn.Linear)
3.2. 定义神经网络类 Net
3.2.1 __init__ 构造函数
3.2.2. forward 向前传播函数
3.3. 训练网络
3.3.1. 可学习参数
3.3.2. 批量处理(Mini-Batch)
3.3.3. 损失函数
3.3.4. 反向传播
3.3.5. 优化器:SGD
总结
1. 实验环境
1.1. 导入库
-
Import numpy as np: 用于处理数组和矩阵运算
Import torch as t :pytorch的核心库,构建和训练深度学习模型
import torch.optim as optim:pytorch的优化器模块,根据计算出的梯度更新神经网络的参数
import torch.nn as nn:PyTorch 提供的神经网络模块,专门用于构建和训练神经网络模型
import torch.nn.functional as F:包含函数操作,如激活函数、损失函数、卷积操作等)
1.2. 版本
-
torch:2.6.0
Python 3.8.8
jupyter-notebook : 6.3.0
2. 基本概念
2.1. Tensor 张量
Tensor是PyTorch中重要的数据结构,可认为是一个高维数组。它可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)以及更高维的数组。
- 标量(0 维):一个数值,例如 3.14。
- 向量(1 维):一组有序的数值,例如 [1, 2, 3]。
- 矩阵(2 维):一个二维的数值表格,例如 [[1, 2], [3, 4]]。
- 高维数组(n 维):更高维的数组,用于存储更复杂的数据结构,例如图像数据(通常为 4 维:batch_size, channels, height, width)。
# 初始化一个 5x3 的张量
x = t.Tensor(5, 3)# 赋值为一个 2x2 的初始化矩阵
x = t.Tensor([[1, 2], [3, 4]])# 获取张量的行数(即第一个维度)
x.size(0)# 获取张量的列数(即第二个维度)
x.size(1)# 获取列数的另一种方式
x.size()[1]# 随机生成一个 5x3 的张量
x = t.rand(5, 3)
y = t.rand(5, 3)# 三种方法来进行两个矩阵相加:
a = x + y # 方法 1:直接相加
b = t.add(x, y) # 方法 2:使用 PyTorch 自带的加法函数
c = t.Tensor(5, 3) # 初始化一个空张量
t.add(x, y, out=c) # 方法 3:将结果存储到 c 中
Tensor 与 NumPy 的转换
# 创建一个全为 1 的张量
a = t.ones(5)# 将张量转换为 NumPy 数组
b = a.numpy()# 创建一个全为 1 的 NumPy 数组
a = np.ones(5)# 将 NumPy 数组转换为张量
b = t.from_numpy(a)
2.2. Scalar 标量
Scalar(标量)是一个没有维度的单一数值,它是零维的张量。标量在 PyTorch 中通常表示为一个只有一个元素的 Tensor。
2.2.1. 获取标量值
(1) 使用 .item()
从包含单一元素的张量中提取该元素的值:
(2) 使用 .tolist()
将标量张量转换为列表:
# 使用 item() 方法获取标量值
scalar = b[0]
scalar_value = scalar.item()# 使用 tolist() 方法将标量张量转换为列表
x = t.Tensor([3.14]) # 创建一个包含单一值的张量
scalar_list = x.tolist() # 将其转换为列表
print(scalar_list) # 输出: [3.14]
2.3. Autograd 自动微分
深度学习中的训练过程本质上是通过反向传播求导数,而 PyTorch 提供的 autograd
模块自动为张量计算梯度,从而避免了手动计算导数的复杂过程。
2.3.1. 启动自动微分
在创建张量时,可以通过 requires_grad=True
来启动自动微分功能,表示该张量需要计算梯度。
x = t.ones(2, 2, requires_grad=True) # 创建一个可以计算梯度的张量
2.3.2. 反向传播
反向传播通过调用 backward()
来计算梯度。以下示例展示了如何计算梯度:
2.3.3. 梯度累加和清零
在每次执行 backward()
之后,PyTorch 会将梯度值累加,因此每次反向传播之前需要手动清零梯度。
#清零梯度,以下划线结束的函数是inplace操作,修改自身的值
x.grad.zero_()
3. LeNet 神经网络
LeNet 是一种经典的卷积神经网络(CNN)结构,广泛应用于图像分类任务。LeNet 模型通常由两个卷积层、两个池化层以及三个全连接层组成。
3.1. 基本概念
3.1.1. torch.nn
torch.nn
是 PyTorch 中专门为构建神经网络设计的模块,封装了构建和训练神经网络所需的大量工具,包括各种层(如卷积层、全连接层、激活函数等)和常用的操作(如损失函数、优化器等)。
3.1.2. nn.Module
类
nn.Module
是 torch.nn
中最重要的类,所有的神经网络模型都应该继承自 nn.Module
。我们可以通过继承 nn.Module
来定义自己的网络模型。在继承 nn.Module
时,需要定义以下两个方法:
__init__
:用于定义模型的结构,初始化网络的各个层。forward
:定义前向传播,描述数据如何通过网络流动。
3.1.3. 卷积层 (nn.Conv2d
)
卷积层用于从输入数据中提取空间特征。nn.Conv2d
用于定义二维卷积层。它的常用参数包括:
- in_channels:输入数据的通道数(例如,RGB图像的通道数为3,灰度图像为1)。
- out_channels:卷积层输出的通道数(卷积核的数量)。
- kernel_size:卷积核的大小,通常是一个整数或元组(
height, width
)。
# 卷积层 '1'表示输入通道数为1→灰度图像, '6'表示输出通道数→卷积核的数量,'5'表示卷积核为5*5
self.conv1 = nn.Conv2d(1, 6, 5)
# 卷积层
self.conv2 = nn.Conv2d(6, 16, 5)
3.1.4. 全连接层 (nn.Linear
)
全连接层是神经网络中非常重要的一部分,主要用于将提取到的特征映射到最终的输出。nn.Linear
定义了一个全连接层,其常用参数为:
- in_features:输入特征的数量。
- out_features:输出特征的数量。
# 仿射层/全连接层,y = Wx + b
#16*5*5是卷积层输出的特征图像展平后的维度,表示每个样本通过卷积层处理后的大小。
self.fc1 = nn.Linear(16*5*5, 120)
#120,84,10全连接层的输出维度。10表示模型的预测类别数。10类
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
3.2. 定义神经网络类 Net
import torch.nn as nn
import torch.nn.functional as F
#包含常见函数,比如激活函数relu,池化操作maxpool
class Net(nn.Module):#定义神经网络类Net,继承于nn.Moduledef __init__(self):#定义构造函数,用来定义网络各个层super(Net, self).__init__()#调用父类nn.module的构造函数# 卷积层 '1'表示输入图片为单通道的灰度图像, '6'表示输出通道数,'5'表示卷积核为5*5self.conv1 = nn.Conv2d(1, 6, 5) # 卷积层self.conv2 = nn.Conv2d(6, 16, 5) # 仿射层/全连接层,y = Wx + b#16*5*5是卷积层输出的特征图像展平后的维度,表示每个样本通过卷积层处理后的大小。self.fc1 = nn.Linear(16*5*5, 120) #120,84,10全连接层的输出维度。10表示模型的预测类别数。10类self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x): #定义向前传播# 卷积 -> 激活函数(ReLU) -> 池化(MaxPool)x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))#self.conv1(x):通过卷积层conv1,将输入x进行卷积操作#F.relu():relu激活函数,将输入的负值变为0,正值不变。#F.max_pool2d(...,(2,2)):最大池化操作,池化窗口大小2*2x = F.max_pool2d(F.relu(self.conv2(x)), 2) # reshape,展平数据,输入全连接层,‘-1’表示自适应x = x.view(x.size()[0], -1) #展平的数据进入全连接层,relu激活函数增加非线性#最后一层输出类别的预测值x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x) return x
#创建Net对象,神经网络
net = Net()
#打印出网络的结构显示每一层参数
print(net)
Net((conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))(fc1): Linear(in_features=400, out_features=120, bias=True)(fc2): Linear(in_features=120, out_features=84, bias=True)(fc3): Linear(in_features=84, out_features=10, bias=True) )
3.2.1 __init__
构造函数
在 __init__
方法中,我们定义了网络的各个层(卷积层、池化层、全连接层)。LeNet 网络一般包含两个卷积层和三个全连接层。
3.2.2. forward
向前传播函数
在 forward
方法中,我们定义了数据如何在网络中流动,包括卷积、池化、激活函数、展平和全连接层的应用。
3.3. 训练网络
3.3.1. 可学习参数
- 权重(weight):网络中的参数,决定了输入和输出之间的关系,影响每一层的输出结果。
- 偏置(bias):每个神经元的额外参数,与权重一起决定神经元的激活值。
3.3.2. 批量处理(Mini-Batch)
批量处理mini-batch:多个样本组成的小批量.
PyTorch 的神经网络层(如卷积层)要求输入的形状是一个 4D 张量(batch_size, channels, height, width)。
只想输入一个样本,input.unsqueeze(0)将batch_size设为1
3.3.3. 损失函数
-
损失函数:衡量网络输出(预测值)与目标值之间的差距
均方误差 (MSE, Mean Squared Error):用于回归问题,计算预测值与真实值之间的平均平方误差。
交叉熵损失 (Cross-Entropy Loss):用于分类问题,衡量真实标签与预测概率分布之间的差异。
3.3.4. 反向传播
反向传播包括三个步骤:
- 正向传播:先通过前向传播计算出模型的输出和损失。
- 计算梯度:然后通过反向传播计算出每个参数的梯度。梯度的绝对值较大,该参数对损失有很大影响,需要大幅调整
- 参数更新:使用这些梯度来调整网络的参数,使得损失最小化。
#梯度变化
print('反向传播前conv1.bias偏置的梯度')
print(net.conv1.bias.grad)# 执行一次前向传播
output = net(input)
target = t.arange(0,10).view(1,10).float()
criterion = nn.MSELoss()# 计算损失
loss = criterion(output, target)# 执行反向传播(retain_graph=True)用于保留计算图
loss.backward(retain_graph=True)# 打印反向传播后的梯度
print('反向传播后conv1.bias偏置的梯度')
print(net.conv1.bias.grad)# 如果需要继续进行其他操作,记得清零梯度
net.zero_grad()
梯度的符号(正负)表示了参数调整的方向:
负梯度:表示 如果减小 当前参数的值,损失函数会减少。当前参数的值较大,应该减少它来减小损失。
正梯度:表示 如果增大 当前参数的值,损失函数会减少。意味着当前参数的值较小,应该增加它来减小损失。
3.3.5. 优化器:SGD
主要任务是 根据反向传播计算出的梯度更新网络中的参数(如权重和偏置),从而使得损失函数逐步降低,最终达到优化目标。
SGD(随机梯度下降)优化器
SGD 是最常见的一种优化器,它的基本思路是每次使用一个 mini-batch(小批量)计算梯度,并使用这个梯度更新网络参数。
import torch.optim as optim
#SGD随机梯度下降,创建优化器,将需要优化的参数传入优化器
#指定学习率learning rate
optimizer = optim.SGD(net.parameters(),lr = 0.01)
#先梯度清零
optimizer.zero_grad()
#前向传播
output = net(input)
#计算损失函数
loss = criterion(output,target)
#反向传播
loss.backward()
#更新参数
optimizer.step()
总结
- LeNet 网络结构:包含卷积层、池化层、全连接层。
nn.Module
和forward
:通过继承nn.Module
和定义forward
方法来实现网络结构。- 损失函数和优化器:通过损失函数(如交叉熵损失)和优化器(如 SGD)来训练网络。