AI 学习课程 / LESSON 09

第九节:图像分类项目

把 CNN 知识连成一个完整项目:先训练基线,再自己预训练并迁移模型,比较数据量、效果和成本,最后逐张分析误判。

真实 CPU 训练结果从头训练与迁移学习错误分析离线 HTML

01 / 我们要解决什么

少量带标签图像,识别四种方向

输入是一张 24×24 灰度图,输出“横线、竖线、左上到右下、左下到右上”中的一种。源域图片清晰,目标域图片对比度低、噪声多,还可能有亮点和线条断口。两个域的标签含义相同,图像分布不同。

源域:学习特征

每类 180 张训练图,训练并保存 CNN 权重。

目标域:少量标签

每类 8 张或 32 张,对照三种训练方案。

项目问题

迁移有无收益?何时冻结?哪类图最容易误判?

实验范围:图像由代码生成,不是真实照片。“预训练权重”来自本节在源域真正训练后保存的模型,不是下载的 ImageNet 权重。实验无需新增依赖,但结果不能直接外推到真实照片。

02 / 先观察输入

同一类图像,视觉条件变了

深色表示小像素值,浅色表示大像素值。切换方向,观察清晰源域与嘈杂目标域。

源域:清晰图

源域验证集中的一张

目标域:噪声图

目标域测试集中的一张

模型只看到像素数组与训练标签,不会看到“横线”这两个字。训练、验证、测试用不同种子独立生成。

03 / 先定协议

验证集挑方案,测试集报告结果

源域训练720 张;预训练。
源域验证160 张;选预训练轮次。
目标训练32 或 128 张。
目标验证160 张;选目标轮次。
目标测试320 张;固定后报告。

三种目标方案使用相同的图像划分、12 轮上限和分类头初值。每种方案按目标验证损失选择最佳轮次,然后分别报告测试成绩。每类数量相等,随机猜测的期望准确率约 25%。绝不使用测试集来训练或挑轮次。

输入 X.shape = (B, 1, 24, 24) 标签 y.shape = (B,);值为 0、1、2 或 3 模型 logits.shape = (B, 4) CrossEntropyLoss(logits, y) → 标量

像素统一裁剪到 [0,1];本项目没有用全数据拟合均值、方差等预处理参数。每类 8 张的目标训练集是每类 32 张训练集的子集,验证和测试集固定。

04 / 把形状算清楚

小型 CNN 的每一层

输入(B,1,24,24)
Conv 1→8;池化(B,8,12,12)
Conv 8→16;池化(B,16,6,6)
Conv 16→24(B,24,6,6)
展平;Linear(B,864)→(B,4)

三层卷积均是 3×3、padding=1、stride=1,因此不改变高宽。两次 2×2 池化使 24→12→6。最后每张图有 24×6×6=864 个特征。全部参数 8188 个:卷积特征层 4728 个,分类头 3460 个。

features(X) = z,z.shape = (B,24,6,6) classifier(z) = logits,logits.shape = (B,4) 预测类别 = argmax(logits, dim=1)

训练时将 logits 直接交给 CrossEntropyLoss;展示各类概率时才调用 softmax。PyTorch 交叉熵文档。

05 / 预训练权重如何复用

加载特征层,换一个新分类头

代码先在源域训练模型,保存 source-pretrained.pt,之后从文件加载卷积特征层。目标任务的分类头重新初始化,不会直接沿用源域输出层。

冻结指特征参数设 requires_grad=False,优化器只更新新分类头。微调指从预训练权重出发,让整个网络继续学习。代码会核对冻结后的特征权重完全没变,微调后确实变了。PyTorch 官方迁移学习教程也展示了这两种方式。

06 / 比较真实结果

目标标签数量会改变结论吗?

方案验证准确率测试准确率最佳轮次可训练参数

绿色:训练损失;橙色:验证损失;蓝色虚线:按验证损失选出的轮次。

这是固定随机种子的一次教学实验,没有进行多种子统计比较。数个百分点的差别不应直接当成普遍规律。

07 / 看错在哪里

混淆矩阵与逐张图像

矩阵行是真实类别,列是预测类别;对角线是预测正确的数量。

各类输出概率

展示集合包含每类代表图、各方案的一部分误判和低置信度图;不是全部 320 张测试图。置信度未校准,不能直接理解为“正确概率”。单张误判可启发假设,不能证明原因。

08 / 成本也要一起看

预训练不是免费的

秒数来自本机极短的 CPU 实验,有环境噪声,只适合理解量级。冻结时特征层不反向传播,但代码仍每批计算卷积前向;若缓存固定特征,可进一步减少重复计算,代价是额外存储。比较总成本时应加上源域预训练;已有权重的“新增训练成本”可以另报。

09 / 自己复现

运行、改一个因素、写项目报告

项目仅使用已安装的 CPU PyTorch 和 NumPy。程序会生成数据、训练预训练权重和六种目标实验,输出 JSON 与 CSV。直接打开本地 HTML 不会重新训练,也无需启动服务。

uv run python lessons/lesson-09-project.py

完整代码 · 结果 CSV · 完整 JSON · 预训练权重

  1. 先手算逐层形状,解释为什么分类头输入为 864。
  2. 比较每类 8 张与 32 张时三种方案。指出验证集和测试集各自用途。
  3. 选两张误判,分别写下“观察到什么”和“可能原因”。
  4. 只改一个因素再运行,例如目标图像噪声、每类训练图数量或是否冻结卷积层;记录变化。
  5. 写一页 Markdown 报告:任务、数据划分、模型、结果、误判、成本、限制和下一步。

10 / 自测

五道题

1. 为什么不能用目标测试集挑最佳轮次?

2. 冻结特征层后优化器更新多少参数?

3. (B,24,6,6) 展平后,每张图有多少个数?

4. 每类 8 张时微调表现差,说明什么?

5. 计算迁移方案总训练成本,要算源域预训练吗?

下一节:文本与序列建模。图像按空间位置处理;文本将引入 Token、顺序和 Embedding。