01-从零入门教程

知识库
知识库文档
/tech-stacks/pytorch/tutorial/01-从零入门教程.md

文档

PyTorch 入门教程 —— 从线性回归到神经网络

本章目标

  • 理解动态计算图与自动微分
  • 手写梯度下降 → 使用 PyTorch 的 optimizer
  • 掌握 nn.Module 的模块化设计思想
  • 理解损失函数与优化器的选择

1. 动态计算图:为什么 PyTorch 如此灵活?

静态图(TensorFlow 1.x): 先定义完整图 → 编译 → 运行

# TF 1.x 风格(不再推荐)
x = tf.placeholder(tf.float32, shape=[None, 784])
W = tf.Variable(tf.zeros([784, 10]))
y = tf.matmul(x, W)  # 这时还没执行,只是在建图
# ... session.run() 才真正执行

动态图(PyTorch): 每一步操作立即执行,图随代码走

x = torch.randn(10, 784)
W = torch.randn(784, 10, requires_grad=True)
y = x @ W           # 立即计算!可以 print、debug
loss = y.sum()
loss.backward()     # 自动计算 W.grad

优势: 你可以在 forward 中使用 if/for/while,这对应变长序列、条件分支等场景极为关键。

2. 从零手写梯度下降 → 使用 PyTorch

阶段一:纯 NumPy 手动求导

import numpy as np

# 数据:y = 3x + 2 + noise
np.random.seed(42)
X = np.random.randn(100, 1)
y = 3 * X + 2 + np.random.randn(100, 1) * 0.3

w, b = np.random.randn(1), np.random.randn(1)
lr = 0.01

for epoch in range(1000):
    y_pred = X * w + b
    loss = ((y_pred - y) ** 2).mean()

    # 手动计算梯度(容易出错!)
    dw = (2 / len(y)) * (X * (y_pred - y)).sum()
    db = (2 / len(y)) * (y_pred - y).sum()

    w -= lr * dw
    b -= lr * db

阶段二:PyTorch autograd 自动求导

import torch

X_t = torch.tensor(X, dtype=torch.float32)
y_t = torch.tensor(y, dtype=torch.float32)

w = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)

for epoch in range(1000):
    y_pred = X_t * w + b
    loss = ((y_pred - y_t) ** 2).mean()

    loss.backward()  # 自动计算 w.grad, b.grad

    with torch.no_grad():  # 更新时不需要梯度
        w -= lr * w.grad
        b -= lr * b.grad
        w.grad.zero_()      # 清零,否则会累积
        b.grad.zero_()

阶段三:PyTorch optimizer + nn.Module

model = nn.Linear(1, 1)           # 一行定义 w,b
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

for epoch in range(1000):
    y_pred = model(X_t)
    loss = criterion(y_pred, y_t)

    optimizer.zero_grad()          # 一行清零所有梯度
    loss.backward()
    optimizer.step()               # 一行更新所有参数

3. nn.Module 模块化设计

class MLP(nn.Module):
    """多层感知机 —— 就像搭乐高"""
    def __init__(self, input_dim, hidden_dim, output_dim):
        super().__init__()
        # 定义"层"(子模块)
        self.fc1 = nn.Linear(input_dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, hidden_dim)
        self.fc3 = nn.Linear(hidden_dim, output_dim)
        self.bn = nn.BatchNorm1d(hidden_dim)
        self.dropout = nn.Dropout(0.3)

    def forward(self, x):
        # 定义"连接关系"
        x = F.relu(self.bn(self.fc1(x)))
        x = F.relu(self.bn(self.fc2(x)))
        x = self.dropout(x)
        x = self.fc3(x)
        return x

# PyTorch 自动追踪所有子模块
model = MLP(784, 256, 10)
print(list(model.children()))       # 遍历子模块
print(sum(p.numel() for p in model.parameters()))  # 总参数量

# 参数可整体迁移
model = model.to("cuda")
model = model.float()               # 转 float32
model = model.half()                # 转 float16(混合精度)

4. 损失函数速查

任务 损失函数 PyTorch
回归 均方误差 nn.MSELoss()
回归 平均绝对误差 nn.L1Loss()
二分类 二元交叉熵 nn.BCEWithLogitsLoss()
多分类 交叉熵(内置 softmax) nn.CrossEntropyLoss()
不平衡分类 Focal Loss 自定义
相似度学习 Triplet Margin Loss nn.TripletMarginLoss()

关键:CrossEntropyLoss 自动含 softmax!

# ❌ 错误 —— 双重 softmax
output = F.softmax(logits, dim=1)
loss = nn.CrossEntropyLoss()(output, labels)

# ✅ 正确 —— CrossEntropyLoss 内部已含 LogSoftmax
loss = nn.CrossEntropyLoss()(logits, labels)

5. 优化器对比

优化器 特性 适用场景
SGD 经典,需手动调 lr 需要强泛化能力时
SGD + Momentum 加速 + 抗震荡 CV 任务常用
Adam 自适应学习率 默认首选,NLP 常用
AdamW Adam + 解耦权重衰减 大模型/Transformer
RMSprop 适合非稳态目标 RNN/强化学习

6. 实用技巧:梯度裁剪

# 防止梯度爆炸(RNN/Transformer 训练必备)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

# 完整的训练步骤
for batch in dataloader:
    optimizer.zero_grad()
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    loss.backward()
    torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
    optimizer.step()

思考题

  1. 动态计算图相比静态图,性能上有损失吗?PyTorch 2.0 的 torch.compile 如何解决?
  2. optimizer.zero_grad() 如果忘记调用会怎样?
  3. model.train()model.eval() 具体影响了哪些层的行为?
  4. 为什么 CrossEntropyLoss 的输入不能经过 softmax?

信息

路径
/tech-stacks/pytorch/tutorial/01-从零入门教程.md
更新时间
2026/5/30