认识迁移学习 

迁移学习(Transfer Learning)是机器学习中的一种重要技术,其核心思想是将在一个任务上学习到的知识(模型参数、特征表示等),迁移应用到另一个相关但不同的任务中,从而提升新任务的学习效率和性能,尤其是在新任务数据有限的情况下。

一、迁移学习的核心动机

传统机器学习通常要求为每个新任务收集大量标注数据并从头训练模型,但现实中面临以下挑战:

  1. 数据稀缺:例如医疗影像分析(罕见疾病样本少)、自动驾驶(危险场景难收集)等领域标注数据昂贵且难以获取;

  2. 计算成本高:从头训练复杂模型(如大型神经网络)需要大量算力和时间;

  3. 知识复用难:人类可以快速将已有的知识(如 “认识猫”)迁移到新任务(如 “区分猫和狗”),但传统模型缺乏这种能力。

迁移学习通过 “知识迁移” 打破这些限制,让模型能 “举一反三”。

二、迁移学习的基本原理

迁移学习的可行性基于一个重要发现:神经网络学习的底层特征具有通用性。例如:

  • 图像领域:预训练模型的前几层通常学习到通用的低级特征(如边缘、纹理),这些特征对不同图像任务(分类、检测、分割)都有帮助;

  • 自然语言处理:预训练语言模型(如 BERT)学习到的词法、句法信息可迁移到多种下游任务(文本分类、问答)。

根据知识迁移的方式,迁移学习主要分为以下几类:

三、迁移学习的主要类型

1. 基于预训练模型的迁移(最常见)

  • 做法: 先在大规模数据(如 ImageNet、Wikipedia)上训练一个基础模型(如 ResNet、BERT),然后在目标任务(如医疗影像分类)上微调(Fine-tune)模型参数。

    • 冻结部分层:通常冻结预训练模型的前几层(提取通用特征),只训练后面几层(适应特定任务);

    • 全量微调:数据充足时,可微调所有参数。

  • 应用:计算机视觉(如用预训练 ResNet 识别特定领域图像)、NLP(如用 BERT 做文本分类)。

2. 特征提取迁移

  • 做法: 使用预训练模型作为 “特征提取器”,将输入数据通过模型转换为固定维度的特征向量,再用这些特征训练新的简单模型(如 SVM、逻辑回归)。

    • 示例:用预训练 CNN 提取图像特征,然后用线性分类器完成特定分类任务。

  • 优势:无需微调复杂模型,适用于计算资源有限的场景。

3. 多任务学习(Multi-Task Learning)

  • 做法: 同时训练多个相关任务(如 “图像分类” 和 “目标检测”),共享底层特征提取层,让模型在学习中发现任务间的共性知识。

  • 应用:推荐系统(同时优化点击率和转化率)、多语言 NLP(共享跨语言表示)。

4. 领域适应(Domain Adaptation)

  • 场景: 源领域(如网络图片)和目标领域(如医疗影像)数据分布不同,但任务相似(如分类)。

  • 做法: 通过对抗训练(如 GAN)或特征对齐,使模型忽略领域差异,学习到领域无关的通用特征。

四、迁移学习的典型应用场景

  • 计算机视觉

    • 医学影像分析:用 ImageNet 预训练模型识别 X 光片 / CT 中的病变;

    • 遥感图像识别:用预训练模型检测卫星图像中的建筑物、植被等。

  • 自然语言处理

    • 小语种任务:用英语预训练的 BERT 模型微调用于阿拉伯语、中文等;

    • 特定领域 NLP:用通用预训练模型处理法律、金融等专业领域文本。

  • 语音识别

    • 低资源方言识别:用普通话预训练模型迁移到粤语、四川话等方言。

  • 强化学习

    • 机器人控制:在仿真环境中训练的策略迁移到真实机器人上。

五、迁移学习的关键挑战

  1. 负迁移(Negative Transfer) 如果源任务与目标任务差异过大,迁移可能反而降低性能(如用 “猫狗分类” 模型迁移到 “癌细胞识别”)。

  2. 领域差异 源领域和目标领域的数据分布不同时(如合成图像→真实图像),需通过领域适应技术对齐。

  3. 任务相关性评估 如何量化两个任务的相关性,以确定迁移是否有效,仍是研究热点。

六、与传统机器学习的对比

维度 传统机器学习 迁移学习
数据需求 每个任务需大量标注数据 可利用其他任务数据,目标任务数据需求减少
训练方式 从头训练模型 复用预训练模型或知识
任务独立性 任务间无知识共享 任务间共享特征或参数
应用场景 数据充足的标准场景 数据稀缺、跨领域、小样本等场景

总结

迁移学习通过 “知识复用” 打破了传统机器学习 “每个任务孤立训练” 的限制,尤其适合数据有限或计算资源受限的场景。从预训练模型微调(如 BERT、GPT)到跨领域知识迁移,它已成为现代 AI 的核心技术之一,推动了医疗、自动驾驶、NLP 等领域的快速发展。未来,随着多模态预训练模型(如 CLIP、GPT-4)的兴起,迁移学习的应用范围将进一步扩大。

预训练模型

预训练模型(Pretrained Model) 是指在大规模基准数据集(如 ImageNet、Wikipedia、COCO 等)上预先训练好的神经网络模型。这些模型已经学习到了通用的特征表示(如视觉模式、语言结构),可通过迁移学习快速适配到其他特定任务中。以下是详细解析:

1. 核心概念与原理

  • 预训练(Pretraining)
    在大规模数据上训练模型,使其自动学习数据的通用模式(如图像中的边缘、纹理,文本中的语法结构)。
  • 迁移学习(Transfer Learning)
    将预训练模型的知识迁移到目标任务(如猫狗分类、情感分析),大幅减少训练所需的数据量和计算资源。
  • 核心优势
    利用他人的计算资源和海量数据,避免从零开始训练模型,尤其适合数据有限的场景。

2. 预训练模型的类型

根据处理的数据类型和任务,预训练模型主要分为以下几类:

(1)计算机视觉模型

