关注

触类旁通——迁移学习、多任务学习与元学习

“智者千虑,必有一失;愚者千虑,必有一得。而真正的学习者,能从他人的‘一得’中,提炼出自己的‘千得’。”


引言:打破数据孤岛,构建知识复用的飞轮

在机器学习的实践中,我们常常陷入一种困境:新任务缺乏足够的标注数据。 无论是为一个新兴市场开发推荐系统,还是为一种罕见疾病构建诊断模型,高质量的标注数据总是稀缺且昂贵的。与此同时,我们手头可能拥有大量在其他相关任务上积累的数据和模型。这些沉睡的知识资产,如同散落在各个“数据孤岛”上的宝藏,亟待被唤醒和连接。

迁移学习(Transfer Learning)、多任务学习(Multi-task Learning)和元学习(Meta-Learning)正是打通这些孤岛、构建知识复用飞轮的三大核心范式。它们共同回答了一个根本性问题:如何利用已有知识,加速新任务的学习

  • 迁移学习像是一位经验丰富的导师,将其在源任务上学到的通用知识(如视觉特征、语言表示)传授给目标任务的学生。
  • 多任务学习则像是一个协同工作的团队,多个相关任务共享一部分公共知识(如底层特征),同时保留各自的专长(如顶层分类器),通过相互监督来提升整体性能。
  • 元学习更进一步,它旨在培养一个“学会学习”的智能体。这个智能体通过在大量不同的任务上进行训练,习得了一套快速适应新任务的通用策略或先验知识。

本章将深入这三种范式的内部机理,不仅阐述其理论基础,更着重于剖析其工程实现细节、架构设计权衡以及在真实场景中的应用案例。我们将从经典的预训练微调,到前沿的小样本学习,一步步揭开“触类旁通”的奥秘。


一、基石:迁移学习(Transfer Learning)的三种主要场景

迁移学习的核心假设是:不同但相关的任务之间,存在可以共享的知识。 根据源域(Source Domain)和目标域(Target Domain)以及源任务(Source Task)和目标任务(Target Task)之间的异同,迁移学习可分为三大场景。

1.1 场景一:归纳式迁移学习(Inductive Transfer Learning)——预训练+微调(Pretraining + Fine-tuning)

这是目前应用最广泛、效果最显著的迁移学习范式,尤其在计算机视觉(CV)和自然语言处理(NLP)领域。

1.1.1 核心思想与工作流
  1. 预训练(Pretraining) 在一个大规模的、通用的源数据集(如 ImageNet, Wikipedia)上,训练一个深度神经网络(如 ResNet, BERT)。这个阶段的目标是让模型学习到丰富的、通用的特征表示(如边缘、纹理、语义概念、句法结构)。
  2. 微调(Fine-tuning) 将预训练好的模型作为起点,在特定的目标任务(如医疗图像分类、金融情感分析)的小规模数据集上,继续训练模型。通常,我们会:
    • 冻结(Freeze) 底层(靠近输入的部分)的权重,因为它们学习的是通用特征。
    • 解冻(Unfreeze) 并微调顶层(靠近输出的部分)的权重,使其适应目标任务的具体需求。
    • 使用一个非常小的学习率,以避免破坏预训练模型中已经学到的宝贵知识。
1.1.2 PyTorch 实战:微调 ResNet 进行自定义图像分类
import torch
import torch.nn as nn
import torchvision.models as models
from torch.optim import AdamW
from torch.utils.data import DataLoader

# 1. 加载预训练的 ResNet50 模型
model = models.resnet50(pretrained=True)

# 2. 冻结所有参数(可选,通常在第一轮微调时这样做)
for param in model.parameters():
    param.requires_grad = False

# 3. 替换最后的全连接层以匹配我们的目标类别数(例如,5个新类别)
num_classes = 5
model.fc = nn.Linear(model.fc.in_features, num_classes)

# 将模型移至 GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

# 4. 定义优化器:只优化新添加的 fc 层
optimizer = AdamW(model.fc.parameters(), lr=1e-3)

# 5. 训练循环(第一阶段:仅训练新层)
model.train()
for epoch in range(5): # 先训练5个epoch
    for inputs, labels in train_loader:
        inputs, labels = inputs.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = nn.CrossEntropyLoss()(outputs, labels)
        loss.backward()
        optimizer.step()

