> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# PyTorch完全指南
- URL: https://blog.vercanti.com/pytorchwan-quan-zhi-nan/
- Published: 2026-08-28T14:34:44.000Z
- Updated: 2026-08-28T14:57:11.000Z
- Description: PyTorch 是由 Meta AI 开发的深度学习框架，以动态计算图（Define-by-Run）为核心，支持 GPU 加速，广泛用于研究和生产环境。 验证安装： Tensor 是 PyTorch 的核心数据结构，类似 NumPy ndarray，但可在 GPU 上运算并支持自动微分。 torch.tensor 参数： PyTorch 通过动态计算图（DAG）自动追踪运算并反向传播梯度。 backward 参数： torch.nn.Module 是所有神经网络模块的基类。 全连接层（线性变换）：y = xW^T + b 参数： 2D 卷积层。 参数：
- Author: yellowdog
- Tags: Python, 框架与库

> 官方文档：<https://pytorch.org/docs/stable/index.html>  
> 官方教程：<https://pytorch.org/tutorials/>

# PyTorch 完全指南

PyTorch 是由 Meta AI 开发的深度学习框架，以动态计算图（Define-by-Run）为核心，支持 GPU 加速，广泛用于研究和生产环境。

---

## 安装

```bash
# 查看 CUDA 版本后选择对应命令：https://pytorch.org/get-started/locally/
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

# CPU-only
pip install torch torchvision torchaudio

```

验证安装：

```python
import torch
print(torch.__version__)
print(torch.cuda.is_available())    # GPU 是否可用
print(torch.backends.mps.is_available())  # Apple Silicon GPU

```

---

## 一、Tensor 张量

Tensor 是 PyTorch 的核心数据结构，类似 NumPy ndarray，但可在 GPU 上运算并支持自动微分。

### 1.1 创建 Tensor

| 函数                                | 说明                                  |
| --------------------------------- | ----------------------------------- |
| torch.tensor(data)                | 从 Python list / NumPy array 创建，复制数据 |
| torch.as\_tensor(data)            | 从数组创建，尽可能共享内存（零拷贝）                  |
| torch.zeros(size)                 | 全零张量                                |
| torch.ones(size)                  | 全一张量                                |
| torch.full(size, fill\_value)     | 填充常数                                |
| torch.eye(n)                      | 单位矩阵                                |
| torch.rand(size)                  | \[0,1) 均匀分布                         |
| torch.randn(size)                 | 标准正态分布                              |
| torch.randint(low, high, size)    | 随机整数                                |
| torch.arange(start, end, step)    | 等差序列                                |
| torch.linspace(start, end, steps) | 等间隔序列                               |
| torch.empty(size)                 | 未初始化张量                              |

**`torch.tensor` 参数：**

| 参数             | 类型               | 默认值       | 说明       |
| -------------- | ---------------- | --------- | -------- |
| data           | array-like       | 必填        | 输入数据     |
| dtype          | torch.dtype      | None（推断）  | 数据类型     |
| device         | str/torch.device | None（CPU） | 存储设备     |
| requires\_grad | bool             | False     | 是否参与梯度计算 |

```python
import torch

# 从列表创建
t = torch.tensor([[1.0, 2.0], [3.0, 4.0]])

# 指定类型和设备
t = torch.tensor([1, 2, 3], dtype=torch.float32, device="cuda")

# 与 NumPy 互转（CPU tensor 共享内存）
import numpy as np
arr = np.array([1.0, 2.0, 3.0])
t = torch.from_numpy(arr)      # 共享内存
arr2 = t.numpy()               # 共享内存

# zeros_like / ones_like（继承形状和设备）
x = torch.rand(3, 4, device="cuda")
z = torch.zeros_like(x)

```

### 1.2 Tensor 属性

```python
t = torch.randn(2, 3, 4)

t.shape        # torch.Size([2, 3, 4])
t.size()       # 同上，可传维度索引 t.size(0) -> 2
t.ndim         # 3
t.numel()      # 24（元素总数）
t.dtype        # torch.float32
t.device       # device(type='cpu')
t.requires_grad  # False
t.is_cuda      # False
t.T            # 转置（2D tensor 的语法糖）

```

### 1.3 数据类型

| dtype                        | 说明                       |
| ---------------------------- | ------------------------ |
| torch.float32 / torch.float  | 单精度浮点（默认）                |
| torch.float64 / torch.double | 双精度浮点                    |
| torch.float16 / torch.half   | 半精度（GPU 混合精度）            |
| torch.bfloat16               | Brain Float（Ampere 架构推荐） |
| torch.int32 / torch.int      | 32 位整数                   |
| torch.int64 / torch.long     | 64 位整数（索引默认类型）           |
| torch.bool                   | 布尔型                      |

```python
t = t.float()     # 转 float32
t = t.long()      # 转 int64
t = t.to(torch.bfloat16)

```

### 1.4 设备管理

```python
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

t = torch.randn(3, 3).to(device)
t = t.cuda()     # 移至 GPU 0
t = t.cpu()      # 移回 CPU

# 指定 GPU 编号
t = t.to("cuda:1")

```

### 1.5 基本运算