模型名称 预训练数据集 适用任务 特点
VGG16/19 ImageNet(1400 万张) 图像分类、目标检测 结构简单,适合初学者,参数量大(138M+)。
ResNet50/101 ImageNet 图像分类、分割、检测 引入残差结构,可训练更深的网络,参数量适中(25M+)。
EfficientNet ImageNet 图像分类、移动端部署 轻量级设计,参数量少(5M~77M)但性能优异。
ViT (Vision Transformer) ImageNet-21K 图像分类、自监督学习 基于 Transformer 架构,无需卷积,参数量极大(300M+)。

(2)自然语言处理模型

模型名称 预训练数据 适用任务 特点
BERT Wikipedia、BookCorpus 文本分类、问答系统 双向 Transformer,擅长理解文本上下文。
GPT-3/4 互联网文本 文本生成、对话系统 单向 Transformer,生成能力强,参数量超千亿。
XLNet 大规模语料库 文本分类、阅读理解 结合自回归和自编码优点,处理长文本能力强。
T5 多任务混合数据 文本生成、翻译 统一为 “文本到文本” 框架,灵活性高。

(3)多模态模型

模型名称 预训练数据 适用任务 特点
CLIP 4 亿张图像 - 文本对 零样本图像分类、检索 学习图像与文本的关联,支持无标注数据的任务。
DALL-E 2 图像 - 文本对 文本到图像生成 基于 CLIP,可根据文本描述生成高质量图像。
FLAVA 图像、文本、图像 - 文本对 多模态理解、生成 统一架构处理多种模态,参数量超百亿。

3. 预训练模型的使用方式

(1)特征提取(Feature Extraction)

  • 冻结预训练模型,仅使用其提取的特征训练新的分类器(如全连接层)。
  • 适用场景:目标任务数据少,希望复用底层特征(如图像边缘、语言词汇)。
# 以ResNet50为例
from tensorflow.keras.applications import ResNet50

base_model = ResNet50(weights='imagenet', include_top=False)
base_model.trainable = False  # 冻结所有层

# 添加自定义分类器
model = tf.keras.Sequential([
    base_model,
    tf.keras.layers.GlobalAveragePooling2D(),
    tf.keras.layers.Dense(256, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')  # 二分类
])

(2)微调(Fine-Tuning)

  • 解冻部分或全部预训练层,在目标任务数据上继续训练,更新权重。
  • 适用场景:目标任务数据充足,需模型适应特定领域的特征(如医学图像、专业文本)。
# 解冻最后几层进行微调
for layer in base_model.layers[-4:]:
    layer.trainable = True

# 编译模型,使用较小学习率
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),
              loss='binary_crossentropy',
              metrics=['accuracy'])

(3)零样本学习(Zero-Shot Learning)

  • 直接使用预训练模型,无需额外训练,通过文本提示完成任务(如 CLIP 模型)。
  • 适用场景:数据极少或需快速验证的场景。
# CLIP模型示例(伪代码)
import torch
import clip

model, preprocess = clip.load("ViT-B/32")
image = preprocess(Image.open("cat.jpg")).unsqueeze(0)
text = clip.tokenize(["a photo of a cat", "a photo of a dog"])

with torch.no_grad():
    image_features = model.encode_image(image)
    text_features = model.encode_text(text)
    logits_per_image, _ = model(image, text)
    probs = logits_per_image.softmax(dim=-1)

print(f"Probability of being a cat: {probs[0][0].item():.4f}")

4. 预训练模型的优势

  • 数据效率

    在小数据集上也能达到良好性能,例如:

    • 从头训练 CNN 需要数万张图像,而迁移预训练模型仅需几百张。
  • 计算成本

    节省训练时间和资源,例如:

    • 在 GPU 上从头训练 ResNet50 需要数天,而微调仅需数小时。
  • 泛化能力

    预训练模型学习的通用特征具有更强的泛化性,减少过拟合风险。

  • 任务适应性

    同一预训练模型可快速适应多种下游任务(如分类、检测、分割)。

5. 预训练模型的局限性

  • 领域差距

    若预训练数据与目标任务领域差异大(如医学图像 vs. ImageNet),效果可能受限。

    • 解决方案:使用领域特定的预训练模型(如 BioBERT、CheXNet)。
  • 模型大小

    大型预训练模型(如 GPT-4)需大量内存和计算资源,部署困难。

    • 解决方案:使用轻量级变体(如 MobileBERT)或模型量化技术。
  • 版权与合规

    部分预训练模型受许可证限制,商业使用需付费或遵守特定条款。

6. 常见预训练模型资源

平台 / 库 支持的预训练模型类型 示例模型
TensorFlow Hub 图像、文本、音频模型 ResNet、EfficientNet、BERT
Hugging Face 自然语言处理(NLP)模型 BERT、GPT、T5、CLIP
PyTorch Hub 图像、音频、视频模型 ResNet、ViT、YOLO
NVIDIA NGC 优化的深度学习模型(含推理加速) Mask R-CNN、BERT Large
Model Zoo 计算机视觉模型 VGG、ResNet、SSD

总结

预训练模型是深度学习的核心工具,通过复用已有知识,显著降低了模型开发的门槛和成本。在实际应用中,你可以根据任务需求选择合适的预训练模型,通过微调或特征提取快速适配到目标场景,尤其适合数据有限的情况。理解预训练模型的原理和使用方式,是构建高效深度学习系统的关键。

有哪些常见的预训练模型?

以下是计算机视觉、自然语言处理、音频处理等领域中常见的预训练模型及其应用场景:

一、计算机视觉(CV)领域

1. 图像分类 / 检测

  • VGG16/19

    • 结构简单(仅由 3×3 卷积堆叠),适合新手理解 CNN 架构。
    • 预训练权重可用于迁移学习(如猫狗分类)。
  • ResNet(ResNet50/101/152)

    • 通过残差连接解决深层网络梯度消失问题,支持 1000 + 层。
    • 广泛用于图像分类、目标检测(如 Faster R - CNN 的骨干网络)。
  • EfficientNet(B0 - B7)

    • 轻量级架构,通过复合缩放(同时调整深度、宽度、分辨率)平衡性能与效率。
    • 适合移动端或边缘设备部署。
  • Vision Transformer(ViT)

    • 将 Transformer 架构引入视觉领域,通过 Patch Embedding 处理图像。
    • 在大规模数据集(如 ImageNet - 21K)上预训练效果优异。

