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 颜色特征 == 绿色: 可能是厨余垃圾 ... 实际的神经网络更复杂,但本质就是用参数组合特征做决策 训练就是找到最好的参数组合