Communitygithub.com

PyTorch 深度学习模式与最佳实践

PyTorch 深度学习模式与最佳实践,用于构建稳健、高效且可复现的训练流程、模型架构和数据加载。在编写或审查 PyTorch 训练循环、模型架构或数据加载时,或者当运行结果无法复现时使用。

PyTorch 深度学习模式与最佳实践 是什么?

该技能提供了一套全面的 PyTorch 开发模式与最佳实践,旨在帮助开发者构建健壮、高效且可复现的深度学习应用。它覆盖了从设备无关的代码编写、实验可复现性设置,到清晰的模型架构组织、规范的训练与验证循环、高效的数据加载管道以及完整的模型检查点管理。此外,还深入讲解了混合精度训练、梯度检查点和 `torch.compile` 等性能优化技巧。该技能适用于任何使用 PyTorch 框架的开发场景,无论是编写新模型、调试训练循环,还是优化 GPU 内存使用。其建议基于大量工程实践,并针对常见的错误模式(如忘记设置 `model.eval()`、在计算图中使用原地操作等)提供了明确的规避指南。通过遵循这些模式,开发者能够显著提高代码质量、减少 Bug,并确保实验结果的可靠复现。本技能的设计思路源于对 PyTorch 官方文档及社区最佳实践的提炼,并以清晰易读的代码示例和对比方式呈现。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/pytorch-patterns

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

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()  # 比手动包裹 torch.no_grad() 块更高效
def evaluate(
    model: nn.Module,
    dataloader: DataLoader,
    criterion: nn.Module,
    device: torch.device,
) -> tuple[float, float]:
    model.eval()  # 始终设置为评估模式——禁用 dropout,使用运行中的BN统计量
    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()
        correct += (output.argmax(1) == target).sum().item()
        total += target.size(0)

    return total_loss / len(dataloader), correct / total

数据管线模式

自定义数据集

# 推荐:带有类型提示的清晰 Dataset
class ImageDataset(Dataset):
    def __init__(
        self,
        image_dir: str,
        labels: dict[str, int],
        transform: transforms.Compose | None = None,
    ) -> None:
        self.image_paths = list(Path(image_dir).glob("*.jpg"))
        self.labels = labels
        self.transform = transform

    def __len__(self) -> int:
        return len(self.image_paths)

    def __getitem__(self, idx: int) -> tuple[torch.Tensor, int]:
        img = Image.open(self.image_paths[idx]).convert("RGB")
        label = self.labels[self.image_paths[idx].stem]

        if self.transform:
            img = self.transform(img)

        return img, label

高效的 DataLoader 配置

# 推荐:优化的 DataLoader
dataloader = DataLoader(
    dataset,
    batch_size=32,
    shuffle=True,            # 训练时打乱
    num_workers=4,           # 并行数据加载
    pin_memory=True,         # 更快的 CPU->GPU 传输
    persistent_workers=True, # 在 epochs 之间保持工作进程存活
    drop_last=True,          # 为 BatchNorm 提供一致的批次大小
)

# 不推荐:缓慢的默认设置
dataloader = DataLoader(dataset, batch_size=32)  # num_workers=0, 没有 pin_memory

针对变长数据的自定义 Collate

# 推荐:在 collate_fn 中填充序列
def collate_fn(batch: list[tuple[torch.Tensor, int]]) -> tuple[torch.Tensor, torch.Tensor]:
    sequences, labels = zip(*batch)
    # 填充到批次中的最大长度
    padded = nn.utils.rnn.pad_sequence(sequences, batch_first=True, padding_value=0)
    return padded, torch.tensor(labels)

dataloader = DataLoader(dataset, batch_size=32, collate_fn=collate_fn)

检查点模式

保存和加载检查点

