主题
字号
CHAPTER 06 ≈ 25 MIN READ

神经网络基础模块

本章目标:掌握PyTorch中构建神经网络的核心组件

神经网络是什么?

在开始学习PyTorch的神经网络模块之前,我们先来理解一个根本性的问题:神经网络到底是什么?

神经网络的直观理解

想象一下你有一堆乐高积木。每个积木块只能做很简单的事情(比如把输入乘以某个数然后加起来)。但是当你把这些积木块堆叠在一起时,它们就能完成非常复杂的事情!

神经网络就是这样一个"积木系统":

神经元的工作原理

一个最简单的神经元(也称为感知机线性单元)是这样工作的:

输入 x -> 乘以权重 w -> 加上偏置 b -> 输出 y

数学公式: $y = w \cdot x + b$

或者用向量形式: $y = \mathbf{w}^T \mathbf{x} + b$

其中:

为什么要多层?

单层神经元只能做线性变换——也就是说,它只能学会画一条直线(或者一个平面)。

但现实世界的问题往往不是线性的!比如:

解决方法:堆叠多层!每加一层,网络就能学习更复杂的模式。

这就是为什么我们需要深度学习——"深度"指的就是层数多!

PyTorch中的神经网络

PyTorch为神经网络提供了完整的工具箱:

组件 作用
nn.Linear 线性变换(全连接层)
nn.Conv2d 卷积层(处理图像)
nn.RNN / nn.LSTM 循环层(处理序列)
nn.ReLU / nn.Sigmoid 激活函数
nn.CrossEntropyLoss 损失函数
torch.optim 优化器

接下来,让我们逐一学习这些组件!


nn.Linear:线性变换层

nn.Linear是PyTorch中最基本的神经网络层,也称为全连接层(Fully Connected Layer)密集层(Dense Layer)

数学公式: $\mathbf{y} = \mathbf{x} \mathbf{W}^T + \mathbf{b}$

或者更详细地: $y_i = \sum_{j=1}^{n} x_j \cdot W_{ij} + b_i$

其中:

import torch.nn as nn

# 创建一个线性层
# 输入维度3,输出维度2
linear = nn.Linear(3, 2)

# 查看线性层的参数
print("权重形状:", linear.weight.shape)  # torch.Size([2, 3])
print("偏置形状:", linear.bias.shape)    # torch.Size([2])

# 前向传播
x = torch.randn(4, 3)  # 批量大小4,输入维度3
output = linear(x)      # 输出维度2
print("输出形状:", output.shape)  # torch.Size([4, 2])

手动验证

# 验证nn.Linear的计算
linear = nn.Linear(3, 2)
x = torch.randn(4, 3)

# PyTorch的实现
output_pytorch = linear(x)

# 手动计算:y = xW^T + b
xWt = x @ linear.weight.t()
output_manual = xWt + linear.bias

print(torch.allclose(output_pytorch, output_manual))  # True

实际应用

# 示例:简单的多层神经网络
class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer1 = nn.Linear(784, 256)
        self.layer2 = nn.Linear(256, 10)

    def forward(self, x):
        x = x.reshape(x.size(0), -1)  # 展平
        x = torch.relu(self.layer1(x))  # 激活函数
        x = self.layer2(x)
        return x

net = SimpleNet()
print(net)
# SimpleNet(
#   (layer1): Linear(in_features=784, out_features=256, bias=True)
#   (layer2): Linear(in_features=256, out_features=10, bias=True)
# )

Mav's Tips:

Python 类与继承基础(结合 PyTorch)

1. 第三方库

Python 的第三方库类似 C 的 #include,是别人封装好的功能,直接调用即可。区别在于 Python 的第三方库需要先用 pip install 安装,自带的标准库(osmath 等)不需要。import as 是简写用途,比如 import torch.nn as nn,之后用 nn 代替 torch.nntorchtorch.nn 是包含关系,torch.nntorch 里专门负责神经网络的子模块,可以单独引入。

类的继承

class Dog(Animal):
    pass

括号内写父类名,表示继承——子类自动拥有父类的所有方法和结构,不需要重写。如果子类只写 pass,直接可以调用父类的方法。

3.super() 的作用

如果子类自己定义了 __init__,会覆盖父类的 __init__,父类的初始化逻辑就被跳过了。super().__init__() 的作用是手动把父类的初始化补回来。

所以"继承 + 自己还要新增内容"的标准写法是:

class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()   # 先跑父类的初始化
        self.layer1 = nn.Linear(784, 256)  # 再加自己的东西

4. 为什么 PyTorch 要用继承而不是直接调用

import 只是让你在文件里能使用某个库的名字。新建一个类时,类本身是空壳,不会自动拥有 nn.Module 的内部结构。

() 里写 nn.Module 是声明继承关系,super().__init__() 是让那套结构真正在对象里初始化起来,两步缺一不可。


nn.Module:神经网络基类

nn.Module是所有神经网络模块的基类。当你定义自己的神经网络时,需要继承nn.Module并实现forward方法。

class MyNetwork(nn.Module):
    def __init__(self):
        super().__init__()  # 调用父类初始化

        # 定义网络层(会自动注册为子模块)
        self.linear1 = nn.Linear(10, 20)
        self.linear2 = nn.Linear(20, 5)
        self.relu = nn.ReLU()

    def forward(self, x):
        # 定义前向传播
        x = self.linear1(x)
        x = self.relu(x)
        x = self.linear2(x)
        return x