```python
a = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([4.0, 5.0, 6.0])

# 逐元素运算
a + b          # 或 torch.add(a, b)
a - b
a * b          # 逐元素乘
a / b
a ** 2         # 幂运算
torch.sqrt(a)
torch.abs(a)
torch.exp(a)
torch.log(a)   # 自然对数

# 矩阵乘法
A = torch.randn(2, 3)
B = torch.randn(3, 4)
C = A @ B                # 等价于 torch.matmul(A, B)
C = torch.mm(A, B)       # 仅限 2D

# 批量矩阵乘
A = torch.randn(8, 2, 3)
B = torch.randn(8, 3, 4)
C = torch.bmm(A, B)      # (8, 2, 4)

# 聚合运算
t.sum()
t.mean()
t.max()
t.min()
t.std()
t.var()
t.sum(dim=0)             # 沿维度聚合，dim 也可写 axis
t.max(dim=1)             # 返回 (values, indices)
t.max(dim=1).values

```

### 1.6 形状操作

| 方法                          | 说明           |
| --------------------------- | ------------ |
| t.view(\*shape)             | 重塑，要求内存连续    |
| t.reshape(\*shape)          | 重塑，自动处理非连续内存 |
| t.squeeze(dim)              | 移除大小为 1 的维度  |
| t.unsqueeze(dim)            | 在指定位置插入维度 1  |
| t.permute(\*dims)           | 维度重排         |
| t.transpose(dim0, dim1)     | 交换两个维度       |
| t.contiguous()              | 确保内存连续       |
| t.flatten(start, end)       | 展平指定维度范围     |
| torch.cat(tensors, dim)     | 沿已有维度拼接      |
| torch.stack(tensors, dim)   | 沿新维度堆叠       |
| torch.split(t, size, dim)   | 分割           |
| torch.chunk(t, chunks, dim) | 均匀分块         |

```python
t = torch.randn(2, 3, 4)

t.view(6, 4)          # (6, 4)
t.reshape(2, -1)      # -1 自动推断，(2, 12)
t.permute(2, 0, 1)    # (4, 2, 3)

# 添加/删除 batch 维度
x = torch.randn(3, 4)
x.unsqueeze(0)        # (1, 3, 4)
x.unsqueeze(0).squeeze(0)  # 恢复 (3, 4)

# 拼接
a = torch.zeros(2, 3)
b = torch.ones(2, 3)
torch.cat([a, b], dim=0)   # (4, 3)
torch.stack([a, b], dim=0) # (2, 2, 3)

```

### 1.7 索引与切片

```python
t = torch.randn(4, 5, 6)

t[0]           # (5, 6)
t[0, 1]        # (6,)
t[0, 1, 2]     # scalar tensor
t[:, 1:3, :]   # 切片
t[..., 0]      # 省略号，等价于 t[:, :, 0]

# 高级索引
idx = torch.tensor([0, 2])
t[idx]         # 选取第 0 行和第 2 行

# 布尔索引
mask = t > 0
t[mask]        # 返回所有正数的一维张量

# gather（按索引收集）
# torch.gather(input, dim, index)

```

---

## 二、Autograd 自动微分

PyTorch 通过动态计算图（DAG）自动追踪运算并反向传播梯度。

### 2.1 requires\_grad

```python
x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x + 1

y.backward()      # 反向传播
print(x.grad)     # dy/dx = 2x + 3 = 7

```

### 2.2 常用 API

| 操作                             | 说明                   |
| ------------------------------ | -------------------- |
| tensor.backward(gradient=None) | 反向传播，标量 tensor 可不传参数 |
| tensor.grad                    | 叶子节点的梯度              |
| tensor.grad\_fn                | 非叶子节点的梯度函数           |
| tensor.is\_leaf                | 是否为叶子节点              |
| tensor.detach()                | 返回一个不参与梯度计算的视图       |
| tensor.detach\_()              | 原地分离                 |
| tensor.retain\_grad()          | 保留非叶节点的梯度（默认丢弃）      |

**`backward` 参数：**

| 参数            | 类型     | 默认值   | 说明                            |
| ------------- | ------ | ----- | ----------------------------- |
| gradient      | Tensor | None  | 非标量 tensor 需传入与自身同形状的权重       |
| retain\_graph | bool   | False | 是否保留计算图（多次 backward 时设为 True） |
| create\_graph | bool   | False | 是否构建高阶导数计算图                   |

```python
# 非标量的 backward 需传 gradient
x = torch.randn(3, requires_grad=True)
y = x ** 2
y.backward(torch.ones_like(y))   # 等价于 y.sum().backward()
print(x.grad)   # 2 * x

# 多次 backward（retain_graph=True）
y.backward(torch.ones_like(y), retain_graph=True)

```

### 2.3 禁用梯度追踪

```python
# 推断/评估时禁用以节省内存和加速
with torch.no_grad():
    y = model(x)

# 函数装饰器
@torch.no_grad()
def evaluate(model, x):
    return model(x)

# 仅分离结果
y = model(x).detach()

```

### 2.4 梯度清零

```python
optimizer.zero_grad()   # 每次反向传播前必须调用
# 或手动清零
for p in model.parameters():
    if p.grad is not None:
        p.grad.zero_()

```