2. 目标检测

  • YOLO 系列(YOLOv5/YOLOv8)

    • 实时目标检测,速度快(YOLOv8 在 RTX 3090 上可达 220 FPS)。
    • 支持自定义数据集训练,广泛用于安防监控。
  • Faster R - CNN

    • 两阶段检测算法,精度高但速度较慢。
    • 适合对准确率要求高的场景(如医疗影像检测)。

3. 分割任务

  • U - Net

    • 编码器 - 解码器结构,专为医学图像分割设计。
    • 常用于细胞、器官分割。
  • DeepLabv3+

    • 基于 ResNet 骨干网络,结合空洞卷积捕获多尺度特征。
    • 适用于语义分割(如自动驾驶中的道路分割)。

二、自然语言处理(NLP)领域

1. 通用预训练模型

  • BERT(Bidirectional Encoder Representations from Transformers)

    • 双向 Transformer 架构,通过掩码语言模型(MLM)和下一句预测(NSP)预训练。
    • 支持多种下游任务:文本分类、命名实体识别(NER)、问答系统。
  • GPT 系列(GPT - 3.5/GPT - 4)

    • 自回归语言模型,单向 Transformer 解码器。
    • 以强大的文本生成能力著称,支持零样本 / 少样本学习。
  • LLaMA 系列(LLaMA - 2)

    • Meta 开源大模型,参数规模从 7B 到 70B 不等。
    • 基于 Transformer 架构,在多语言任务上表现优异。

2. 特定任务模型

  • T5(Text - to - Text Transfer Transformer)

    • 将所有 NLP 任务统一为文本到文本的转换,如 “翻译:英语→法语”。
    • 适合多任务学习。
  • XLNet

    • 结合自回归和自编码优点,通过排列语言模型(PLM)预训练。
    • 在长文本理解任务(如文档摘要)中表现突出。

三、多模态领域

  • CLIP(Contrastive Language - Image Pretraining)

    • 联合训练图像编码器和文本编码器,学习图像与文本的关联。
    • 支持零样本图像分类(如根据文本描述识别图像)。
  • DALL - E 2/Stable Diffusion

    • 文本到图像生成模型,基于扩散概率模型(Diffusion Model)。
    • 可根据文本描述生成高质量图像。
  • BERT - Vision - Language(VL - BERT)

    • 融合图像特征(如 Faster R - CNN 提取的区域特征)和文本特征。
    • 用于视觉问答(VQA)、图像描述生成。

四、音频处理领域

  • Wav2Vec 2.0

    • 自监督学习模型,通过对比学习预训练音频特征。
    • 支持语音识别(ASR)、语音分类等任务。
  • Hubert

    • 基于隐藏单元 BERT(Hidden - unit BERT)的音频预训练模型。
    • 在低资源语言的语音识别中表现优异。

五、选择建议

  • 任务匹配
    如分类任务优先选 ResNet/VGG,生成任务选 GPT/DALL - E。
  • 数据规模
    小数据集用轻量级模型(如 MobileNet),大数据集可用 ViT/BERT。
  • 计算资源
    移动端选 EfficientNet,GPU 集群可用 LLaMA - 2。

通过迁移学习利用预训练模型,可大幅降低训练成本并提升性能。

冻结和解冻

在深度学习中,冻结(Freezing) 和 解冻(Unfreezing) 是迁移学习中调整预训练模型的重要操作,用于控制哪些层的参数参与训练。以下是详细解析:

1. 核心概念

冻结(Freezing)

  • 操作:将预训练模型的某些层(通常是卷积层或编码器)的参数设置为不可训练(trainable = False)。
  • 效果:在训练过程中,这些层的权重保持不变,仅更新未冻结层的参数。

解冻(Unfreezing)

  • 操作:将之前冻结的层重新设置为可训练(trainable = True)。
  • 效果:允许这些层的参数在训练中更新,通常用于微调(Fine-Tuning)。

2. 为什么需要冻结和解冻?

冻结的作用

  1. 快速适应小数据集
    当目标任务数据有限时,冻结预训练模型的底层(已学习通用特征),仅训练顶层(适应特定任务),避免过拟合。
  2. 节省计算资源
    减少需要训练的参数数量,加速训练过程(如冻结 ResNet50 的前 100 层)。

解冻的作用

  1. 微调高级特征
    在目标任务数据充足时,解冻部分或全部层,让模型进一步学习特定领域的特征(如医学图像中的细胞结构)。
  2. 提升模型性能
    通过微调,模型可以适应预训练数据中未包含的模式,提高准确率。

3. 何时冻结?何时解冻?

场景 推荐策略
数据量极少(几百张图像) 冻结所有预训练层,仅训练自定义分类器(如全连接层)。
数据量中等(几千张图像) 先冻结所有层训练分类器,再解冻最后几层(如 ResNet 的最后一个卷积块)微调。
数据量充足(数万张图像) 解冻全部层,使用小学习率整体微调。
预训练与目标任务领域差异大 解冻更多层(如医学图像任务解冻大部分卷积层)。

4. 代码实现示例

(1)冻结预训练模型的全部层

from tensorflow.keras.applications import ResNet50

# 加载预训练模型
base_model = ResNet50(weights='imagenet', include_top=False)

# 冻结所有层
for layer in base_model.layers:
    layer.trainable = False

# 添加自定义分类器(仅训练这部分)
model = tf.keras.Sequential([
    base_model,
    tf.keras.layers.GlobalAveragePooling2D(),
    tf.keras.layers.Dense(256, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')  # 二分类
])

(2)解冻最后几层进行微调

# 解冻最后几层(如最后4层)
for layer in base_model.layers[-4:]:
    layer.trainable = True

# 重新编译模型,使用小学习率
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),
              loss='binary_crossentropy',
              metrics=['accuracy'])

