3.2 训练循环 (Training Loop)


文档摘要

3.2 训练循环 (Training Loop) PyTorch 训练循环 (Training Loop) 详解与实践 训练循环的重要性与基本概念 训练循环是任何深度学习模型的“心脏”,它定义了模型如何从数据中学习,并逐步提升性能。一个典型的训练循环包含以下几个关键步骤: 数据加载 (Data Loading): 从数据集中获取训练数据批次 (batch)。 前向传播 (Forward Pass): 将数据输入模型,计算模型的输出 (预测值)。 损失计算 (Loss Calculation): 根据模型的预测值和真实标签,计算损失函数 (Loss Function),衡量模型预测的误差。

3.2 训练循环 (Training Loop)

PyTorch 训练循环 (Training Loop) 详解与实践

1. 训练循环的重要性与基本概念

训练循环是任何深度学习模型的“心脏”,它定义了模型如何从数据中学习,并逐步提升性能。一个典型的训练循环包含以下几个关键步骤:

  1. 数据加载 (Data Loading): 从数据集中获取训练数据批次 (batch)。

  2. 前向传播 (Forward Pass): 将数据输入模型,计算模型的输出 (预测值)。

  3. 损失计算 (Loss Calculation): 根据模型的预测值和真实标签,计算损失函数 (Loss Function),衡量模型预测的误差。

  4. 反向传播 (Backward Pass): 利用损失值,通过反向传播算法计算模型参数的梯度 (Gradient)。

  5. 参数更新 (Parameter Update): 根据计算出的梯度,使用优化器 (Optimizer) 更新模型的参数,减小损失函数的值。

  6. 评估与监控 (Evaluation & Monitoring): 在训练过程中,定期评估模型在验证集上的性能,并监控训练过程,例如损失值和指标的变化。

这个循环会重复多次,每次循环被称为一个 迭代 (Iteration)批次 (Batch)。 完整地遍历一次训练数据集被称为一个 轮次 (Epoch)。 训练过程通常会进行多个轮次,直到模型在验证集上的性能不再提升或达到预设的训练目标。

2. 训练循环的组成部分详解

为了更深入地理解训练循环,我们逐一解析其核心组成部分,并结合 PyTorch 代码进行说明。

2.1 数据加载 (Data Loading)

深度学习通常需要处理大量数据,为了高效地训练模型,我们通常将数据分成小批量 (batches) 进行处理。PyTorch 提供了 DatasetDataLoader 两个核心类来实现数据加载。

  • Dataset: 抽象类,用于表示数据集。你需要继承 Dataset 并实现 __len____getitem__ 两个方法。

    • __len__(self): 返回数据集的大小。

    • __getitem__(self, index): 根据索引 index 返回数据集中对应的样本和标签。

  • DataLoader: 迭代器,用于批量加载数据,并提供数据打乱 (shuffle)、多线程加载等功能。

代码示例:自定义 Dataset

import torch from torch.utils.data import Dataset, DataLoader class CustomDataset(Dataset): def __init__(self, data, labels): self.data = data self.labels = labels def __len__(self): return len(self.data) def __getitem__(self, index): sample = self.data[index] label = self.labels[index] return sample, label # 示例数据 data = torch.randn(100, 10) # 100个样本,每个样本10个特征 labels = torch.randint(0, 2, (100,)) # 100个样本的二分类标签 dataset = CustomDataset(data, labels) dataloader = DataLoader(dataset, batch_size=32, shuffle=True) # 迭代 DataLoader 获取数据批次 for batch_idx, (inputs, targets) in enumerate(dataloader): print(f"Batch index: {batch_idx}, Input shape: {inputs.shape}, Target shape: {targets.shape}")

