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)。
importtorchimporttorch.nnasnnclassLinearRegressionModel(nn.Module):def__init__(self):super().__init__()# 定义层:输入特征1个,输出特征1个# y = w * x + bself.linear=nn.Linear(in_features=1,out_features=1)defforward(self,x):# 定义前向传播路径out=self.linear(x)returnout 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:自适应学习率,快且收敛好(推荐)。
importtorch.optimasoptim criterion=nn.MSELoss()optimizer=optim.SGD(model.parameters(),lr=0.01)三、完整的训练循环 (重构昨日代码)
看看代码变得多么简洁:
x_train=torch.rand(100,1)*10y_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. 训练循环forepochinrange(1000):# 前向y_pred=model(x_train)loss=criterion(y_pred,y_train)# 反向 (标准三步走)optimizer.zero_grad()# 清零loss.backward()# 求导optimizer.step()# 更新ifepoch%100==0:print(f"Epoch{epoch}, Loss:{loss.item():.4f}")# 查看学习到的参数# model.parameters() 是一个生成器forname,paraminmodel.named_parameters():print(name,param.item())四、实战:手写数字识别 (MNIST)
这是深度学习界的"Hello World"。我们要训练一个网络,识别 0-9 的手写数字图片。
4.1 加载数据 (Torchvision)
PyTorch 提供了torchvision库,内置了常用数据集。
importtorchimporttorch.nnasnnimporttorch.optimasoptimfromtorchvisionimportdatasets,transformsfromtorch.utils.dataimportDataLoader# 数据预处理:转为 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 个分类的概率。
classMLP(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()# 激活函数defforward(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)returnx model=MLP()4.3 训练与测试
device=torch.device("cuda"iftorch.cuda.is_available()else"cpu")model.to(device)optimizer=optim.Adam(model.parameters(),lr=0.001)criterion=nn.CrossEntropyLoss()deftrain(epoch):model.train()# 切换到训练模式forbatch_idx,(data,target)inenumerate(train_loader):data,target=data.to(device),target.to(device)optimizer.zero_grad()output=model(data)loss=criterion(output,target)loss.backward()optimizer.step()ifbatch_idx%100==0:print(f"Epoch{epoch}[{batch_idx*len(data)}/{len(train_loader.dataset)}] Loss:{loss.item():.6f}")deftest():model.eval()# 切换到评估模式test_loss=0correct=0withtorch.no_grad():fordata,targetintest_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 个 Epochforepochinrange(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 - 小程序后端开发上(待更新)