(3)逐层解冻策略(进阶)

# 第一阶段:冻结所有层,训练分类器
for layer in base_model.layers:
    layer.trainable = False
model.fit(...)

# 第二阶段:解冻最后一个卷积块
for layer in base_model.layers[-16:]:  # ResNet最后一个卷积块约16层
    layer.trainable = True
model.compile(optimizer=Adam(1e-5), ...)
model.fit(...)

# 第三阶段:解冻更多层(可选)
for layer in base_model.layers[-32:]:
    layer.trainable = True
model.compile(optimizer=Adam(1e-6), ...)  # 更小的学习率
model.fit(...)

5. 冻结和解冻的注意事项

  • 编译模型的影响

    • 在修改 trainable 状态后,必须重新编译模型才能生效。
    • 重新编译时,建议降低学习率(如从 1e-3 降至 1e-5),避免剧烈更新预训练权重。
  • 层解冻的粒度

    • 通常按 “块” 解冻(如 ResNet 的卷积块),而非单独层,以保持特征的完整性。
    • 底层(如前几层卷积)通常保留通用特征,无需解冻;高层(靠近输出)更专注特定任务。
  • 防止灾难性遗忘

    • 若一次性解冻过多层且学习率过大,模型可能忘记预训练的知识,导致性能下降。

6. 可视化对比:冻结 vs. 解冻

假设预训练模型有 100 层,目标任务是猫狗分类:

策略 可训练参数比例 训练速度 适合场景 典型准确率(小数据集)
冻结全部层 ~1% 极快 数据极少(<1000 张) ~85%
解冻最后 10 层 ~5% 较快 数据中等(1000~5000 张) ~92%
解冻全部层 100% 数据充足(>10000 张) ~95%

总结

  • 冻结:固定预训练模型的参数,快速适应小数据场景,减少过拟合。
  • 解冻:释放部分参数,让模型学习特定领域特征,提升性能。
   合理使用冻结和解冻策略,能在不同数据规模下平衡训练效率和模型准确率,是迁移学习中的核心技巧。

在迁移学习中,“先冻结所有层,再解冻某些层” 是一种常见且推荐的策略,但并非唯一方式。这种策略的核心是通过分阶段训练,平衡模型对通用特征的保留和对目标任务的适配,尤其适合数据量有限的场景。以下是具体分析:

1. 为什么 “先冻结所有层,再解冻部分层” 是主流?

这种策略的逻辑基于预训练模型的特征学习规律:
预训练模型(如 ResNet、VGG)的底层通常学习通用特征(如边缘、纹理、颜色),高层学习更抽象的任务相关特征(如 “猫的耳朵”“狗的爪子”)。

  • 第一阶段:冻结所有层,训练新分类器

    • 目的:用预训练的特征提取能力,快速适配目标任务的分类逻辑(如猫狗的区分)。
    • 优势:避免小数据集下的过拟合(预训练权重已学习稳定特征),训练速度快。
  • 第二阶段:解冻部分高层,微调

    • 目的:让模型的高层特征(更贴近具体任务)进一步适应目标数据的细节(如不同品种猫狗的差异)。
    • 优势:在不破坏底层通用特征的前提下,提升模型对特定任务的拟合能力,精度更高。

2. 其他常见策略(根据数据量调整)

  • 仅冻结底层,直接训练高层(适用于中等数据量)
    例如:冻结 ResNet 的前 50 层(通用特征),让后 50 层(抽象特征)和分类器一起训练。
    适合场景:目标任务与预训练任务(如 ImageNet)有一定差异,但数据量足够支撑部分层的更新。

  • 完全不冻结(适用于大数据量)
    直接用预训练模型初始化权重,所有层参与训练(学习率调小)。
    适合场景:目标任务数据量极大(如数万张猫狗图),模型可以在保留通用特征的同时,完全适配新任务。

  • 逐层解冻(进阶策略)
    先冻结所有层训练分类器,再解冻最后 1 个卷积块,训练后再解冻倒数第 2 个卷积块,逐步微调。
    优势:避免一次性解冻过多层导致的 “灾难性遗忘”(忘记预训练的有用特征)。

3. 总结:策略选择的核心依据

目标任务数据量 推荐策略(以猫狗分类为例) 核心逻辑
极少(几百张) 只冻结所有层,训练分类器(不微调) 优先保证不 overfit
中等(几千张) 先冻结所有层训练分类器,再解冻最后 1-2 个卷积块微调 平衡通用特征与任务适配
充足(数万张以上) 解冻全部层,小学习率整体微调 完全适配目标任务细节
 

结论:“先冻结所有层,再解冻部分层” 是迁移学习中最通用、最安全的策略,尤其适合数据量有限的场景(如几百到几千张猫狗图)。它既能利用预训练模型的优势,又能通过微调逐步提升精度,是工程实践中的首选。

自定义分类器

在迁移学习中,自定义分类器(Custom Classifier)是针对目标任务新设计的输出层或多层结构,用于将预训练模型提取的通用特征转换为目标任务的预测结果(如分类标签)。它不属于预训练模型的一部分,而是根据具体任务需求新增的组件。

1. 自定义分类器的核心作用

预训练模型(如 ResNet、VGG)的原始输出层是为其训练任务设计的(例如 ImageNet 的 1000 类分类),而迁移学习的目标任务(如猫狗分类)通常类别不同,因此需要用自定义分类器替换原始输出层,实现:

 
  • 将预训练模型提取的高维特征(如 2048 维向量)映射到目标任务的类别空间(如 2 类:猫 / 狗)。
  • 学习目标任务的特定分类逻辑(如 “猫的面部特征” 与 “狗的面部特征” 的区分规则)。

