news 2026/9/30 3:19:20

迁移学习域适应实战:从H∆H-Divergence到MDD的算法落地与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
迁移学习域适应实战:从H∆H-Divergence到MDD的算法落地与避坑指南

简介:本资源为清华大学龙明盛老师《迁移学习理论与算法》的PDF讲义,面向机器学习方向的研究生、算法工程师及希望系统理解域适应理论的读者,用于解决源域与目标域分布不一致时的模型泛化问题。压缩包内共1个PDF文件,大小约13.68MB,内容以理论推导与算法框架为主,涵盖H∆H-Divergence、DAN、DANN、MCD、MDD等经典方法,并延伸至DEV深度嵌入式模型选择与Accuracy、Precision、Recall、F1-score等评估实现环节。讲义从监督学习误差界出发,串联偏差-方差-漂移权衡,逐步引出域不变表示与域对抗表示两条算法主线,同时讨论域间差异、数据不均衡与模型选择等挑战,并展望多模态迁移、元学习迁移及小样本迁移等方向。目前已有268人学习,适合作为迁移学习课程讲义或研究入门参考,帮助读者建立从理论到算法的完整认知脉络。

1. 迁移学习理论与算法:从 H∆H-Divergence 到 MDD 的落地路线图

源域上准确率 99%,换到目标域直接掉到 60% 出头——这个场景做域适应的同行基本都遇到过。龙明盛老师这份《迁移学习理论与算法》讲义,核心就是回答一个问题:当源域分布 P 和目标域分布 Q 不一致时,目标域上的泛化误差到底能不能被界定、被优化。讲义从 H∆H-Divergence 出发,串起 DAN、DANN、MCD、MDD 四条算法路线,最后落到 DEV 做模型选择。适合已经写过 PyTorch 训练循环、想系统补齐域适应理论到实现链路的从业者。下面按「理论怎么立住 → 算法怎么复现 → 坑在哪 → 怎么验证」拆开讲。

2. H∆H-Divergence:目标域误差上界怎么推出来

2.1 从源域风险到目标域风险的三角不等式

讲义第 9 到 12 页的核心推导,是把目标域风险 ϵ_Q(h) 拆成三项。设源域风险 ϵ_P(h)、目标域风险 ϵ_Q(h)、理想联合假设 h* = argmin_h [ϵ_P(h) + ϵ_Q(h)],则:

ϵ_Q(h) ≤ ϵ_P(h) + [ϵ_P(h*) + ϵ_Q(h*)] + |ϵ_P(h,h*) − ϵ_Q(h,h*)|

三项分别是:源域经验风险、理想假设的不可约误差 ϵ_ideal、以及源域和目标域在假设对 (h, h*) 上的分歧差。前两项好理解,第三项才是域适应的主战场。讲义用三角不等式一步步推,逻辑上没有跳步,但第一次看容易卡在「为什么分歧差能代表域间差异」——因为 h 和 h* 在源域和目标域上的不一致程度,本身就反映了两个分布对同一假设空间的「拉扯」。

把第三项对所有假设对取上确界,就得到 H∆H-Divergence 的定义:

