PyTorch 第一个神经网络(长文讲解)

PyTorch 第一个神经网络:从零开始理解深度学习

在人工智能的浪潮中,神经网络是驱动智能系统的核心引擎。对于初学者来说,掌握 PyTorch 这一主流深度学习框架,是迈出实战第一步的关键。今天,我们就来一步步搭建一个真正意义上的“第一个神经网络”——不依赖复杂理论,而是通过真实代码与直观解释,带你亲手训练一个简单的分类模型。

你不需要有深厚的数学背景,只需要一点耐心和对编程的好奇心。就像学习骑自行车,一开始可能会摇晃,但只要坚持踩踏板,很快就能稳稳前行。

为什么选择 PyTorch?

PyTorch 是由 Facebook AI 团队开发的开源深度学习框架,以其动态计算图、简洁的 API 和强大的社区支持而广受欢迎。相比 TensorFlow 等框架,PyTorch 更加“Pythonic”,代码风格自然流畅,尤其适合研究和快速原型开发。

在构建“PyTorch 第一个神经网络”时,你会感受到它的直观性:定义模型像写函数一样简单,训练过程像流水线一样清晰。这种体验,正是它被越来越多高校和企业采用的原因。

准备工作:环境与数据

在动手之前,我们需要准备好运行环境。建议使用 Python 3.8 或更高版本,并通过 pip 安装 PyTorch。

pip install torch torchvision torchaudio

这个命令会安装 PyTorch 核心库、图像处理工具包和音频处理模块。其中 torch 是我们今天的核心。

接下来,我们使用一个经典的数据集——MNIST 手写数字数据集。它包含 6 万张训练图像和 1 万张测试图像,每张图像是 28x28 像素的灰度图,代表数字 0 到 9。

import torch
import torchvision
import torchvision.transforms as transforms

transform = transforms.Compose([
    transforms.ToTensor(),           # 将PIL图像转为Tensor
    transforms.Normalize((0.5,), (0.5,))  # 归一化:均值0.5,标准差0.5
])

trainset = torchvision.datasets.MNIST(
    root='./data', 
    train=True, 
    download=True, 
    transform=transform
)

trainloader = torch.utils.data.DataLoader(
    trainset, 
    batch_size=4,           # 每次加载4张图片
    shuffle=True,           # 打乱数据顺序,避免模型学习到顺序
    num_workers=2           # 使用2个子进程并行加载
)

testset = torchvision.datasets.MNIST(
    root='./data', 
    train=False, 
    download=True, 
    transform=transform
)

testloader = torch.utils.data.DataLoader(
    testset, 
    batch_size=4, 
    shuffle=False, 
    num_workers=2
)

classes = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')

注释说明:

  • transforms.ToTensor() 将图像像素值从 0~255 缩放到 0.0~1.0,这是神经网络的常见输入范围。
  • Normalize 进一步将数据中心化到 0,方差缩放到 1,有助于训练稳定。
  • DataLoader 是 PyTorch 的数据管道,它能自动分批、打乱、多线程加载数据,极大提升效率。
  • batch_size=4 表示每次训练时只处理 4 张图片,适合初学者调试。

构建神经网络模型

现在我们来定义一个简单的神经网络。它由三个部分组成:输入层、隐藏层和输出层。

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # 定义第一个全连接层:输入784维(28x28像素展平),输出128维
        self.fc1 = nn.Linear(28 * 28, 128)
        # 定义第二个全连接层:输入128维,输出64维
        self.fc2 = nn.Linear(128, 64)
        # 定义输出层:输入64维,输出10维(对应10个数字类别)
        self.fc3 = nn.Linear(64, 10)

    def forward(self, x):
        # 展平输入图像:将28x28的二维数据变成784的一维向量
        x = x.view(-1, 28 * 28)
        # 第一层:输入通过fc1,再经过ReLU激活函数
        x = F.relu(self.fc1(x))
        # 第二层:通过fc2,再激活
        x = F.relu(self.fc2(x))
        # 输出层:直接输出10个类别的得分(未归一化)
        x = self.fc3(x)
        return x

net = Net()

