1 构建依赖库
使用pytorch构建神经网络,主要依赖于torch.nn
torch.nn
是 PyTorch 库中用于构建神经网络的模块化接口。torch.nn
提供了各种用于构建神经网络的类和函数,使得构建、训练和评估神经网络变得更加方便。下面是 torch.nn
的一些常用子模块以及它们的功能和使用方法:
torch.nn.Linear
: 线性层是神经网络中最常用的层之一。它实现了线性变换,将输入张量与权重矩阵相乘并加上偏置向量,产生输出张量。通常用于实现全连接层。可以使用torch.nn.Linear(in_features, out_features)
创建线性层。
示例:import torch import torch.nn as nn # 创建一个线性层,输入特征为5,输出特征为3 linear_layer = nn.Linear(5, 3)
torch.nn.Conv2d
: 卷积层用于处理图像数据或其他具有局部结构的数据。卷积层通过滑动一个卷积核(kernel)在输入数据上进行操作,提取出不同区域的特征。可以使用torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
创建卷积层
示例:import torch import torch.nn as nn # 创建一个2D卷积层,输入通道数为3,输出通道数为16,核大小为3x3 conv_layer = nn.Conv2d(3, 16, kernel_size=3)
torch.nn.ReLU
: ReLU(Rectified Linear Unit)是一种激活函数,常用于神经网络中引入非线性映射。ReLU 将负输入值置零,保留正值。可以使用torch.nn.ReLU()
创建 ReLU 激活函数。
示例:import torch import torch.nn as nn # 创建一个ReLU激活函数 relu = nn.ReLU()
torch.nn.Dropout
: Dropout 是一种正则化技术,用于减少过拟合。在训练过程中,随机将一部分神经元输出置零,以减少神经元之间的依赖性。可以使用torch.nn.Dropout(p)
创建 Dropout 层,其中 p 是置零概率。
示例:import torch import torch.nn as nn # 创建一个Dropout层,置零概率为0.5 dropout = nn.Dropout(0.5)
torch.nn.BatchNorm2d
: Batch Normalization 是一种用于加速神经网络训练的技术。它对每个小批量数据在通道维度上进行归一化,使得网络更加稳定且收敛更快。可以使用torch.nn.BatchNorm2d(num_features)
创建 Batch Normalization 层。
示例:import torch import torch.nn as nn # 创建一个Batch Normalization层,输入通道数为3 batchnorm = nn.BatchNorm2d(3)
以上只是 torch.nn
模块中的一小部分常用子模块,还有很多其他子模块可用于构建不同类型的神经网络。使用这些子模块,可以更加灵活地构建自定义的神经网络架构来适应不同的任务和数据。
2 pytorch构建神经网络的基本流程
以下为具体的流程,在初期进行学习时,可以根据具体的流程步骤进行,防止训练出错,熟练之后就不需要局限于固定的流程步骤,可以根据实际使用的需要进行调整。
- 定义一个可学习的参数的神经网络
- 遍历训练数据集
- 处理输入数据使其流向神经网络
- 计算损失值
- 将网络参数的梯度进行反向传播
- 以一定的规则更新网络权重
2.1 构建一个简单的神经网络
一般情况下,神经网络的构建主要是有__init__
和forward
这两个函数构成,但根据实际神经网络计算的需要,可以自定义一些新的函数,来辅助网络的计算,已达到更好的计算效果。
使用__init__
初始化神经网络的每一层,输入、输出大小等,不包含具体的计算函数;使用forward
函数使用定义好大小的神经网络层进行计算,需要包含使用的损失函数,池化操作等;在forward
函数进行计算时,如需要特定的计算内容,可在类中自定义新的函数。
#导入所需要的包
import torch
import torch.nn as nn
import torch.nn.functional as F
#定义一个简单的网络类
class Net(nn.Module):
def __init__(self):
#定义第一层卷积层,输入维度为1,输出维度为6,卷积核大小为3*3
self.conv1 = nn.Conv2d(1, 6, 3)
#定义第二层卷积层,输入维度为6,输出维度为16,卷积核大小为3*3
self.conv2 = nn.Conv2d(6, 16, 3)
#定义三层全连接网络
self.fc1 = nn.Linear(16*6*6, 120)
self.fc1 = nn.Linear(120, 84)
self.fc1 = nn.Linear(84, 10)
#使用上面定义的网络结构层,进行运算。
def forward(self, x):
#在(2, 2)的池化窗口下进行最大池化操作
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
#保证张量的形状与全连接层的大小相匹配
def num_flat_features(self, x):
#计算除第0个维度以外的batch_size
size = x.size()[1:]
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net) #输出的内容是Net网络的具体逻辑架构,及每一层的输出输出维度和所用卷积池化的大小等信息
在上述代码中我们定义了一个简单的类,来构建一个神经网络,注意:网络的输入要与实际数据的格式相匹配,最终的输出要与分类的种类相匹配。此外可以使用net.parametes()
来查看网络中所有可训练的参数。
2.2 损失函数
损失函数的输入是(output, target)计算出一个数值来评估output和target之间的差距大小。在torch.nn
中有许多可供使用的损失函数。以下是一些常用的损失函数:
-
交叉熵损失函数(CrossEntropyLoss):用于多分类问题,计算模型输出的概率分布与真实标签之间的交叉熵。它是分类任务中最常用的损失函数之一。
- 计算公式:H(p, q) = - ∑ p(x) * log(q(x))
- 调用方式:
loss_fn = torch.nn.CrossEntropyLoss() loss = loss_fn(predictions, targets)
-
均方损失函数(MSELoss):用于回归问题,计算模型输出与真实标签之间的平均平方差。它对于连续值的预测任务非常常用。
- 计算公式:MSE = (1/n) * ∑ (y_pred - y_true)^2
- 调用方式:
loss_fn = torch.nn.MSELoss() loss = loss_fn(predictions, targets)
-
二元交叉熵损失函数(BCELoss):用于二分类问题,计算模型输出的概率与真实标签之间的二元交叉熵。通常与 Sigmoid 激活函数一起使用。
- 计算公式:BCE = - [y_true * log(y_pred) + (1-y_true) * log(1-y_pred)]
- 调用方式:
loss_fn = torch.nn.BCELoss() loss = loss_fn(predictions, targets)
-
KL 散度损失函数(KLDivLoss):用于衡量两个概率分布之间的相似性,常用于生成对抗网络(GAN)等模型中。
- 计算公式:KL(p || q) = ∑ p(x) * log(p(x) / q(x))
- 调用方式:
loss_fn = torch.nn.KLDivLoss() loss = loss_fn(torch.log(predictions), targets)
-
平衡误差损失函数(BCEWithLogitsLoss):结合了 Sigmoid 激活函数和二元交叉熵损失,用于二分类问题。在计算过程中,它同时处理概率和对数概率,通常比单独使用 BCELoss 更稳定。
- 计算公式:BCE = - [y_true * log(sigmoid(y_pred)) + (1-y_true) * log(1-sigmoid(y_pred))]
- 调用方式:
loss_fn = torch.nn.BCEWithLogitsLoss() loss = loss_fn(predictions, targets)
-
信息熵损失函数(NLLLoss):用于多分类问题,计算模型输出的负对数概率与真实标签之间的交叉熵。通常与 Softmax 激活函数一起使用。
- 计算公式:NLL = - ∑ log(p(x))
- 调用方式:
loss_fn = torch.nn.NLLLoss() loss = loss_fn(torch.log(predictions), targets)
在上述代码中,predictions
是模型的输出预测值,targets
是真实标签。可以根据任务类型和网络输出进行相应的选择,并使用适当的损失函数来计算模型的损失值。
2.3 反向传播
反向传播是用来计算神经网络中参数的梯度,并使用梯度下降算法更新参数的过程。这个过程使得神经网络能够通过优化损失函数来学习和调整模型的参数。
反向传播基于链式法则(chain rule)。它利用从输出层到输入层的正向计算过程中计算得到的中间结果,通过链式法则依次计算每个参数的梯度。将损失函数对模型参数的梯度反向传播回每个参数,并更新参数以减小损失。
通过调用loss.backward()
对整张图的loss进行自动求导,所有属性requires_grad = Ture
的Tensor张量都参与进梯度求导运算当中,并将梯度累加到Tensor的.grad
属性当中。此外,在进行反向传播之前还需要将梯度清零,否则梯度会在不同批次数据之间被累加。
#梯度清零
net.zero_grad()
#反向传播
loss.backward()
2.4 更新网络参数
要更新神经网络的参数,可以使用优化器(例如 SGD、Adam 或 RMSprop)来处理梯度更新的过程。在 PyTorch 中,优化器可以自动管理参数的更新,并提供一些优化算法来调整参数值以减小损失函数的值。
在下面的代码示例中,我们定义了一个优化器 optimizer
,它使用 SGD(随机梯度下降)算法来更新模型的参数。在训练循环中,执行了以下代码来进行梯度更新:
# 创建优化器,并指定要优化的参数
optimizer = optim.SGD(net.parameters(), lr=0.01)
optimizer.zero_grad() # 清零梯度
# 反向传播
loss.backward()
# 梯度更新
optimizer.step()
这里的 optimizer.zero_grad()
用于清零之前计算的梯度,替代了之前的net.zero_grad()
以避免梯度累积。然后我们进行网络计算、损失计算和反向传播,得到参数的梯度。最后,通过调用 optimizer.step()
来根据梯度更新模型的参数。
在每次迭代中,这个过程会自动计算参数的梯度并更新参数值,使得模型逐步优化,减小损失函数的值。您以根据自己的需求选择不同的优化器和超参数,以获得更好的模型性能。
请注意,通过在每次迭代中调用 optimizer.step()
,优化器会自动更新模型的参数。所以在训练循环中确保每次迭代都有这个操作,以便更新参数。
2.5 完整构建过程
#导入所需要的包
import torch
import torch.nn as nn
import torch.nn.functional as F
#定义一个简单的网络类
class Net(nn.Module):
def __init__(self):
#定义第一层卷积层,输入维度为1,输出维度为6,卷积核大小为3*3
self.conv1 = nn.Conv2d(1, 6, 3)
#定义第二层卷积层,输入维度为6,输出维度为16,卷积核大小为3*3
self.conv2 = nn.Conv2d(6, 16, 3)
#定义三层全连接网络
self.fc1 = nn.Linear(16*6*6, 120)
self.fc1 = nn.Linear(120, 84)
self.fc1 = nn.Linear(84, 10)
#使用上面定义的网络结构层,进行运算。
def forward(self, x):
#在(2, 2)的池化窗口下进行最大池化操作
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
#保证张量的形状与全连接层的大小相匹配
def num_flat_features(self, x):
#计算除第0个维度以外的batch_size
size = x.size()[1:]
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
criterion = nn.CrossEntropyLoss()
# 创建优化器,并指定要优化的参数
optimizer = optim.SGD(net.parameters(), lr=0.01)
# 训练循环
for epoch in range(10): # 假设进行 10 个训练迭代
optimizer.zero_grad() # 清零梯度
# 前向传播
inputs = torch.randn(1, 1, 32, 32) # 输入数据
targets = torch.tensor([0, 1, 0, 1 0, 1 0, 1 0, 1]) # 真实标签
outputs = model(inputs) # 前向计算得到预测输出
# 计算损失
loss = criterion(outputs, targets)
# 反向传播
loss.backward()
# 梯度更新
optimizer.step()
# 输出训练信息
print(f"Epoch {epoch+1}: Loss = {loss.item()}")
# 完成训练,可以使用模型进行预测等操作