- 教程
- 人工智能
- 机器学习
- 深度学习
【免费下载链接】AI-For-Beginners
12 Weeks, 24 Lessons, AI for All!
本篇指南基于 AI-For-Beginners 课程第 8 课实验文档 lab/README.md 展开,带你从零完成一个真实场景任务:基于 Oxford-IIIT 宠物数据集,先用朴素 CNN 建立基线,再通过迁移学习(预训练 VGG/ResNet 作为特征提取器 + 自定义分类头)训练一个 35 类狗猫品种分类器。读完并动手完成后,你将掌握数据集组织方式、两种框架(TensorFlow / PyTorch)下的迁移学习标准流程、特征提取器冻结技巧,以及预训练网络在跨域图像上的能力边界。
任务背景:从宠物托管所到自动品种识别
实验文档给出的场景设定是:你需要为一家宠物托管所(pet nursery)开发一个用于登记所有宠物的应用,其中最有价值的功能之一就是从照片中自动识别宠物品种。为此,实验选用了真实拍摄(real-life)的宠物图像数据集,用迁移学习(transfer learning)来完成品种分类任务。
这并非一个玩具级分类问题,而是接近真实生产场景的图像分类任务:图像是真实照片(存在姿态、光照、背景差异),类别数达到 35 个品种。课程在 Transfer Learning 课程主文档 中解释了为什么迁移学习是这类任务的合理选择:从零训练卷积网络的大部分时间都花在让网络学会通用的低层特征(线条、边缘、纹理)上,而一个在 ImageNet 这类大规模通用图像集上预训练好的网络已经具备了优秀的特征提取能力——很多时候只需在这些特征之上搭建一个分类器就能取得不错的效果。
数据集:Oxford-IIIT 宠物图像集
实验使用 Oxford-IIIT(Oxford-IIIT Pet Dataset,数据说明以原始文档引用的 Oxford 官方页面为准)宠物数据集,包含 35 个狗和猫的品种。原始文档给出的下载方式如下(该命令序列在实验 Notebook 的首个代码单元格中原样出现):
!wget https://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz !tar xfz images.tar.gz !rm images.tar.gz解压后所有图片集中在名为images的目录中,文件名本身包含品种名(类名)。OxfordPets.ipynb Notebook 中会先展示前几张图片并打印文件名,让你直观确认这一命名规则,然后用下面这段代码把所有图片按品种整理到对应子目录,以便复用ImageFolder/image_dataset_from_directory这类“按目录即类别”的加载方式:
for fn in os.listdir('images'): cls = fn[:fn.rfind('_')].lower() # 类名 = 文件名最后一个下划线之前的部分,转小写 os.makedirs(os.join('images', cls), exist_ok=True) os.replace(os.path.join('images', fn), os.path.join('images', cls, fn))注意类名的截取方式是fn[:fn.rfind('_')],即取文件名最后一个下划线之前的内容——这正是 Oxford-IIIT 文件名“品种_编号”格式的约定。整理完成后,类数不再硬编码,而是动态统计目录数量:
num_classes = len(os.listdir('images')) num_classes这种“由目录结构反推类别数”的写法与前面课程中 Cats vs. Dogs 实验的加载方式保持一致,也为后面替换分类器输出维度(num_classes个输出神经元)埋下伏笔。
环境准备
仓库提供了可直接运行的 Binder 环境定义,依赖与本实验所需框架一一对应:
- binder/environment.yml:Python 3.8.12,通过 conda 安装 PyTorch 1.11.0 / torchvision 0.12.0、OpenCV 4.5、numpy、matplotlib 等,并通过 pip 追加 binder/requirements.txt 中的依赖(TensorFlow 2.13.1、Keras、torchinfo 1.8.0 等);
- 从源码结构看,实验 Notebook 支持 TensorFlow 与 PyTorch 两条路线(每个待填代码单元格内均注明了对应框架的写法),可按环境选择其一;
- 课程主文档明确提示:VGG 级别的网络“需要较高的计算能力,无法轻松在 CPU 上解决”,因此建议使用带 GPU 的算力运行本实验,并先用少量 epoch 验证流程。
第一步:先训练一个基线 CNN(并观察它的不足)
OxfordPets.ipynb 的实验设计是“先自己训、再迁移学习”。在 “Preparing dataset for Deep Learning” 阶段,Notebook 给出的关键准备步骤包括:
- 转换为张量:把所有图片转为张量,同时创建对应标签(类别编号)张量。框架内置工具:
- TensorFlow:
tf.keras.preprocessing.image_dataset_from_directory - PyTorch:
torchvision.datasets.ImageFolder
- TensorFlow:
- 统一尺寸:从预览图可以看到 Oxford 图片基本都接近正方形比例,因此需要把所有图片 resize 为正方形尺寸;
- 划分训练/测试集并组织成mini-batches(定义数据加载器)。
在 “Define a neural network” 阶段,Notebook 给出一套可操作的架构检查清单:
- 遵循金字塔结构:网络越深,卷积核(filter)数量应越多;
- 层间不要漏掉**激活函数(ReLU)**和Max Pooling;
- 末端分类器可以有一层或多层隐藏层,但输出神经元数必须等于类别数(本实验为 35)。
最后一层激活函数与损失函数的搭配尤其容易出错,Notebook 给出了两个框架的精确搭配:
| 框架 | 输出层激活 | 损失函数 | 说明 |
|---|---|---|---|
| TensorFlow / Keras | softmax | sparse_categorical_crossentropy | sparse 版本直接接受“类别编号”作为标签,而非 one-hot 向量 |
| PyTorch | 无激活(裸 logits) | CrossEntropyLoss | 该损失内部自动执行 softmax,无需手动加 Softmax |
Notebook 还有一条提示:PyTorch 中可用LazyLinear替代Linear,它只需要n_out一个参数,输入维度会在首次forward时自动推断,省去手动计算分类器输入维度的麻烦。
训练阶段的要求同样具体:每个 epoch 记录训练集与测试集的准确率,并绘制曲线以检查是否过拟合。而实验的“预期结果”是——即使你每一步都做对了,这个从零训练的基线模型准确率也会比较低。这正是引入迁移学习的动机。
第二步:迁移学习——把预训练网络当特征提取器
进入 “Transfer Learning” 部分后,Notebook 提示可以自由选择 VGG-16/VGG-19、ResNet50 等预训练模型。整个流程分四步,且每一步都对应一个待填代码单元格:
1. 重新加载数据集(不同的归一化)
迁移学习对输入的归一化要求与从零训练时不同,因此 Notebook 明确要求用另一套 transforms 重新加载数据集,并提示“如果用的是 VGG-16/VGG-19,需要做标准的变换”。仓库中配套的 pytorchcv.py 封装了这套标准变换(第 147–155 行的common_transform),可以直接参考其参数:
std_normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # ImageNet 均值/方差 trans = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), # 裁剪到预训练网络期望的 224x224 输入 transforms.ToTensor(), std_normalize])这里[0.485, 0.456, 0.406]/[0.229, 0.224, 0.225]是 ImageNet 各通道的均值与标准差。归一化参数必须与预训练时的前处理一致,否则特征分布会偏离预训练权重的“预期输入”,效果会明显变差——这也是 Transfer Learning 课程主文档 强调“pre-trained models as feature extractors”时必须先做正确前处理的原因。
2. 加载预训练网络
对应 Notebook 中# vgg = ...单元格:
- PyTorch:
torchvision.models.vgg16(pretrained=True) - TensorFlow:
keras.applications.VGG16(weights='imagenet')(Keras 应用库中还内置 ResNet50、Inception 等同族模型)
从源码结构看,本仓库同目录的 TransferLearningPyTorch.ipynb 完整演示了这一步及其后续:加载后可先用一张样本图推理,输出是 1000 个 ImageNet 类别中的某一个编号;仓库还附带离线类名映射文件 imagenet_classes.json,可用于把编号翻译回类名而无需联网查询。
3. 构建面向本任务的分类模型
Notebook 给出两个框架的结构性差异提示:
- PyTorch:VGG 对象里有一个名为
classifier的插槽,把它整体替换为输出num_classes个神经元的分类器即可(配套演示中的做法是vgg.classifier = nn.Linear(25088, 2),其中 25088 = 512×7×7,是 VGG-16 特征图展平后的维度); - TensorFlow:把 VGG 当作一层放进
Sequential模型作第一层(keras.applications.VGG16(include_top=False, input_shape=(224,224,3))),后面接Flatten和自建的分类层。
替换后的模型参数结构在配套演示中被总结得很清楚:总参数量约 1500 万,但真正参与训练的只有分类头那约 5 万个参数——“用少量样本微调少量参数”正是迁移学习在小数据集上可行的核心原因。
4. 冻结特征提取器,再开始训练
Notebook 要求显式完成 “MAKE VGG Layers not trainable”:
- PyTorch:遍历
vgg.features.parameters(),将每个参数的requires_grad置为False; - TensorFlow:对作为层使用的 VGG 设
trainable = False。
冻结的必要性在配套 Notebook 中有明确解释:如果分类头未训练就与特征提取器一起端到端更新,随机初始化分类层的大误差会破坏卷积层的预训练权重。这也是后续“先冻结训练、再选择性解冻微调”策略的前提(见下文扩展阅读)。
训练时的两个实操建议来自 Notebook 原文:
- 迁移学习训练较慢,可以先只跑很少的 epoch(例如 3 个),确认流程与指标趋势后再续训提升精度;
- 训练函数不会在 epoch 结束前打印任何内容,属于正常现象,需耐心等待。
训练完成后 Notebook 提示“现在好多了”(It seems much better now),即同样的宠物图像分类任务,精度从基线 CNN 的“比较低”提升到可用水平。
可选步骤:计算 Top-3 准确率
Notebook 最后有一个可选单元格:用“上一课实验中的同样代码”计算Top-3 准确率。在 35 个外观相近的犬猫品种之间,Top-1 对“看起来像哪个品种”的区分仍然困难,而 Top-3 能更好地衡量模型是否把正确品种排进了头部候选——对宠物托管所这种“给出候选让工作人员确认”的产品形态,Top-3 往往比 Top-1 更贴近实际使用体验。
实验结论:迁移学习的适用边界
实验文档的 Takeaway 给出了两条边界判断,值得逐条记住:
- 正向结论:迁移学习与预训练网络让真实世界图像分类问题的求解“相对容易”——这正是本实验从低精度基线跃升到可用精度的机制;
- 适用边界:预训练网络对与训练分布相似的图像效果好;如果转而分类差异很大的图像(例如医学影像),结果“可能差得多”。课程主文档的配套说明补充了更细的机理:VGG-16 本身就是在包含猫狗的图片集上预训练的,所以本例复用到了网络中现成的猫狗模式;对于“工厂产线上的零件、不同树种叶片”这类与 ImageNet 分布差异更大的领域对象,应预期更低精度——此时应考虑领域数据增训或微调更深层(fine-tuning)等策略。
扩展阅读:把本实验放回课程上下文
完成 OxfordPets 实验后,建议按以下顺序在本仓库继续深入:
- TransferLearningPyTorch.ipynb:Cats vs. Dogs 数据集上的完整迁移学习演示,包含三种递进做法——VGG 特征向量 + 小分类器(约 98% 精度)、整网替换 classifier 并冻结特征层、解冻微调(先冻结训练数个 epoch 稳定分类头,再把
requires_grad改回True,并调低学习率如lr=0.0001,注意解冻初期精度会先降后升); - TransferLearningTF.ipynb:TensorFlow 版本,额外演示了逐层解冻技巧(先只解冻 VGG 卷积基的最后 4 层,因为高层包含与目标更相关的高层模式)与模型保存/加载(
model.save/load_model); - pytorchcv.py 与 tfcv.py:课程封装的训练工具,其中
train/train_long函数实现了逐 epoch 的精度/损失记录与曲线绘制——本实验“训练并绘图检查过拟合”的要求正是建立在这类工具之上;check_image_dir还会自动检出并移除损坏图片文件(Oxford 数据清洗时可直接复用); - TrainingTricks.md:课程指定的延伸阅读,覆盖数值区间保持、权重初始化(Xavier/glorot)、Batch Normalization、Dropout、过拟合抑制(早停、权重衰减、模型平均)、优化器(SGD 动量、Adagrad/Adam 族、梯度裁剪)与学习率衰减等训练技巧,与迁移学习实验中的“解冻后精度先降后升”等现象直接相关。
适用前提与限制:本实验依赖 Oxford-IIIT 宠物图片包的在线下载(约数百 MB 级 tar 包)与 VGG 预训练权重的加载,需要联网;端到端训练 VGG 级网络对 CPU 不友好,请以 GPU 环境为准。若你的目标是复现课程完整体验,优先使用仓库提供的 Binder 环境定义(binder/environment.yml)创建运行环境,再按 OxfordPets.ipynb 中“基线 CNN → 迁移学习 → 可选 Top-3”的单元格顺序逐步填空即可。
- 教程
- 人工智能
- 机器学习
- 深度学习
【免费下载链接】AI-For-Beginners
12 Weeks, 24 Lessons, AI for All!
相关推荐
AI-For-Beginners 牛津宠物犬猫品种分类实战:用 VGG-16 迁移学习训练 35 类真实图像分类器
AI For Beginners 牛津宠物犬猫品种分类实战:用 VGG 16 迁移学习训练 35 类真实图像分类器 本文基于 AI For Beginners
教程人工智能机器学习深度学习AI-For-Beginners 牛津宠物实验实战:用迁移学习构建 35 犬猫品种图像分类器
AI For Beginners 牛津宠物实验实战:用迁移学习构建 35 犬猫品种图像分类器 本篇指南基于 AI For Beginners 课程第 08 课(
教程人工智能机器学习深度学习AI-For-Beginners 迁移学习实战:用 VGG 预训练模型完成牛津宠物 35 品种图像分类
AI For Beginners 迁移学习实战:用 VGG 预训练模型完成牛津宠物 35 品种图像分类 本文围绕 AI For Beginners 课程“第 4
教程人工智能机器学习深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考