d_H∆H(P, Q) ≜ sup_{h,h'∈H} |ϵ_P(h,h') − ϵ_Q(h,h')|

这个量是 hypothesis-independent 的,只依赖假设空间 H,不依赖具体某个 h。Ben-David 2010 那篇 Machine Learning 的文章给了这个定义,讲义把它作为整条算法线的理论锚点。

2.2 泛化界与 VC 维复杂度项

讲义第 12 页给出完整泛化界:

ϵ_Q(h) ≤ ϵ̂_P(h) + d_H∆H(P̂, Q̂) + ϵ_ideal + O(√(d log n / n) + √(d log m / m))

其中 d 是 H 的 VC 维,n、m 分别是源域和目标域样本数。这个式子落地时有三个直接推论:

  • ϵ̂_P(h) 是源域经验风险,训练时直接优化;
  • d_H∆H 是域间差异项,DAN 用 MMD 逼近、DANN 用域判别器逼近、MCD 用分类器一致性逼近;
  • 复杂度项随样本量增大而衰减,所以目标域无标签样本也不是越多越好,得看它能不能有效降低 d_H∆H 的估计方差。

提示:讲义里 ϵ_ideal 假设很小,这是域适应的前提。如果源域和目标域任务本身差异过大(比如源域是自然图像、目标域是医学影像且类别空间不同),ϵ_ideal 不再小,整个界就松了,这时候硬做域适应收益有限。

2.3 从 IPM 到 MMD 的逼近路径

讲义第 13 页把 H∆H-Divergence 往 Integral Probability Metric 上靠。对二值假设 h,|h(x) − h'(x)| ≠ 0 等价于一个指示函数 δ(x),于是:

d_H∆H(P, Q) = sup_{δ∈H∆H} |E_P[δ(x)≠0] − E_Q[δ(x)≠0]|

这正好是 IPM 的形式 d_F(P,Q) = sup_{f∈F} |E_P f(x) − E_Q f(x)|。当 F 取 RKHS 中的核函数时,IPM 退化成 MMD:

d_k²(P, Q) = ‖E_P[φ(x_s)] − E_Q[φ(x_t)]‖²_Hk

这一步是 DAN 的理论入口。实操中,MMD 的核选择直接决定效果,讲义提到 MK-MMD(多核 MMD),我一般会用 5 个不同带宽的高斯核做加权,带宽取 {1, 2, 4, 8, 16} 倍中位数距离,权重用多核学习自动学。单核 MMD 在特征维度超过 512 后经常出现数值不稳定,多核是更稳的选择。

3. DAN 与 DANN:两条逼近路线的实现差异

3.1 DAN 的 MK-MMD 适配层与损失配置

DAN 的结构是在 AlexNet 或 ResNet 的 fc6、fc7、fc8 三层后各接一个 MK-MMD 适配层,源域和目标域共享特征提取器,只在损失函数里加分布匹配项。讲义第 14 页给出的目标函数:

min_θ max_k∈K (1/n_a) Σ L(θ(x_a), y_a) + λ Σ_{ℓ=l1}^{l2} d_k²(P̂_ℓ, Q̂_ℓ)

落地时几个关键参数:

参数常用取值说明
λ0.1 ~ 1.0分布匹配损失权重,太大源域精度掉,太小域适应无效
适配层fc6, fc7, fc8越靠后适配越关键,fc8 层 MMD 对分类边界影响最大
核数量5少于 3 个核 MK-MMD 退化成单核,多于 10 个计算开销线性涨
batch 比例源:目标 = 1:1讲义实验里 1:1 最稳,目标域样本少时可降到 1:0.5

一个最小可跑的 MMD 损失实现:

import torch import torch.nn as nn class MKMMDLoss(nn.Module): def __init__(self, kernels=(1, 2, 4, 8, 16)): super().__init__() self.kernels = kernels def gaussian_kernel(self, x, y, sigma): # 计算成对欧氏距离平方 xx = x.pow(2).sum(1, keepdim=True) yy = y.pow(2).sum(1, keepdim=True) dist = xx + yy.t() - 2 * x @ y.t() return torch.exp(-dist / (2 * sigma ** 2)) def forward(self, source, target): loss = 0.0 for sigma in self.kernels: k_ss = self.gaussian_kernel(source, source, sigma) k_tt = self.gaussian_kernel(target, target, sigma) k_st = self.gaussian_kernel(source, target, sigma) # MMD 无偏估计 loss += k_ss.mean() + k_tt.mean() - 2 * k_st.mean() return loss / len(self.kernels)

逻辑说明:每个核算一个 MMD 估计,最后取平均。参数 sigma 是高斯核带宽,取 1 到 16 覆盖不同尺度。注意 k_ss 和 k_tt 的均值项不能省,否则估计有偏。训练时把这个 loss 乘 λ 加到分类 loss 上,反向传播只更新特征提取器和分类器,不单独更新核参数。

3.2 DANN 的梯度反转层与域判别器

DANN 的思路完全不同:不显式算 MMD,而是训一个域判别器 D(x) 去区分源域和目标域特征,特征提取器则要骗过 D。讲义第 15 页给出上界:

d_H∆H(P, Q) ≤ sup_{D∈H_D} |E_P[D(x)=1] + E_Q[D(x)=0]|

实现上靠梯度反转层(GRL),前向传播恒等,反向传播把梯度乘 −λ:

class GradientReversal(torch.autograd.Function): @staticmethod def forward(ctx, x, lambd): ctx.lambd = lambd return x.view_as(x) @staticmethod def backward(ctx, grad_output): return -ctx.lambd * grad_output, None class DANN(nn.Module): def __init__(self, feature_dim=256, num_classes=10): super().__init__() self.feature = nn.Sequential( nn.Linear(512, feature_dim), nn.ReLU(), nn.Linear(feature_dim, feature_dim), nn.ReLU() ) self.classifier = nn.Linear(feature_dim, num_classes) self.domain_disc = nn.Sequential( nn.Linear(feature_dim, 128), nn.ReLU(), nn.Linear(128, 2) ) def forward(self, x, lambd=1.0): feat = self.feature(x) cls_out = self.classifier(feat) rev_feat = GradientReversal.apply(feat, lambd) dom_out = self.domain_disc(rev_feat) return cls_out, dom_out

参数说明:λ 控制域对抗强度,讲义实验里从 0 线性增到 1 比固定 1 更稳,因为训练初期特征还没学好,强对抗会让分类器崩。域判别器输出 2 类(源/目标),用交叉熵训。常见翻车点是 λ 设太大导致源域精度暴跌,我一般先用 λ=0.1 跑通再往上调。

3.3 DAN 与 DANN 的选型边界

两条路线不是替代关系。DAN 的 MMD 是显式统计距离,训练稳定、超参少,但适配层位置和核带宽需要调;DANN 的对抗训练更灵活,能适配任意特征维度,但训练不稳定、对 λ 和判别器容量敏感。我的经验是:特征维度低于 512、数据量中等时优先 DAN;特征维度高、源域目标域差异大时用 DANN,但要做好 λ 调度。讲义第 7 页那张对比图里 MDD 88.9、MCD 87.7、DANN 86.5、DAN 82.9,这个排序在 Office-31 上是稳的,但换到 VisDA 这种大差异数据集,DANN 和 MCD 的差距会缩小。

4. MCD 与 MDD:分类器一致性与间隔差异

4.1 MCD 的三步对抗训练

MCD(Maximum Classifier Discrepancy)用两个分类器 F1、F2 的预测差异作为域差异度量。讲义第 16 页给出差异损失:

d(p1, p2) = (1/K) Σ_k |p1_k − p2_k|

用 L1 距离而非 L2,讲义明确说实验里 L2 效果不好。训练分三步循环:

  • Step A:用源域标签训 G、F1、F2,保证源域判别性;
  • Step B:固定 G,训 F1、F2 最大化目标域上的差异,让分类器能检测出目标域中偏离源域支撑的样本;
  • Step C:固定 F1、F2,训 G 最小化目标域差异,让特征向源域支撑靠拢。
def mcd_step_a(g, f1, f2, src_x, src_y, opt_g, opt_f): feat = g(src_x) loss = nn.functional.cross_entropy(f1(feat), src_y) + \ nn.functional.cross_entropy(f2(feat), src_y) opt_g.zero_grad(); opt_f.zero_grad() loss.backward() opt_g.step(); opt_f.step() def mcd_step_b(g, f1, f2, src_x, src_y, tgt_x, opt_f): feat_s, feat_t = g(src_x), g(tgt_x) loss_s = nn.functional.cross_entropy(f1(feat_s), src_y) + \ nn.functional.cross_entropy(f2(feat_s), src_y) p1, p2 = nn.functional.softmax(f1(feat_t), 1), nn.functional.softmax(f2(feat_t), 1) loss_adv = (p1 - p2).abs().mean() loss = loss_s - loss_adv # 最大化差异 opt_f.zero_grad(); loss.backward(); opt_f.step() def mcd_step_c(g, f1, f2, tgt_x, opt_g): feat_t = g(tgt_x) p1, p2 = nn.functional.softmax(f1(feat_t), 1), nn.functional.softmax(f2(feat_t), 1) loss = (p1 - p2).abs().mean() # 最小化差异 opt_g.zero_grad(); loss.backward(); opt_g.step()

参数说明:Step B 里源域分类损失不能省,讲义说去掉后性能显著下降。Step C 对同一 mini-batch 重复 n 次,n 是超参,常用 3 到 5。三步顺序讲义说不重要,关键是分类器和生成器在源域分类正确的前提下对抗。

4.2 MDD 的间隔差异散度

MDD(Margin Disparity Discrepancy)是讲义作者自己的后续工作,把 H∆H-Divergence 推广到多类间隔损失。核心是把二值假设的分歧差换成间隔差异:

d_f(P, Q) = sup_{h'∈H} (E_P[Φ_f ∘ h'] − E_Q[Φ_f ∘ h'])

其中 Φ_f 是间隔损失函数。MDD 在 Office-31 上拿到 88.9,比 MCD 高 1.2 个点,代价是训练更复杂、对间隔参数敏感。落地时如果 MCD 已经够用,不必强上 MDD;只有在类别数多、类间间隔小(比如细粒度分类)时,MDD 的间隔项才有明显收益。

4.3 四条路线的横向对比

算法域差异度量训练稳定性超参敏感度适用场景
DANMK-MMD高中(λ、核带宽)特征维度中等、数据量中等
DANN域判别器中高(λ 调度)特征维度高、差异大
MCD分类器 L1 差异中中(n 重复次数)分类边界清晰的任务
MDD间隔差异中高(间隔参数)多类、细粒度

选型时先看目标域有没有少量标签:有标签优先用监督域适应,无标签再上这四条。再看源域目标域类别空间是否一致,不一致的话这些方法都不直接适用。

5. 避坑与排查:域适应训练里最常见的五个翻车点

5.1 源域精度正常但目标域完全不涨

现象:源域验证集 98%,目标域测试集和不用域适应时一样。原因通常是域差异损失权重太小,或者适配层加错了位置。解决:先把 λ 从 0.1 调到 1.0 试,DAN 确认适配层在 fc6 之后而不是只在最后;DANN 确认梯度反转层接在特征提取器输出而不是分类器输出之后。

5.2 训练 loss 震荡不收敛

现象:域差异 loss 上下跳,源域精度跟着抖。原因是对抗训练中判别器太强或 λ 太大。解决:DANN 用 λ 从 0 线性增到 1 的调度;MCD 的 Step C 重复次数从 5 降到 3;DAN 的核带宽如果取太小(比如全小于 1),MMD 估计方差大,换成 {1,2,4,8,16} 这组。

5.3 目标域 batch 里混入源域样本

现象:目标域评估时精度虚高,换一批数据就掉。原因是数据加载器没做域标记隔离,或者 shuffle 时把两个域混在一起。解决:源域和目标域各用一个 DataLoader,训练时手动 zip 或按比例采样,评估时严格只用目标域测试集。这个坑血泪经验,我见过不止一次。

5.4 MMD 数值溢出

现象:MK-MMD loss 变成 nan。原因是高斯核里距离平方太大,exp 下溢。解决:在算距离前对特征做 L2 归一化,或者把 sigma 下限设成中位数距离的 0.5 倍。另一个办法是用 log-sum-exp 技巧算核矩阵,但实现复杂,归一化更直接。

5.5 模型选择用源域验证集

现象:源域验证集选出的模型在目标域上不是最优。原因是源域验证集和目标域分布不一致,源域上的最优不等于目标域最优。讲义第 4 部分专门讲 DEV(Deep Embedded Validation),用重要性加权在源域验证集上估计目标域风险。实操中如果不想上 DEV,至少要用目标域的无标签数据做一次伪标签验证,别直接拿源域验证集选模型。

6. DEV 与评估:模型选择怎么做才不玄学

6.1 DEV 的重要性加权思路

DEV 的核心是用源域验证集估计目标域风险,靠的是重要性加权。设源域验证集特征分布 p(x)、目标域特征分布 q(x),权重 w(x) = q(x)/p(x),则目标域风险估计为:

R̂_Q(h) = (1/n) Σ w(x_i) · L(h(x_i), y_i)

w(x) 用域判别器估计,训练一个二分类器区分源域验证集和目标域特征,输出概率比就是权重。讲义里 DEV 还加了嵌入空间的方差控制,避免权重方差过大导致估计不稳。

def dev_weight(source_feat, target_feat): # 训练域判别器估计密度比 disc = nn.Sequential(nn.Linear(source_feat.size(1), 128), nn.ReLU(), nn.Linear(128, 1), nn.Sigmoid()) opt = torch.optim.Adam(disc.parameters(), lr=1e-3) for _ in range(500): pred_s = disc(source_feat) pred_t = disc(target_feat) loss = -torch.log(pred_s + 1e-8).mean() - torch.log(1 - pred_t + 1e-8).mean() opt.zero_grad(); loss.backward(); opt.step() with torch.no_grad(): # 密度比 w = p_t / p_s ≈ (1-d)/d d = disc(source_feat) w = (1 - d) / (d + 1e-8) return w.clamp(max=10.0) # 截断避免极端权重

参数说明:判别器训练 500 步足够,权重截断上限 10 是经验值,不截断的话个别样本权重能到几百,估计方差爆炸。DEV 选模型时,对每个候选模型算加权风险,取最小的。

6.2 评估指标的选择

讲义第 5 部分提到 Accuracy、Precision、Recall、F1。域适应场景下我建议主看目标域 Accuracy 和 per-class Recall。原因:域适应经常出现某些类适配好、某些类崩掉的情况,只看 Accuracy 会掩盖类间不均衡。per-class Recall 能暴露哪些类没适配过去,方便回头调适配层或加类权重。

6.3 一个完整的验证流程

从那以后我每次跑域适应实验,都强制走一遍这个流程:先在源域训练集上训基线,记录源域验证集精度;加域适应模块,用源域验证集粗选超参;再用 DEV 在源域验证集上估计目标域风险,选最终模型;最后在目标域测试集上只跑一次,记录 Accuracy 和 per-class Recall。目标域测试集绝不参与任何调参,这是底线。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 3:19:11

桌面整理终极指南:从文件流动机制到长期保持白纸效果

我帮人整理电脑差不多有十年了,经手的桌面没有一百也有八十个。说实话,绝大多数的"乱"并不是懒,而是没有一个明确的规则来告诉文件该往哪儿去。桌面这个位置很特殊,它是你每天打开电脑第一眼看到的东西,它承…

作者头像 李华
网站建设 2026/9/30 3:19:09

深度解析正规优选飞扬专升本有哪些王牌专业

随着近年辽宁专升本报考人数逐年上涨,2026年全省报考人数突破12万,同比增长18%,学历提升需求持续释放。目前辽宁本地学历培训市场覆盖周边12个地级市及下属县域,行业普遍存在三大痛点:一是收费不透明,部分机…

作者头像 李华
网站建设 2026/9/30 3:18:53

中国移动统一DPI规范解读:LTE信令采集解析服务器接口实现与XDR输出

简介:这份资源是中国移动通信集团发布的企业标准文档,版本号2.0.8,面向DPI设备制造商、LTE网络运维人员及信令分析工程师,用于解决统一DPI设备在LTE信令采集解析与接口对接中的规范化问题。文档系统定义了LTE数据合成服务器的接口…

作者头像 李华
网站建设 2026/9/30 3:18:46

Spring AI构建Text-to-SQL:准确率从42%到93%的工程实践

1. 为什么非要自己再包一层:Spring AI解决的是接入,不是SQL准确性做了几年Java后端,又跳进大模型应用开发这个坑之后,我最大的感受是:很多人一提Text-to-SQL,就以为买了张大模型API的会员卡就万事大吉。实际…

作者头像 李华
网站建设 2026/9/30 3:17:32

如何构建可量化的网络安全防御能力评价体系

简介:《网络安全防御能力评价体系框架》是360政企安全在2021年推出的实战化网络安全防御能力度量与评价方法,面向企业安全负责人、安全主管及安全规划团队,着力解决传统等级保护、ISO 27001等度量方式难以预判威胁、缺乏有效性评估的难题&…

作者头像 李华
网站建设 2026/9/30 3:17:21

js-md5 实战避坑指南:UTF-8 编码、跨端一致性与增量哈希

1. 项目概述:为什么一个“简单使用”值得专门写一篇长文?“js-md5的简单使用”——看到这个标题,很多人第一反应是:“不就是引入个库、调个函数吗?三行代码的事,还用写文章?”我刚入行那会儿也这…

作者头像 李华