---

## 三、nn.Module 神经网络

`torch.nn.Module` 是所有神经网络模块的基类。

### 3.1 定义模型

```python
import torch.nn as nn
import torch.nn.functional as F

class MyModel(nn.Module):
    def __init__(self, in_features: int, out_features: int):
        super().__init__()
        self.fc1 = nn.Linear(in_features, 128)
        self.fc2 = nn.Linear(128, out_features)
        self.dropout = nn.Dropout(p=0.5)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        x = F.relu(self.fc1(x))
        x = self.dropout(x)
        return self.fc2(x)

model = MyModel(784, 10)

```

### 3.2 模型信息

```python
print(model)                        # 打印模型结构
sum(p.numel() for p in model.parameters())           # 总参数量
sum(p.numel() for p in model.parameters() if p.requires_grad)  # 可训练参数量

# 递归遍历子模块
for name, module in model.named_modules():
    print(name, module)

# 遍历参数
for name, param in model.named_parameters():
    print(name, param.shape)

```

### 3.3 训练/评估模式

```python
model.train()    # 启用 Dropout、BatchNorm 的训练行为
model.eval()     # 禁用 Dropout，BatchNorm 使用运行统计

```

### 3.4 Sequential 容器

```python
model = nn.Sequential(
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Dropout(0.5),
    nn.Linear(256, 10),
)

# 有序字典版本
from collections import OrderedDict
model = nn.Sequential(OrderedDict([
    ("fc1", nn.Linear(784, 256)),
    ("relu", nn.ReLU()),
    ("fc2", nn.Linear(256, 10)),
]))
model.fc1   # 按名称访问

```

### 3.5 ModuleList 和 ModuleDict

```python
# ModuleList：列表，参数会被自动注册
self.layers = nn.ModuleList([nn.Linear(128, 128) for _ in range(6)])

# ModuleDict：字典
self.heads = nn.ModuleDict({
    "cls": nn.Linear(128, 10),
    "reg": nn.Linear(128, 4),
})

```

---

## 四、常用层

### 4.1 nn.Linear

全连接层（线性变换）：`y = xW^T + b`

**参数：**

| 参数            | 类型   | 默认值  | 说明     |
| ------------- | ---- | ---- | ------ |
| in\_features  | int  | 必填   | 输入特征数  |
| out\_features | int  | 必填   | 输出特征数  |
| bias          | bool | True | 是否添加偏置 |

```python
fc = nn.Linear(128, 64)
x = torch.randn(32, 128)  # (batch, in_features)
y = fc(x)                 # (32, 64)

```

### 4.2 nn.Conv2d

2D 卷积层。

**参数：**

| 参数            | 类型            | 默认值     | 说明                              |
| ------------- | ------------- | ------- | ------------------------------- |
| in\_channels  | int           | 必填      | 输入通道数                           |
| out\_channels | int           | 必填      | 输出通道数（滤波器数）                     |
| kernel\_size  | int/tuple     | 必填      | 卷积核大小                           |
| stride        | int/tuple     | 1       | 步长                              |
| padding       | int/tuple/str | 0       | 填充，"same" 保持尺寸                  |
| dilation      | int/tuple     | 1       | 膨胀率                             |
| groups        | int           | 1       | 分组卷积，groups=in\_channels 即深度可分离 |
| bias          | bool          | True    | 偏置                              |
| padding\_mode | str           | "zeros" | 填充模式                            |

```python
conv = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
x = torch.randn(8, 3, 224, 224)   # (N, C, H, W)
y = conv(x)                        # (8, 64, 224, 224)

# 输出尺寸公式：floor((H + 2*padding - dilation*(kernel-1) - 1) / stride + 1)

```

### 4.3 nn.BatchNorm2d

批归一化，一般放在 Conv 之后、激活函数之前。

**参数：**

| 参数                    | 类型    | 默认值  | 说明                             |
| --------------------- | ----- | ---- | ------------------------------ |
| num\_features         | int   | 必填   | 通道数（与 Conv out\_channels 相同）   |
| eps                   | float | 1e-5 | 数值稳定项                          |
| momentum              | float | 0.1  | 运行均值/方差的更新动量                   |
| affine                | bool  | True | 是否有可学习的 scale/bias（gamma/beta） |
| track\_running\_stats | bool  | True | 是否追踪运行统计                       |

```python
block = nn.Sequential(
    nn.Conv2d(64, 64, 3, padding=1),
    nn.BatchNorm2d(64),
    nn.ReLU(inplace=True),
)

```

其他归一化：`nn.LayerNorm`（NLP 常用）、`nn.GroupNorm`、`nn.InstanceNorm2d`。

### 4.4 nn.Dropout / nn.Dropout2d

| 参数      | 类型    | 默认值   | 说明     |
| ------- | ----- | ----- | ------ |
| p       | float | 0.5   | 随机置零概率 |
| inplace | bool  | False | 是否原地操作 |

`nn.Dropout2d` 以通道为单位置零（适用于卷积特征图）。

### 4.5 nn.Embedding

将整数索引映射到稠密向量（词嵌入）。

