深度学习入门(PyTorch)
神经网络是如何工作的?用 PyTorch 从零构建第一个深度学习模型,理解张量、前向传播、反向传播和训练循环的本质。
深度学习核心术语
- Tensor(张量) 多维数组,PyTorch 的基本数据单元。0维张量是标量,1维是向量,2维是矩阵,3维及以上叫张量。与 NumPy ndarray 的主要区别是 Tensor 支持 GPU 计算和自动微分。
- 计算图(Computational Graph) 记录张量运算历史的有向无环图(DAG)。PyTorch 使用动态计算图——每次前向传播都即时构建计算图,使得 Python 控制流(if/for)可以改变网络结构,调试更直观。
-
自动微分(Autograd)
PyTorch 自动计算梯度的机制。在
requires_grad=True的 Tensor 上执行运算后,调用.backward()即可自动计算所有参与运算的 Tensor 的梯度,无需手动推导链式法则。 -
nn.Module
PyTorch 神经网络模块的基类。所有网络结构都继承它,获得参数管理(
.parameters())、设备转移(.to(device))、训练/评估模式切换(.train()/.eval())等能力。 -
优化器(Optimizer)
根据梯度更新模型参数的算法。常用 Adam(自适应学习率,大多数情况的默认选择)、SGD with Momentum(图像任务)。优化器持有对模型参数的引用,调用
step()即完成参数更新。 -
损失函数(Loss Function)
衡量模型预测与真实标签差距的函数。分类任务常用
CrossEntropyLoss,回归任务常用MSELoss。损失值越小,模型越准确。
Tensor —— PyTorch 的核心数据结构
PyTorch 的 Tensor 本质上是 NumPy ndarray 的升级版:支持 GPU 加速,并且记录计算历史用于自动微分。你已经会 NumPy,PyTorch Tensor 几乎零成本上手。
import torch
import torch.nn as nn
import numpy as np
# 创建 Tensor(语法几乎和 NumPy 一样)
t1 = torch.tensor([1.0, 2.0, 3.0])
t2 = torch.zeros(3, 4)
t3 = torch.ones(2, 3)
t4 = torch.randn(4, 4) # 标准正态分布(权重初始化常用)
t5 = torch.arange(0, 10)
# Tensor 与 NumPy 互转
np_arr = np.array([1, 2, 3])
tensor = torch.from_numpy(np_arr) # NumPy → Tensor
back = tensor.numpy() # Tensor → NumPy
# GPU 加速(有 NVIDIA GPU 时)
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"使用: {device}")
t4 = t4.to(device) # 把 Tensor 移到 GPU
# 形状操作(和 NumPy 几乎一样)
x = torch.randn(32, 28, 28)
print(x.shape) # torch.Size([32, 28, 28])
x_flat = x.view(32, -1) # (32, 784)
x_flat = x.reshape(32, -1) # 等价写法
自动微分(Autograd)
PyTorch 最神奇的功能是自动微分:只需写前向传播代码,PyTorch 会自动计算所有参数的梯度。这是神经网络反向传播的核心,也是 PyTorch 比 NumPy 强大得多的地方。
梯度(Gradient)与反向传播
梯度告诉你:如果稍微增加某个参数,损失会增加还是减少?反向传播通过链式法则从损失出发,计算网络中每个参数的梯度。然后用梯度下降法(参数 -= 学习率 × 梯度)更新参数,让损失逐渐减小。
# requires_grad=True 告诉 PyTorch:需要计算这个 Tensor 的梯度
x = torch.tensor(2.0, requires_grad=True)
w = torch.tensor(3.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)
# 前向传播:y = w*x + b
y = w * x + b # 7.0
loss = (y - 5.0) ** 2 # (7-5)² = 4.0(假设目标是5)
# 反向传播:自动计算所有梯度
loss.backward()
# 查看梯度(d_loss/d_w = 2*(y-5)*x = 2*2*2 = 8)
print(f"w 的梯度: {w.grad}") # 8.0
print(f"b 的梯度: {b.grad}") # 4.0
# 用梯度更新参数(梯度下降)
lr = 0.1
with torch.no_grad(): # 更新时不需要跟踪梯度
w -= lr * w.grad
b -= lr * b.grad
print(f"更新后 w={w.item():.2f}, b={b.item():.2f}")
构建神经网络
PyTorch 用 nn.Module 来定义网络结构。继承它,在 __init__ 里定义各层,在 forward 里定义数据流向——这就是第 6 章 OOP 的真实应用!
import torch
import torch.nn as nn
import torch.nn.functional as F
class MLP(nn.Module):
"""多层感知机(全连接神经网络)"""
def __init__(self, input_dim, hidden_dim, output_dim):
super().__init__()
# nn.Linear:全连接层,参数会自动注册和更新
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.dropout = nn.Dropout(0.3) # 防过拟合
def forward(self, x):
# ReLU:激活函数,引入非线性
x = F.relu(self.fc1(x))
x = self.dropout(x)
x = F.relu(self.fc2(x))
x = self.fc3(x) # 最后一层不加激活(交给损失函数处理)
return x
# 创建模型
model = MLP(input_dim=784, hidden_dim=128, output_dim=10)
print(model)
# 统计参数数量
total_params = sum(p.numel() for p in model.parameters())
print(f"总参数量: {total_params:,}")
# 前向传播测试
x = torch.randn(32, 784) # 模拟 32 张 MNIST 图像
output = model(x)
print(f"输出形状: {output.shape}") # (32, 10)
完整训练循环
PyTorch 的训练流程是显式的:你自己写训练循环,每一步都清清楚楚。这比 scikit-learn 的 fit() 更底层,但也更灵活、更容易理解深度学习的本质。
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
# 准备数据(实际项目用真实数据集)
X_train = torch.randn(1000, 20)
y_train = (X_train[:, 0] > 0).long() # 二分类标签
dataset = TensorDataset(X_train, y_train)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
# 初始化模型、损失函数、优化器
model = MLP(20, 64, 2)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 训练循环
for epoch in range(20):
model.train() # 切换到训练模式
total_loss = 0
for batch_x, batch_y in dataloader:
# 1. 清零梯度(上一步留下的)
optimizer.zero_grad()
# 2. 前向传播
logits = model(batch_x)
loss = criterion(logits, batch_y)
# 3. 反向传播(计算梯度)
loss.backward()
# 4. 更新参数
optimizer.step()
total_loss += loss.item()
avg_loss = total_loss / len(dataloader)
if (epoch + 1) % 5 == 0:
print(f"Epoch [{epoch+1:2d}/20] Loss: {avg_loss:.4f}")
# 推理(评估/预测)
model.eval()
with torch.no_grad(): # 不计算梯度,节省内存
test_x = torch.randn(100, 20)
test_y = (test_x[:, 0] > 0).long()
outputs = model(test_x)
preds = outputs.argmax(dim=1)
acc = (preds == test_y).float().mean()
print(f"测试准确率: {acc:.4f}")
四步训练循环记住了吗?
PyTorch 训练的四个固定步骤:①zero_grad() → ②前向传播 → ③loss.backward() → ④optimizer.step()。这个顺序不能错,特别是每次都要先清零梯度,否则梯度会累加。背下这四步,任何神经网络都能训练。
保存与加载模型
# 保存方式1:只保存权重(推荐)
torch.save(model.state_dict(), "model_weights.pt")
# 加载权重
new_model = MLP(20, 64, 2) # 先创建同结构的模型
new_model.load_state_dict(torch.load("model_weights.pt"))
new_model.eval()
# 保存方式2:保存整个模型(包括结构)
torch.save(model, "full_model.pt")
loaded = torch.load("full_model.pt")
# 保存训练检查点(含优化器状态,方便继续训练)
torch.save({
"epoch": epoch,
"model_state_dict": model.state_dict(),
"optimizer_state_dict": optimizer.state_dict(),
"loss": avg_loss,
}, "checkpoint.pt")
# 从检查点恢复
checkpoint = torch.load("checkpoint.pt")
model.load_state_dict(checkpoint["model_state_dict"])
optimizer.load_state_dict(checkpoint["optimizer_state_dict"])
start_epoch = checkpoint["epoch"] + 1
常见误区与高级技巧
忘记 optimizer.zero_grad() 导致梯度累加
PyTorch 的梯度是累加的,不是每次 backward 后自动清零。如果不在每个 batch 前调用 optimizer.zero_grad(),梯度会越来越大,训练会出错。唯一的例外是梯度累积(Gradient Accumulation)——刻意跳过几个 batch 的 zero_grad 来模拟更大的 batch size。
忘记 model.eval() 导致测试结果不稳定
Dropout 和 BatchNorm 在训练和推理时行为不同:Dropout 训练时随机丢弃神经元,推理时关闭;BatchNorm 训练时用批次统计量,推理时用运行均值。不切换 model.eval(),每次推理结果会不一样。
import torch
from torch.cuda.amp import GradScaler, autocast
# 混合精度训练:用 float16 做前向传播,float32 做参数更新
# 显存节省 ~50%,速度提升 ~2x(在支持 Tensor Core 的 GPU 上)
scaler = GradScaler() # 防止 float16 下梯度下溢
for batch_x, batch_y in dataloader:
optimizer.zero_grad()
# 自动使用 float16 进行前向传播
with autocast():
logits = model(batch_x)
loss = criterion(logits, batch_y)
# scaler 自动处理 float16 的梯度缩放
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
# 使用学习率调度器(Scheduler)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(
optimizer, T_max=50 # 余弦退火,每50轮完成一个周期
)
for epoch in range(100):
# ... 训练循环
scheduler.step() # 每 epoch 后更新学习率
print(f"LR: {scheduler.get_last_lr()[0]:.6f}")
本章小结
PyTorch 深度学习核心要素:Tensor(GPU 加速的多维数组)、Autograd(自动微分)、nn.Module(网络定义基类)、DataLoader(批量数据加载)、优化器+损失函数+训练循环。四步训练循环口诀:清零梯度 → 前向传播 → 反向传播 → 更新参数(zero_grad → forward → backward → step)。常见陷阱:忘记 zero_grad(梯度累加)、推理时忘记 eval() 和 no_grad()(Dropout/BN 行为不对,浪费计算)。进阶技术:混合精度训练(节省显存2倍)、学习率调度器(余弦退火等)、梯度裁剪(防止梯度爆炸)。