# 6. 解冻部分底层,并使用更小的学习率进行第二阶段微调
for param in model.parameters():
    param.requires_grad = True

# 只解冻最后几个残差块(例如,layer4)
for param in model.layer4.parameters():
    param.requires_grad = True

# 使用更小的学习率
optimizer = AdamW([
    {'params': model.layer4.parameters(), 'lr': 1e-5},
    {'params': model.fc.parameters(), 'lr': 1e-4}
])

# 继续训练...

关键技巧

  • 分层学习率(Layer-wise Learning Rates) 越靠近输入的层,学习率越小;越靠近输出的层,学习率越大。这可以通过 PyTorch 的 param_groups 灵活实现。
  • 渐进式解冻(Progressive Unfreezing) 先训练新层,再逐步解冻并微调更深层的网络,这是一种稳定且高效的策略。

1.2 场景二:特征提取(Feature Extraction)

当目标任务的数据集非常小,或者计算资源有限时,微调整个模型可能会导致过拟合。此时,我们可以将预训练模型视为一个固定的特征提取器

1.2.1 工作流程
  1. 固定(Fix) 整个预训练模型的权重。
  2. 前向传播:将目标数据集的样本通过预训练模型,得到其在某一层(通常是倒数第二层)的输出,即特征向量
  3. 训练浅层分类器:将这些特征向量作为输入,训练一个简单的机器学习模型(如 SVM、逻辑回归)来完成最终的分类任务。

这种方法的优点是计算开销极小,且几乎不会过拟合,因为特征提取器是固定的。

1.3 场景三:领域自适应(Domain Adaptation)

这是迁移学习中最富挑战性的场景。源域和目标域的数据分布 P(X)P(X)P(X) 不同(例如,源域是晴天拍摄的街景图片,目标域是雨天拍摄的),但任务相同(都是语义分割)。

1.3.1 核心挑战:领域偏移(Domain Shift)

由于 PS(X)≠PT(X)P_S(X) \neq P_T(X)PS(X)=PT(X),直接将在源域上训练的模型应用于目标域,性能通常会急剧下降。

1.3.2 解决方案:对抗性领域自适应(Adversarial Domain Adaptation)

一个强大的思路是学习一个领域不变的特征表示。其代表作是 **DANN **(Domain-Adversarial Neural Networks)。

DANN 架构

  • 特征提取器 GfG_fGf:将输入映射到特征空间。
  • 标签预测器 GyG_yGy:基于特征预测标签(在源域上有监督信号)。
  • 领域分类器 GdG_dGd:试图区分特征来自源域还是目标域。

对抗训练过程

  1. 最大化 GdG_dGd 的损失:训练 GfG_fGfGyG_yGy,使得 GdG_dGd 无法区分特征的来源(即特征是领域不变的)。
  2. 最小化 GdG_dGd 的损失:同时训练 GdG_dGd,使其尽可能准确地区分领域。

通过这种对抗博弈,GfG_fGf 被迫学习到对标签预测有用、但对领域判别无用的特征。

PyTorch 风格的伪代码

# 前向传播
src_feat = feature_extractor(src_data)
tgt_feat = feature_extractor(tgt_data)

src_pred = label_predictor(src_feat)
domain_pred_src = domain_classifier(src_feat)
domain_pred_tgt = domain_classifier(tgt_feat)

# 计算损失
label_loss = CrossEntropyLoss(src_pred, src_labels)
domain_loss = BCELoss(domain_pred_src, ones) + BCELoss(domain_pred_tgt, zeros)

# 对抗性梯度反转(Gradient Reversal Layer, GRL)
# 在反向传播时,对 domain_loss 的梯度乘以 -1
grl_domain_loss = grad_reverse(domain_loss)

total_loss = label_loss + lambda * grl_domain_loss
total_loss.backward()

其中 grad_reverse 是一个自定义的梯度反转层,是 DANN 的关键。


二、协同增效:多任务学习(Multi-task Learning, MTL)

如果说迁移学习是“站在巨人的肩膀上”,那么多任务学习则是“众人拾柴火焰高”。MTL 通过同时学习多个相关任务,利用任务间的共性与差异,来提升每个任务的泛化能力。