**参数：**

| 参数                    | 类型    | 默认值   | 说明             |
| --------------------- | ----- | ----- | -------------- |
| num\_embeddings       | int   | 必填    | 词汇表大小          |
| embedding\_dim        | int   | 必填    | 嵌入维度           |
| padding\_idx          | int   | None  | 该索引的嵌入向量恒为 0   |
| max\_norm             | float | None  | 嵌入向量最大范数       |
| scale\_grad\_by\_freq | bool  | False | 按词频缩放梯度        |
| sparse                | bool  | False | 稀疏梯度更新（大词汇表加速） |

```python
emb = nn.Embedding(10000, 256, padding_idx=0)
x = torch.randint(0, 10000, (32, 50))   # (batch, seq_len)
y = emb(x)                              # (32, 50, 256)

```

### 4.6 nn.LSTM

**参数：**

| 参数            | 类型    | 默认值   | 说明                              |
| ------------- | ----- | ----- | ------------------------------- |
| input\_size   | int   | 必填    | 输入特征数                           |
| hidden\_size  | int   | 必填    | 隐状态维度                           |
| num\_layers   | int   | 1     | 堆叠层数                            |
| bias          | bool  | True  | 偏置                              |
| batch\_first  | bool  | False | True 时输入为 (batch, seq, feature) |
| dropout       | float | 0     | 层间 dropout（num\_layers > 1 时有效） |
| bidirectional | bool  | False | 双向                              |
| proj\_size    | int   | 0     | 输出投影维度                          |

```python
lstm = nn.LSTM(input_size=256, hidden_size=512, num_layers=2,
               batch_first=True, bidirectional=True)

x = torch.randn(32, 50, 256)     # (batch, seq, feature)
output, (h_n, c_n) = lstm(x)
# output: (32, 50, 1024)  双向 hidden_size * 2
# h_n:    (4, 32, 512)    num_layers * num_directions, batch, hidden

```

### 4.7 nn.MultiheadAttention

**参数：**

| 参数           | 类型    | 默认值   | 说明                           |
| ------------ | ----- | ----- | ---------------------------- |
| embed\_dim   | int   | 必填    | 总嵌入维度                        |
| num\_heads   | int   | 必填    | 头数（embed\_dim 须被整除）          |
| dropout      | float | 0.0   | 注意力权重 dropout                |
| bias         | bool  | True  | Q/K/V 投影偏置                   |
| batch\_first | bool  | False | True 时输入为 (batch, seq, feat) |

```python
attn = nn.MultiheadAttention(embed_dim=512, num_heads=8, batch_first=True)
q = k = v = torch.randn(32, 50, 512)
out, weights = attn(q, k, v)    # out: (32, 50, 512)

```

---

## 五、激活函数

| 函数         | nn 模块         | F 函数           | 说明                             |
| ---------- | ------------- | -------------- | ------------------------------ |
| ReLU       | nn.ReLU       | F.relu         | max(0, x)，最常用                  |
| GELU       | nn.GELU       | F.gelu         | Transformer 常用                 |
| SiLU/Swish | nn.SiLU       | F.silu         | x \* sigmoid(x)                |
| Sigmoid    | nn.Sigmoid    | F.sigmoid      | 二分类输出                          |
| Tanh       | nn.Tanh       | F.tanh         | RNN 隐状态                        |
| Softmax    | nn.Softmax    | F.softmax      | 多分类输出（dim 必须指定）                |
| LogSoftmax | nn.LogSoftmax | F.log\_softmax | 配合 NLLLoss                     |
| LeakyReLU  | nn.LeakyReLU  | F.leaky\_relu  | 负半轴斜率 negative\_slope（默认 0.01） |

```python
# nn 模块作为层（有状态的放 Sequential 中）
act = nn.ReLU(inplace=True)   # inplace=True 节省内存，不能用在需要保留输入的场合

# F 函数用在 forward 中
def forward(self, x):
    return F.gelu(self.fc(x))

```

---

## 六、损失函数

### 6.1 常用损失

| 损失函数                 | 场景            | 说明                    |
| -------------------- | ------------- | --------------------- |
| nn.CrossEntropyLoss  | 多分类           | 内置 Softmax，输入为 logits |
| nn.BCEWithLogitsLoss | 二分类/多标签       | 内置 Sigmoid，输入为 logits |
| nn.MSELoss           | 回归            | 均方误差                  |
| nn.L1Loss            | 回归            | 平均绝对误差，对异常值鲁棒         |
| nn.SmoothL1Loss      | 目标检测回归        | Huber Loss            |
| nn.NLLLoss           | 配合 LogSoftmax | 负对数似然                 |
| nn.KLDivLoss         | 分布对齐          | KL 散度，输入须为 log 概率     |

### 6.2 CrossEntropyLoss 参数

| 参数               | 类型     | 默认值    | 说明                      |
| ---------------- | ------ | ------ | ----------------------- |
| weight           | Tensor | None   | 各类别权重（处理类别不平衡）          |
| ignore\_index    | int    | \-100  | 忽略某个类别标签（如 padding）     |
| reduction        | str    | "mean" | "none" / "sum" / "mean" |
| label\_smoothing | float  | 0.0    | 标签平滑（正则化，推荐 0.1）        |

