ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

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

迁移学习域适应实战:从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 退化成 MMDd_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, keepdimTrue) yy y.pow(2).sum(1, keepdimTrue) 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_dim256, num_classes10): 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, lambd1.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 的三步对抗训练MCDMaximum 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 的间隔差异散度MDDMargin 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 降到 3DAN 的核带宽如果取太小比如全小于 1MMD 估计方差大换成 {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 部分专门讲 DEVDeep 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(), lr1e-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(max10.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。目标域测试集绝不参与任何调参这是底线。希望帮到你。本文还有配套的精品资源点击获取
返回列表