迁移学习理论基础:源域与目标域、领域自适应、泛化界限
说实话,刚开始接触迁移学习那会儿,我也被一堆术语搞晕过。什么源域、目标域、领域漂移……听着就像另一门学问。但等你真正上手做算法迁移,会发现这些概念就是你每天都要打交道的核心工具。说白了,迁移学习要解决的,就是一个很朴素的问题:做过的经验能不能帮到新任务?
源域与目标域——你手里的牌和想打的牌
用我最习惯的说法来理解:源域是你已经有的、数据量充足的那个领域。目标域是你真正想应用、但数据稀缺的领域。
关键定义:
- 源域(Source Domain):包含大量标注样本,模型在此训练得很棒。
- 目标域(Target Domain):少量或无标注,但我们需要模型在这也能表现好。
- 领域漂移(Domain Shift):两个域的数据分布不一样。这是迁移学习要面对的根本困难。
举个例子。我在一个电商推荐项目里遇到过这种情况:源域是电脑端用户点击行为数据,样本量上千万。目标域是新上线的移动端,数据才攒了几天。模型在电脑端准确率92%,一到手机端就掉到68%。为什么?用户行为习惯不同、页面布局不同、甚至屏幕尺寸都影响了点击分布。这就是典型的领域漂移。
你想想看,如果源域和目标域的数据分布一模一样,那还迁移什么?直接训练就行了。所以迁移学习的核心假设就是:分布不同,但底层有共享的知识。
领域自适应——让模型学会“随遇而安”
领域自适应(Domain Adaptation)是迁移学习里最实用的分支之一。我个人的习惯是把它分成三类:
| 类型 | 核心思路 | 适用场景 |
|---|---|---|
| 基于样本的 | 给源域样本重新加权,挑出更接近目标域分布的样本 | 源域和目标域数据量都比较大 |
| 基于特征的 | 把两个域映射到同一个特征空间,让分布对齐 | 特征维度高,容易找到不变量 |
| 基于模型的 | 把源域训练好的模型参数或结构,微调到目标域 | 目标域数据极少(比如几十张图片) |
基于特征的方法是我用得最多的。记得有一次做医学图像分类,三个医院的CT数据完全不是同一个风格。我用MMD(最大均值差异)来测量两个域的特征分布差距,然后通过对抗训练迫使特征提取器提取出“医院无关”的特征。做完之后,跨医院准确率从55%直接跳到81%。
我的小技巧:做领域自适应之前,先可视化一下源域和目标域的特征分布(t-SNE或PCA都行)。如果两个域已经重叠得不错,那就不用折腾了——直接联合训练就行。如果完全分离,再去选对齐方法。
泛化界限——迁移学习能多好?
做工程的人可能觉得理论界限没什么用,但说实话,我几次翻车都跟轻视理论有关。泛化界限告诉你:在理论上,目标域误差的上限是多少。
最经典的公式长这样:
ε_T(h) ≤ ε_S(h) + d_H(𝒟_S, 𝒟_T) + λ
翻译成人话:
- ε_T(h):模型在目标域的误差(你最终想控制的东西)
- ε_S(h):模型在源域的误差(已经很低了)
- d_H(𝒟_S, 𝒟_T):两个域之间的H-散度(分布差距,你只能靠对齐来减小它)
- λ:理想情况下最优模型在两个域上的联合误差(这是天花板,由你选择的模型结构决定)
它告诉你的三件实事:
- 源域训练得再好,目标域误差也不会比分布差距小太多。
- 如果两个域差异太大(d_H 很大),再怎么迁移也救不回来——这时候应该考虑数据增强或重新数据采集,而不是强做迁移。
- λ 的存在意味着:选一个表达能力不足的模型,就算分布完全对齐也没用。
我曾经踩过的坑:有一个项目,我用ResNet-18做某类工业缺陷检测,源域准确率99%,目标域怎么调整都只有60%多。翻来覆去想不明白。后来算了d_H,发现两个域之间的H-散度高达0.4(0~1范围)。也就是说,迁移的天花板撑死也就70%。最后我老老实实去标注了两百张目标域样本,下限就从0.4降到了0.15,目标域准确率直接拉到93%。
实战中如何量化领域差距?
我一般不满足于靠感觉。推荐两个可计算的距离指标:
| 方法 | 原理简记 | 什么时候用 |
|---|---|---|
| MMD (最大均值差异) | 把两个分布的样本映射到RKHS,求均值差的范数 | 特征分布是高维向量时好用,计算快 |
| CORAL (相关性对齐) | 对齐源域和目标域的协方差矩阵 | 特征维度不太高,希望保持二阶统计信息 |
| H-散度估计 | 训练一个二分类器区分“源域样本 vs 目标域样本”,看分类误差 | 不想手动设计特征,让模型自己去学差距 |
举个代码感受一下MMD有多直接:
def compute_mmd(x, y, kernel='rbf'):
"""
x: 源域特征 (n_samples, n_features)
y: 目标域特征 (m_samples, n_features)
返回标量MMD值
"""
xx = tf.matmul(x, x, transpose_b=True)
xy = tf.matmul(x, y, transpose_b=True)
yy = tf.matmul(y, y, transpose_b=True)
# RBF核参数取中位数距离的平方
gamma = 1.0 / (2.0 * tf.reduce_mean(xx - 2*tf.reduce_mean(x) + 2*tf.reduce_mean(y)))
k_xx = tf.exp(-gamma * (xx - 2*tf.reduce_mean(xx) + tf.reduce_mean(xx)))
k_xy = tf.exp(-gamma * (xy - 2*tf.reduce_mean(xy) + tf.reduce_mean(yy)))
k_yy = tf.exp(-gamma * (yy - 2*tf.reduce_mean(yy) + tf.reduce_mean(yy)))
mmd = tf.reduce_mean(k_xx) + tf.reduce_mean(k_yy) - 2*tf.reduce_mean(k_xy)
return mmd
一句话总结:源域是你过去的经验,目标域是你现在的挑战,领域自适应是桥梁,泛化界限是桥梁的承重标记。别在上面跑超重的卡车——知道极限在哪,才不会出事故。