```python
criterion = nn.CrossEntropyLoss(label_smoothing=0.1, ignore_index=0)

# 输入：logits (N, C) 或 (N, C, H, W)，目标：类别索引 (N,) 或 (N, H, W)
logits = torch.randn(32, 10)
targets = torch.randint(0, 10, (32,))
loss = criterion(logits, targets)

```

---

## 七、优化器

### 7.1 常用优化器

| 优化器           | 说明                                 |
| ------------- | ---------------------------------- |
| optim.SGD     | 随机梯度下降，支持 momentum 和 weight\_decay |
| optim.Adam    | 自适应学习率，一般首选                        |
| optim.AdamW   | Adam + 正确的 weight decay（推荐替代 Adam） |
| optim.RMSprop | 适合 RNN                             |
| optim.Adagrad | 稀疏梯度任务                             |

### 7.2 SGD 参数

| 参数            | 类型       | 默认值   | 说明                       |
| ------------- | -------- | ----- | ------------------------ |
| params        | iterable | 必填    | 模型参数（model.parameters()） |
| lr            | float    | 必填    | 学习率                      |
| momentum      | float    | 0     | 动量系数                     |
| weight\_decay | float    | 0     | L2 正则化系数                 |
| nesterov      | bool     | False | Nesterov 动量              |
| dampening     | float    | 0     | 动量抑制                     |

### 7.3 AdamW 参数

| 参数            | 类型       | 默认值          | 说明                            |
| ------------- | -------- | ------------ | ----------------------------- |
| params        | iterable | 必填           | 模型参数                          |
| lr            | float    | 1e-3         | 学习率                           |
| betas         | tuple    | (0.9, 0.999) | 一阶/二阶矩衰减系数                    |
| eps           | float    | 1e-8         | 数值稳定项                         |
| weight\_decay | float    | 1e-2         | 权重衰减（AdamW 的 decoupled decay） |
| amsgrad       | bool     | False        | 使用 AMSGrad 变体                 |

```python
import torch.optim as optim

# 全局参数
optimizer = optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2)

# 分组参数（不同层不同 lr）
optimizer = optim.AdamW([
    {"params": model.backbone.parameters(), "lr": 1e-4},
    {"params": model.head.parameters(), "lr": 1e-3},
], weight_decay=1e-2)

# 训练循环中
optimizer.zero_grad()
loss.backward()
optimizer.step()

```

---

## 八、学习率调度器

```python
from torch.optim import lr_scheduler

```

| 调度器                                                           | 说明                            |
| ------------------------------------------------------------- | ----------------------------- |
| StepLR(optimizer, step\_size, gamma)                          | 每 step\_size 个 epoch 乘以 gamma |
| MultiStepLR(optimizer, milestones, gamma)                     | 在指定 epoch 降低 lr               |
| ExponentialLR(optimizer, gamma)                               | 每 epoch 乘以 gamma              |
| CosineAnnealingLR(optimizer, T\_max)                          | 余弦退火，T\_max 为半周期              |
| CosineAnnealingWarmRestarts(optimizer, T\_0)                  | 带重启的余弦退火                      |
| ReduceLROnPlateau(optimizer, mode, factor, patience)          | 验证指标停滞时降低 lr                  |
| OneCycleLR(optimizer, max\_lr, total\_steps)                  | 1Cycle 策略（训练超级快）              |
| LinearLR(optimizer, start\_factor, end\_factor, total\_iters) | 线性变化                          |
| SequentialLR(optimizer, schedulers, milestones)               | 串联多个调度器                       |

```python
scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)

for epoch in range(100):
    train(...)
    scheduler.step()           # epoch-based 调度器每 epoch 调用一次
    print(scheduler.get_last_lr())

# OneCycleLR 在每个 batch 后调用
scheduler = lr_scheduler.OneCycleLR(optimizer, max_lr=1e-2,
                                     total_steps=len(train_loader) * epochs)
for batch in train_loader:
    ...
    optimizer.step()
    scheduler.step()

# Warmup + Cosine：用 SequentialLR
warmup = lr_scheduler.LinearLR(optimizer, start_factor=0.01, total_iters=5)
cosine = lr_scheduler.CosineAnnealingLR(optimizer, T_max=95)
scheduler = lr_scheduler.SequentialLR(optimizer, [warmup, cosine], milestones=[5])

```

---

## 九、数据处理

### 9.1 Dataset

```python
from torch.utils.data import Dataset, DataLoader

class ImageDataset(Dataset):
    def __init__(self, paths, labels, transform=None):
        self.paths = paths
        self.labels = labels
        self.transform = transform

    def __len__(self):
        return len(self.paths)

    def __getitem__(self, idx):
        img = Image.open(self.paths[idx]).convert("RGB")
        if self.transform:
            img = self.transform(img)
        return img, self.labels[idx]

```

PyTorch 内置 Dataset：`torchvision.datasets.ImageFolder`、`torchvision.datasets.CIFAR10`、`torchvision.datasets.MNIST` 等。

### 9.2 DataLoader 参数