# 创建网络实例
net = MyNetwork()

# 查看网络结构
print(net)

# 查看所有可学习参数
for name, param in net.named_parameters():
    print(f"{name}: {param.shape}")

nn.Module的常用方法

方法 作用
net.parameters() 返回所有参数迭代器
net.named_parameters() 返回参数名称和迭代器
net.state_dict() 返回参数字典(用于保存模型)
net.load_state_dict() 加载参数字典
net.train() 切换到训练模式
net.eval() 切换到评估模式
net.to(device) 移动到指定设备

模型保存与加载

import torch
from torch import nn

# 定义模型
model = nn.Linear(3, 1)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 保存模型和优化器状态
torch.save({
    'model_state_dict': model.state_dict(),
    'optimizer_state_dict': optimizer.state_dict(),
    'epoch': 10
}, 'checkpoint.pth')
print("模型已保存!")

# 加载检查点
checkpoint = torch.load('checkpoint.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']

print(f"模型已加载,恢复到第 {epoch} 轮训练!")

Mav's Tips: parameters() 简单说就是返回所有 requires_grad=True 的参数。 name_parameters() 是返回参数和结构 state_dict() 以字典形式返回所有参数 load_state_dict() 加载所有参数到新模型中 torch.save() 把数据保存到文件中,搭配 state_dict() 使用 torch.load() 是把文件数据加载出来


nn.Sequential:快速搭建网络

nn.Sequential是一个简单的容器,用于快速搭建顺序执行的神经网络。

# 方法1:直接传入层
net = nn.Sequential(
    nn.Linear(10, 20),
    nn.ReLU(),
    nn.Linear(20, 5)
)

print(net)
# Sequential(
#   (0): Linear(in_features=10, out_features=20, bias=True)
#   (1): ReLU()
#   (2): Linear(in_features=20, out_features=5, bias=True)
# )

x = torch.randn(3, 10)
output = net(x)
print(output.shape)  # torch.Size([3, 5])

结合OrderedDict可以给每一层命名

from collections import OrderedDict

net = nn.Sequential(OrderedDict([
    ('fc1', nn.Linear(10, 20)),
    ('relu', nn.ReLU()),
    ('fc2', nn.Linear(20, 5))
]))

print(net.fc1.weight.shape)  # 可以用名字访问

适用场景:适合构建简单的线性网络。复杂网络(跳跃连接、多分支等)需要使用nn.Module手动定义。


nn.CrossEntropyLoss:交叉熵损失

nn.CrossEntropyLoss是分类问题中最常用的损失函数。它结合了LogSoftmax和NLLLoss。

import torch.nn as nn

# 创建损失函数
criterion = nn.CrossEntropyLoss()

# 模拟预测输出(未归一化的分数,也叫logits)
predictions = torch.randn(4, 10)  # 4个样本,10个类别

# 真实标签
labels = torch.tensor([3, 0, 5, 7])

# 计算损失
loss = criterion(predictions, labels)
print("损失值:", loss.item())

注意:使用CrossEntropyLoss时不需要显式地应用softmax,因为损失函数内部已经处理了这一点。

手动验证交叉熵

def manual_cross_entropy(pred, label):
    # 1. softmax归一化
    exp_pred = torch.exp(pred)
    probs = exp_pred / exp_pred.sum(dim=1, keepdim=True)

    # 2. 取正确类别的概率
    label_prob = probs[range(len(label)), label]

    # 3. 计算负对数似然
    loss = -torch.log(label_prob).mean()
    return loss

predictions = torch.randn(4, 10)
labels = torch.tensor([3, 0, 5, 7])

loss_pytorch = nn.CrossEntropyLoss()(predictions, labels)
loss_manual = manual_cross_entropy(predictions, labels)

print("PyTorch损失:", loss_pytorch.item())
print("手动计算:", loss_manual.item())
print("相近:", torch.allclose(loss_pytorch, loss_manual))

**Mav's Tips:**如果你已经对数据使用了 Softmax,那就应该使用 nn.NLLLoss


torch.optim:优化器

torch.optim提供了各种优化算法,用于更新神经网络的参数。

import torch.optim as optim

# 创建网络
net = nn.Linear(10, 2)

# 创建SGD优化器
optimizer = optim.SGD(net.parameters(), lr=0.01)

# 或者创建Adam优化器(通常收敛更快)
optimizer = optim.Adam(net.parameters(), lr=0.001)

标准训练循环

for epoch in range(100):
    for data, target in dataloader:
        # 1. 前向传播
        output = net(data)
        loss = criterion(output, target)

        # 2. 清零梯度
        optimizer.zero_grad()

        # 3. 反向传播
        loss.backward()

        # 4. 更新参数
        optimizer.step()

常用优化器对比

优化器 优点 缺点 适用场景
SGD 简单、理论基础好 收敛慢、需要调参 研究基线
Adam 自适应学习率、易调参 可能泛化稍差 实际应用首选
AdamW 正则化效果好 - 推荐使用
RMSprop 自适应学习率 - RNN等

学习率调度器

# 每10个epoch降低学习率
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)

for epoch in range(100):
    # 训练...
    scheduler.step()
    print(f"Epoch {epoch}, 学习率: {scheduler.get_last_lr()[0]}")