
关于
PyTorch 深度学习模式和最佳实践,用于构建健壮、高效和可复现的训练管道、模型架构和数据加载。
name: pytorch-patterns description: PyTorch 深度学习模式和最佳实践,用于构建健壮、高效和可复现的训练流水线、模型架构和数据加载。 origin: ECC
PyTorch 开发模式
惯用的 PyTorch 模式和最佳实践,用于构建健壮、高效和可复现的深度学习应用。
何时激活
- 编写新的 PyTorch 模型或训练脚本
- 审查深度学习代码
- 调试训练循环或数据流水线
- 优化 GPU 内存使用或训练速度
- 设置可复现的实验
核心原则
1. 设备无关代码
始终编写在 CPU 和 GPU 上都能工作的代码,不要硬编码设备。
# 好:设备无关
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = MyModel().to(device)
data = data.to(device)
# 差:硬编码设备
model = MyModel().cuda() # 没有 GPU 会崩溃
data = data.cuda()
2. 可复现性优先
设置所有随机种子以获得可复现的结果。
# 好:完整的可复现性设置
def set_seed(seed: int = 42) -> None:
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
np.random.seed(seed)
random.seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
# 差:无种子控制
model = MyModel() # 每次运行权重不同
3. 显式形状管理
始终记录和验证张量形状。
# 好:带形状注释的前向传播
def forward(self, x: torch.Tensor) -> torch.Tensor:
# x: (batch_size, channels, height, width)
x = self.conv1(x) # -> (batch_size, 32, H, W)
x = self.pool(x) # -> (batch_size, 32, H//2, W//2)
x = x.view(x.size(0), -1) # -> (batch_size, 32*H//2*W//2)
return self.fc(x) # -> (batch_size, num_classes)
# 差:无形状跟踪
def forward(self, x):
x = self.conv1(x)
x = self.pool(x)
x = x.view(x.size(0), -1) # 这个大小是多少?
return self.fc(x) # 这能工作吗?
模型架构模式
整洁的 nn.Module 结构
# 好:组织良好的模块
class ImageClassifier(nn.Module):
def __init__(self, num_classes: int, dropout: float = 0.5) -> None:
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
nn.MaxPool2d(2),
)
self.classifier = nn.Sequential(
nn.Dropout(dropout),
nn.Linear(64 * 16 * 16, num_classes),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.features(x)
x = x.view(x.size(0), -1)
return self.classifier(x)
# 差:所有逻辑都在 forward 中
class ImageClassifier(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
x = F.conv2d(x, weight=self.make_weight()) # 每次调用都创建权重!
return x
正确的权重初始化
# 好:显式初始化
def _init_weights(self, module: nn.Module) -> None:
if isinstance(module, nn.Linear):
nn.init.kaiming_normal_(module.weight, mode="fan_out", nonlinearity="relu")
if module.bias is not None:
nn.init.zeros_(module.bias)
elif isinstance(module, nn.Conv2d):
nn.init.kaiming_normal_(module.weight, mode="fan_out", nonlinearity="relu")
elif isinstance(module, nn.BatchNorm2d):
nn.init.ones_(module.weight)
nn.init.zeros_(module.bias)
model = MyModel()
model.apply(model._init_weights)
训练循环模式
标准训练循环
# 好:包含最佳实践的完整训练循环
def train_one_epoch(
model: nn.Module,
dataloader: DataLoader,
optimizer: torch.optim.Optimizer,
criterion: nn.Module,
device: torch.device,
scaler: torch.amp.GradScaler | None = None,
) -> float:
model.train() # 始终设置训练模式
total_loss = 0.0
for batch_idx, (data, target) in enumerate(dataloader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad(set_to_none=True) # 比 zero_grad() 更高效
# 混合精度训练
with torch.amp.autocast("cuda", enabled=scaler is not None):
output = model(data)
loss = criterion(output, target)
if scaler is not None:
scaler.scale(loss).backward()
scaler.unscale_(optimizer)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
scaler.step(optimizer)
scaler.update()
else:
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
total_loss += loss.item()
return total_loss / len(dataloader)
验证循环
# 好:正确的评估方式
@torch.no_grad()
def validate(model, dataloader, criterion, device):
model.eval()
total_loss = 0.0
correct = 0
total = 0
for data, target in dataloader:
data, target = data.to(device), target.to(device)
output = model(data)
total_loss += criterion(output, target).item()
pred = output.argmax(dim=1)
correct += pred.eq(target).sum().item()
total += target.size(0)
return total_loss / len(dataloader), correct / total
兼容工具
Claude CodeCursor
标签
AI与机器学习