| 参数                  | 类型       | 默认值   | 说明                       |
| ------------------- | -------- | ----- | ------------------------ |
| dataset             | Dataset  | 必填    | 数据集                      |
| batch\_size         | int      | 1     | 批次大小                     |
| shuffle             | bool     | False | 是否打乱（训练集设 True）          |
| num\_workers        | int      | 0     | 数据加载并行进程数                |
| pin\_memory         | bool     | False | 将数据固定到内存（GPU 训练时加速）      |
| drop\_last          | bool     | False | 丢弃最后一个不完整批次              |
| collate\_fn         | callable | None  | 自定义批次组装函数                |
| sampler             | Sampler  | None  | 自定义采样策略（与 shuffle 互斥）    |
| prefetch\_factor    | int      | 2     | 每个 worker 预取的批次数         |
| persistent\_workers | bool     | False | 保持 worker 存活（多 epoch 加速） |

```python
loader = DataLoader(
    dataset,
    batch_size=32,
    shuffle=True,
    num_workers=4,
    pin_memory=True,
    persistent_workers=True,
)

```

### 9.3 torchvision.transforms

```python
from torchvision import transforms

train_transform = transforms.Compose([
    transforms.RandomResizedCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ColorJitter(brightness=0.4, contrast=0.4, saturation=0.4),
    transforms.ToTensor(),                    # PIL/ndarray -> Tensor，[0,255]->[0,1]
    transforms.Normalize(                     # 减均值除标准差
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225]
    ),
])

val_transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

```

**v2 API（推荐，支持 Tensor 输入和更多数据增强）：**

```python
from torchvision.transforms import v2

transform = v2.Compose([
    v2.RandomResizedCrop(224, antialias=True),
    v2.RandomHorizontalFlip(),
    v2.ToDtype(torch.float32, scale=True),
    v2.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

```

---

## 十、完整训练流程

```python
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torch.optim import AdamW
from torch.optim.lr_scheduler import CosineAnnealingLR

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

model = MyModel().to(device)
optimizer = AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2)
scheduler = CosineAnnealingLR(optimizer, T_max=num_epochs)
criterion = nn.CrossEntropyLoss()

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True,
                          num_workers=4, pin_memory=True)
val_loader = DataLoader(val_dataset, batch_size=128, shuffle=False, num_workers=4)

def train_epoch(model, loader, optimizer, criterion, device):
    model.train()
    total_loss = 0.0
    correct = 0

    for images, labels in loader:
        images, labels = images.to(device, non_blocking=True), labels.to(device, non_blocking=True)

        optimizer.zero_grad()
        logits = model(images)
        loss = criterion(logits, labels)
        loss.backward()

        nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)  # 梯度裁剪
        optimizer.step()

        total_loss += loss.item() * images.size(0)
        correct += (logits.argmax(1) == labels).sum().item()

    return total_loss / len(loader.dataset), correct / len(loader.dataset)

@torch.no_grad()
def evaluate(model, loader, criterion, device):
    model.eval()
    total_loss = 0.0
    correct = 0

    for images, labels in loader:
        images, labels = images.to(device), labels.to(device)
        logits = model(images)
        loss = criterion(logits, labels)
        total_loss += loss.item() * images.size(0)
        correct += (logits.argmax(1) == labels).sum().item()

    return total_loss / len(loader.dataset), correct / len(loader.dataset)

best_acc = 0.0
for epoch in range(num_epochs):
    train_loss, train_acc = train_epoch(model, train_loader, optimizer, criterion, device)
    val_loss, val_acc = evaluate(model, val_loader, criterion, device)
    scheduler.step()

    print(f"Epoch {epoch+1}: train_loss={train_loss:.4f} train_acc={train_acc:.4f} "
          f"val_loss={val_loss:.4f} val_acc={val_acc:.4f}")

    if val_acc > best_acc:
        best_acc = val_acc
        torch.save(model.state_dict(), "best_model.pth")

```

---

## 十一、模型保存与加载

### 11.1 保存/加载 state\_dict（推荐）

```python
# 保存
torch.save(model.state_dict(), "model.pth")

# 加载
model = MyModel()
model.load_state_dict(torch.load("model.pth", map_location="cpu"))
model.eval()

```

`map_location="cpu"` 将 GPU 上保存的模型加载到 CPU，避免跨设备问题。

### 11.2 保存完整检查点

```python
checkpoint = {
    "epoch": epoch,
    "model": model.state_dict(),
    "optimizer": optimizer.state_dict(),
    "scheduler": scheduler.state_dict(),
    "best_acc": best_acc,
}
torch.save(checkpoint, "checkpoint.pth")

# 恢复
ckpt = torch.load("checkpoint.pth", map_location=device)
model.load_state_dict(ckpt["model"])
optimizer.load_state_dict(ckpt["optimizer"])
scheduler.load_state_dict(ckpt["scheduler"])
start_epoch = ckpt["epoch"] + 1

```

### 11.3 TorchScript 导出

```python
scripted = torch.jit.script(model)
scripted.save("model_scripted.pt")
loaded = torch.jit.load("model_scripted.pt")

```

### 11.4 ONNX 导出

