第三章:PyTorch 训练与评估 第三章:PyTorch 训练与评估 3.1 数据准备:模型的基石 训练和评估模型的第一步,也是至关重要的一步,是准备数据。高质量的数据是模型性能的基石。在PyTorch中,我们通常使用 和 来高效地加载和处理数据。 3.1.1 :定义数据访问方式 是一个抽象类,用于表示数据集。我们需要继承 并实现两个关键方法: : 返回数据集的样本数量。 : 根据给定的索引 返回一个样本。一个样本通常包含输入数据和对应的标签。 代码示例:自定义Dataset 假设我们有一个简单的线性回归数据集,包含输入特征 和目标值 。 3.1.2 :高效数据加载 负责从 中批量加载数据,并提供诸如数据打乱、多线程加载等功能,以加速训练过程。
训练和评估模型的第一步,也是至关重要的一步,是准备数据。高质量的数据是模型性能的基石。在PyTorch中,我们通常使用 torch.utils.data.Dataset 和 torch.utils.data.DataLoader 来高效地加载和处理数据。
Dataset:定义数据访问方式Dataset 是一个抽象类,用于表示数据集。我们需要继承 Dataset 并实现两个关键方法:
__len__(self): 返回数据集的样本数量。
__getitem__(self, idx): 根据给定的索引 idx 返回一个样本。一个样本通常包含输入数据和对应的标签。
代码示例:自定义Dataset
假设我们有一个简单的线性回归数据集,包含输入特征 x 和目标值 y。
import torch from torch.utils.data import Dataset class LinearRegressionDataset(Dataset): def __init__(self, x, y): self.x = torch.tensor(x, dtype=torch.float32) self.y = torch.tensor(y, dtype=torch.float32) self.n_samples = len(x) def __getitem__(self, index): return self.x[index], self.y[index] def __len__(self): return self.n_samples # 示例数据 X_train = [[1], [2], [3], [4]] Y_train = [[2], [4], [6], [8]] dataset = LinearRegressionDataset(X_train, Y_train) first_data = dataset[0] features, labels = first_data print(features, labels) # 输出: tensor([1.], dtype=torch.float32) tensor([2.], dtype=torch.float32) print(len(dataset)) # 输出: 4
DataLoader:高效数据加载DataLoader 负责从 Dataset 中批量加载数据,并提供诸如数据打乱、多线程加载等功能,以加速训练过程。
代码示例:使用DataLoader
from torch.utils.data import DataLoader # 创建DataLoader train_loader = DataLoader(dataset=dataset, batch_size=2, shuffle=True) # 迭代DataLoader data_iter = iter(train_loader) first_batch = next(data_iter) features, labels = first_batch print(features, labels) # 输出类似: tensor([[3.], [1.]]) tensor([[6.], [2.]]) (由于shuffle=True,顺序可能不同) # 训练循环中使用DataLoader num_epochs = 2 for epoch in range(num_epochs): for i, (inputs, targets) in enumerate(train_loader): # 前向传播, 反向传播, 更新权重 (将在后续章节介绍) print(f'Epoch: {epoch+1}/{num_epochs}, Step: {i+1}, Inputs: {inputs.shape}, Labels: {targets.shape}')
mermaid 图:数据加载流程
内容详解:
Dataset 的作用: Dataset 将数据抽象成一个可迭代的对象,隐藏了数据加载和预处理的复杂性,使得我们可以专注于模型的设计和训练逻辑。
DataLoader 的作用: DataLoader 提供了更高级的数据加载功能,例如:
Batching: 将多个样本组合成一个批次,提高计算效率。
Shuffling: 在每个epoch开始前打乱数据顺序,避免模型学习到批次顺序的偏差。
并行加载: 使用多线程或多进程加速数据加载,尤其是在数据量较大时。
数据预处理: 实际应用中,数据预处理通常在 Dataset 的 __getitem__ 方法中进行,例如数据标准化、归一化、数据增强等。
在PyTorch中,我们使用 torch.nn.Module 类来构建神经网络模型。模型由各种层(例如线性层、卷积层、循环层等)组成,这些层在 forward 方法中被组合起来,定义了数据在网络中的流动方式。
nn.Module:模型基类所有PyTorch模型都必须继承 nn.Module 类。我们需要在子类中定义模型的层结构和 forward 方法。
代码示例:线性回归模型
import torch.nn as nn class LinearRegressionModel(nn.Module): def __init__(self, input_size, output_size): super(LinearRegressionModel, self).__init__() self.linear = nn.Linear(input_size, output_size) # 定义一个线性层 def forward(self, x): out = self.linear(x) # 前向传播 return out # 创建模型实例 input_dim = 1 output_dim = 1 model = LinearRegressionModel(input_dim, output_dim)
PyTorch 提供了丰富的预定义层和容器,例如:
nn.Linear: 线性层(全连接层)。
nn.Conv2d, nn.ConvTranspose2d: 卷积层和转置卷积层(用于图像处理)。
nn.ReLU, nn.Sigmoid, nn.Tanh: 激活函数。
nn.MaxPool2d, nn.AvgPool2d: 池化层。
nn.RNN, nn.LSTM, nn.GRU: 循环神经网络层(用于序列数据)。
nn.Sequential: 顺序容器,用于按顺序组合多个层。
代码示例:多层感知机 (MLP)
class MLPModel(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(MLPModel, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, output_size) def forward(self, x): out = self.fc1(x) out = self.relu(out) out = self.fc2(out) return out # 创建MLP模型实例 input_dim = 28*28 # 假设输入是 MNIST 图像 (28x28) hidden_dim = 100 output_dim = 10 # MNIST 数字分类 (0-9) mlp_model = MLPModel(input_dim, hidden_dim, output_dim)
mermaid 图:MLP模型结构
内容详解:
nn.Module 的作用: nn.Module 是构建PyTorch模型的基石,它封装了模型的参数和操作,并提供了方便的模型管理和扩展机制。
forward 方法: forward 方法定义了数据通过模型的计算流程。当我们调用 model(input_data) 时,实际上是调用了模型的 forward 方法。
模型层和容器的选择: 选择合适的模型层和容器取决于具体的任务类型和数据特征。例如,卷积层通常用于图像处理,循环层用于序列数据处理,线性层用于处理表格数据等。
为了让模型学习到有用的知识,我们需要定义损失函数和优化器。
损失函数衡量模型预测结果与真实标签之间的差距。训练的目标是最小化损失函数。PyTorch 在 torch.nn 中提供了各种常用的损失函数,例如:
nn.MSELoss: 均方误差损失 (Mean Squared Error Loss),用于回归任务。
nn.CrossEntropyLoss: 交叉熵损失 (Cross-Entropy Loss),用于多分类任务。
nn.BCELoss: 二元交叉熵损失 (Binary Cross-Entropy Loss),用于二分类任务。
nn.L1Loss: L1 损失 (Mean Absolute Error Loss),平均绝对误差损失,用于回归任务。
代码示例:选择损失函数
# 回归任务使用 MSELoss criterion_regression = nn.MSELoss() # 分类任务使用 CrossEntropyLoss criterion_classification = nn.CrossEntropyLoss()
优化器负责根据损失函数的梯度更新模型参数,从而使模型逐渐逼近最优解。PyTorch 在 torch.optim 中提供了各种优化算法,例如:
optim.SGD: 随机梯度下降 (Stochastic Gradient Descent)。
optim.Adam: Adam 优化器 (Adaptive Moment Estimation)。
optim.RMSprop: RMSprop 优化器 (Root Mean Square Propagation)。
代码示例:选择优化器
import torch.optim as optim # 使用 SGD 优化器 optimizer_sgd = optim.SGD(model.parameters(), lr=0.01) # lr: 学习率 # 使用 Adam 优化器 optimizer_adam = optim.Adam(model.parameters(), lr=0.001)
mermaid 图:损失函数与优化器在训练中的作用
内容详解:
损失函数的选择: 损失函数的选择取决于具体的任务类型。回归任务通常使用衡量数值差异的损失函数,分类任务通常使用衡量类别分布差异的损失函数。
优化器的选择: 不同的优化器具有不同的特性和适用场景。Adam 和 RMSprop 通常比 SGD 收敛更快,但也可能更容易陷入局部最优解。SGD 具有更好的泛化性能,但需要仔细调整学习率等超参数。
学习率 (Learning Rate): 学习率是优化器的一个重要超参数,它控制参数更新的步长。学习率过大可能导致模型震荡,学习率过小可能导致收敛速度过慢。
训练循环是模型学习的核心流程,它迭代地进行前向传播、计算损失、反向传播和参数更新。
伪代码:训练循环
for epoch in range(num_epochs): for inputs, targets in data_loader: # 1. 前向传播 (Forward Pass): 计算模型输出 outputs = model(inputs) # 2. 计算损失 (Calculate Loss): 衡量模型预测与真实标签的差距 loss = criterion(outputs, targets) # 3. 反向传播 (Backward Pass): 计算梯度 optimizer.zero_grad() # 清空之前的梯度 loss.backward() # 计算梯度 # 4. 参数更新 (Update Parameters): 根据梯度更新模型参数 optimizer.step() # 更新参数 # (可选) 打印训练信息,例如 epoch, step, loss if (step+1) % print_interval == 0: print(f'Epoch [{epoch+1}/{num_epochs}], Step [{step+1}/{total_steps}], Loss: {loss.item():.4f}')
代码示例:完整的训练循环 (线性回归)
# ... (数据准备, 模型构建, 损失函数, 优化器 已定义) ... num_epochs = 1000 learning_rate = 0.01 optimizer = optim.SGD(model.parameters(), lr=learning_rate) criterion = nn.MSELoss() for epoch in range(num_epochs): for inputs, targets in train_loader: # 前向传播 outputs = model(inputs) loss = criterion(outputs, targets) # 反向传播和优化 optimizer.zero_grad() loss.backward() optimizer.step() if (epoch+1) % 100 == 0: print (f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}') print('Training finished')
mermaid 图:训练循环流程
内容详解:
Epoch 和 Step:
Epoch: 指整个数据集被模型完整地训练一遍的次数。
Step (Iteration): 指模型参数更新的次数。在一个 epoch 中,通常会进行多次 step,每次 step 处理一个 batch 的数据。
optimizer.zero_grad(): 在每次反向传播之前,需要将优化器的梯度缓冲区清零。因为PyTorch默认会累积梯度,如果不清零,梯度会不断累加,导致参数更新错误。
loss.backward(): 调用 loss.backward() 触发反向传播,计算模型参数的梯度。
optimizer.step(): 调用 optimizer.step() 根据计算出的梯度更新模型参数。
训练过程监控: 在训练循环中,通常会打印损失函数值、学习率等信息,以便监控训练进度和调整超参数。
模型训练完成后,我们需要在验证集或测试集上评估模型的性能,以了解模型的泛化能力。评估过程与训练过程类似,但不进行反向传播和参数更新。
model.eval())在评估模型之前,需要将模型设置为评估模式 model.eval()。这会影响某些层的行为,例如 Dropout 和 BatchNorm 层,在评估模式下会停止随机失活和使用全局统计量。
torch.no_grad())在评估过程中,我们不需要计算梯度,可以使用 torch.no_grad() 上下文管理器来关闭梯度计算,以节省计算资源并加速评估过程。
评估指标用于量化模型的性能。常用的评估指标取决于任务类型,例如:
分类任务:
准确率 (Accuracy): 预测正确的样本数占总样本数的比例。
精确率 (Precision), 召回率 (Recall), F1-score: 用于更细致地评估分类模型的性能,尤其是在类别不平衡的情况下。
AUC-ROC: 受试者工作特征曲线下面积,用于评估二分类模型的排序能力。
回归任务:
均方根误差 (RMSE): 均方误差的平方根。
平均绝对误差 (MAE): 平均绝对误差。
R 平方 (R-squared): 决定系数,衡量模型拟合数据的程度。
代码示例:模型评估 (分类任务 - 准确率)
# ... (模型构建, 数据准备 - 假设有测试集 test_dataset 和 test_loader) ... def evaluate_accuracy(model, data_loader): correct_predictions = 0 total_samples = 0 with torch.no_grad(): # 关闭梯度计算 model.eval() # 设置为评估模式 for inputs, labels in data_loader: outputs = model(inputs) _, predicted_labels = torch.max(outputs, 1) # 获取概率最大的类别 total_samples += labels.size(0) correct_predictions += (predicted_labels == labels).sum().item() accuracy = correct_predictions / total_samples return accuracy test_accuracy = evaluate_accuracy(model, test_loader) print(f'Test Accuracy: {test_accuracy:.4f}')
mermaid 图:模型评估流程
内容详解:
评估模式的重要性: 评估模式确保模型在评估阶段的行为与训练阶段不同,例如 Dropout 层不进行随机失活,BatchNorm 层使用全局统计量,从而更准确地反映模型的泛化能力。
评估指标的选择: 评估指标的选择取决于具体的任务类型和业务目标。例如,在医疗诊断领域,召回率可能比精确率更重要,因为我们更关注尽可能找出所有患病的人,即使误诊一些健康人。
验证集与测试集:
验证集 (Validation Set): 用于在训练过程中调整超参数和选择模型。
测试集 (Test Set): 用于在模型开发完成后,最终评估模型的泛化能力。测试集在模型开发过程中不应该被使用,以避免模型过拟合测试集。
训练好的模型需要保存下来,以便后续部署和使用。PyTorch 提供了方便的模型保存和加载功能。
torch.save)可以使用 torch.save 函数保存模型的 state_dict 或整个模型。
保存 state_dict (推荐): 只保存模型的参数字典,文件较小,更灵活。
# 保存 state_dict torch.save(model.state_dict(), 'model.pth')
保存整个模型: 保存模型的结构和参数,文件较大,依赖于模型代码。
# 保存整个模型 torch.save(model, 'model_complete.pth')
torch.load)可以使用 torch.load 函数加载保存的模型。
加载 state_dict: 需要先创建模型实例,然后加载 state_dict。
# 加载 state_dict model = LinearRegressionModel(input_dim, output_dim) # 创建模型实例 (结构需要与保存时一致) model.load_state_dict(torch.load('model.pth')) model.eval() # 设置为评估模式
加载整个模型: 直接加载模型实例。
# 加载整个模型 model = torch.load('model_complete.pth') model.eval() # 设置为评估模式
mermaid 图:模型保存与加载流程
内容详解:
state_dict vs. 完整模型: 推荐保存 state_dict,因为它更轻量级,更灵活,且与模型代码解耦。加载 state_dict 时,只需要模型结构与保存时一致即可,而不需要完全相同的代码环境。
模型部署: 保存的模型可以用于模型部署,例如在 Web 应用、移动应用或嵌入式设备中使用。
掌握模型训练与评估是深度学习实践的基础。在实际应用中,我们还需要不断尝试不同的模型结构、超参数和训练策略,并结合有效的评估方法,才能构建出高性能的深度学习模型。
后续学习方向:
更复杂的模型结构: 学习卷积神经网络 (CNN)、循环神经网络 (RNN)、Transformer 等更复杂的模型结构,以应对不同的任务需求。
更高级的优化算法: 探索更高级的优化算法,例如 AdamW, LookAhead 等,以加速训练和提高模型性能。
正则化技术: 学习 Dropout, BatchNorm, L1/L2 正则化等技术,以防止模型过拟合。
模型调优: 学习超参数调优、模型集成等技术,以进一步提升模型性能。
希望本章内容能够帮助读者扎实掌握PyTorch模型训练与评估的基础知识,为后续的深度学习实践打下坚实的基础。