4.5 隔行传艺:跨模态跨任务与持续学习蒸馏 本节摘要:师傅不一定与徒弟同行。跨模态蒸馏让文本师傅教视觉徒弟、语音师傅教唇动徒弟,用另一模态的丰富先验补本模态数据的贫瘠;跨任务蒸馏把别的任务学到的判断迁移过来;持续学习场景里,蒸馏还有一个特殊角色——旧模型给新模型"留话",防止新知识把老手艺冲掉。本节讲这三种"隔行传艺"的桥接方法与失效模式。 前几节的师生至少同模态同任务。本节把师生差再放大一级:模态不同、任务不同、甚至"时代不同"。它们共享一个核心难题——桥怎么搭。 跨模态:借别行的师傅 最经典的组合是文本教视觉。做行人检索时,文本描述数据远比成对的行人图像丰富;做图像标注时,语言模型对"什么该出现在画面里"的先验远比小视觉模型深。
本节摘要:师傅不一定与徒弟同行。跨模态蒸馏让文本师傅教视觉徒弟、语音师傅教唇动徒弟,用另一模态的丰富先验补本模态数据的贫瘠;跨任务蒸馏把别的任务学到的判断迁移过来;持续学习场景里,蒸馏还有一个特殊角色——旧模型给新模型"留话",防止新知识把老手艺冲掉。本节讲这三种"隔行传艺"的桥接方法与失效模式。
前几节的师生至少同模态同任务。本节把师生差再放大一级:模态不同、任务不同、甚至"时代不同"。它们共享一个核心难题——桥怎么搭。
最经典的组合是文本教视觉。做行人检索时,文本描述数据远比成对的行人图像丰富;做图像标注时,语言模型对"什么该出现在画面里"的先验远比小视觉模型深。跨模态蒸馏的流程是:文本教师对描述文本产出表示或分布,经过一个跨模态桥接层(对齐映射或共享嵌入空间)映射到视觉学生的输出空间,学生以此为额外监督。
桥接层是成败关键。最简单的做法是把两个模态的表示投影到同一维度再算距离;进阶做法是共享一个跨模态编码器,让"文本描述 a"与"图像 a"在桥接空间里互相靠近——这本质上是把对齐目标从"逐点相等"放宽为"同一事物的两种描述在桥上汇合",思想与 2.3 的关系蒸馏一脉相承:坐标不必同,格局要相同。
import torch import torch.nn as nn import torch.nn.functional as F class CrossModalBridge(nn.Module): """跨模态桥接:把文本教师表示投到视觉学生的特征空间。""" def __init__(self, text_dim, visual_dim): super().__init__() self.proj = nn.Sequential( nn.Linear(text_dim, visual_dim), nn.LayerNorm(visual_dim), ) def forward(self, t_feat): return self.proj(t_feat) def cross_modal_kd(student_feat, teacher_text_feat, bridge): v = student_feat.flatten(2).mean(-1) # 视觉特征池化成向量 t = bridge(teacher_text_feat) # 文本表示过桥 v = F.normalize(v, dim=-1) t = F.normalize(t, dim=-1) return 1.0 - (v * t).sum(-1).mean() # 余弦距离损失 bridge = CrossModalBridge(text_dim=768, visual_dim=256) s_feat = torch.randn(16, 256, 7, 7) # 学生视觉特征图 t_feat = torch.randn(16, 768) # 文本教师表示 loss = cross_modal_kd(s_feat, t_feat, bridge) print("跨模态蒸馏损失:", loss.item()) # 输出示例: # 跨模态蒸馏损失: 0.47 # 归一化后算余弦:幅值与坐标系解耦,只比"方向是否汇合"。
跨任务蒸馏不加模态这个维度,只跨任务边界:用分类教师教检测学生(分类先验帮检测器认物体),用识别教师教分割学生。桥接方式通常是对齐共享骨干的深层特征——两个任务的骨干学到的高层语义有大量重叠,任务头各干各的。跨任务蒸馏最常见的坑是负迁移:教师任务与学生任务的语义冲突(如分类教师的"平移不变偏好"妨碍检测的定位精度)会让某些层越学越差。处置办法是给对齐损失加任务感知门控:哪层对齐后任务损失恶化,就关掉哪层的对齐。
持续学习里蒸馏扮演的角色最特殊:模型 A 在任务一上训练完成,现在要在同一网络上继续学任务二。直接训,任务一的手艺会被冲掉(灾难性遗忘)。蒸馏给出的解法优雅而便宜——把旧模型 A 冻结副本当作任务一的师傅,新模型在学任务二的同时,向 A 的旧输出做蒸馏,约束"新知识别把老判断改得面目全非"。旧模型的输出在这里不是学习目标,而是"记忆的锚"。
import torch import torch.nn.functional as F def continual_kd_step(new_model, old_model, x_new, y_new, lam=1.0, T=2.0): """持续学习一步:新任务硬损失 + 旧模型输出蒸馏锚。""" old_model.eval() new_logits = new_model(x_new) task = F.cross_entropy(new_logits, y_new) with torch.no_grad(): old_logits = old_model(x_new) # 旧输出当"记忆锚":温度低(T=2),约束强度适中 anchor = F.kl_div( F.log_softmax(new_logits / T, dim=-1), F.softmax(old_logits / T, dim=-1), reduction="batchmean") * T * T loss = task + lam * anchor loss.backward() return loss.item(), task.item(), anchor.item() # 输出说明:lam 大则旧手艺保得多、新任务学得慢; # 工程上按"旧任务回测精度不低于阈值的 lam 最大值"来定。 old = torch.nn.Linear(32, 10); new = torch.nn.Linear(32, 10) x = torch.randn(8, 32); y = torch.randint(0, 10, (8,)) l, lt, la = continual_kd_step(new, old, x, y) print(f"总损失 {l:.3f} = 新任务 {lt:.3f} + 记忆锚 {la:.3f}") # 输出示例: # 总损失 3.526 = 新任务 2.412 + 记忆锚 1.114
背景。 细粒度鸟类识别任务,标注图像只有 1.2 万张,直接训练的强模型 84.5%;而网络文本里有海量"某种鸟——羽毛、习性"的结构化描述。
操作。 用一个大型语言模型做文本教师:对每个鸟种的描述文本产出 768 维表示;搭一层线性桥接投到视觉空间(256 维);学生的全局特征经归一化后与过桥的文本表示做余弦对齐(权重 0.3),叠加常规图像分类损失(权重 0.7)。对齐只在学生骨干的最后一层做,浅层不动。
结果。 学生 87.9%,比纯视觉训练高 3.4 个点;其中对稀有鸟种(训练图像少于 50 张的类目)的提升最大,达到 6.1 个点。
解读。 收益分布极不均匀——稀有类受益最大,原因正是跨模态的特长:文本描述对稀有鸟种的信息量几乎不随图像数量打折,而视觉数据是瓶颈。这条案例给出跨模态蒸馏的选型判据:本模态数据长尾越严重、另一模态先验越富,隔行传艺的收益越大。反过来,本模态数据充足时,桥接层引入的对齐误差可能得不偿失。
变式。 桥接层从线性升级为小跨模态注意力模块,代价与收益都上一个台阶;语音教唇动(唇读)、文本教图像分割(用描述划定区域)是两个成熟方向;持续学习场景里,若新旧任务差异极大,把记忆锚的权重按任务相似度动态调整,比固定 lam 更稳。
⚠️ 常见坑:跨模态对齐全域硬做。文本表示的幅值分布与视觉特征天然不同量级,先归一化再对齐是底线;跳过归一化的余弦或 MSE,训练早期损失就被幅值差主导,桥等于没搭。
💡 关键直觉:隔行传艺的本质是"借数据"。文本师傅教视觉徒弟,借的是互联网上取之不尽的文本先验;旧模型给新模型留话,借的是已经付过学费的旧经验——两件事共享同一个经济账。