2. 为什么自定义分类器不属于预训练模型?

  • 训练目标不同
    预训练模型的原始输出层是为大规模通用数据集(如 ImageNet)训练的,而自定义分类器是为具体目标任务(如猫狗、疾病诊断)训练的,两者任务差异可能很大。

  • 结构不同
    例如:

    • 预训练 ResNet 的输出层是 Dense(1000, activation='softmax')(对应 1000 类)。
    • 猫狗分类的自定义分类器可能是 Dense(1, activation='sigmoid')(二分类)。
  • 参数来源不同
    预训练模型的参数来自大规模数据集的预训练,而自定义分类器的参数是在目标任务数据上从零开始训练的(或随机初始化后训练)。

3. 自定义分类器的常见结构

根据目标任务的复杂度,自定义分类器可以是简单的单层,也可以是多层结构:

(1)简单分类器(适用于多数场景)

from tensorflow.keras.applications import ResNet50

# 加载预训练模型(不含原始输出层)
base_model = ResNet50(weights='imagenet', include_top=False)

# 自定义分类器(新增部分)
model = tf.keras.Sequential([
    base_model,  # 预训练的特征提取部分
    tf.keras.layers.GlobalAveragePooling2D(),  # 压缩特征图为向量
    tf.keras.layers.Dense(1, activation='sigmoid')  # 自定义输出层(二分类)
])

(2)复杂分类器(适用于数据量较大或任务复杂时)

model = tf.keras.Sequential([
    base_model,
    tf.keras.layers.GlobalAveragePooling2D(),
    tf.keras.layers.Dense(256, activation='relu'),  # 新增隐藏层
    tf.keras.layers.Dropout(0.5),  # 防止过拟合
    tf.keras.layers.Dense(1, activation='sigmoid')  # 输出层
])

4. 自定义分类器与预训练模型的协作流程

  1. 预训练模型负责特征提取
    输入图像经过预训练模型的卷积层、池化层等,输出高维特征向量(如 ResNet50 的输出为 (7,7,2048) 的特征图,经池化后为 2048 维向量)。

  2. 自定义分类器负责决策输出
    将高维特征向量通过全连接层、激活函数等,转换为目标任务的预测结果(如 “猫” 的概率为 0.92)。

5. 总结

  • 自定义分类器:是为目标任务新增的 “决策层”,用于将预训练模型的通用特征转换为具体任务的输出,参数从零开始训练。
  • 与预训练模型的关系:预训练模型提供 “特征提取能力”,自定义分类器提供 “任务适配能力”,两者协作完成迁移学习。
 

这种 “预训练特征提取 + 自定义分类器” 的模式,正是迁移学习高效的核心原因 —— 无需重复学习通用特征,只需聚焦于目标任务的特有规律。

自定义分类器,那原来预训练模型的分类器咋办?

在迁移学习中,预训练模型的原始分类器会被移除或替换,一般在导入时会直接不导入,因为它是为原始任务(如 ImageNet 的 1000 类分类)设计的,通常不适用于目标任务(如猫狗二分类)。以下是具体处理方式和原因:

1. 为什么要移除原始分类器?

  • 类别数量不匹配
    例如,ImageNet 预训练模型的输出层可能有 1000 个神经元(对应 1000 类),而猫狗分类只需 1 个神经元(二分类)或 2 个神经元(多分类)。

  • 特征抽象程度不同
    预训练模型的最后几层可能针对原始任务过度拟合(如区分 “非洲象” 和 “亚洲象”),而目标任务可能需要更通用的特征(如区分 “猫” 和 “狗”)。

  • 避免干扰
    如果保留原始分类器,模型可能会优先学习与目标任务无关的模式,导致性能下降。

2. 如何处理原始分类器?

(1)直接移除(最常见)

在加载预训练模型时,通过 include_top=False 参数排除原始分类器(通常是全连接层),只保留特征提取部分(卷积基)。

 
from tensorflow.keras.applications import ResNet50

# 加载预训练模型(不含原始分类器)
base_model = ResNet50(weights='imagenet', include_top=False)

(2)替换为自定义分类器

在预训练模型的特征提取部分后,添加自定义分类器:

  
model = tf.keras.Sequential([
    base_model,  # 预训练的特征提取部分
    tf.keras.layers.GlobalAveragePooling2D(),  # 压缩特征图
    tf.keras.layers.Dense(256, activation='relu'),  # 自定义隐藏层
    tf.keras.layers.Dropout(0.5),
    tf.keras.layers.Dense(1, activation='sigmoid')  # 自定义输出层(二分类)
])

3. 保留原始分类器的特殊场景

在极少数情况下,你可能希望保留原始分类器的一部分:

  • 多任务学习:原始分类器用于一个任务,同时添加新分类器用于另一个任务。

    # 假设原始模型有两个输出分支
    base_model = ...  # 加载预训练模型
    
    # 保留原始分类器(用于任务1)
    output1 = base_model.output
    
    # 添加新分类器(用于任务2)
    x = tf.keras.layers.GlobalAveragePooling2D()(base_model.output)
    output2 = tf.keras.layers.Dense(1, activation='sigmoid')(x)
    
    model = tf.keras.Model(inputs=base_model.input, outputs=[output1, output2])
    
  • 微调原始分类器
    如果目标任务与预训练任务高度相似(如从 ImageNet 的 “猫科动物” 类别微调为 “宠物猫品种” 分类),可以解冻并微调原始分类器的部分层。

4. 总结:处理原始分类器的标准流程

  1. 移除原始分类器:使用 include_top=False 加载预训练模型。
  2. 添加自定义分类器:根据目标任务设计新的全连接层结构。
  3. 训练策略
    • 先冻结预训练层,只训练自定义分类器。
    • 再解冻部分预训练层,微调整体模型(可选)。

这种方式能充分利用预训练模型的特征提取能力,同时避免原始分类器对目标任务的干扰,是迁移学习的标准操作流程。

CNN中哪一层属于分类器?

在 CNN 中,全连接层通常属于分类器。

