- 教程
- 人工智能
- 机器学习
- 深度学习
【免费下载链接】AI-For-Beginners
12 Weeks, 24 Lessons, AI for All!
在计算机视觉领域,从零训练一个卷积神经网络(CNN)既耗时又昂贵:它需要海量标注数据,并且大量算力被消耗在"学习如何从图片中提取通用模式"这件事上。迁移学习(Transfer Learning)正是破解这一困局的经典方案——把在 ImageNet 这样的大规模数据集上训练好的模型当作"特征提取器",仅在其顶端追加一个轻量分类器,就能在自定义图像分类任务上快速获得高精度结果。
本篇以微软 AI-For-Beginners 课程第 8 课(迁移学习课程文档)为骨架,结合仓库中的 PyTorch / TensorFlow 双版本 Notebook 与配套实验,带你完整走一遍"加载预训练模型 → 提取特征 → 替换分类层 → 冻结与微调 → 保存部署"的全流程,并延伸讲解"理想猫"可视化与对抗攻击等进阶玩法。读完本文,你将具备用 VGG/ResNet/MobileNet 快速搭建自定义分类器的实战能力。
为什么要迁移学习:训练 CNN 的高成本
训练一个 CNN 之所以"贵",在于网络的绝大部分参数都在学习通用底层特征——从低层的像素组合(如水平线、垂直线、笔触)到高层的特征组合(如眼睛、火焰)。这些特征对图像内容具有普适性:一旦在一个足够大、足够多样化的数据集上学会提取它们,网络便可以被复用到新的任务中。
于是自然产生一个问题:能否拿一个在 A 数据集上训练好的网络,直接改造用于 B 数据集的分类,而无需重新完整训练?
答案是肯定的,这正是**迁移学习(Transfer Learning)**的名字由来:把知识从一个神经网络模型"迁移"到另一个。通常的做法是:从一个在 ImageNet 这类大规模图像数据集上预训练好的模型出发,在其提取出的特征之上堆叠一个新的分类器——在很多情况下,仅凭这一步就能取得很好的效果。
补充一点:Transfer Learning 一词也出现在教育学等非计算机领域,泛指把某一领域的知识迁移应用到另一领域。
预训练模型作为特征提取器
卷积网络内部是一系列层级化堆叠的层:浅层捕捉边缘、纹理等低层模式,深层组合出更抽象的高层模式。只要在足够大且多样的数据集上训练,网络就能学到这些通用特征。Keras 与 PyTorch 都内置了加载常见架构预训练权重的函数,且绝大多数权重来自 ImageNet。上一课文档中详细列举了这些架构(见 CNN 架构文档),其中三个最具代表性的选择:
| 模型 | 特点与适用场景 |
|---|---|
| VGG-16 / VGG-19 | 结构相对简单、精度良好,是体验迁移学习工作原理的入门首选 |
| ResNet | 微软研究院 2015 年提出的残差网络家族,层数更深、资源需求更高 |
| MobileNet | 体积小巧,面向移动端;算力受限、可适当牺牲精度时选用 |
下图是 VGG-16 网络从一张猫的图片中提取出的特征可视化:
数据集:Cats vs. Dogs(猫狗大战)
本课实战使用非常贴近真实场景的Cats and Dogs(猫 vs 狗)图像分类数据集(可通过仓库 Notebook 中的下载命令获取)。该数据集包含大量猫、狗照片,是验证迁移学习能力的经典素材。
数据预处理(PyTorch 版)
PyTorch Notebook(TransferLearningPyTorch.ipynb)中,数据加载与预处理要点如下:
- 下载并解压数据集到
data目录; - 清理损坏图片:数据集中存在少量损坏文件,需要调用
check_image_dir逐目录检查剔除; - 标准化到预训练网络期望的输入分布:使用
torchvision.transforms组合变换,其中std_normalize的均值和标准差取自 ImageNet 统计值:
std_normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) trans = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), std_normalize])- 用
torchvision.datasets.ImageFolder加载data/PetImages目录,再以random_split划分出 2 万张训练样本与其余测试样本。
注意:ImageFolder 会以子目录名(Cat / Dog)自动识别类别,这是目录结构式数据集的标准做法。
数据加载(TensorFlow 版)
TensorFlow Notebook(TransferLearningTF.ipynb)则使用 Keras 内置的image_dataset_from_directory以生成器方式按批次加载数据——真实场景中图像数据集往往无法全部装入内存,生成器(generator)是更贴近工程实践的选择:
data_dir = 'data/PetImages' batch_size = 64 ds_train = keras.preprocessing.image_dataset_from_directory( data_dir, validation_split = 0.2, subset = 'training', seed = 13, image_size = (224,224), batch_size = batch_size ) ds_test = keras.preprocessing.image_dataset_from_directory( data_dir, validation_split = 0.2, subset = 'validation', seed = 13, image_size = (224,224), batch_size = batch_size )两个关键点:
- 训练/验证两次调用必须使用相同的
seed,否则随机划分不一致; - 数据集自动从目录名生成类别列表,可通过
ds_train.class_names查看(如['Cat', 'Dog'])。
实战一:PyTorch 迁移学习全流程
加载预训练模型并验证
PyTorch 中一行即可加载在 ImageNet 上预训练的 VGG-16:
vgg = torchvision.models.vgg16(pretrained=True) sample_image = dataset[0][0].unsqueeze(0) res = vgg(sample_image) print(res[0].argmax())返回的数字是 ImageNet 类别编号,可配合imagenet_classes.json类表翻译成可读的类别名(例如输出['n02123159', 'tiger_cat'],即虎斑猫)。通过summary(vgg, input_size=(1,3,224,224))可以查看 VGG-16 的网络结构:约 1.38 亿总参数,前段是卷积特征提取器(features),后段是输出 1000 类的全连接分类器(classifier)。
方案一:特征提取 + 轻量分类器
最省算力的路线是先用预训练网络离线提取特征,再在特征上训练一个小型分类器:
- 去掉最终分类层,使用
vgg.features(sample_image)获得尺寸为512×7×7的特征张量; - 用
DataLoader分批前向计算,把特征存入feature_tensor、标签存入label_tensor; - 在特征之上定义一个单层全连接分类器(如
Linear(512*7*7, 2) + LogSoftmax),即可在猫狗分类上达到约 98% 的验证精度。
net = torch.nn.Sequential(torch.nn.Linear(512*7*7,2),torch.nn.LogSoftmax()).to(device) history = train(net,train_loader,test_loader)这一路线几乎不需要训练 GPU 资源,因为预训练网络的权重完全不动。
方案二:端到端替换分类层并冻结特征提取器
另一种做法是保留 VGG-16 整体参与训练,但只替换并训练最后分类器:
vgg.classifier = torch.nn.Linear(25088,2).to(device) for x in vgg.features.parameters(): x.requires_grad = False此时模型总参数约 1500 万,但可训练参数仅约 5 万(即新分类层的权重)。冻结卷积特征提取器的原因很关键:否则未训练的随机分类层会在大误差回传中摧毁预训练权重。训练完成后可用torch.save(vgg, 'data/cats_dogs.pth')保存模型,随时用torch.load重新加载,避免实验破坏模型后重头再来。
微调(Fine-tuning)
如果自定义对象与 ImageNet 图片差异较大,仅靠冻结特征可能不够。此时可以解冻卷积层继续训练:
for x in vgg.features.parameters(): x.requires_grad = True train_long(vgg, train_loader, test_loader, loss_fn=torch.nn.CrossEntropyLoss(), epochs=1, print_freq=90, lr=0.0001)注意事项:
- 先冻结训练数轮、稳定分类层权重,再解冻,否则大误差会破坏预训练卷积权重;
- 解冻后训练明显变慢(梯度需穿过全部深层回传),且即使使用较低学习率,训练初期精度也可能先下降,随后才超越冻结方案。
实战二:TensorFlow / Keras 迁移学习全流程
加载与预处理
Keras 通过keras.applications暴露预训练模型。与 PyTorch 类似,输入前必须先按模型要求预处理:
vgg = keras.applications.VGG16(weights=_weights_path) inp = keras.applications.vgg16.preprocess_input(x_sample[:1]) res = vgg(inp) print(f"Most probable class = {tf.argmax(res,1)}") keras.applications.vgg16.decode_predictions(res.numpy())要点:
preprocess_input会按 VGG 原始训练时的预处理方式归一化(逐通道减去均值),不可省略;decode_predictions返回概率最高的前 n 个类别及其名称,便于读懂原始输出。
特征提取 + 轻量分类器
用VGG16(include_top=False)得到不含分类层的特征提取器,特征张量尺寸为7×7×512。配合 TensorFlow 的 Dataset API,可用map把原始图像数据集变换为特征数据集:
ds_features_train = ds_train.take(50).map(lambda x,y : (vgg(x),y))随后在特征之上堆叠一个带sigmoid激活的 Dense 层,用binary_crossentropy损失训练即可(猫狗二分类),一次训练即可获得约 95% 的验证精度。
端到端训练与冻结
Keras 的妙处在于:VGG-16 模型本身也可以作为一个"层"嵌入新网络:
model = keras.models.Sequential() model.add(keras.applications.VGG16(include_top=False,input_shape=(224,224,3))) model.add(keras.layers.Flatten()) model.add(keras.layers.Dense(1,activation='sigmoid')) model.layers[0].trainable = False通过model.layers[0].trainable = False冻结特征提取器,此时 1500 万参数中仅约 2.5 万参与训练。训练完成后可用model.save('data/cats_dogs.tf')与keras.models.load_model保存/恢复。
微调
解冻时不必一次性放开全部卷积层,可以先只放开最后 4 层(它们编码了与目标任务更相关的高层模式):
model.layers[0].trainable = True for i in range(len(model.layers[0].layers)-4): model.layers[0].layers[i].trainable = False此时可训练参数显著增加(约 710 万,占总参数一半),配合较低学习率再训练若干轮即可微调出更高精度。GPU 可用tf.config.list_physical_devices('GPU')检查,Keras 会在可用时自动启用 GPU 加速。
更深的网络:ResNet 与 Batch Normalization
VGG-16 只是计算机视觉架构的入门款。PyTorch 与 Keras 还提供了更多预训练网络,其中使用最频繁的包括微软的ResNet与 Google 的Inception。以 PyTorch 的torchvision.models.resnet18()(或 Keras 的keras.applications.ResNet50())为例,其结构与 VGG 遵循同样的范式:卷积特征提取器 + 最终分类器(fc),因此可以完全照搬上面的迁移学习流程,只需把基座模型换成 ResNet 即可观察精度变化(想体会真正的深模型,可以尝试 ResNet-151/ResNet-152)。
这些网络中多了一种新层:Batch Normalization(批归一化)。其核心思想是:深度网络训练时各层数值容易漂移出合理区间,而批归一化层会计算当前 mini-batch 上全部值的均值与标准差,据此在送入下一层前对信号做归一化,从而显著提升深网络训练的稳定性。
进阶玩法一:可视化"理想猫"
预训练网络的"大脑"里其实存放着各种抽象概念——包括一只理想的猫(以及理想的狗、理想的斑马等)。能否把这幅图像可视化出来?难点在于模式分散在数以百万计的权重中,且呈层级结构组织。
一个可行方法是:从一张随机噪声图出发,用梯度下降优化技术调整图像,直到网络认为它是猫。
但直接这样做的结果会非常接近随机噪声——因为"让网络认为输入是猫"的路径有无数条,其中很多在视觉上毫无意义。这些图像虽然包含大量猫的特征模式,却没有任何约束让它们看起来像猫。
改进方案是往损失函数中加入一个**variation loss(变化损失)**项。它衡量图像相邻像素的相似程度:最小化 variation loss 会让图像更平滑、消除噪声,从而暴露出更赏心悦目的模式。下图是分别以高概率被分类为"猫"和"斑马"的"理想图像":
| 理想猫 | 理想斑马 |
进阶玩法二:对抗攻击(Adversarial Attacks)
类似的思路可以反其道而行之:构造对抗样本。假设我们要欺骗网络,让它把狗认成猫——取一张被网络正确识别为狗的图片,用梯度下降对它做微小改动,直到网络开始把它分类为猫:
| 狗的原始图片 | 被分类为猫的狗图片 |
令人惊讶的是,上述"理想斑马"与对抗样本的视觉风格截然不同,这揭示了网络架构在对象识别方式中扮演着重要角色。上述所有结果的复现代码都在 AdversarialCat_TF.ipynb 中,其中还演示了如何把简单的梯度下降替换为 Keras 内置优化器(apply_gradients)来加速优化。课程也建议你尝试对 ResNet 做同样的对抗攻击,并对比结果。
延伸阅读:让训练更高效的技巧
迁移学习之外,TrainingTricks.md 系统整理了深度网络训练的关键技巧:
- 数值区间控制:让网络内所有数值保持在合理尺度(如 [-1,1] 或 [0,1]),避免浮点精度问题;
- 权重初始化:推荐 Xavier 初始化(
glorot,即N(0,√(2/(n_in+n_out))))等,使信号在前向/反向传播中保持稳定,对抗梯度消失/爆炸; - Dropout:训练时随机丢弃 10%–50% 的神经元,既能把优化带出局部极小,也可视为隐式模型平均,Dropout.ipynb 中展示了它在 MNIST 上的加速与提精度效果;
- 过拟合预防:早停(Early Stopping)、显式权重衰减/正则化、模型平均;
- 优化器选择:动量 SGD、Adagrad、RMSProp 与 Adam——没有把握时直接用 Adam;
- 梯度裁剪与学习率衰减:当梯度范数超过阈值 θ 时按范数归一;随训练轮次乘以 0.98 等系数逐步降低学习率。
课程作业:用 Oxford-IIIT 宠物数据集实战
学完上述内容后,课程配套作业(lab/README.md)会把你推向更真实的场景:为宠物店开发一款"拍照自动识别品种"的应用,使用包含35 个猫狗品种的Oxford-IIIT Pets数据集完成迁移学习分类器。
数据获取命令(下载images.tar.gz并解压,具体下载地址见 lab 文档)后,即可打开 OxfordPets.ipynb 开始实验。作业的核心收获在于:预训练网络在同类图像上效果很好,但一旦切换到差异巨大的领域(如医学影像),效果往往会大打折扣——这提醒我们迁移学习也有边界。
结论与挑战
借助迁移学习,你能够在短时间内为自定义物体分类任务构建出高精度的分类器。不过也要看到:这类更复杂的任务对算力要求明显提升,难以在 CPU 上轻松完成——这正是下一课尝试用更轻量实现、以略低精度换取更低计算资源的原因。
课后挑战:两个 Notebook 末尾都提到,迁移学习在与训练数据相似的数据上表现最佳(例如识别一种新动物)。不妨用完全新颖的图像类型做实验,观察你的迁移模型表现如何,以此体会迁移学习的适用边界。
自测与回顾:完成 TrainingTricks.md 的阅读,并尝试使用其他基座架构(ResNet、MobileNet)复现整套流程,对比精度的变化。
- 教程
- 人工智能
- 机器学习
- 深度学习
【免费下载链接】AI-For-Beginners
12 Weeks, 24 Lessons, AI for All!
相关推荐
AI-For-Beginners 课程实战:预训练网络与迁移学习(Transfer Learning)完整指南
AI For Beginners 课程实战:预训练网络与迁移学习(Transfer Learning)完整指南 本指南基于 AI For Beginners 课
教程人工智能机器学习深度学习QQ空间数据备份:3分钟把历史说说导出成Excel
QQ空间数据备份:3分钟把历史说说导出成Excel 三年后想翻翻当年发的QQ空间,发现平台上没给你留任何存档——自己不做QQ空间数据备份,记录就真没了。GetQ
教程人工智能机器学习深度学习ML-For-Beginners迁移学习:预训练模型微调
ML For Beginners迁移学习:预训练模型微调 你是否还在为训练高性能机器学习模型而烦恼?数据不足、算力不够、训练时间过长?本文将带你探索迁移学习(T
教程机器学习人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考