```python
dummy_input = torch.randn(1, 3, 224, 224).to(device)
torch.onnx.export(
    model, dummy_input, "model.onnx",
    input_names=["input"], output_names=["output"],
    dynamic_axes={"input": {0: "batch"}, "output": {0: "batch"}},
    opset_version=17,
)

```

---

## 十二、混合精度训练（AMP）

混合精度（FP16/BF16）在不损失精度的前提下降低显存、加速训练。

```python
from torch.cuda.amp import autocast, GradScaler

scaler = GradScaler()

for images, labels in train_loader:
    images, labels = images.to(device), labels.to(device)

    optimizer.zero_grad()
    with autocast():              # 自动选择 FP16 操作
        logits = model(images)
        loss = criterion(logits, labels)

    scaler.scale(loss).backward()
    scaler.unscale_(optimizer)
    nn.utils.clip_grad_norm_(model.parameters(), 1.0)
    scaler.step(optimizer)
    scaler.update()

```

BF16（Ampere 及以上架构更稳定）：

```python
with autocast(dtype=torch.bfloat16):
    ...

```

---

## 十三、迁移学习

```python
import torchvision.models as models

# 加载预训练模型
backbone = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)

# 冻结主干参数
for param in backbone.parameters():
    param.requires_grad = False

# 替换分类头
backbone.fc = nn.Linear(backbone.fc.in_features, num_classes)

# 只有分类头参与训练
optimizer = AdamW(backbone.fc.parameters(), lr=1e-3)

# 微调：解冻后使用更小学习率
for param in backbone.parameters():
    param.requires_grad = True
optimizer = AdamW([
    {"params": backbone.layer4.parameters(), "lr": 1e-4},
    {"params": backbone.fc.parameters(), "lr": 1e-3},
])

```

---

## 十四、实用工具

### 14.1 torch.nn.functional

`F` 函数是无状态的纯函数，适合在 `forward` 中调用：

```python
import torch.nn.functional as F

F.relu(x)
F.gelu(x)
F.softmax(x, dim=-1)
F.log_softmax(x, dim=-1)
F.cross_entropy(logits, targets)
F.binary_cross_entropy_with_logits(logits, targets)
F.mse_loss(pred, target)
F.dropout(x, p=0.5, training=self.training)   # 注意传 training 参数
F.pad(x, pad=(1, 1, 1, 1))     # (左, 右, 上, 下)
F.interpolate(x, size=(H, W), mode="bilinear", align_corners=False)
F.normalize(x, p=2, dim=1)     # L2 归一化

```

### 14.2 梯度裁剪

```python
nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)   # L2 范数裁剪
nn.utils.clip_grad_value_(model.parameters(), clip_value=0.5) # 按值裁剪

```

### 14.3 随机种子固定

```python
def set_seed(seed: int = 42):
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    import numpy as np, random
    np.random.seed(seed)
    random.seed(seed)
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

```

### 14.4 torch.compile（PyTorch 2.0+）

```python
model = torch.compile(model)   # 图编译，自动优化，首次运行有编译开销

```

### 14.5 profiling

```python
from torch.profiler import profile, record_function, ProfilerActivity

with profile(activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
             record_shapes=True) as prof:
    with record_function("model_inference"):
        model(x)

print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10))

```

---

## 十五、最佳实践

- **设备管理**：用 `device = torch.device("cuda" if torch.cuda.is_available() else "cpu")` 统一管理，所有 tensor 和模型调用 `.to(device)`，DataLoader 设置 `pin_memory=True` 后在训练循环中用 `non_blocking=True` 传输。
- **梯度清零**：`optimizer.zero_grad(set_to_none=True)` 比 `zero_grad()` 更高效（直接释放梯度内存）。
- **`model.eval()` \+ `torch.no_grad()`**：评估和推断时两者缺一不可，`eval()` 改变 BN/Dropout 行为，`no_grad()` 节省内存。
- **避免 `.item()` 在循环内频繁调用**：每次 `.item()` 会同步 CPU/GPU，应累积后再统计。
- **使用 `persistent_workers=True`**：多 epoch 训练时避免每 epoch 重建 worker 进程。
- **AdamW 替代 Adam**：Adam 的 L2 正则化实现有误（混入自适应学习率），AdamW 是修复版。
- **权重初始化**：PyTorch 对大多数层有合理默认初始化，自定义时用 `nn.init.kaiming_normal_`（ReLU）或 `nn.init.xavier_uniform_`（Tanh/Sigmoid）。

---

## 十六、常见陷阱

### 忘记调用 optimizer.zero\_grad()

```python
# 错误：梯度累积，结果不对
for batch in loader:
    loss = criterion(model(x), y)
    loss.backward()
    optimizer.step()

# 正确
for batch in loader:
    optimizer.zero_grad()
    loss = criterion(model(x), y)
    loss.backward()
    optimizer.step()

```

### 评估时忘记 model.eval()

```python
# 错误：BN 和 Dropout 处于训练模式
with torch.no_grad():
    preds = model(val_x)

# 正确
model.eval()
with torch.no_grad():
    preds = model(val_x)
model.train()   # 评估结束后恢复训练模式

```

