趋近智
将高级优化算法、学习率调度策略、正则化 (regularization)方法和归一化 (normalization)策略整合起来,对于构建实用且精密的训练循环至关重要。有效训练深度复杂的CNN通常需要的远不止基本的model.fit()调用。可以构建一个自定义训练循环来结合这些高级技术。这涉及到使用Python以及PyTorch等框架中常用的概念来组织实现。
假设您已准备好模型 (model)、通过数据加载器 (train_loader, val_loader) 加载的数据集,以及一个基础损失函数 (loss function)(如 CrossEntropyLoss)。我们的目标是在标准训练流程中增加以下功能:
首先,我们初始化必要的组件。我们会将模型移动到合适的设备(例如GPU)。
import torch
import torch.nn as nn
import torch.optim as optim
from torch.cuda.amp import GradScaler, autocast
from torch.optim.lr_scheduler import OneCycleLR
# 假设'model'是您定义的CNN架构
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
# 1. 高级优化器:AdamW
# 注意weight_decay参数的处理方式,它与标准Adam不同,此处的处理是正确的。
optimizer = optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2)
# 2. 学习率调度器:OneCycleLR
# 需要total_steps = epochs * len(train_loader)
# max_lr通常应通过LR范围测试来确定,但我们在此设置一个占位符。
epochs = 10
total_steps = epochs * len(train_loader)
scheduler = OneCycleLR(optimizer, max_lr=1e-2, total_steps=total_steps)
# 3. 带有标签平滑的损失函数
# 标签平滑通过降低模型置信度来帮助防止过拟合。
# 通常使用0.1的值。
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
# 4. 混合精度训练:GradScaler
# scaler有助于管理梯度缩放,以防止float16下的下溢。
scaler = GradScaler(enabled=torch.cuda.is_available() and torch.backends.cudnn.is_available())
OneCycleLR调度在整个训练过程中大幅改变学习率。它从低值开始,增加到最大值(max_lr),然后衰减。可视化这一点有助于理解其表现。
# 示例可视化数据(替换为实际的调度器步骤)
steps = list(range(total_steps))
lrs = []
# 模拟学习率变化(需要虚拟优化器状态更新)
temp_optimizer = optim.AdamW([torch.zeros(1)], lr=1e-3) # 虚拟参数
temp_scheduler = OneCycleLR(temp_optimizer, max_lr=1e-2, total_steps=total_steps)
for _ in steps:
lrs.append(temp_scheduler.get_last_lr()[0])
temp_optimizer.step() # 需要调用step来推进调度器
temp_scheduler.step()
OneCycleLR策略在总训练步数上生成的学习率曲线。注意其预热、峰值和冷却阶段。
现在,我们将这些整合到一个执行一个训练步骤(处理一个批次)的函数中。主要增加的是在前向传播中使用autocast,以及在反向传播 (backpropagation)和优化器步进中使用scaler。
def train_step(model, batch, optimizer, criterion, scaler, scheduler, device):
"""执行一个带有高级功能的训练步骤。"""
model.train() # 将模型设置为训练模式
inputs, targets = batch
inputs, targets = inputs.to(device), targets.to(device)
optimizer.zero_grad()
# 在前向传播中使用autocast(混合精度)
with autocast(enabled=scaler.is_enabled()):
outputs = model(inputs)
# 损失计算通过criterion初始化隐式使用平滑目标
loss = criterion(outputs, targets)
# 缩放损失并执行反向传播
scaler.scale(loss).backward()
# 可选:梯度裁剪(前面已讨论)
# torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# Scaler执行优化器步进
scaler.step(optimizer)
# 为下一次迭代更新scaler
scaler.update()
# 步进学习率调度器(对于OneCycleLR,按批次进行)
scheduler.step()
# 返回损失用于监控
return loss.item(), scheduler.get_last_lr()[0]
我们现在可以组装完整的跨周期的训练循环,结合train_step函数并增加验证和监控。
# --- 监控设置(示例:使用列表,实际中可与TensorBoard/WandB结合) ---
train_losses = []
learning_rates = []
val_accuracies = []
# -----------------------------------------------------------------------------------------
print("开始高级训练...")
for epoch in range(epochs):
epoch_loss = 0.0
model.train() # 确保模型处于训练模式
for batch_idx, batch in enumerate(train_loader):
loss, current_lr = train_step(model, batch, optimizer, criterion, scaler, scheduler, device)
epoch_loss += loss
# --- 监控 ---
if batch_idx % 100 == 0: # 每100个批次记录一次
print(f"Epoch {epoch+1}/{epochs}, Batch {batch_idx}/{len(train_loader)}, Loss: {loss:.4f}, LR: {current_lr:.6f}")
learning_rates.append(current_lr)
# -----------------
avg_epoch_loss = epoch_loss / len(train_loader)
train_losses.append(avg_epoch_loss)
print(f"Epoch {epoch+1} Average Training Loss: {avg_epoch_loss:.4f}")
# --- 验证阶段 ---
model.eval() # 将模型设置为评估模式
correct = 0
total = 0
with torch.no_grad(): # 禁用验证期间的梯度计算
for batch in val_loader:
inputs, targets = batch
inputs, targets = inputs.to(device), targets.to(device)
# 即使在验证期间,如果需要,也可以使用autocast保持一致性,但通常不是必需的
with autocast(enabled=scaler.is_enabled()):
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1)
total += targets.size(0)
correct += (predicted == targets).sum().item()
accuracy = 100 * correct / total
val_accuracies.append(accuracy)
print(f"Epoch {epoch+1} Validation Accuracy: {accuracy:.2f}%")
# ----------------------
# --- 训练后 ---
# 保存模型、绘制指标等。
print("训练完成。")
# 示例:绘制损失曲线
# (需要matplotlib)
# import matplotlib.pyplot as plt
# plt.plot(range(1, epochs + 1), train_losses, label='训练损失')
# plt.xlabel('周期')
# plt.ylabel('损失')
# plt.legend()
# plt.show()
# ---------------------
实现这些高级技术有时会带来新的挑战:
NaN值。确保您的网络层与混合精度兼容。检查scaler.get_scale()的值;如果它变得非常小或inf/NaN,请调整GradScaler的init_scale或growth_interval。OneCycleLR的max_lr是一个敏感的超参数 (hyperparameter)。强烈建议事先进行LR范围测试。调度器、优化器(特别是weight_decay)和批次大小之间的关系需要仔细调整。label_smoothing因子(例如0.1)是另一个可能需要调整的超参数。本实践练习演示了如何将本章中的几种强大技术整合到一个连贯的训练循环中。虽然这种设置比简单方法涉及更多代码,但在训练速度、稳定性、模型鲁棒性以及复杂任务上的最终表现方面可能带来的提升,使掌握这些高级循环成为一项有价值的技能,对于从事有难度计算机视觉问题的深度学习 (deep learning)实践者而言。
这部分内容有帮助吗?
© 2026 ApX Machine LearningAI伦理与透明度•