全连接层的每个神经元都与前一层的所有神经元相连,它可以将卷积层和池化层提取到的 “分布式特征表示” 映射到样本标记空间。经过前面层次的特征提取后,全连接层会将提取到的特征进行综合,再通过激活函数(如 softmax 函数)进行非线性变换,输出每个类别的概率,从而实现分类功能。例如,在一个用于图像分类的 CNN 中,最后一层全连接层会输出与类别数量相同维度的向量,每个元素代表对应类别的得分,经过 softmax 函数后转换为概率,概率最高的类别即为预测结果。

实验:猫狗分类迁移学习

之前,我们使用Tensorflow和CNN进行过猫狗分类识别的手动训练,通过训练和验证最终得到了一个模型文件。

使用Tensorflow和CNN进行猫狗图片训练的实战总结-CSDN博客

这里我们不去从头到尾自己手动训练,而是基于预训练模型来进行迁移训练。

选择预训练模型

哪个预训练模型比较适合进行猫狗分类识别的迁移学习,并且可以让新手用来进行练习的?

ResNet50、MobileNetV2 和 VGG16 等预训练模型都比较适合新手进行猫狗分类识别的迁移学习,以下是具体介绍:

  • ResNet502:该模型具有残差连接结构,能有效解决深度神经网络中的梯度消失和退化问题,可提取到更复杂的图像特征,有助于提高分类精度。其网络规模适中,计算量和参数量相对不是特别巨大,新手在普通的计算机硬件上也能较顺利地进行训练和调试。并且网上有许多基于 ResNet50 进行猫狗分类的教程和示例代码,方便新手学习和参考。
  • MobileNetV2:这是一种轻量级的卷积神经网络,采用深度可分离卷积技术,大幅减少了模型参数和计算量,运行速度快,对硬件要求较低,即使是在资源有限的设备上也能较好地运行。对于新手来说,使用 MobileNetV2 可以更快地看到训练结果,便于理解迁移学习的过程,同时也能保证一定的分类准确率。
  • VGG16:VGG16 结构简单且统一,通过堆叠多个 3×3 的小卷积核来提取图像特征,网络结构清晰,容易被新手理解。它在 ImageNet 等大型数据集上进行过预训练,具有较强的特征提取能力。新手可以很容易地找到相关资料和代码,了解如何在其基础上修改最后全连接层的输出节点数为 2,以适应猫狗分类任务。

除了前面提到的 ResNet50、MobileNetV2 和 VGG16 外,InceptionV3、AlexNet 等 CNN 预训练模型也比较适合新手进行猫狗分类识别的迁移学习,以下是具体介绍:

  • InceptionV35:该模型采用了并行的卷积结构,能在增加网络深度和宽度的同时,减少计算量。它在 ImageNet 数据集上有很好的表现,学到了丰富的图像特征。对于猫狗分类任务,新手可以利用其预训练权重,去掉原模型的顶部分类层,添加适合猫狗二分类的层,然后进行微调。网上有许多基于 InceptionV3 进行猫狗分类的代码示例,方便新手参考学习,按照步骤操作能较快上手迁移学习过程。
  • AlexNet6:作为经典的 CNN 模型,AlexNet 结构相对简单,容易被新手理解。它包含 5 个卷积层和 3 个全连接层,对硬件要求不高,在普通电脑上即可进行训练。新手可以借助其在 ImageNet 上的预训练模型,将最后一层输出节点数改为 2,以适应猫狗分类任务,通过对 AlexNet 的学习和实践,新手能更好地掌握 CNN 的基本结构和迁移学习的原理。

这里我选择VGG16

在使用 TensorFlow/Keras 加载 VGG16 时,不需要手动下载预训练模型文件。框架会自动处理模型权重的下载和缓存,具体说明如下:

1. 自动下载的原理

当你执行 VGG16(weights='imagenet') 时:

  • TensorFlow 会检查本地缓存目录(默认路径为 ~/.keras/models/)是否已存在 VGG16 的预训练权重文件(vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5,如果 include_top=False)。
  • 若不存在,会自动从 TensorFlow 的官方模型仓库下载(约 500MB 左右),并保存到缓存目录,后续使用时直接读取本地文件,无需重复下载。
  • 如下所示:

2. 手动下载的情况(极少需要)

如果因网络问题(如无法访问外部链接)导致自动下载失败,可手动下载后放到缓存目录:

3. 验证是否已缓存

首次运行 VGG16(weights='imagenet') 时,控制台会显示下载进度(如 Downloading data from ...)。下载完成后,后续运行会直接加载本地文件,无下载提示。

总结

新手无需手动处理 VGG16 的预训练模型,直接通过 weights='imagenet' 参数即可自动加载。这是 TensorFlow/Keras 为简化使用流程设计的特性,让迁移学习更易上手。

开始实验 

以下是一个使用 TensorFlow 和 VGG16 实现猫狗分类迁移学习的可执行 Python 程序。该程序包含数据准备、模型构建、训练和预测全流程,适合新手练习。 

import tensorflow as tf
from tensorflow.keras.applications import VGG16
from tensorflow.keras.layers import Dense, Flatten, Dropout
from tensorflow.keras.models import Model
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
import scipy  # 导入整个scipy库

# ----------------------------
# 1. 配置参数
# ----------------------------
# 图像尺寸(VGG16 输入要求为 224x224)
img_size = (224, 224)
# 批量大小(根据电脑配置调整,显卡差可设为 8)
batch_size = 8
# 训练轮数
epochs = 10
# 数据集路径(请替换为你的实际路径)
train_dir = r"C:\Users\admin\Desktop\cat_dog\train"
val_dir = r"C:\Users\admin\Desktop\cat_dog\validation"

# ----------------------------
# 2. 数据增强与加载
# ----------------------------
# 训练集数据增强(解决数据量少的问题)
train_datagen = ImageDataGenerator(
    rescale=1./255,          # 归一化到 [0,1]
    rotation_range=20,       # 随机旋转±20度
    width_shift_range=0.2,   # 随机水平平移
    height_shift_range=0.2,  # 随机垂直平移
    horizontal_flip=True,    # 随机水平翻转
    zoom_range=0.2           # 随机缩放
)

# 验证集仅归一化(不增强)
val_datagen = ImageDataGenerator(rescale=1./255)

