简介:清华大学龙明盛老师的《迁移学习理论与算法》PDF讲义,系统讲解迁移学习的理论与算法体系,适合机器学习、深度学习方向的科研人员和高年级学生阅读。内容从监督学习与迁移学习的基本设定切入,重点介绍H∆H-散度、DAN、DANN、MCD、MDD等代表性方法,以及DEV模型选择、评估与实现细节,帮助读者理解域间差异本质并掌握经典算法脉络。资源为1个PDF文件,压缩包大小13.68MB,画质清晰、公式完整,适合用于课程学习、组会研讨或算法复现参考。目前已有270人学习下载。讲义以龙明盛在清华大学的课堂教学材料为底本,融理论推导与算法示例于一体,既说明了偏差-方差-漂移权衡等关键概念,也展示了如何从理论界限推导出有效算法,对希望系统掌握迁移学习技术栈的读者有较高实用价值。
1. 迁移学习理论与算法:一份把目标域泛化误差“算清楚”的讲义
“迁移学习理论与算法”是我最近拆的一份课程讲义,来自某高校软件学院某课题组的研究生课程配套材料,共 37 页,信息密度很高:从 H∆H 散度定义、目标域误差上界的不等式推导,一路讲到 DAN、DANN、MCD、MDD 和 DEV 模型选择。它解决的问题很具体:源域标了一堆数据,目标域没标签,模型凭什么在目标域还能用,误差上界由哪几项组成。适合做跨域图像分类、文本分类、语音识别这类训练分布和部署分布不一致场景的人;新手能沿着推导路径建立直觉,熟手能直接把它对应到损失函数和超参数上。
2. 从源风险到目标风险:H∆H 散度如何拆解模型误差
2.1 三条误差链:经验风险、分布偏移、理想联合假设
迁移学习的标准设定是“独立且不同分布”,也就是源域分布 P(x,y) 和目标域分布 Q(x,y) 不相等,但任务又相关。常规监督学习只优化源域经验风险 ε̂_P(h),迁移学习要回答的是:这个 h 放到目标域后,ε_Q(h) 到底受什么控制。
讲义里的核心不等式是:对任意 h ∈ H,目标域风险被源域风险加上两项“看不见”的风险控制:
ε_Q(h) ≤ ε_P(h) + [ε_P(h*) + ε_Q(h*)] + |ε_P(h, h*) − ε_Q(h, h*)|其中 h* 是理想联合假设,也就是在源域和目标域上同时表现最好的那个分类器。右侧第二项叫理想联合风险,它衡量两个域“可迁移的天花板”有多高——如果源域和目标域的类别边界本身就对不上,这一项就会很大。第三项是差异差异(disparity difference),它衡量 h 和理想假设 h* 在源域、目标域上行为差距的差值。
工程上更常用的是带经验估计的完整泛化界:
ε_Q(h) ≤ ε̂_P(h) + d_HΔH(P̂, Q̂) + ε_ideal + O(√(d log n/n) + √(d log m/m))这里 ε̂_P(h) 是源域训练误差,d_HΔH 是经验域间散度,d 是假设空间的 VC 维,n 和 m 分别是源域和目标域样本数。这个式子解释了两种常见现象:一是目标域样本太少时,散度估计本身就不置信,模型表现自然飘;二是源域训练误差压得再低,如果域间散度大,目标域照样崩。
讲义里还借用了“Bias-Variance-Shift”诊断框架:训练误差高是偏差问题,开发集误差高是方差问题,开发集和测试集不一致是数据集偏移问题。迁移学习处理的是第三种——所以如果你只在源域里切一个开发集来调参,本质上是在调方差,不是调漂移,这就是后面 DEV 方法要解决的事。
2.2 用 MMD 近似 H∆H 散度:多核带宽与 RKHS
H∆H 散度的定义是:
d_HΔH(P, Q) = sup_{h,h'∈H} | ε_P(h, h') − ε_Q(h, h') |它找的是假设空间里最“挑事”的两个分类器,让它们在源域和目标域上的不一致程度差别最大化。这个定义很理论,直接优化不现实,但讲义给出了一个关键变换:H∆H 散度可以写成积分概率度量(IPM)的形式:
d_F(P, Q) = sup_{f∈F} | E_{x∼P}[f(x)] − E_{x∼Q}[f(x)] |如果函数族 F 是再生核希尔伯特空间(RKHS)里的核函数,这个 IPM 就变成了最大平均差异(MMD)。DAN 用的就是多核 MMD(MK-MMD),把多个不同带宽的高斯核做凸组合:
d_k²(P, Q) = || E_P[φ(x_s)] − E_Q[φ(x_t)] ||²_H_k工程上的重点是一个容易被忽略的细节:MMD 的带宽 σ 不能拍脑袋定一个。常见做法是设置多个候选带宽(比如 1、10、100),或者用中位数启发式——取样本对距离的中位数做初始 σ。我一般会先跑一个小 batch,把特征距离的中位数打出来,再决定带宽范围;多核组合的意义就是让你不用在带宽上做太精细的网格搜索,但带宽范围最好能覆盖特征距离的实际尺度,否则 MMD 值会趋近于 0,等于没加约束。
2.3 用对抗判别器近似 H∆H 散度:从 sup 到 min-max
MMD 是统计距离的近似,另一条路线是用判别器去逼近 H∆H 散度的上界。讲义里的推导是把两个分类器的不一致转化成域判别器 D(x) 的胜负:
d_HΔH(P, Q) ≤ sup_{D∈H_D} | E_P[D(x)=1] + E_Q[D(x)=0] |这个式的意思是说:如果能训练出一个判别器,完美区分样本来自源域还是目标域,那这个判别器的精度就度量了域间差异。DANN 的思路就是把“sup”这一步用一个神经网络学出来,整体构成 min-max 博弈:特征提取器想让判别器分不清,判别器拼命想分清。
这里有一个值得记住的判断标准:训练结束后域判别器精度越接近 0.5,说明特征分布越对齐;如果精度一路冲到 0.98 以上,说明对抗训练没有真正起到作用,多半是特征提取器被判别器压制住了,后面第 5 章会专门讲这个现场。
| 近似方式 | 理论基础 | 实现形态 | 典型方法 |
|---|---|---|---|
| 统计距离 | RKHS 中的 IPM | 核函数计算 | DAN、MK-MMD |
| 对抗判别 | 域判别器上界 | 梯度反转/对抗训练 | DANN |
| 分类器一致性 | 分类器边界差异 | 双分类器不一致最小化 | MCD |
3. DAN 与 DANN 实现:损失项组合和两个关键参数
3.1 DAN:在 fc6-fc8 上对齐 MMD,损失怎么拼
DAN 的套路是在深度网络的多个任务相关层上做分布对齐。以经典的 conv1-conv5 + fc6-fc8 结构为例:底部卷积层冻结(保留通用特征),fc6 和 fc7 层微调,fc8 层重新学习;MMD 项加在 fc6、fc7、fc8 的输出上,强制源域和目标域在这些高层表示上分布一致。
损失形式:
L = (1/n_a) Σ L_ce(θ(x_a_i), y_a_i) + λ Σ_{l=l1}^{l2} d_k²(P_l, Q_l)第一项是源域上的交叉熵分类损失,第二项是多个层的 MK-MMD 之和。这里 λ 控制分类和分布对齐的平衡,常见做法是先固定一个比较小的值(比如 0.1),然后观察源域精度和目标域精度的相对变化。伪代码实现:
def mmd_loss(fs, ft, bandwidths): # fs, ft: 源域和目标域特征, 形状都是 [B, D] total = 0.0 for bw in bandwidths: # 高斯核矩阵 k_ss = gaussian_kernel(fs, fs, bw).mean() k_st = gaussian_kernel(fs, ft, bw).mean() k_tt = gaussian_kernel(ft, ft, bw).mean() total += k_ss + k_tt - 2.0 * k_st return total for xs, ys, xt in train_loader: # 分别提取源域和目标域在高层的特征 fs = net.features(xs) ft = net.features(xt) loss_ce = F.cross_entropy(net.classifier(fs), ys) loss_mmd = mmd_loss(fs, ft, bandwidths=[1.0, 10.0, 100.0]) loss = loss_ce + lambda_w * loss_mmd loss.backward()代码逻辑:gaussian_kernel 内部计算两两样本的 RBF 核值,MMD 的期望差等价于“源域内相似度 + 目标域内相似度 − 两倍跨域相似度”。带宽列表取 1、10、100 是经验值,如果特征经过 L2 归一化,带宽范围可以整体缩小。有几个实现细节值得注意:MMD 项至少要拿一个 batch 的源域和一个 batch 的目标域算,batch 太小会导致估计方差大;一般 batch size 取 32 或 64,并且源域和目标域样本数要基本持平。
3.2 DANN:梯度反转层的 λ 退火,避免模型直接摆烂
DANN 的网络里多了一个域判别器 Gd,接在特征提取器 Gf 之后,判别器要判断特征来自源域还是目标域。关键部件是梯度反转层(GRL):前向时直通不变,反向时把梯度乘以一个负常数 −λ,这样源域分类损失、域判别损失在同一个反向过程中共同作用——分类器想让特征有判别性,梯度反转层又强行把特征的域信息抹掉。
损失函数写成一个统一形式:
E = Σ L_y(Gy(Gf(x_i)), y_i) − λ Σ L_d(Gd(Gf(x_i)), d_i)注意第二项前面是减号,配合 GRL 的负梯度实现。判别器自己是在 max 这个损失,特征提取器是在 min。梯度反转层的 PyTorch 风格实现:
class GradientReverseLayer(torch.autograd.Function): @staticmethod def forward(ctx, x, lam): ctx.lam = lam return x.clone() @staticmethod def backward(ctx, grad_output): return -ctx.lam * grad_output, None # 训练循环中动态计算 lambda p = step / total_steps # 训练进度, 0 -> 1 lam = 2.0 / (1.0 + math.exp(-10.0 * p)) - 1.0 feat = extractor(torch.cat([xs, xt], dim=0)) feat_rev = GradientReverseLayer.apply(feat, lam) loss_domain = domain_loss(discriminator(feat_rev), domain_labels)这里最容易被忽略的是 λ 的调度。如果一开始就把 λ 设得很大,特征提取器会被迫快速抹掉域信息,源域分类精度同时掉下来,最后两个域都学不好。常见做法是用一个从 0 平滑上升到 1 的退火函数,前 10%~20% 步让网络先把源域分类任务学个大概,再逐步加大对抗强度。判别器的学习率一般比特征提取器略低,否则判别器收敛太快,特征提取器追赶不上,loss 就震荡。
3.3 MCD:双分类器 L1 不一致最小化,三步训练循环
MCD 的思路和 DANN 不同:它不用域判别器,而是用两个分类器 F1、F2 在目标域上的输出差异作为“域差异信号”。两个分类器共享同一个特征提取器 G,但初始化或训练方式不同,所以对目标域样本的预测会出现分歧。算法在讲义里拆成三步,核心是:
- Step A:用源域数据同时训练 G、F1、F2 做标准分类,这是保底步骤,确保特征有任务判别性。
- Step B:固定 G,训练 F1、F2 最大化它们在目标域上的输出差异,同时仍然在源域上做分类。
- Step C:固定 F1、F2,训练 G 最小化这个差异,让目标域特征在分类器眼里不再有分歧。Step C 通常重复 k 次。
差异度量用的是 L1 距离:
d(p1, p2) = (1/K) Σ | p1_k − p2_k |其中 K 是类别数,p1_k、p2_k 是两个分类器对第 k 类的概率输出。讲义里特别提到 L2 距离效果不好,原因是 L2 对概率差做了平方放大,个别类别的较大分歧会主导梯度,训练不稳定。伪代码:
for xs, ys, xt in loader: # Step A: 源域分类 loss_a = ce(F1(G(xs)), ys) + ce(F2(G(xs)), ys) opt_all.zero_grad(); loss_a.backward(); opt_all.step() # Step B: 固定 G, 最大化目标域分歧 d_b = l1_dist(F1(G(xt)), F2(G(xt))) loss_b = ce(F1(G(xs)), ys) + ce(F2(G(xs)), ys) - lambda_adv * d_b opt_clf.zero_grad(); loss_b.backward(); opt_clf.step() # Step C: 固定 F1/F2, 最小化目标域分歧, 重复 k 次 for _ in range(k): d_c = l1_dist(F1(G(xt)), F2(G(xt))) opt_gen.zero_grad(); d_c.backward(); opt_gen.step()Step B 里的-lambda_adv * d_b是最大化分歧的关键。如果去掉源域分类损失直接最大化分歧,两个分类器很可能一起漂移到离谱的边界。Step C 的 k 是个超参数,取值 4 是常见起点:k 太小,生成器跟不上分类器的对抗强度;k 太大,生成器可能过度拟合当前分类器,训练变慢。实际调试时如果发现目标域精度上不去,先把 k 降到 1 看看 baseline,再往上加。
4. MDD 与 DEV:目标域没标签时,怎么选模型和调超参
4.1 MDD:用边缘差异散度收紧 H∆H 上界
MDD(Margin Disparity Discrepancy)是 H∆H 散度的一种改进形式,针对分类问题引入“边缘”概念。H∆H 散度度量的是两个假设在源域和目标域上不一致程度的差值,但它对假设的输出没有做 margin 约束。MDD 的思路是:把分类器的预测置信度考虑进去,用带 margin 的差异作为度量,这样上界通常比 H∆H 散度更紧,尤其是在类别边界不清晰的数据上。
从实现角度看,MDD 的损失仍然可以用类似领域对抗的方式去优化,但判别目标从“域来源”换成了“边界一致性”。这也解释了为什么讲义里 MDD 和 DAN、CDAN 这类方法放在一起比较:它既可以跟 MMD 类方法组合,也可以跟对抗类方法组合。我对 MDD 的实际感受是:当源域和目标域类别数不一致,或者存在细粒度类别时,MDD 比单纯对齐分布更稳,因为它的注意力在决策边界附近,而不是整个分布。
4.2 DEV:嵌入验证集如何寻找“后悔药”
目标域没有标签,模型选择就成了黑匣子。最常规的做法是从源域里切一个验证集调超参,但这个验证集只代表源域分布,选出的模型在目标域上经常不是最优的。讲义里的 DEV(Deep Embedded Validation)解决的就是这个问题:它把目标域样本嵌入到特征空间,通过一种可迁移的验证准则来估计模型在目标域上的表现,不需要目标域标签。
实操上,我会把 DEV 理解成一套“代理验证”流程:先在源域上训练一个足够好的特征提取器,把目标域样本投影到特征空间;然后计算目标域特征在分类器输出上的置信度一致性、域对齐程度等指标,用这些指标替代目标域准确率来做模型选择。比如在两个候选 checkpoint 之间比较,不只看源域验证集精度,还要看目标域特征的领域内紧凑性、跨域最近邻类别一致性。注意:DEV 不是让你拿目标域真实标签去算准确率,那个在无监督域适应设定下拿不到;它训练的是“代理指标”,所以指标的选取要尽量和最终任务相关。
4.3 训练配置:学习率、批量大小、λ 与验证指标
基于讲义的三个算法,我把训练里最容易影响结果的参数整理成一个速查表:
| 参数 | 常见范围 | 失效现象 | 调整习惯 |
|---|---|---|---|
| SGD 学习率 | 0.001~0.01 | 震荡或收敛过慢 | 用 warmup + 指数退火 |
| batch size | 32~64 | 域间估计噪声大 | 源域和目标域同 batch 且数量一致 |
| MMD λ | 0.1~1 | 源域精度明显掉点 | 先固定 0.1,再看目标域变化 |
| DANN λ | 0 平滑升到 1 | 对抗 loss 震荡 | 前 20% 步保持低位 |
| MCD k | 1~4 | 生成器被分类器带偏 | 先从 k=1 起调 |
| 动量/权重衰减 | 0.9 / 5e-4 | 内部协变量偏移 | 保持预训练初始化 |
验证指标不能只看源域准确率。至少要看一组:源域 CE loss、域判别器精度(或 MMD 值)、源域验证集精度。
5. 迁移学习训练避坑:四个现场与对应的处理
5.1 现象一:对抗训练损失震荡不收敛
训练 DANN 或 MCD 时,前几千步一直正常,跑到几万步以后损失开始在两个值之间来回摆,目标域指标没有任何净增长。
原因:对抗强度 λ 上升太快,或者判别器/分类器的学习率比特征提取器高太多,导致对抗双方里一方单方面碾压,另一方不断被拉回原点。
解决:把 λ 的退火区间拉长,前 20%~30% 的训练步都维持在 0.1 以下;判别器学习率调到特征提取器的 1/2 或 1/3;MCD 里则检查 Step B 的源域分类损失是否过大,如果过大说明两个分类器已经偏离源域边界,减小 λ_adv 或者提升 Step A 的执行频率。
5.2 现象二:换到更强的分类器后目标域反而掉点
在源域验证集上选出的模型,拿去做目标域测试时性能比更早的 checkpoint 还差。
原因:源域验证集和源域训练集同分布,它衡量的是“方差”而不是“漂移”。一个在源域上精度很高的模型,可能把源域特有的背景、纹理、噪声都学进去了,这些特征在目标域上不存在甚至有害。
解决:不要只用一个源域验证集选模型。至少同时记录目标域特征的域判别器精度或 MMD 值,在两个候选模型之间做折中;或者用第 4 章说的 DEV 代理指标,观察目标域特征在分类器输出上的置信度一致性。这条是我实际项目里踩过最深的坑,从那以后我每个迁移实验都默认打印三个指标:源域验证集精度、目标域代理指标、判别器精度。
5.3 现象三:目标域性能忽高忽低,复现不稳定
同一个训练脚本,跑两次得到的结果差了 3~5 个百分点,而且不是随机种子的问题,是每次验证时目标域上表现都不稳定。
原因:目标域数据量少,验证时如果只抽样一个 batch 去算指标,样本噪声会很大;另外如果目标域里类别不平衡,小 batch 很容易抽到特定类别,导致指标虚高或虚低。
解决:验证时尽量把整个目标域都过一遍,而不是抽一个 batch;训练中如果要定期评估,固定 random seed,保证每次验证采样一致;最终报告结果时用多个 seed 取均值和标准差,至少做三次。如果目标域非常大,也要按类别分层采样来算指标。
5.4 现象四:域判别器准确率接近 100%,目标域精度却上不去
DANN 训练到后期,域判别器几乎完美区分源域和目标域样本,但目标域分类精度一直没有明显提升。
原因:对抗训练失效了——判别器太强,特征提取器无法通过梯度反转学到有用的域不变特征,只能退化成在原始特征上硬扛。判别器精度高说明两个域的特征分布还没有对齐,问题不在目标域,在对抗平衡。
解决:降低判别器容量,或者降低它的学习率;减小 λ;给特征提取器的梯度做 clip,防止单步更新过大。另外检查一下判别器输入特征是否经过了归一化,特征尺度不一致时 MMD 和判别器都会被某几个维度主导。我遇到这种情况时,会把 λ 直接砍到 0.1,重新从一个小值开始,然后观察源域和目标域精度的联合变化。
6. 实战习惯:把理论边界当作训练日志里的第四条曲线
理论边界里的三项不是摆设,我后来把它们直接对应到训练监控指标上。训练过程里始终抓住四条曲线:源域分类损失、源域验证精度、域差异代理(MMD 或判别器精度)、目标域代理指标(留出少量目标样本,或者用 DEV 类的嵌入一致性)。画在一起之后,很多迁移学习的问题一眼就能看出来。
| 边界项 | 对应监控曲线 | 异常信号 |
|---|---|---|
| 源域经验风险 | 源域 CE loss / 验证精度 | 源域 loss 不降 |
| 域间差异 | MMD 值 / 判别器精度 | 数值趋近 1.0 或迟迟不降 |
| 模型选择 | 目标域代理指标 | 代理指标和源域指标走势相反 |
有一次我跑一个跨域任务,源域验证精度已经到 92%,MMD 也降下来了,所以当时以为没问题。结果切到目标域只有 61%,后来发现是判别器精度在 0.95 以上但 MMD 在震荡——说明是特征对齐的局部假象,而不是真的分布接近。从那以后,我每次训练都强制把判别器精度和 MMD 一起打印,缺一个都不算完整复现。
这份讲义最实用的地方,是把误差上界里的每一项都变成了可以计算的量。实践时不要只盯着源域精度,把源域/目标域的差异项纳入每个 checkpoint 的评估里,很多翻车都能提前看到苗头。希望对你有帮助。
本文还有配套的精品资源,点击获取