跳转至

第四章:神经网络基础

神经网络是什么

神经网络是一种模仿人脑结构的计算模型,由大量"神经元"连接而成。每个神经元接收输入,进行计算,产生输出。

输入层        隐藏层         输出层
  ○ ────────→ ○ ────────→ ○
  ○ ────────→ ○ ────────→ ○
  ○ ────────→ ○ ────────→ ○

核心概念

权重和偏置

神经网络的核心是**权重(weight)和**偏置(bias)

# 一个简单的神经元
x = torch.tensor([1.0])  # 输入
w = torch.tensor([2.0])  # 权重
b = torch.tensor([0.5])  # 偏置

y = w * x + b  # 输出 = 2.0 * 1.0 + 0.5 = 2.5

类比理解

概念 类比 作用
输入 x 原材料 神经网络处理的数据
权重 w 加工参数 决定输入的重要性
偏置 b 基础调整 调整输出的基准值
输出 y 成品 处理后的结果

权重是"学"来的

重要区分:权重参数和训练数据是两回事。

概念 是什么 来源
训练数据 喂给模型的输入和答案 你提供的
权重参数 模型内部的参数 模型自己学习的

类比:学生考试

训练数据 = 教材和习题(学生学习的材料)
权重参数 = 学生脑子里的知识(学习后形成的)

代码对比

# 训练数据(你提供的)
x = torch.tensor([1.0, 2.0, 3.0])      # 输入
y_true = torch.tensor([2.0, 4.0, 6.0]) # 正确答案

# 权重参数(模型学习的)
w = torch.randn(1, requires_grad=True) # 初始随机,训练中调整
b = torch.randn(1, requires_grad=True)

# 模型预测
y_pred = w * x + b  # 用权重参数处理训练数据

初始值 vs 最终值

虽然初始值是我们给的(随机生成),但最终值是模型自己学到的:

初始: w = 0.5(随机的)
训练中: w = 0.5 → 0.8 → 1.2 → 1.5 → 1.9 → 2.0
最终: w = 2.0(模型学到的)

使用 nn.Module

定义模型

import torch
import torch.nn as nn

class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        # 定义层
        self.fc1 = nn.Linear(784, 256)  # 全连接层:784 → 256
        self.fc2 = nn.Linear(256, 128)  # 全连接层:256 → 128
        self.fc3 = nn.Linear(128, 10)   # 全连接层:128 → 10
        self.relu = nn.ReLU()           # 激活函数

    def forward(self, x):
        # 定义前向传播
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 创建模型
model = SimpleNet()
print(model)

输出:

SimpleNet(
  (fc1): Linear(in_features=784, out_features=256, bias=True)
  (fc2): Linear(in_features=256, out_features=128, bias=True)
  (fc3): Linear(in_features=128, out_features=10, bias=True)
  (relu): ReLU()
)

使用 Sequential 简化

model = nn.Sequential(
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Linear(256, 128),
    nn.ReLU(),
    nn.Linear(128, 10)
)

常用层

全连接层(Linear)

每个输入与每个输出相连:

fc = nn.Linear(784, 256)
# 输入: 784 个特征
# 输出: 256 个特征
# 参数: 784 × 256 + 256 = 200,960 个

卷积层(Conv2d)

用于图像处理,提取局部特征:

conv = nn.Conv2d(
    in_channels=3,     # 输入通道数(RGB图像为3)
    out_channels=64,   # 输出通道数(卷积核数量)
    kernel_size=3,     # 卷积核大小 3×3
    stride=1,          # 步长
    padding=1          # 填充
)

池化层(Pooling)

降低特征图尺寸:

# 最大池化:取窗口内最大值
maxpool = nn.MaxPool2d(kernel_size=2, stride=2)

# 平均池化:取窗口内平均值
avgpool = nn.AvgPool2d(kernel_size=2, stride=2)

归一化层

加速训练,防止过拟合:

# BatchNorm:对每个批次归一化
bn = nn.BatchNorm2d(64)

# LayerNorm:对每个样本归一化
ln = nn.LayerNorm(256)

# Dropout:随机丢弃神经元,防止过拟合
dropout = nn.Dropout(0.5)  # 丢弃 50%

激活函数

激活函数给神经网络引入非线性,让它能学习复杂的模式。

# ReLU:最常用,正数保持,负数变0
relu = nn.ReLU()
# 输入: [-2, -1, 0, 1, 2]
# 输出: [ 0,  0, 0, 1, 2]