注释说明:

  • nn.Module 是 PyTorch 所有神经网络模块的基类,自定义模型必须继承它。
  • nn.Linear 表示全连接层,即每个输入神经元与每个输出神经元相连。
  • F.relu 是激活函数,它让神经元只在输入大于0时“兴奋”,模拟生物神经元的行为。
  • x.view(-1, 28*28) 将图像从 (batch, 1, 28, 28) 转为 (batch, 784),这是模型输入的必要步骤。
  • forward 方法定义了数据如何流动,是模型的“大脑”。

损失函数与优化器

训练神经网络的核心是“让模型不断试错,逐步减少错误”。这需要两个关键组件:损失函数和优化器。

import torch.optim as optim

criterion = nn.CrossEntropyLoss()

optimizer = optim.SGD(net.parameters(), lr=0.001)

注释说明:

  • CrossEntropyLoss 是分类任务的标准损失函数。它结合了 Softmax 和负对数似然,能自动处理输出概率。
  • optimizer.SGD 是最基础的优化算法,它根据梯度方向调整权重,学习率 lr=0.001 控制每次更新的幅度。
  • net.parameters() 会自动提取模型中所有可训练的参数(权重和偏置),供优化器使用。

开始训练:完整训练循环

现在,我们进入真正的训练阶段。一个完整的训练过程包含前向传播、计算损失、反向传播和参数更新四个步骤。

for epoch in range(2):  # 训练2个周期(epoch)
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # 1. 获取输入数据和标签
        inputs, labels = data
        
        # 2. 梯度清零(防止累积)
        optimizer.zero_grad()
        
        # 3. 前向传播:模型预测输出
        outputs = net(inputs)
        
        # 4. 计算损失
        loss = criterion(outputs, labels)
        
        # 5. 反向传播:计算梯度
        loss.backward()
        
        # 6. 优化器更新参数
        optimizer.step()
        
        # 累积损失
        running_loss += loss.item()
        
        # 每2000个批次打印一次损失
        if i % 2000 == 1999:
            print(f'Epoch: {epoch + 1}, Batch: {i + 1}, Loss: {running_loss / 2000:.3f}')
            running_loss = 0.0

print('训练完成!')

注释说明:

  • epoch=2 表示整个数据集被遍历两次。实际项目中可能需要几十甚至上百个 epoch。
  • optimizer.zero_grad() 清除上一轮的梯度,避免影响当前更新。
  • loss.backward() 是反向传播的核心,它计算每个参数对损失的敏感度。
  • optimizer.step() 根据梯度更新权重。
  • 每 2000 个批次输出一次损失,帮助我们观察训练趋势。

模型测试与评估

训练结束后,我们需要在未见过的数据上测试模型表现。

correct = 0
total = 0
with torch.no_grad():  # 不计算梯度,节省内存
    for data in testloader:
        images, labels = data
        outputs = net(images)
        # 获取最大得分的类别作为预测结果
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f'测试集准确率: {100 * correct / total:.2f}%')

注释说明:

  • torch.no_grad() 禁用梯度计算,因为测试阶段不需要反向传播。
  • torch.max(outputs.data, 1) 找出每张图预测的最高得分类别(即模型认为的数字)。
  • predicted == labels 返回布尔数组,sum().item() 统计正确预测的数量。

运行这段代码,你可能会看到类似“测试集准确率: 90.12%”的结果。这说明你的“PyTorch 第一个神经网络”已经成功训练并具备了一定的识别能力!

总结与展望

通过这篇文章,你已经亲手完成了从环境搭建、数据准备、模型定义到训练评估的完整流程。这个过程虽然简单,却是所有深度学习项目的基石。

记住:每一个复杂的神经网络,都是从这样一个“最小可运行版本”开始的。你今天的每一步,都在为未来的模型设计、图像识别、自然语言处理打下基础。

接下来,你可以尝试:

  • 增加网络层数或调整隐藏层大小
  • 更换优化器(如 Adam)
  • 添加正则化(Dropout)
  • 将模型保存为 .pt 文件,用于后续加载使用

当你看到模型准确率从 10% 提升到 90% 时,那种成就感,是编程世界中最动人的时刻之一。

现在,你不再是“看别人写代码”的人,而是真正“写出代码”的开发者。欢迎进入深度学习的大门。