第四章:神经网络基础¶
神经网络是什么¶
神经网络是一种模仿人脑结构的计算模型,由大量"神经元"连接而成。每个神经元接收输入,进行计算,产生输出。
核心概念¶
权重和偏置¶
神经网络的核心是**权重(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 最终值:
虽然初始值是我们给的(随机生成),但最终值是模型自己学到的:
使用 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)¶
每个输入与每个输出相连:
卷积层(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)
梯度下降原理¶
梯度指向损失增大的方向,反方向就是损失减小的方向。
类比:下山找最低点,梯度告诉你哪个方向是下坡。
完整训练流程¶
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 数据集训练一个手写数字识别模型。