# Sigmoid:压缩到 0-1 之间,用于二分类
sigmoid = nn.Sigmoid()
# 输入: [-2, -1, 0, 1, 2]
# 输出: [0.12, 0.27, 0.5, 0.73, 0.88]

# Tanh:压缩到 -1 到 1 之间
tanh = nn.Tanh()

# Softmax:转换为概率分布,用于多分类
softmax = nn.Softmax(dim=1)

# GELU:Transformer 常用
gelu = nn.GELU()

为什么需要非线性

如果没有激活函数,多层神经网络等价于单层:

# 没有激活函数
y = W2(W1x + b1) + b2
# 等价于
y = Wx + b  # 仍然是线性变换!

# 有激活函数
y = W2(relu(W1x + b1)) + b2
# 不等价于任何单层网络,能学习复杂模式

损失函数

损失函数衡量模型预测与真实值的差距。

常用损失函数

# 交叉熵损失:用于多分类
ce_loss = nn.CrossEntropyLoss()

# 均方误差:用于回归
mse_loss = nn.MSELoss()

# 二分类交叉熵:用于二分类
bce_loss = nn.BCELoss()

示例

# 多分类示例
outputs = torch.randn(3, 10)  # 3个样本,10个类别
labels = torch.tensor([0, 5, 9])  # 真实标签

loss = nn.CrossEntropyLoss()
result = loss(outputs, labels)
print(result)  # 损失值

# 回归示例
predictions = torch.tensor([1.5, 2.3, 3.1])
targets = torch.tensor([1.0, 2.0, 3.0])

loss = nn.MSELoss()
result = loss(predictions, targets)
print(result)  # tensor(0.1167)

优化器

优化器根据梯度更新参数,让损失变小。

常用优化器

import torch.optim as optim

model = SimpleNet()

# SGD:随机梯度下降
optimizer = optim.SGD(model.parameters(), lr=0.01)

# SGD + 动量
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

# Adam:自适应学习率,最常用
optimizer = optim.Adam(model.parameters(), lr=0.001)

# AdamW:Adam + 权重衰减
optimizer = optim.AdamW(model.parameters(), lr=0.001)

梯度下降原理

梯度指向损失增大的方向,反方向就是损失减小的方向。

类比:下山找最低点,梯度告诉你哪个方向是下坡。

# 参数更新公式
w = w - lr * grad

# lr 是学习率,控制步长
# grad 是梯度,指示方向

完整训练流程

import torch
import torch.nn as nn
import torch.optim as optim

# 1. 定义模型
model = nn.Sequential(
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Linear(256, 128),
    nn.ReLU(),
    nn.Linear(128, 10)
)

# 2. 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 3. 训练循环
for epoch in range(10):
    for batch_x, batch_y in dataloader:
        # 步骤1:清零梯度
        optimizer.zero_grad()

        # 步骤2:前向传播
        outputs = model(batch_x)

        # 步骤3:计算损失
        loss = criterion(outputs, batch_y)

        # 步骤4:反向传播
        loss.backward()

        # 步骤5:更新参数
        optimizer.step()

    print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")

训练步骤详解

步骤 代码 作用
1 optimizer.zero_grad() 清零梯度,防止累加
2 outputs = model(x) 前向传播,计算预测值
3 loss = criterion(outputs, y) 计算损失
4 loss.backward() 反向传播,计算梯度
5 optimizer.step() 更新参数

模型保存和加载

# 保存模型参数(推荐)
torch.save(model.state_dict(), 'model.pth')

# 加载模型参数
model.load_state_dict(torch.load('model.pth'))

# 保存完整模型
torch.save(model, 'model_full.pth')

# 加载完整模型
model = torch.load('model_full.pth')

小结

本章学习了:

  • ✅ 神经网络的基本概念
  • ✅ 权重和偏置的作用
  • ✅ 使用 nn.Module 定义模型
  • ✅ 常用层和激活函数
  • ✅ 损失函数和优化器
  • ✅ 完整训练流程

核心概念

概念 作用
权重参数 模型要学习的参数
训练数据 喂给模型的输入和答案
损失函数 衡量预测与真实的差距
优化器 根据梯度更新参数
梯度下降 沿梯度反方向更新,减小损失

下一章

第五章:图像分类实战 - 用 MNIST 数据集训练一个手写数字识别模型。