2.1 共享-特化架构设计

MTL 模型的核心在于其参数共享机制。最常见的架构是 Hard Parameter Sharing

2.1.1 Hard Parameter Sharing (硬参数共享)
  • 架构:多个任务共享一个底层的共享表示层(Shared Representation Layers),而在顶层各自拥有任务特定的输出层(Task-specific Heads)。
  • 优点:结构简单,正则化效果强,能有效防止过拟合。
  • 缺点:如果任务间相关性很低,可能会产生负迁移(Negative Transfer),即一个任务的学习会损害另一个任务的性能。

TensorFlow/Keras 实现示例

import tensorflow as tf
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras.models import Model

# 共享的底层
input_layer = Input(shape=(100,))
shared = Dense(128, activation='relu')(input_layer)
shared = Dense(64, activation='relu')(shared)

# 任务A的头
task_a = Dense(32, activation='relu', name='task_a_dense')(shared)
output_a = Dense(1, activation='sigmoid', name='task_a_output')(task_a)

# 任务B的头
task_b = Dense(32, activation='relu', name='task_b_dense')(shared)
output_b = Dense(10, activation='softmax', name='task_b_output')(task_b)

# 构建模型
model = Model(inputs=input_layer, outputs=[output_a, output_b])

# 编译模型,为每个任务指定损失函数和权重
model.compile(
    optimizer='adam',
    loss={'task_a_output': 'binary_crossentropy', 'task_b_output': 'categorical_crossentropy'},
    loss_weights={'task_a_output': 1.0, 'task_b_output': 0.5} # 关键:损失平衡
)
2.1.2 Soft Parameter Sharing (软参数共享)
  • 架构:每个任务都有自己的完整网络,但通过在对应层的参数之间施加约束(如 L2 距离惩罚),来鼓励它们彼此相似。
  • 优点:灵活性高,可以处理相关性较弱的任务。
  • 缺点:参数量大,训练复杂。

2.2 损失平衡的艺术

在 MTL 中,不同任务的损失函数往往具有不同的量级和收敛速度。简单地将它们相加会导致模型被某个“响亮”的任务主导,而忽略其他“安静”的任务。

2.2.1 手动调整(Manual Tuning)

最直接的方法是为每个任务的损失分配一个固定的权重 λi\lambda_iλi
Ltotal=∑i=1TλiLi \mathcal{L}_{total} = \sum_{i=1}^{T} \lambda_i \mathcal{L}_i Ltotal=i=1TλiLi
但这需要大量的实验和经验。

2.2.2 自动平衡(Automatic Balancing):Uncertainty Weighting

Kendall et al. (2018) 提出了一种优雅的解决方案:将损失权重与任务的不确定性关联起来

  • 核心思想:对于噪声大、难以学习的任务(高不确定性),应赋予其较小的权重;反之亦然。
  • 实现:为每个任务引入一个可学习的参数 σi2\sigma_i^2σi2(代表方差),总损失变为:
    Ltotal=∑i=1T12σi2Li+log⁡σi \mathcal{L}_{total} = \sum_{i=1}^{T} \frac{1}{2\sigma_i^2} \mathcal{L}_i + \log \sigma_i Ltotal=i=1T2σi21Li+logσi
    在训练过程中,σi\sigma_iσi 会自动调整,以找到最优的损失平衡点。

这种方法将损失平衡问题转化为一个最大似然估计问题,具有坚实的理论基础。


三、终极目标:元学习(Meta-Learning / “Learning to Learn”)

元学习的目标不再是学习一个特定任务的模型,而是学习一个能够快速适应任何新任务的算法或先验。 它是解决小样本学习(Few-shot Learning)问题的利器。

3.1 核心范式:MAML (Model-Agnostic Meta-Learning)

MAML 是元学习领域最具影响力的算法之一,其思想简洁而强大。

3.1.1 问题设定:N-way K-shot
  • 支持集(Support Set):包含 NNN 个类别,每个类别有 KKK 个样本(例如,5-way 1-shot:5个类别,每个类别1张图片)。
  • 查询集(Query Set):包含来自相同 NNN 个类别的新样本。
  • 目标:仅用支持集,就能让模型在查询集上表现良好。