# 加载训练集
train_generator = train_datagen.flow_from_directory(
    train_dir,
    target_size=img_size,
    batch_size=batch_size,
    class_mode='binary'  # 二分类(猫=0,狗=1)
)

# 加载验证集
val_generator = val_datagen.flow_from_directory(
    val_dir,
    target_size=img_size,
    batch_size=batch_size,
    class_mode='binary'
)

# ----------------------------
# 3. 构建模型(迁移学习 VGG16)
# ----------------------------
# 加载预训练的 VGG16(不含顶部分类器)
base_model = VGG16(
    weights='imagenet',    # 使用 ImageNet 预训练权重
    include_top=False,     # 不包含顶部的全连接层
    input_shape=(img_size[0], img_size[1], 3)  # 输入图像尺寸
)

# 冻结 VGG16 的所有层(第一阶段不训练)
base_model.trainable = False

# 添加自定义分类器
x = base_model.output
x = Flatten()(x)                  # 展平特征图
x = Dense(512, activation='relu')(x)  # 全连接层
x = Dropout(0.5)(x)               # 防止过拟合
predictions = Dense(1, activation='sigmoid')(x)  # 二分类输出

# 构建完整模型
model = Model(inputs=base_model.input, outputs=predictions)

# 编译模型
model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
    loss='binary_crossentropy',    # 二分类损失函数
    metrics=['accuracy']           # 监控准确率
)

# 查看模型结构
model.summary()

# ----------------------------
# 4. 训练模型(第一阶段:冻结 base_model)
# ----------------------------
history = model.fit(
    train_generator,
    steps_per_epoch=train_generator.samples // batch_size,
    epochs=epochs,
    validation_data=val_generator,
    validation_steps=val_generator.samples // batch_size
)

# ----------------------------
# 5. 微调模型(可选,第二阶段)
# ----------------------------
# 解冻 VGG16 的最后几层(仅微调高层)
base_model.trainable = True
# 只解冻最后 4 层(根据需要调整)
for layer in base_model.layers[:-4]:
    layer.trainable = False

# 重新编译(使用更小的学习率)
model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),  # 学习率缩小 10 倍
    loss='binary_crossentropy',
    metrics=['accuracy']
)

# 继续训练(总轮数可增加)
fine_tune_epochs = 5
total_epochs = epochs + fine_tune_epochs
history_fine = model.fit(
    train_generator,
    steps_per_epoch=train_generator.samples // batch_size,
    epochs=total_epochs,
    initial_epoch=history.epoch[-1],  # 从上一阶段结束处开始
    validation_data=val_generator,
    validation_steps=val_generator.samples // batch_size
)

# ----------------------------
# 6. 绘制训练曲线
# ----------------------------
def plot_history(history, title):
    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs_range = range(len(acc))

    plt.figure(figsize=(12, 4))
    plt.subplot(1, 2, 1)
    plt.plot(epochs_range, acc, label='Training Accuracy')
    plt.plot(epochs_range, val_acc, label='Validation Accuracy')
    plt.legend(loc='lower right')
    plt.title(f'{title} Accuracy')

    plt.subplot(1, 2, 2)
    plt.plot(epochs_range, loss, label='Training Loss')
    plt.plot(epochs_range, val_loss, label='Validation Loss')
    plt.legend(loc='upper right')
    plt.title(f'{title} Loss')
    plt.show()

# 绘制第一阶段和微调阶段的曲线
plot_history(history, 'First Stage Training')
plot_history(history_fine, 'Fine Tuning')

# ----------------------------
# 7. 保存模型
# ----------------------------
model.save('cat_dog_classifier_vgg16.h5')
print("模型已保存为:cat_dog_classifier_vgg16.h5")

# ----------------------------
# 8. 预测单张图片(示例)
# ----------------------------
from PIL import Image
import numpy as np

def predict_image(image_path):
    # 加载并预处理图片
    img = Image.open(image_path).resize(img_size)
    img_array = np.array(img) / 255.0  # 归一化
    img_array = np.expand_dims(img_array, axis=0)  # 增加 batch 维度

    # 预测
    prediction = model.predict(img_array)
    if prediction < 0.5:
        print(f"预测结果:猫(概率:{1 - prediction[0][0]:.2f})")
    else:
        print(f"预测结果:狗(概率:{prediction[0][0]:.2f})")

# 测试预测(替换为你的图片路径)
# predict_image('test_cat.jpg')  # 测试猫图片
# predict_image('test_dog.jpg')  # 测试狗图片

程序说明

  1. 数据增强:通过旋转、平移等操作扩充训练数据,缓解过拟合。
  2. 迁移学习流程
    • 第一阶段:冻结 VGG16 所有层,仅训练自定义分类器(快速收敛)。
    • 第二阶段(可选):解冻 VGG16 最后几层,用小学习率微调(进一步提升准确率)。
  3. 结果可视化:训练过程中会显示准确率和损失曲线,方便观察模型性能。
  4. 模型保存:训练完成后保存为 h5 文件,可用于后续预测。

使用提示

  • 若电脑配置较低(无独立显卡),可减小 batch_size(如 8 或 4),并跳过微调阶段。
  • 数据集不足时,可通过数据增强提升效果,或减少自定义分类器的神经元数量(如将 512 改为 256)。
  • 预测时,将 predict_image 函数中的图片路径替换为你的测试图片路径即可。

通过此程序,新手可直观体验迁移学习的流程,理解 VGG16 特征提取与自定义分类器的结合方式。

之前自己训练的时候,整个过程15轮,30分钟就训练完了,这个一轮就花了快30分钟了,还是一次只输入8张图片训练,看样子复杂度很高,不过只有一轮,精确度就达到了0.8+,效果还可以?电脑风扇呼呼地转,真怕受不了烧掉了。

直接停掉吧,反正知道过程是什么样的就行。

