前一段时间写了迁移学习第一类方法里边际分布自适应的核心思路,也就是把源域和目标域的整体分布拉近。今天把第二块补完:条件分布自适应。这是在实际项目中碰得更多的场景,因为大多数时候源域和目标域不仅整体数据偏移,连类别层面的规律都不一样。只做边际对齐,往往效果撑不住。
1. 为什么条件分布不对齐会让模型失灵
先说一个我当年踩过的例子。某次做设备状态监测的跨工况迁移,源域是设备在A工况下采集的振动数据,目标域是B工况,两个工况的振动幅值范围差不多,频谱特征也有重叠,当时我偷懒只用了边际分布自适应,做完之后特征空间里两堆数据的质心确实重合了,但分类准确率还是停留在六成上下,跟不迁移差不多。
后来仔细看特征分布才发现问题:源域里故障样本的振动特征集中在低频段,目标域里故障样本因为工况转速变了,特征整体挪到了高频段。边际分布自适应做的事情是把两个域的总体分布拉近,但它在拉的过程中,会把源域的故障样本特征也强行扯到目标域正常样本的附近,结果类别边界完全乱掉。
这个问题的本质是:**边际分布对齐只保证了 P(Xs) 和 P(Xt) 接近,但完全没有约束 P(Y|X) **。而分类器最终依赖的是条件分布。打个比方,边际分布自适应好比把两群人的整体身高分布调成一致,但没管男女性别的比例和对应关系。条件分布自适应要做的,是在拉近数据分布的同时,把“每个类别下的数据长什么样”也拉近。
那为什么不干脆直接对齐 P(Y|X)?理论上应该这么做,但实际里面有几个坎:
- 目标域没有标签,P(Y|X) 没法直接估计。
- 即便能估计,条件分布是输入到输出的映射,在高维空间里它的结构非常复杂,直接建模代价太高。
- 迁移学习的场景里源域和目标域的类别集合通常一致,但每个类别的数据形态差异很大,直接对整体分布做变换会丢失类别结构信息。
所以这类方法的主流做法,是转而对齐类条件分布 P(X|Y)。根据贝叶斯公式,P(Y|X) 和 P(X|Y)、P(Y) 之间有确定关系,如果源域和目标域的 P(X|Y) 对齐了,同时再兼顾一下 P(Y),那类别层面的判别规律就有机会对齐。这也是条件分布自适应方法群里面最核心的一个出发点。
2. 从贝叶斯公式出发:对齐类条件分布为什么是可行路径
把这个问题用公式再捋一遍。我们希望目标域的 P_t(Y|X) 跟源域的 P_s(Y|X) 尽量一致,但 P_t(Y|X) 拿不到。贝叶斯公式给出了分解:
P(Y|X) = P(X|Y) × P(Y) / P(X)
这个公式说明,条件分布可以被拆成三块:类条件分布 P(X|Y)、类别先验 P(Y)、以及边际分布 P(X)。条件分布自适应要做的是通过改造样本的表示或权重,让源域和目标域在这三块上逐步趋同。当然实际方法不会三块同时做得特别精细,主流思路是用 P(X|Y) 的对齐来近似带动 P(Y|X) 的对齐,再辅以类别权重的修正。
这里有个很关键的设计决策:为什么对齐 P(X|Y) 就可以,而不需要精确建模 P(Y|X)。原因是,对于分类任务来说,真正决定决策边界的是每个类别的数据形态。比如区分猫和狗,如果源域里猫的毛色分布、体型特征和目标域一致,那么从图像到“猫/狗”的映射规律就直接可迁移了。而 P(X|Y) 在这种场景下刚好捕捉的就是“猫这个类别的样本长什么样”。
另一个角度是样本量。P(X|Y) 的估计是密度估计问题,只需要每个类别下的样本足够就行;P(Y|X) 的估计是回归/分类问题,需要输入到输出的监督信号。目标域没有标签,后者根本学不了。所以对齐 P(X|Y) 从工程上讲是唯一方便落地的那条路。
那怎么对齐 P(X|Y)?延续上一篇介绍的 MMD 思路,对每个类别 c,分别计算源域和目标域在某个特征空间里的均值差异,再把所有类别的差异加起来作为损失。注意这里要分两个情况讨论,下面仔细展开。
2.1 目标域标签存在时:直接按类别计算类条件MMD
这个情况在实际项目里相对少见,但理解它有助于打通后面的伪标签做法。当目标域有一部分标注数据时,可以直接把数据按类别分组。假设类别数为 C,那么对每个 c ∈ 1..C,取出源域里的 X_s^(c) 和目标域里的 X_t^(c),计算它们之间的 MMD:
Dist_c = || (1 / n_s^(c)) Σ φ(x_s^(c)) - (1 / n_t^(c)) Σ φ(x_t^(c)) ||_H
总的损失就是所有类别的 MMD 之和。这个做法直观、干净,不需要任何额外假设。但问题在于目标域的带标签数据稀缺,很多迁移场景下目标域标签数量是零,只有在半监督或小样本目标域的情况下才能用。
2.2 目标域标签不存在时:用伪标签做条件分布对齐
这是更常见的场景。目标域没有标签,但我们需要把数据按类别分组。办法是先训练一个基础分类器(可以用源域数据训练),然后对目标域预测出伪标签,再按伪标签分组去计算类条件 MMD。但伪标签不可能全对,错误的标签分组会把类别信息污染掉。
这时候不同的方法会选择不同的对策:
- 迭代式优化:先用粗略伪标签进行对齐,得到更好的特征表示后重新预测伪标签,再继续对齐,如此循环。
- 置信度过滤:只保留预测置信度较高的目标域样本来计算 MMD,低置信度的样本不参与本轮条件分布对齐,留到后续迭代。
- 软标签加权:不把伪标签当作硬类别,而是把每个样本属于各个类别的概率作为权重,对MMD的每一项做加权。
从我实际测试的经验看,迭代式的做法提升最明显,特别是第一轮伪标签只有六成准确率的情况下,通过两三轮迭代,分类准确率往往能涨十个百分点以上;置信度过滤则更适合那种伪分类器本身方差很大的场景,能防止错误的伪标签把类条件分布带偏。软标签加权理论优美,但计算复杂度高,而且权重估计不准的时候反而引入噪声。
2.3 JDA把边际和条件一起拉近:一个目标函数里做两件事
学术界比较有代表性的方法是联合分布自适应(JDA),它对这两件事的处理方式非常清晰。JDA 的设计思路是:不吃“只做边际或只做条件”这种单边亏,而是在同一个优化目标里,把边际分布距离和条件分布距离加起来,一起最小化。
用公式来表达,JDA 的优化目标可以写成:
Dist = D(P_s(X), P_t(X)) + Σ_{c=1}^C D(P_s(X|Y=c), P_t(X|Y=c))
其中 D 是 MMD 距离。第一项对应边际分布对齐,第二项对应条件分布对齐。这样在拉中心的同时,也把类别层面的结构拉近。
在求解时,JDA 用了一个很聪明的操作:把两类距离都统一成核矩阵上的迹优化问题。具体来说,定义核矩阵 K,然后定义一个 MMD 矩阵 L,把边际项和条件项统一写成 tr(KL) 的形式,然后用 PCA 的思路求解一个变换矩阵 W,使得变换后的特征空间中上述距离最小。
这个目标函数有一个值得注意的特征:边际分布对齐项可以通过闭式解轻松求,条件分布对齐项因为依赖伪标签,属于“用当前结果算距离、再反过来优化表示”的循环过程。所以 JDA 的整个训练流程天然就是一个迭代过程,且迭代过程通常会收敛得比较快,一般三到五次就能稳定。
3. JDA的完整推导:从MMD到核矩阵的迹优化
JDA 的核心推导值得坐下来把每一步捋清楚,因为在代码实现时你最终要面对的就是那几个矩阵怎么构造。我尽量写得直白一些。
首先我们有一个核映射 φ,把原始数据映射到再生核希尔伯特空间。对整个数据集(源域和目标域合并)计算核矩阵 K,维度是 (n_s + n_t) × (n_s + n_t):
K[i][j] = <φ(x_i), φ(x_j)>
然后定义 MMD 矩阵 L。这个矩阵在边际和条件两种情况下取值方式不同。对于边际分布对齐项,L 的构造跟上一篇写的一样:同域位置为 1/n_s² + 1/n_t²,跨域位置为 -1/(n_s·n_t)。对于条件分布对齐项,取值为:
- 源域同类内 (i,j 都属于源域且类别同为 c):L[i][j] = 1/(n_s^(c))²
- 目标域同类内 (i,j 都属于目标域且伪标签同为 c):L[i][j] = 1/(n_t^(c))²
- 一源一目标同伪标签 c:L[i][j] = -1/(n_s^(c)·n_t^(c))
- 其他位置:0
真实的 L 是边际项的 L 和条件项的 L 的加权和,也就是把两部分加在一起。
优化问题变成:寻找变换矩阵 W,使得新的表示 Z = WᵀK 的条件下,类间距离最小:
min tr(Wᵀ K L K W),同时满足 Wᵀ K H K W = I
这里 H = I - (1/n) 11ᵀ是中心化矩阵,作用是去掉均值,让数据在变换空间中分布更合理。加上这个约束是为了防止 W 退化到零解。
这是个典型的广义特征值问题。对 (K L Kᵀ) W = λ (K H Kᵀ) W 做特征分解,取前 d 个最小特征值对应的特征向量组成 W,然后得到变换后的特征 Z = WᵀK。原始数据在变换后,边际分布和条件分布的距离都被压小了,而且是同时压小。
为什么用最小特征值而不像PCA那样用最大特征值?因为我们的目标是最小化 tr(Wᵀ K L K W),而 W 满足正则化约束后,这个目标等价于广义特征值中的 Rayleigh 商,最小化对应的就是最小的特征值方向。
矩阵的维度也值得留意。如果不做核近似,K 的维度是 (n_s + n_t)²。当数据量到一万以上,这个矩阵是不可接受的。实际工程里通常会配合随机傅里叶特征或 Nyström 近似来降低矩阵规模,这也是JDA能从论文走向实际项目的关键。我在自己的实验里试过 5000 个样本,用普通 PC 跑核矩阵得几十秒,但用 Nyström 近似压到几百维后,几秒就出结果,效果只差一两个百分点。
4. 手写一个JDA并验证:对齐前后准确率对比
纸上谈兵没有意义,我用一个公开可复现的小实验来验证 JDA 的实际效果。实验设置是:用一组合成的二维高斯分布数据,源域有标签,目标域数据整体做了偏移和高斯噪声增强,类别结构也有变化。然后用三种方式对比:不迁移、仅边际分布自适应(TCA)、JDA。
4.1 数据构造和基础分类器设置
源域三个类别,每个类别 200 个样本,高斯分布均值分别落在 (-3, 0)、(0, 3)、(3, 0),协方差矩阵为单位阵。目标域样本做了两类改动:一是所有样本整体平移 (+4, +2),二是类别内部的协方差矩阵做了旋转,比如第一类从标准圆变成沿 45° 方向拉伸的椭圆。这种设定既包含边际偏移,又包含类别结构变化,可以明显看出不同方法在条件分布对齐上的差异。
使用 RBF 核支持向量机作为基分类器。先直接在源域训练 SVM,然后用来预测目标域标签,得到 baseline 准确率。
4.2 JDA核心代码实现要点
我不打算贴整段完整代码,只把核心逻辑的骨架写出来,主要看三个环节:L矩阵构造、广义特征分解、伪标签迭代。
第一个环节是 L 矩阵构造。这里的关键是伪标签数组 y_t_pseudo 需要随着迭代更新,所以 L 矩阵也要在每轮迭代时重新计算。第二和第三个环节相对固定。广义特征分解可以用 scipy.linalg.eigh 直接求。
实际操作时还有个容易出问题的点:K H K 可能奇异。解决办法是加上一个很小的正则项 λI,我一般用 1e-3。这个正则项还会影响最终特征的范数,若发现投影后特征范数异常,优先调整它。
4.3 实验对比结果
第一轮,直接用源域SVM预测目标域,准确率只有 42.5%——三类任务,这个成绩基本等于随机好一点。
第二轮,先做 TCA(只对齐边际分布),预测准确率 55.8%。数据整体偏移被纠正了,但因为类别内部结构差异还在,分类效果提升有限。
第三轮,做 JDA,第一轮伪标签预测准确率 46%,迭代了四轮之后,最终在目标域上的预测准确率是 71.3%。大幅领先前两种方案,虽然还谈不上完美,但对于一个完全无监督的迁移场景,这个提升已经很实在。
还有一个值得注意的细节:JDA 每一轮迭代,伪标签准确率的变化轨迹是 46.0% → 55.2% → 65.8% → 70.1% → 71.3%。到了第四轮就基本收敛了,后面再加迭代收益很小。所以实际应用中设迭代上限 5 次左右即可,不需要为了追求收敛而跑几十轮。
5. 实操中的两个容易踩的坑
5.1 伪标签质量差时JDA会帮倒忙
JDA 对伪标签质量敏感。如果基础分类器在目标域上的伪标签准确率低于五成,JDA 的条件分布对齐项里混入了大量错误的类间对应,这时对齐不仅没帮助,反而会把源域的类别结构也拉坏。我试过一些极端场景,伪标签准确率只有三成多的时候,JDA 跑完比不跑还差。
应对办法有两个。
一个是尽可能把源域分类器做扎实再预测伪标签。源域数据充足的情况下,可以先在源域上做交叉验证调参,确保基本分类器在源域测试集上有足够好的性能;有验证集的话,还可以用验证集上的置信度阈值来筛选伪标签。
另一个是用置信度过滤。具体做法:用源域分类器预测目标域时,同时得到 softmax 概率。设定一个阈值,只有最高概率超过该阈值的样本才进入类条件 MMD 的计算,其他样本暂时挂起。阈值一般取 0.7 到 0.9 之间,具体数值可以通过少量目标域标注(如果有)来校准。
5.2 类别不平衡条件下直接求和会偏向大类别
类条件 MMD 是按类别累加的。如果源域里类别 A 有 10000 个样本,类别 B 只有 100 个,那么类别 A 的 MMD 项在总损失里天然占主导,优化器会把资源集中在对齐大类上,小类的对齐效果会被牺牲。
很多真实工业场景恰恰是少数类才重要——故障检测里故障样本就是小类。如果不处理类别不平衡,JDA 会把正常样本对齐得很好,故障样本反而被忽略。处理办法是给每个类别的 MMD 项加权,权重取为类别样本量的倒数,或者用 min-max 归一化。我在自己任务里常用倒数权重,简单直接,效果稳定。
6. 条件分布自适应的变体:从JDA到CORAL-family
JDA 开创了把边际和条件放进同一个优化框架的范式,但它不是唯一选择。条件分布自适应方向上还有几个常见变体,按场景选型。
6.1 目标域有少量标签:半监督条件分布自适应
有些项目里目标域不是完全没有标签,而是只有少量,比如每个类别 5 到 10 个样例。这种情况可以用带真实标签的样本对伪标签进行校准,或者在目标域真实标签上额外加一个监督项。相比完全无监督,这种方式稳定性好很多,因为类条件分布估计有了锚点。
实现时我用过一种做法:初始伪标签由源域分类器赋予,然后用目标域的少量真实标签对伪标签进行全局偏差修正——比如发现分类器对某个类别有系统性偏好,就在该类别的 MMD 计算上做加权。效果比纯伪标签迭代好,且修正成本很低。
6.2 深度网络场景:用对抗方式做条件分布对齐
如果把 JDA 的思路移植到深度网络里,一个自然的做法是在特征提取器后面加上域判别器,用对抗训练来对齐分布。但简单的对抗域判别只对齐边际分布,没法按类别区分。改进的做法是条件域对抗网络,在域判别器里把类别信息作为一个额外输入。这样判别器不仅能判断一块特征来自哪个域,还能判断它属于哪个类别,对抗训练就同时考虑了类别结构。
条件域对抗网络在图像分类的跨域任务上表现不错,但训练难度比 JDA 大得多,需要谨慎调节对抗损失和分类损失的权重。预算有限的话,我建议先从 JDA 这类经典方法试起,它们在中等规模数据上已经能给出有意义的提升。
6.3 相关方法选型对比
为了便于选择,我把几种常见方法放在一张表里直观对比:
| 方法 | 对齐内容 | 是否需要伪标签 | 迭代 | 适用数据规模 | 实现复杂度 |
|---|---|---|---|---|---|
| TCA | 仅边际分布 | 否 | 无需迭代 | 中等/大 | 低 |
| JDA | 边际 + 条件分布 | 是 | 需3-5轮 | 中等/大 | 中 |
| CORAL | 二阶统计量对齐 | 否 | 无需迭代 | 大 | 极低 |
| 条件域对抗网络 | 边际 + 条件分布 | 是(类别条件器) | 端到端训练 | 大(图像等) | 高 |
选型时有一条经验:如果数据量小且目标域只有几十个样本,CORAL 这类轻量方法可能比 JDA 更合适,因为核矩阵的构造需要足够样本撑起统计显著性;如果数据量到几千以上,JDA 的类条件统计就有意义了;如果是深度特征,那么条件域对抗网络这类端到端方法更自然。
最后一个提醒:条件分布自适应依赖类别结构,所以任务里的类别定义必须稳定。如果源域和目标域的类别划分本身就不一致(比如一个把背景也算作一类,另一个不区分),那所有基于类条件的对齐方法都会失效。动手之前先确认这个前提,能省下很多无用功。