内容详解:

  • CustomDataset 继承了 Dataset 类,并根据示例数据实现了 __len____getitem__ 方法。

  • DataLoader 接收 dataset 作为输入,并设置了 batch_size=32shuffle=Truebatch_size 定义了每个批次的大小,shuffle=True 表示在每个 epoch 开始前打乱数据顺序,有助于模型学习更鲁棒的特征。

  • 循环迭代 dataloader 可以获取一个个批次的数据,每个批次包含 inputstargets

2.2 前向传播 (Forward Pass)

前向传播是指将数据输入模型,经过模型的层层计算,得到最终输出的过程。在 PyTorch 中,模型通常是一个继承自 nn.Module 的类,前向传播的逻辑在 forward() 方法中定义。

代码示例:定义一个简单的模型

import torch.nn as nn class SimpleModel(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(SimpleModel, self).__init__() self.linear1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.linear2 = nn.Linear(hidden_size, output_size) def forward(self, x): out = self.linear1(x) out = self.relu(out) out = self.linear2(out) return out # 初始化模型 input_size = 10 hidden_size = 20 output_size = 2 model = SimpleModel(input_size, hidden_size, output_size) # 输入数据 (一个批次的数据) inputs = torch.randn(32, input_size) # batch_size=32, input_size=10 # 前向传播 outputs = model(inputs) print(f"Output shape: {outputs.shape}") # 输出形状: torch.Size([32, 2])

内容详解:

  • SimpleModel 类继承了 nn.Module,定义了一个包含两个线性层和一个 ReLU 激活函数的简单模型。

  • forward(self, x) 方法定义了前向传播的逻辑,接收输入 x,经过线性层和激活函数处理后,返回模型的输出 out

  • model(inputs) 调用了模型的 forward() 方法,实现了前向传播,得到了模型的预测输出 outputs

2.3 损失计算 (Loss Calculation)

损失函数 (Loss Function) 用于衡量模型预测值与真实标签之间的差异。训练的目标是最小化损失函数的值。PyTorch 的 torch.nn 模块提供了各种常用的损失函数,例如:

  • nn.CrossEntropyLoss: 用于多分类任务。

  • nn.MSELoss: 用于回归任务。

  • nn.BCELoss: 用于二分类任务 (输出概率值)。

代码示例:计算损失

import torch.nn as nn # ... (模型定义 SimpleModel 和 数据加载 DataLoader 代码省略) ... # 损失函数 criterion = nn.CrossEntropyLoss() # 真实标签 (一个批次的标签) targets = torch.randint(0, output_size, (32,)) # batch_size=32, output_size=2 # 计算损失 loss = criterion(outputs, targets) print(f"Loss value: {loss.item()}")

内容详解:

  • nn.CrossEntropyLoss() 初始化了交叉熵损失函数,适用于多分类任务。

  • criterion(outputs, targets) 计算了模型输出 outputs 和真实标签 targets 之间的交叉熵损失值。

  • loss.item() 将损失值从 PyTorch 张量 (Tensor) 转换为 Python 数值。

2.4 反向传播 (Backward Pass)

反向传播算法利用链式法则,计算损失函数关于模型参数的梯度。这些梯度指明了参数应该如何调整才能减小损失函数的值。在 PyTorch 中,调用 loss.backward() 即可自动完成反向传播。

代码示例:反向传播

# ... (模型定义 SimpleModel, 数据加载 DataLoader, 损失计算 criterion 代码省略) ... # 清零梯度 (重要步骤,每次反向传播前需要清零) model.zero_grad() # 反向传播 loss.backward() # 查看模型参数的梯度 (例如第一个线性层的权重梯度) print(model.linear1.weight.grad)

内容详解:

  • model.zero_grad() 必须在每次反向传播之前调用。PyTorch 默认梯度是累积的,如果不清零,梯度会叠加到之前的梯度上,导致参数更新错误。

  • loss.backward() 自动计算损失函数 loss 关于模型所有可学习参数的梯度,并将梯度存储在每个参数的 grad 属性中 (例如 model.linear1.weight.grad)。

2.5 参数更新 (Parameter Update)

优化器 (Optimizer) 根据反向传播计算出的梯度,更新模型的参数。PyTorch 的 torch.optim 模块提供了多种优化算法,例如:

  • optim.SGD: 随机梯度下降 (Stochastic Gradient Descent)。

  • optim.Adam: 自适应矩估计 (Adaptive Moment Estimation)。

  • optim.RMSprop: 均方根传播 (Root Mean Square Propagation)。

代码示例:参数更新

import torch.optim as optim # ... (模型定义 SimpleModel, 数据加载 DataLoader, 损失计算 criterion, 反向传播 loss.backward() 代码省略) ... # 优化器 (使用 Adam 优化器,学习率 lr=0.001) optimizer = optim.Adam(model.parameters(), lr=0.001) # 参数更新 optimizer.step() # 再次查看模型参数 (例如第一个线性层的权重,参数已经更新) print(model.linear1.weight)

内容详解:

  • optim.Adam(model.parameters(), lr=0.001) 初始化 Adam 优化器,并将模型的参数 model.parameters() 传递给优化器。 lr=0.001 设置学习率为 0.001。

  • optimizer.step() 根据梯度和优化算法,更新模型的参数。

2.6 评估与监控 (Evaluation & Monitoring)

在训练过程中,我们需要定期评估模型在验证集上的性能,并监控训练过程中的指标,例如损失值、准确率等。这有助于我们了解模型的训练状态,并及时调整训练策略。

代码示例:简单的评估指标计算

# ... (模型定义 SimpleModel, 数据加载 DataLoader, 损失计算 criterion, 反向传播 loss.backward(), 参数更新 optimizer.step() 代码省略) ... # 假设我们已经有一个验证集 DataLoader: val_dataloader def evaluate_accuracy(model, dataloader, device): model.eval() # 设置模型为评估模式 (关闭 dropout, batch normalization 等) correct_predictions = 0 total_samples = 0 with torch.no_grad(): # 在评估阶段禁用梯度计算,节省内存和计算资源 for inputs, targets in dataloader: inputs = inputs.to(device) # 将数据移动到设备 (GPU 或 CPU) targets = targets.to(device) outputs = model(inputs) _, predicted_labels = torch.max(outputs, 1) # 获取预测的最大概率的类别索引 total_samples += targets.size(0) correct_predictions += (predicted_labels == targets).sum().item() accuracy = correct_predictions / total_samples return accuracy # 假设 device 为 'cuda' 或 'cpu' device = torch.device('cpu') # 示例使用 CPU model.to(device) # 将模型移动到设备 # ... (训练循环代码,每个 epoch 结束后进行评估) ... val_accuracy = evaluate_accuracy(model, val_dataloader, device) print(f"Validation Accuracy: {val_accuracy:.4f}")

内容详解:

  • evaluate_accuracy 函数用于计算模型在给定 dataloader 上的准确率。

  • model.eval() 将模型设置为评估模式。这会影响一些特定层的行为,例如 Dropout 和 BatchNorm。在评估阶段,我们通常关闭 Dropout 和 BatchNorm。

  • torch.no_grad() 上下文管理器用于禁用梯度计算,在评估阶段不需要计算梯度,可以节省内存和计算资源。

  • torch.max(outputs, 1) 获取模型输出 outputs 在维度 1 上的最大值和索引。索引即为模型预测的类别标签。

  • 函数返回计算得到的准确率。

3. 训练循环代码实践 (完整示例)

下面是一个完整的 PyTorch 训练循环代码示例,包含了数据加载、模型定义、损失函数、优化器、前向传播、反向传播、参数更新和简单的评估指标计算。

import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import Dataset, DataLoader import numpy as np # 1. 数据准备 (示例数据) num_samples = 1000 input_size = 10 hidden_size = 20 output_size = 2 learning_rate = 0.001 num_epochs = 10 batch_size = 32 # 生成随机数据和标签 data = torch.randn(num_samples, input_size) labels = torch.randint(0, output_size, (num_samples,)) # 划分训练集和验证集 (8:2 比例) train_size = int(0.8 * num_samples) val_size = num_samples - train_size train_data, val_data = torch.split(data, [train_size, val_size]) train_labels, val_labels = torch.split(labels, [train_size, val_size]) # 自定义 Dataset class CustomDataset(Dataset): def __init__(self, data, labels): self.data = data self.labels = labels def __len__(self): return len(self.data) def __getitem__(self, index): return self.data[index], self.labels[index] train_dataset = CustomDataset(train_data, train_labels) val_dataset = CustomDataset(val_data, val_labels) train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) val_dataloader = DataLoader(val_dataset, batch_size=batch_size) # 2. 模型定义 class SimpleModel(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(SimpleModel, self).__init__() self.linear1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.linear2 = nn.Linear(hidden_size, output_size) def forward(self, x): out = self.linear1(x) out = self.relu(out) out = self.linear2(out) return out model = SimpleModel(input_size, hidden_size, output_size) # 3. 损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=learning_rate) # 4. 设备配置 (使用 CPU 或 GPU) device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model.to(device) # 5. 训练循环 for epoch in range(num_epochs): model.train() # 设置模型为训练模式 (启用 dropout, batch normalization 等) running_loss = 0.0 for batch_idx, (inputs, targets) in enumerate(train_dataloader): inputs = inputs.to(device) targets = targets.to(device) # 前向传播 outputs = model(inputs) loss = criterion(outputs, targets) # 反向传播和参数更新 optimizer.zero_grad() loss.backward() optimizer.step() running_loss += loss.item() if (batch_idx + 1) % 10 == 0: # 每 10 个 batch 打印一次训练信息 print(f"Epoch [{epoch+1}/{num_epochs}], Batch [{batch_idx+1}/{len(train_dataloader)}], Loss: {running_loss/10:.4f}") running_loss = 0.0 # 每个 epoch 结束后评估验证集 val_accuracy = evaluate_accuracy(model, val_dataloader, device) print(f"Epoch [{epoch+1}/{num_epochs}], Validation Accuracy: {val_accuracy:.4f}") print("Finished Training")

代码详解:

  • 代码包含了数据准备、模型定义、损失函数、优化器、设备配置和训练循环的完整流程。

  • 在每个 epoch 中,模型首先被设置为训练模式 model.train()

  • 循环遍历 train_dataloader 获取数据批次,进行前向传播、损失计算、反向传播和参数更新。

  • 每 10 个 batch 打印一次训练损失。

  • 每个 epoch 结束后,调用 evaluate_accuracy 函数评估模型在验证集上的准确率,并打印验证准确率。

  • 训练循环重复 num_epochs 次。

4. 训练循环流程图 (Mermaid Graph)

使用 Mermaid 绘制训练循环的流程图,更直观地展示训练过程:

流程图解释:

  1. 开始训练循环 (Start Training Loop):训练过程的起点。

  2. 加载数据批次 (Load Data Batch):从 DataLoader 获取一个批次的训练数据。

  3. 前向传播 (Forward Pass):将数据输入模型,计算模型输出。

  4. 计算损失 (Calculate Loss):根据模型输出和真实标签,计算损失值。

  5. 反向传播 (Backward Pass):根据损失值,计算模型参数的梯度。

  6. 参数更新 (Parameter Update):使用优化器根据梯度更新模型参数。

  7. 评估与监控 (Evaluation & Monitoring) (可选):定期评估模型性能,监控训练指标。

  8. 是否达到训练轮次上限或满足停止条件? (Epoch Limit or Stop Condition?):判断是否需要继续训练,例如是否达到预设的 epoch 数,或者验证集性能是否不再提升。

    • 否 (No):返回步骤 2,继续下一个迭代。

    • 是 (Yes):结束训练循环。

  9. 结束训练循环 (End Training Loop):训练完成。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U