# 推荐:包含所有训练状态的完整检查点
def save_checkpoint(
    model: nn.Module,
    optimizer: torch.optim.Optimizer,
    epoch: int,
    loss: float,
    path: str,
) -> None:
    torch.save({
        "epoch": epoch,
        "model_state_dict": model.state_dict(),
        "optimizer_state_dict": optimizer.state_dict(),
        "loss": loss,
    }, path)

def load_checkpoint(
    path: str,
    model: nn.Module,
    optimizer: torch.optim.Optimizer | None = None,
) -> dict:
    checkpoint = torch.load(path, map_location="cpu", weights_only=True)
    model.load_state_dict(checkpoint["model_state_dict"])
    if optimizer:
        optimizer.load_state_dict(checkpoint["optimizer_state_dict"])
    return checkpoint

# 不推荐:只保存模型权重(无法恢复训练)
torch.save(model.state_dict(), "model.pt")

性能优化

混合精度训练

# 推荐:使用 GradScaler 的 AMP
scaler = torch.amp.GradScaler("cuda")
for data, target in dataloader:
    with torch.amp.autocast("cuda"):
        output = model(data)
        loss = criterion(output, target)
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()
    optimizer.zero_grad(set_to_none=True)

大模型的梯度检查点

# 推荐:用计算换内存
from torch.utils.checkpoint import checkpoint

class LargeModel(nn.Module):
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # 在反向传播期间重新计算激活以节省内存
        x = checkpoint(self.block1, x, use_reentrant=False)
        x = checkpoint(self.block2, x, use_reentrant=False)
        return self.head(x)

使用 torch.compile 加速

# 推荐:编译模型以加快执行速度(PyTorch 2.0+)
model = MyModel().to(device)
model = torch.compile(model, mode="reduce-overhead")

# 模式:"default" (安全), "reduce-overhead" (更快), "max-autotune" (最快)

快速参考:PyTorch 惯用写法

惯用写法描述
model.train() / model.eval()在训练/评估前始终设置模式
torch.no_grad()推理时禁用梯度
optimizer.zero_grad(set_to_none=True)更高效的梯度置零
.to(device)设备无关的张量/模型放置
torch.amp.autocast混合精度,速度提升 2 倍
pin_memory=True更快的 CPU→GPU 数据传输
torch.compileJIT 编译加速(2.0+)
weights_only=True安全加载模型
torch.manual_seed可复现的实验
gradient_checkpointing用计算换内存

应避免的反模式

# 不推荐:验证时忘记设置 model.eval()
model.train()
with torch.no_grad():
    output = model(val_data)  # Dropout 仍然激活!BatchNorm 使用批统计量!

# 推荐:始终设置评估模式
model.eval()
with torch.no_grad():
    output = model(val_data)

# 不推荐:原地操作破坏自动求导
x = F.relu(x, inplace=True)  # 可能破坏梯度计算
x += residual                  # 原地加法破坏自动求导图

# 推荐:非原地操作
x = F.relu(x)
x = x + residual

# 不推荐:在训练循环内重复地将数据移到 GPU
for data, target in dataloader:
    model = model.cuda()  # 每次迭代都移动模型!

# 推荐:在循环之前移动模型一次
model = model.to(device)
for data, target in dataloader:
    data, target = data.to(device), target.to(device)

# 不推荐:在 backward 之前使用 .item()
loss = criterion(output, target).item()  # 从计算图中分离!
loss.backward()  # 错误:无法通过 .item() 反向传播

# 推荐:仅在日志记录时使用 .item()
loss = criterion(output, target)
loss.backward()
print(f"Loss: {loss.item():.4f}")  # 在 backward 之后使用 .item() 没问题

# 不推荐:不当使用 torch.save
torch.save(model, "model.pt")  # 保存整个模型(脆弱,不可移植)

# 推荐:保存 state_dict
torch.save(model.state_dict(), "model.pt")

请牢记:PyTorch 代码应该做到设备无关、可复现且对内存敏感。如有疑问,使用 torch.profiler 进行分析,并使用 torch.cuda.memory_summary() 检查 GPU 内存。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相关技能