PS C:\Users\admin\Desktop\misc\newest_pressuretest - backup-OK> & C:/Users/admin/AppData/Local/Programs/Python/Python310/python.exe "c:/Users/admin/Desktop/misc/newest_pressuretest - backup-OK/phone_control-master/test.py"
2025-07-11 15:41:44.792066: I tensorflow/core/util/port.cc:113] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
WARNING:tensorflow:From C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\keras\src\losses.py:2976: The name tf.losses.sparse_softmax_cross_entropy is deprecated. Please use tf.compat.v1.losses.sparse_softmax_cross_entropy instead.

Found 25000 images belonging to 2 classes.
Found 25000 images belonging to 2 classes.
WARNING:tensorflow:From C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\keras\src\backend.py:1398: The name tf.executing_eagerly_outside_functions is deprecated. Please use tf.compat.v1.executing_eagerly_outside_functions instead.

2025-07-11 15:41:49.740850: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: SSE SSE2 SSE3 SSE4.1 SSE4.2 AVX2 AVX_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
WARNING:tensorflow:From C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\keras\src\layers\pooling\max_pooling2d.py:161: The name tf.nn.max_pool is deprecated. Please use tf.nn.max_pool2d instead.

Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 input_1 (InputLayer)        [(None, 224, 224, 3)]     0

 block1_conv1 (Conv2D)       (None, 224, 224, 64)      1792

 block1_conv2 (Conv2D)       (None, 224, 224, 64)      36928

 block1_pool (MaxPooling2D)  (None, 112, 112, 64)      0

 block2_conv1 (Conv2D)       (None, 112, 112, 128)     73856

 block2_conv2 (Conv2D)       (None, 112, 112, 128)     147584

 block2_pool (MaxPooling2D)  (None, 56, 56, 128)       0

 block3_conv1 (Conv2D)       (None, 56, 56, 256)       295168

 block3_conv2 (Conv2D)       (None, 56, 56, 256)       590080

 block3_conv3 (Conv2D)       (None, 56, 56, 256)       590080

 block3_pool (MaxPooling2D)  (None, 28, 28, 256)       0

 block4_conv1 (Conv2D)       (None, 28, 28, 512)       1180160

 block4_conv2 (Conv2D)       (None, 28, 28, 512)       2359808

 block4_conv3 (Conv2D)       (None, 28, 28, 512)       2359808

 block4_pool (MaxPooling2D)  (None, 14, 14, 512)       0

 block5_conv1 (Conv2D)       (None, 14, 14, 512)       2359808

 block5_conv2 (Conv2D)       (None, 14, 14, 512)       2359808

 block5_conv3 (Conv2D)       (None, 14, 14, 512)       2359808

 block5_pool (MaxPooling2D)  (None, 7, 7, 512)         0

 flatten (Flatten)           (None, 25088)             0

 dense (Dense)               (None, 512)               12845568

 dropout (Dropout)           (None, 512)               0

 dense_1 (Dense)             (None, 1)                 513

=================================================================
Total params: 27560769 (105.14 MB)
Trainable params: 12846081 (49.00 MB)
Non-trainable params: 14714688 (56.13 MB)
_________________________________________________________________
Epoch 1/10
WARNING:tensorflow:From C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\keras\src\utils\tf_utils.py:492: The name tf.ragged.RaggedTensorValue is deprecated. Please use tf.compat.v1.ragged.RaggedTensorValue instead.

WARNING:tensorflow:From C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\keras\src\engine\base_layer_utils.py:384: The name tf.executing_eagerly_outside_functions is deprecated. Please use tf.compat.v1.executing_eagerly_outside_functions instead.

3125/3125 [==============================] - ETA: 0s - loss: 0.3433 - accuracy: 0.8446 

为什么dense经过dropout之后再有个dense_1,并且参数量少好多?

在神经网络中,Dense 层后接 Dropout 再连接另一个 Dense 层是一种常见的架构设计,而参数量的减少主要与层的神经元数量设置有关。以下是详细解释:

1. 为什么要在 Dense 后加 Dropout?

Dropout 的作用

  • 防止过拟合:在训练过程中,Dropout 会随机 “丢弃” 一部分神经元(如 50%),迫使网络学习更鲁棒的特征,减少对特定神经元的依赖。
  • 正则化效果:类似于 L2 正则化,通过随机失活神经元,降低模型复杂度,提高泛化能力。

典型结构

model = tf.keras.Sequential([
    Dense(512, activation='relu'),  # 第一个 Dense 层(512 个神经元)
    Dropout(0.5),                   # Dropout 层(训练时随机丢弃 50% 的神经元)
    Dense(128, activation='relu'),  # 第二个 Dense 层(128 个神经元)
])

2. 为什么第二个 Dense 层的参数量少?

参数量计算公式
对于 Dense 层,参数量 = 输入神经元数量 × 输出神经元数量 + 输出神经元数量(偏置项)

示例分析
假设:

  • 第一个 Dense 层有 512 个神经元,输入维度为 2048(例如从预训练模型提取的特征)。
  • 第二个 Dense 层有 128 个神经元。

则:

  • 第一个 Dense 层参数量
    2048(输入) × 512(输出) + 512(偏置) = 1,049,088
  • 第二个 Dense 层参数量
    512(输入) × 128(输出) + 128(偏置) = 65,664

关键原因
第二个 Dense 层的参数量少,是因为它的输入维度(512)远小于第一个 Dense 层的输入维度(2048),同时输出神经元数量(128)也较少。Dropout 层本身不改变维度,只是在训练时随机丢弃神经元,因此不会直接影响参数量。

3. 为什么要减少参数量?

  • 避免过拟合:参数量过大的模型容易记住训练数据中的噪声,导致泛化能力下降。
  • 计算效率:参数量少的模型训练和推理速度更快,占用内存更少。
  • 特征压缩:通过减少神经元数量,迫使网络学习更紧凑、更有代表性的特征(如从 512 维压缩到 128 维)。

总结

  • Dropout 的作用:防止过拟合,提高模型泛化能力。
  • 参数量减少的原因:第二个 Dense 层的输入维度和输出神经元数量均小于第一个 Dense 层。
  • 设计逻辑:通过逐步压缩特征维度,提取更高级、更抽象的特征表示,同时避免模型过于复杂。
Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