3.1.2 MAML 的“内外循环”思想

MAML 的训练过程在一个由许多“小任务”(episodes)组成的数据集上进行。

  1. 外循环(Outer Loop / Meta-Update) 学习一个良好的模型初始化参数 θ\thetaθ
  2. 内循环(Inner Loop / Task-Adaptation) 对于每一个采样的小任务 Ti\mathcal{T}_iTi
    • 使用当前的 θ\thetaθ 作为初始参数。
    • 在该任务的支持集上进行少量梯度下降(通常1步),得到任务特定的参数 θi′\theta_i'θi
      θi′=θ−α∇θLTi(θ) \theta_i' = \theta - \alpha \nabla_\theta \mathcal{L}_{\mathcal{T}_i}(\theta) θi=θαθLTi(θ)
    • 计算 θi′\theta_i'θi 在该任务的查询集上的损失 LTi(θi′)\mathcal{L}_{\mathcal{T}_i}(\theta_i')LTi(θi)
  3. 外循环更新:汇总所有任务的查询损失,更新元参数 θ\thetaθ
    θ←θ−β∇θ∑TiLTi(θi′) \theta \leftarrow \theta - \beta \nabla_\theta \sum_{\mathcal{T}_i} \mathcal{L}_{\mathcal{T}_i}(\theta_i') θθβθTiLTi(θi)

精髓:MAML 寻找的不是一个在某个任务上表现最好的 θ\thetaθ,而是一个距离所有任务的最优解都很近的 θ\thetaθ。 从这个 θ\thetaθ 出发,只需要很少的几步梯度更新,就能很好地适应任何一个新任务。

3.1.3 MAML 的 PyTorch 实现要点

MAML 的实现难点在于二阶导数(Second-order Derivatives)。因为外循环的梯度 ∇θL(θi′)\nabla_\theta \mathcal{L}(\theta_i')θL(θi) 依赖于内循环的更新 θi′\theta_i'θi,而 θi′\theta_i'θi 本身又是 θ\thetaθ 的函数。

# 伪代码:MAML 的核心更新步骤
meta_optimizer.zero_grad()

for task in sampled_tasks:
    # 内循环:在支持集上更新
    support_loss = loss_fn(model(support_x), support_y)
    # 计算内循环梯度
    fast_weights = get_updated_weights(model, support_loss, inner_lr)
    
    # 外循环:在查询集上评估
    query_pred = model(query_x, fast_weights) # 使用更新后的权重进行前向
    query_loss = loss_fn(query_pred, query_y)
    
    # 累积查询损失用于外循环更新
    total_meta_loss += query_loss

# 关键:这里的 backward() 会计算二阶梯度
total_meta_loss.backward()
meta_optimizer.step()

为了提高效率,实践中常使用**一阶近似 MAML **(FOMAML),即在外循环更新时忽略二阶导数项,这在很多情况下效果依然很好。

3.2 元学习的其他范式

  • 基于度量的方法(Metric-based) 如 Prototypical Networks。它不学习一个分类器,而是学习一个嵌入函数,将样本映射到一个度量空间。在该空间中,同类样本的原型(均值)距离很近。分类时,只需计算查询样本到各个原型的距离即可。
  • 基于记忆的方法(Memory-based) 如 **MANN **(Memory-Augmented Neural Networks)。它为模型配备一个外部的、可读写的记忆矩阵,用于显式地存储和检索过去的经验。

结语:从知识复用到智能涌现

迁移学习、多任务学习和元学习,构成了一个从知识迁移知识协同,再到学习能力本身的演进谱系。

  • 迁移学习教会我们如何重用已有的、静态的知识。
  • 多任务学习展示了如何通过协作,让知识在动态交互中变得更强大。
  • 元学习则指向了更远的未来:构建一个能够自主进化其学习能力的智能体。

在数据日益成为核心资产的时代,掌握这些“触类旁通”的技术,意味着我们不再需要为每一个新问题都从零开始。我们可以站在过去所有成功与失败的肩膀上,以指数级的速度探索未知的疆域。这不仅是工程效率的提升,更是智能本质的一次深刻模拟。

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/htw250056/article/details/160286331

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--