PyTorch 神经网络从零搭建实战:基础概念与完整项目实现
·
PyTorch 神经网络从零搭建实战:基础概念与完整项目实现
摘要: 本文从神经网络基础概念出发,讲解 PyTorch 中 nn.Module 的使用方法,通过波士顿房价预测和手写数字识别两个完整实战项目,手把手教你搭建、训练、保存和加载神经网络模型。
适用读者:深度学习入门者 | 难度:入门 | 预计阅读时间:35 分钟
文章目录
一、前言
PyTorch 提供了 torch.nn 模块来方便地构建和训练神经网络。本文将从最基础的概念讲起,通过两个完整的实战项目,帮助你掌握 PyTorch 神经网络开发的完整流程。
学习完本文后,你将能够:
- 理解神经网络的核心组件(层、激活函数、损失函数、优化器)
- 使用
nn.Module自定义网络结构 - 完成从数据加载到模型保存的完整训练流程
- 加载已保存的模型进行推理
二、神经网络核心概念
2.1 什么是神经网络
神经网络是一种模仿生物神经系统结构的计算模型,由大量相互连接的"神经元"组成。每个神经元接收输入信号,经过加权求和与非线性变换后产生输出。
2.2 核心组件
| 组件 | 作用 | PyTorch 对应 |
|---|---|---|
| 层(Layer) | 对数据进行线性变换 | nn.Linear, nn.Conv2d |
| 激活函数 | 引入非线性 | nn.ReLU, nn.Sigmoid |
| 损失函数 | 衡量预测与真实值的差距 | nn.MSELoss, nn.CrossEntropyLoss |
| 优化器 | 更新模型参数 | torch.optim.Adam, torch.optim.SGD |
2.3 前向传播与反向传播
- 前向传播(Forward):数据从输入层经过各隐藏层到输出层,得到预测结果
- 反向传播(Backward):根据损失函数计算梯度,从输出层反向传播到输入层
- 参数更新:优化器根据梯度更新模型参数
三、PyTorch 网络搭建基础
3.1 继承 nn.Module
所有自定义网络都需要继承 torch.nn.Module 类:
import torch
import torch.nn as nn
class MyNet(nn.Module):
def __init__(self, n_input, n_output):
super(MyNet, self).__init__()
# 定义网络层
self.hidden = nn.Linear(n_input, 100)
self.predict = nn.Linear(100, n_output)
def forward(self, x):
# 定义前向传播逻辑
out = self.hidden(x)
out = torch.relu(out) # 激活函数
out = self.predict(out)
return out
3.2 使用 Sequential 快速搭建
对于简单的层堆叠,可以使用 nn.Sequential:
import torch.nn as nn
model = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 128),
nn.ReLU(),
nn.Linear(128, 10)
)
四、实战项目一:波士顿房价预测
这是一个经典的回归问题,我们将搭建一个简单的全连接网络来预测房价。
4.1 数据准备与网络定义
import torch
import numpy as np
# 读取数据
ff = open('data').readlines()
data = []
for item in ff:
out = item.strip().split(' ')
data.append(out)
data = np.array(data).astype(np.float32)
# 划分特征和标签
Y = data[:, -1]
X = data[:, 0:-1]
# 划分训练集和测试集
X_train = torch.tensor(X[0:496, ...], dtype=torch.float32)
Y_train = torch.tensor(Y[0:496, ...], dtype=torch.float32)
X_test = torch.tensor(X[496:, ...], dtype=torch.float32)
Y_test = torch.tensor(Y[496:, ...], dtype=torch.float32)
# 定义网络
class Net(torch.nn.Module):
def __init__(self, n_feature, n_output):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(n_feature, 100)
self.predict = torch.nn.Linear(100, n_output)
def forward(self, x):
out = self.hidden(x)
out = torch.relu(out)
out = self.predict(out)
return out
4.2 训练循环
net = Net(13, 1)
loss_func = torch.nn.MSELoss()
optimizer = torch.optim.Adam(net.parameters(), lr=0.01)
for i in range(10000):
# 前向传播
pred = net.forward(X_train)
pred = torch.squeeze(pred) # 去掉多余维度
loss = loss_func(pred, Y_train) * 0.001
# 反向传播与参数更新
optimizer.zero_grad() # 梯度归零
loss.backward() # 计算梯度
optimizer.step() # 更新参数
if i % 1000 == 0:
print('ite:{}, loss:{}'.format(i, loss.item()))
# 测试集评估
pred_test = net.forward(X_test)
pred_test = torch.squeeze(pred_test)
loss_test = loss_func(pred_test, Y_test) * 0.001
4.3 保存与加载模型
# 保存整个模型
torch.save(net, 'model.pkl')
# 加载模型
# net = torch.load('model.pkl')
# 推荐方式:只保存模型参数
torch.save(net.state_dict(), 'params.pkl')
# 加载参数
# net = Net(13, 1)
# net.load_state_dict(torch.load('params.pkl'))
五、实战项目二:手写数字识别(MNIST)
这是一个经典的分类问题,我们将使用 CNN(卷积神经网络)来识别手写数字。
5.1 数据加载
import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms
import torch.utils.data as data_utils
# 加载 MNIST 数据集
train_data = dataset.MNIST(
root='mnist',
train=True,
transform=transforms.ToTensor(),
download=True
)
test_data = dataset.MNIST(
root='mnist',
train=False,
transform=transforms.ToTensor(),
download=False
)
# 创建数据加载器
train_loader = data_utils.DataLoader(
dataset=train_data,
batch_size=64,
shuffle=True
)
test_loader = data_utils.DataLoader(
dataset=test_data,
batch_size=64,
shuffle=True
)
5.2 定义 CNN 网络
class CNN(torch.nn.Module):
def __init__(self):
super(CNN, self).__init__()
# 卷积层
self.conv = torch.nn.Sequential(
torch.nn.Conv2d(1, 32, kernel_size=5, padding=2),
torch.nn.BatchNorm2d(32),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2)
)
# 全连接层
self.fc = torch.nn.Linear(14 * 14 * 32, 10)
def forward(self, x):
out = self.conv(x)
# 将多维特征图展平为向量
out = out.view(out.size()[0], -1)
out = self.fc(out)
return out
关键理解: 卷积层输出的是多维特征图(如
[batch_size, 32, 14, 14]),全连接层需要一维向量输入,所以需要用view将其展平为[batch_size, 14*14*32]。
5.3 训练模型
cnn = CNN()
cnn = cnn.cuda() # 放到 GPU 上
loss_func = torch.nn.CrossEntropyLoss() # 分类问题用交叉熵损失
optimizer = torch.optim.Adam(cnn.parameters(), lr=0.01)
for epoch in range(10):
# 训练阶段
for i, (images, labels) in enumerate(train_loader):
images = images.cuda()
labels = labels.cuda()
outputs = cnn(images)
loss = loss_func(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 测试阶段
accuracy = 0
loss_test = 0
for i, (images, labels) in enumerate(test_loader):
images = images.cuda()
labels = labels.cuda()
outputs = cnn(images)
loss_test += loss_func(outputs, labels)
_, pred = outputs.max(1) # 取概率最大的类别
accuracy += (pred == labels).sum().item()
accuracy = accuracy / len(test_data)
loss_test = loss_test / (len(test_data) // 64)
print('epoch:{}, accuracy:{}, loss:{}'.format(
epoch + 1, accuracy, loss_test.item()))
5.4 保存并加载模型进行推理
# 保存模型
torch.save(cnn, 'mnist_cnn.pkl')
# 加载模型进行推理
cnn = torch.load('mnist_cnn.pkl', weights_only=False)
cnn = cnn.cuda()
cnn.eval() # 切换到评估模式
accuracy = 0
for i, (images, labels) in enumerate(test_loader):
images = images.cuda()
labels = labels.cuda()
outputs = cnn(images)
_, pred = outputs.max(1)
accuracy += (pred == labels).sum().item()
# 可视化预测结果
images = images.cpu().numpy()
labels = labels.cpu().numpy()
pred = pred.cpu().numpy()
for idx in range(images.shape[0]):
im_data = images[idx]
im_label = labels[idx]
im_pred = pred[idx]
print('label:', im_label, 'pred:', im_pred)
accuracy = accuracy / len(test_data)
print('Final accuracy:', accuracy)
重要提示:
- 新版 PyTorch 中加载模型需要设置
weights_only=False- 推理时要调用
model.eval()切换到评估模式,这会影响 Dropout 和 BatchNorm 层的行为- 加载模型时必须定义与训练时相同的网络类
六、训练流程总结
完整的 PyTorch 神经网络训练流程如下:
# 1. 导入库
import torch
import torch.nn as nn
# 2. 数据准备
# - 定义 Dataset 和 DataLoader
# - 数据预处理(归一化、增强等)
# 3. 定义网络
class MyModel(nn.Module):
def __init__(self):
super().__init__()
# 定义层
def forward(self, x):
# 前向传播
return x
# 4. 初始化
model = MyModel().cuda()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 5. 训练循环
for epoch in range(num_epochs):
model.train() # 训练模式
for batch in train_loader:
inputs, labels = batch
inputs, labels = inputs.cuda(), labels.cuda()
outputs = model(inputs) # 前向传播
loss = criterion(outputs, labels) # 计算损失
optimizer.zero_grad() # 梯度清零
loss.backward() # 反向传播
optimizer.step() # 更新参数
# 6. 验证
model.eval() # 评估模式
# ... 在验证集上评估
# 7. 保存模型
torch.save(model.state_dict(), 'model.pth')
关键注意事项
- 设备管理:使用
torch.device('cuda' if torch.cuda.is_available() else 'cpu')确保 GPU 可用时加速训练 - 梯度清零:每次迭代前必须执行
optimizer.zero_grad()防止梯度累积 - 模式切换:训练时
model.train(),评估时model.eval()(影响 Dropout、BatchNorm 等层) - 数据结构:处理数据时要清楚数据的维度关系,知道如何用 Tensor 和 NumPy 进行取值和转换
七、总结
本文要点回顾:
- nn.Module 是 PyTorch 神经网络的基础类,所有自定义网络都需要继承它
- 前向传播 在
forward方法中定义,反向传播 由 PyTorch 自动完成 - 训练五步:前向传播 → 计算损失 → 梯度清零 → 反向传播 → 更新参数
- 数据处理 要清楚数据结构(维度、格式),灵活使用 Tensor 和 NumPy 操作
- 模型保存与加载 推荐使用
state_dict()方式,更灵活安全
掌握这些基础后,你就可以尝试更复杂的网络结构和任务了。
参考资料
更多推荐
所有评论(0)