1. 模型的本质是什么?
模型 = 一个数学函数 + 一堆参数(权重)
最简单的例子:线性回归
y = w * x + b
- w和b就是"参数"(也叫权重)
- 训练就是找到最好的w和b,让预测值y尽量接近真实值
神经网络就是把很多这样的函数叠加起来:
第1层: h1 = w1 * x + b1
第2层: h2 = w2 * h1 + b2
第3层: h3 = w3 * h2 + b3
...
输出层: y = wn * hn-1 + bn
用代码理解:
# 一个最简单的"模型"
class SimpleModel:
def __init__(self):
# 这些就是"参数",初始是随机的
self.w1 = random() # 比如 0.5
self.w2 = random() # 比如 0.3
self.b = random() # 比如 0.1
def forward(self, x):
# 这就是"前向传播",用参数计算输出
return self.w1 * x + self.w2 * x**2 + self.b
# 模型就是:结构(forward函数)+ 参数(w1, w2, b)
# 保存模型 = 保存这些参数的值
2. 训练的本质是什么?
训练 = 不断调整参数,让模型的输出越来越接近正确答案
┌─────────────────────────────────────────────────────────────┐
│ 第1步:随机初始化参数 │
│ w = 0.5, b = 0.1(瞎猜的) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第2步:前向传播(用当前参数计算预测值) │
│ 输入x=2,预测 y_pred = 0.5*2 + 0.1 = 1.1 │
│ 真实答案 y_true = 3.0 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第3步:计算损失(预测值和真实值差多远) │
│ loss = (y_pred - y_true)² = (1.1 - 3.0)² = 3.61 │
│ loss越大,说明预测越差 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第4步:反向传播(计算每个参数对loss的影响) │
│ 求导:∂loss/∂w = ? ∂loss/∂b = ? │
│ 这一步是PyTorch/TensorFlow自动帮你算的 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第5步:更新参数(让loss变小) │
│ w_new = w_old - learning_rate * ∂loss/∂w │
│ b_new = b_old - learning_rate * ∂loss/∂b │
│ 这就是"梯度下降" │
└─────────────────────────────────────────────────────────────┘
↓
重复第2-5步,直到loss足够小
用代码理解:
# 完整的训练循环
model = SimpleModel()
optimizer = SGD(model.parameters(), lr=0.01) # 优化器,lr就是learning_rate
for epoch in range(100): # 训练100轮
for x, y_true in training_data:
# 第2步:前向传播
y_pred = model.forward(x)
# 第3步:计算损失
loss = (y_pred - y_true) ** 2
# 第4步:反向传播(PyTorch自动算梯度)
loss.backward()
# 第5步:更新参数
optimizer.step()
optimizer.zero_grad()
print(f"Epoch {epoch}, Loss: {loss}")
# Loss会越来越小:3.61 → 2.1 → 0.8 → 0.2 → 0.05 → ...
3. ResNet-50是什么?
ResNet-50 = 一个有50层的神经网络结构
"结构"是什么意思?就是定义了:
- 有多少层
- 每层有多少个神经元
- 层与层之间怎么连接
ResNet的特点是有"残差连接"(跳跃连接),解决深层网络难训练的问题
┌─────┐ ┌─────┐ ┌─────┐
│第1层│ ──→ │第2层│ ──→ │第3层│ ──→ ...
└─────┘ └─────┘ └─────┘
↑ │
└────────────┘ ← 这就是"残差连接",让梯度能直接传过去
ResNet-50的参数量:约2500万个参数
# ResNet-50的结构(简化版)
class ResNet50:
def __init__(self):
self.conv1 = Conv2d(...) # 第1层,有若干参数
self.conv2 = Conv2d(...) # 第2层
self.conv3 = Conv2d(...) # 第3层
# ... 一共50层
self.fc = Linear(2048, 1000) # 最后一层,输出1000类
# 总共约2500万个参数(w和b)
4. 预训练权重是什么?
预训练权重 = 别人已经训练好的参数值
ImageNet是一个超大数据集:
- 1400万张图片
- 1000个类别(猫、狗、汽车、飞机...)
有人(Google/Facebook)用这个数据集训练了ResNet-50:
- 花了几周时间
- 用了几十张GPU
- 得到了一组很好的参数值
这组参数就是"预训练权重",可以下载使用
预训练权重文件长什么样?
# 预训练权重就是一个字典,存储每一层的参数值
pretrained_weights = {
"conv1.weight": [[0.23, 0.45, ...], [0.12, 0.67, ...], ...], # 第1层的w
"conv1.bias": [0.01, 0.02, ...], # 第1层的b
"conv2.weight": [[...], [...], ...], # 第2层的w
"conv2.bias": [...], # 第2层的b
# ... 所有层的参数
}
# 保存成文件:resnet50-imagenet.pth(约100MB)
5. 迁移学习是什么意思?
迁移学习 = 借用别人训练好的参数,在自己的数据上微调
为什么能迁移?
- 神经网络的前面几层学到的是"通用特征":边缘、纹理、形状
- 这些特征对所有图像任务都有用
- 只需要调整最后几层,适应你的具体任务
打个比方:
- 预训练 = 学会了"看图的基本能力"(识别边缘、颜色、形状)
- 迁移学习 = 用这个能力去做"垃圾分类"这个具体任务
迁移学习的代码:
from torchvision.models import resnet50, ResNet50_Weights
# ========== 方法1:从头训练(不用预训练权重)==========
model = resnet50(weights=None) # 随机初始化参数
# 需要大量数据 + 很长时间才能训练好
# ========== 方法2:迁移学习(用预训练权重)==========
# 第1步:加载预训练权重
model = resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)
# 现在model的参数已经是ImageNet上训练好的值了
# 第2步:冻结前面的层(不训练,保持原样)
for param in model.parameters():
param.requires_grad = False # 冻结,不更新
# 第3步:替换最后一层(适应你的任务)
# 原来:输出1000类(ImageNet的类别数)
# 现在:输出4类(垃圾分类:可回收、有害、厨余、其他)
model.fc = nn.Linear(2048, 4) # 只有这一层需要训练
# 第4步:只训练最后一层
optimizer = SGD(model.fc.parameters(), lr=0.001) # 只优化fc层
# 这样训练很快,因为只需要调整最后一层的参数!
迁移学习的效果对比:
| 方法 | 需要数据量 | 训练时间 | 准确率 |
|---|---|---|---|
| 从头训练 | 100万+ | 几天 | 可能很差 |
| 迁移学习 | 几万 | 几小时 | 很好 |
6. 训练集、验证集、测试集是干嘛的?
这是防止"作弊"的机制
想象一个学生准备考试:
- 训练集 = 平时做的练习题(可以反复做,知道答案)
- 验证集 = 模拟考试(检验学习效果,调整学习方法)
- 测试集 = 正式考试(最终评估,只考一次)
如果用练习题的成绩来评价学生,不公平!
因为他可能把题目背下来了,但不会举一反三
具体作用:
# 数据划分:80%训练,10%验证,10%测试
all_data = load_data() # 2.9万张图片
train_data = all_data[:23200] # 训练集:2.32万
val_data = all_data[23200:26100] # 验证集:0.29万
test_data = all_data[26100:] # 测试集:0.29万
# 训练过程
for epoch in range(50):
# 1. 在训练集上训练
model.train()
for x, y in train_data:
loss = compute_loss(model(x), y)
loss.backward()
optimizer.step()
# 2. 在验证集上评估(不训练,只看效果)
model.eval()
val_accuracy = evaluate(model, val_data)
print(f"Epoch {epoch}, 验证集准确率: {val_accuracy}")
# 3. 根据验证集效果调整策略
if val_accuracy < last_accuracy:
# 验证集效果变差了,可能过拟合了
# 可以早停、调学习率、加正则化等
learning_rate *= 0.1
# 训练完成后,在测试集上最终评估
test_accuracy = evaluate(model, test_data)
print(f"最终测试集准确率: {test_accuracy}") # 这个数字才能对外说
为什么要分开?
问题:过拟合 = 模型把训练数据"背下来"了,但不会泛化
例子:
训练集准确率: 99%(练习题全对)
测试集准确率: 60%(考试不及格)
这说明模型没有真正"学会",只是记住了训练数据
验证集的作用:
在训练过程中监控模型的"泛化能力"
如果验证集准确率开始下降,就该停止训练了(早停)
测试集的作用:
最终评估模型效果,这个数字才能写进论文/简历
注意:测试集只能用一次!不能用它来调参数
7. 保存模型保存的是什么?
保存模型 = 保存所有参数的值
模型文件里存的就是一个字典:
{
"layer1.weight": tensor([[0.23, 0.45, ...], ...]),
"layer1.bias": tensor([0.01, 0.02, ...]),
"layer2.weight": tensor([[...], ...]),
...
}
这些数字就是训练完成后每个参数的最优值
代码演示:
# ========== 保存模型 ==========
# 方法1:只保存参数(推荐)
torch.save(model.state_dict(), "model_weights.pth")
# 文件大小约100MB(取决于模型参数量)
# 方法2:保存整个模型(包括结构和参数)
torch.save(model, "model_full.pth")
# ========== 加载模型 ==========
# 方法1:先定义结构,再加载参数
model = ResNet50() # 先创建模型结构
model.load_state_dict(torch.load("model_weights.pth")) # 再加载参数
# 现在model的参数就是训练好的值了
# 方法2:直接加载整个模型
model = torch.load("model_full.pth")
# ========== 使用模型 ==========
model.eval() # 切换到评估模式
image = load_image("垃圾.jpg")
prediction = model(image) # 输出:[0.1, 0.8, 0.05, 0.05] → 有害垃圾
8. 参数为什么有用?
参数 = 模型从数据中学到的"知识"
训练前:
参数是随机的 → 输入垃圾图片 → 输出随机猜测
训练后:
参数被调整过 → 输入垃圾图片 → 输出正确分类
参数的值"编码"了:
- 什么样的边缘特征代表塑料瓶
- 什么样的颜色特征代表厨余垃圾
- 什么样的形状特征代表电池
这些知识就存储在那2500万个参数里
直观理解:
想象参数是"决策规则":
if 参数w1 > 0.5 and 边缘特征 == 圆形:
可能是塑料瓶
if 参数w2 > 0.3 and 颜色特征 == 绿色:
可能是厨余垃圾
...
实际的神经网络更复杂,但本质就是用参数组合特征做决策
训练就是找到最好的参数组合