### inplace 操作破坏计算图

```python
# 错误：inplace 操作会破坏 autograd
x = x.relu_()   # 如果 x 是中间变量

# 正确：非 inplace
x = F.relu(x)

# ReLU(inplace=True) 在 Sequential 中通常安全，但用于 residual 连接时须谨慎

```

### CPU/GPU tensor 混合运算

```python
# 错误：a 在 GPU，b 在 CPU
loss = a + b

# 正确
b = b.to(a.device)

```

### DataLoader num\_workers 与 Windows 的问题

在 Windows 上多进程 DataLoader 需要在 `if __name__ == "__main__":` 保护下运行，否则会导致无限 fork。

```python
# Windows 上
if __name__ == "__main__":
    loader = DataLoader(dataset, num_workers=4)
    for batch in loader:
        ...

```

### .item() 阻断 GPU 流水线

```python
# 低效：每个 batch 都强制 GPU/CPU 同步
for batch in loader:
    loss = criterion(model(x), y)
    running_loss += loss.item()   # 同步点

# 更好：累积 tensor，最后统一转换
losses = []
for batch in loader:
    loss = criterion(model(x), y)
    losses.append(loss.detach())
total_loss = torch.stack(losses).mean().item()

```

### CrossEntropyLoss 输入不是 logits

```python
# 错误：对 softmax 输出再用 CrossEntropyLoss（双重 softmax）
probs = F.softmax(logits, dim=-1)
loss = F.cross_entropy(probs, targets)

# 正确：直接传 logits
loss = F.cross_entropy(logits, targets)

```

---

## 最佳实践

**训练循环统一调用 `optimizer.zero_grad(set_to_none=True)`**：`set_to_none=True`（PyTorch 2.0+ 默认）将梯度置为 `None` 而非全零，节省内存且在下一次 `backward` 时直接分配新张量，比 `zero_grad()` 更高效。

**用 `torch.compile()` 加速模型（PyTorch 2.0+）**：一行代码即可将模型编译为优化的内核，多数场景有 10–30% 的训练吞吐提升，对推理提升更明显。首次调用有编译开销，适合长时间训练任务。

```python
model = MyModel().to(device)
model = torch.compile(model)   # 透明包装，接口不变

```

**数据加载器用 `pin_memory=True` \+ `num_workers`**：`pin_memory=True` 让数据预先锁定在内存，GPU 复制更快；`num_workers` 设为 CPU 核数的一半（通常 4–8），避免 GIL 瓶颈。Windows 下 `num_workers` 必须在 `if __name__ == '__main__'` 保护下使用。

**用 `torch.no_grad()` 包裹推理代码**：推理阶段不需要计算梯度，`with torch.no_grad()` 禁用自动微分图构建，降低内存占用并提速约 30%；评估时还需额外调用 `model.eval()` 关闭 Dropout 和 BatchNorm 的训练行为。

**模型保存用 `state_dict` 而非整个模型**：`torch.save(model.state_dict(), path)` 只保存权重，与模型类定义解耦，迁移和版本管理更灵活；加载时用 `model.load_state_dict(torch.load(path, weights_only=True))`，`weights_only=True` 避免反序列化任意代码。

---

## 常见陷阱

### 陷阱：忘记调用 `optimizer.zero_grad()` 导致梯度累积

**现象：** 损失异常偏高，训练不收敛，或某些步骤梯度爆炸。  
**原因：** PyTorch 默认将梯度累加到 `.grad` 属性，若每步训练前不清零，梯度会在多个批次间叠加。  
**解决：** 在每次 `loss.backward()` 之前调用 `optimizer.zero_grad()`；仅在需要梯度累积（模拟大批次）时故意跳过清零，但须明确计数 N 步后再 `step()`。

### 陷阱：`loss.backward()` 报错 `Trying to backward through the graph a second time`

**现象：** 第二次调用 `loss.backward()` 时抛出 RuntimeError。  
**原因：** PyTorch 默认在 `backward` 后释放中间计算图节点。若需多次反传（如 GAN 中生成器和判别器分别反传），需保留计算图。  
**解决：** 第一次调用时传 `retain_graph=True`：`loss.backward(retain_graph=True)`；或重构训练循环，每次从前向传播重新构建计算图。

### 陷阱：CPU 张量与 GPU 张量混用导致 `RuntimeError: expected device`

**现象：** 模型在 CUDA 上，输入张量在 CPU，前向传播时报设备不匹配错误。  
**原因：** PyTorch 不会自动跨设备移动张量，所有参与计算的张量必须在同一设备上。  
**解决：** 统一用 `tensor.to(device)` 或 `tensor.cuda()` 移动张量，在数据加载循环中确保每个批次都移动到目标设备：`x, y = x.to(device), y.to(device)`。

---

## 参见

[Pandas完全指南](https://blog.vercanti.com/pandas-wan-quan-zhi-nan/)  
[asyncio异步编程完全指南](https://blog.vercanti.com/asyncio-yi-bu-bian-cheng-wan-quan-zhi-nan/)  
[scikit-learn完全指南](https://blog.vercanti.com/scikit-learn-wan-quan-zhi-nan/)