Day 79:【99天精通Python】深度学习入门 (PyTorch) 下篇 - 搭建神经网络 (nn.Module)
Day 79:【99天精通Python】深度学习入门 (PyTorch) 下篇 - 搭建神经网络 (nn.Module)
前言
欢迎来到第79天!
在昨天的课程中,我们手动管理权重 w 和 b,手动计算 Loss,手动更新梯度。这对于理解原理很有帮助,但如果要搭建一个几百层的神经网络,这样写非得累死。
PyTorch 提供了强大的 torch.nn 模块,它封装了各种神经网络层(全连接层、卷积层、循环层)和损失函数。我们只需要像搭积木一样把它们拼在一起,就能构建出复杂的深度学习模型。
本节内容:
nn.Module:神经网络的基类- 常用层:
nn.Linear,nn.ReLU - 损失函数:
nn.MSELoss,nn.CrossEntropyLoss - 优化器:
torch.optim(SGD, Adam) - 实战练习:手写数字识别 (MNIST)
一、搭建神经网络:搭积木
在 PyTorch 中,所有的神经网络都必须继承 nn.Module 类。
1.1 定义模型结构
假设我们要解决昨天的线性回归问题 y = wx + b,这在神经网络中就是一个全连接层 (Linear Layer)。
import torch
import torch.nn as nn
class LinearRegressionModel(nn.Module):
def __init__(self):
super().__init__()
# 定义层:输入特征1个,输出特征1个
# y = w * x + b
self.linear = nn.Linear(in_features=1, out_features=1)
def forward(self, x):
# 定义前向传播路径
out = self.linear(x)
return out
model = LinearRegressionModel()
print(model)
二、损失函数与优化器
2.1 损失函数 (Loss Function)
不需要手写 (y-y_pred)**2 了,直接调用:
- 回归问题:
nn.MSELoss()(均方误差) - 分类问题:
nn.CrossEntropyLoss()(交叉熵)
2.2 优化器 (Optimizer)
不需要手写 w -= lr * w.grad 了,优化器帮我们管理所有参数的更新。
- SGD (随机梯度下降):最基础,稳但慢。
- Adam:自适应学习率,快且收敛好(推荐)。
import torch.optim as optim
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
三、完整的训练循环 (重构昨日代码)
看看代码变得多么简洁:
x_train = torch.rand(100, 1) * 10
y_train = 2 * x_train + 5 + torch.randn(100, 1) * 0.5
# 1. 实例化模型、损失、优化器
model = LinearRegressionModel()
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 2. 训练循环
for epoch in range(1000):
# 前向
y_pred = model(x_train)
loss = criterion(y_pred, y_train)
# 反向 (标准三步走)
optimizer.zero_grad() # 清零
loss.backward() # 求导
optimizer.step() # 更新
if epoch % 100 == 0:
print(f"Epoch {epoch}, Loss: {loss.item():.4f}")
# 查看学习到的参数
# model.parameters() 是一个生成器
for name, param in model.named_parameters():
print(name, param.item())
四、实战:手写数字识别 (MNIST)
这是深度学习界的"Hello World"。我们要训练一个网络,识别 0-9 的手写数字图片。
4.1 加载数据 (Torchvision)
PyTorch 提供了 torchvision 库,内置了常用数据集。
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 数据预处理:转为 Tensor,并归一化
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)) # MNIST的均值和标准差
])
# 下载数据集
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('./data', train=False, transform=transform)
# 数据加载器 (自动分批次 Batch)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)
4.2 定义网络 (多层感知机 MLP)
图片是 28x28 像素的,我们要把它拉平成 784 个特征,然后通过几层全连接层,最后输出 10 个分类的概率。
class MLP(nn.Module):
def __init__(self):
super().__init__()
# 定义层
self.fc1 = nn.Linear(784, 512)
self.fc2 = nn.Linear(512, 256)
self.fc3 = nn.Linear(256, 10) # 10分类
self.relu = nn.ReLU() # 激活函数
def forward(self, x):
# x.shape: [64, 1, 28, 28] -> 展平 -> [64, 784]
x = x.view(-1, 784)
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x) # 最后一层不需要激活(CrossEntropyLoss会处理Softmax)
return x
model = MLP()
4.3 训练与测试
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
optimizer = optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
def train(epoch):
model.train() # 切换到训练模式
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
if batch_idx % 100 == 0:
print(f"Epoch {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)}] Loss: {loss.item():.6f}")
def test():
model.eval() # 切换到评估模式
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += criterion(output, target).item()
# 获取预测结果 (最大概率的索引)
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
print(f'\nTest set: Accuracy: {correct}/{len(test_loader.dataset)} ({100. * correct / len(test_loader.dataset):.2f}%)\n')
# 运行 3 个 Epoch
for epoch in range(1, 4):
train(epoch)
test()
运行只需几分钟,准确率通常能达到 98% 以上!
五、常见问题
Q1:model.train() 和 model.eval() 有什么用?
有些层(如 Dropout, BatchNorm)在训练和测试时的行为是不一样的。
- 训练时:Dropout 会随机丢弃神经元。
- 测试时:Dropout 不工作,利用所有神经元。
所以必须手动切换模式。
Q2:nn.ReLU 是什么?
激活函数。如果没有它,多少层网络叠加最后都等价于一层线性变换(线性代数原理)。激活函数引入了非线性,让神经网络能拟合任意复杂的曲线。
Q3:view(-1, 784) 是什么?
相当于 NumPy 的 reshape。-1 表示自动推断 Batch Size。
六、小结
关键要点:
- nn.Module 是所有模型的父类。
- Optimizer 帮我们自动更新权重。
- DataLoader 帮我们自动分批处理数据。
- 三步走:
zero_grad->backward->step,背下来!
七、课后作业
- 保存模型:查阅
torch.save和torch.load,将训练好的 MNIST 模型保存到硬盘,并写一个新的脚本加载它进行预测。 - 卷积神经网络 (CNN):将 MLP 改为 CNN(使用
nn.Conv2d和nn.MaxPool2d)。CNN 提取图片特征的能力更强,准确率应该能达到 99%。 - CIFAR-10:MNIST 是黑白的,尝试挑战 CIFAR-10 数据集(彩色图片分类,飞机、汽车、鸟等)。
下节预告
Day 80:项目篇 - 微信小程序后端开发 (上) - 深度学习有点烧脑,我们换个口味。明天开始,我们将开发一个微信小程序的后端 API,打通手机与服务器的连接。
系列导航:
- 上一篇:Day 78 - 深度学习PyTorch上
- 下一篇:Day 80 - 小程序后端开发上(待更新)
更多推荐


所有评论(0)