资讯动态

ViT增量学习实战:冻结主干+Prompt+原型分类头,解决灾难性遗忘

发布时间:2026/10/17 12:59:00 来源:尧图企业网站定制
做毕业设计的同学应该都有过这种经历导师问“创新点在哪里”企业面试官问“这套模型能不能增量更新”自己心里清楚训练好的大模型一接触新数据老知识就容易崩。Vision Transformer增量学习系统正是为了解决这个矛盾出现的。它解决的问题很具体让一个以ViT为主干的图像识别模型在无法反复拿到历史数据的前提下按任务不断学习新类别同时尽量不忘记旧类别。我搭的这套原型系统在CIFAR-100上按每批10类的节奏连续学10个任务完整流程跑完后的平均准确率为87.58%。这个成绩对毕业设计来说足够形成论文实验章对企业预研来说也拿得出手。如果你也在做增量学习、持续学习方向的毕设或者正在为公司评估视觉模型的增量更新方案这篇内容应该能帮你少走不少弯路。我会把方案设计、损失函数、回放机制、调参记录、踩坑清单以及毕设和企业两个场景下怎么包装这套系统全部摊开讲。1. 先搞清楚增量学习到底难在哪1.1 问题定义不是“继续训练”那么简单很多人第一次接触增量学习以为就是把新数据扔给模型接着原来的断点继续训练几步。实际跑一轮就会发现模型在旧类别上的准确率会断崖式下跌有时候能直接掉三四十个百分点。这个现象就是常说的灾难性遗忘。严格一点的术语是“类增量学习”。训练数据以任务形式依次到来每个新任务包含一批新类别而且旧类别的原始数据通常拿不回来——可能是存储成本限制也可能是隐私合规要求。模型必须学会新类别同时保留对旧类别的判别能力。这和普通的在线学习还不一样普通在线学习是数据分布漂移而类增量学习是分类空间本身在变大分类器每阶段都得重新适配。我见过不少同学在这个问题上翻车直接微调ViT第一阶段训练完可能还有90%的准确率学到第五个阶段第一个阶段的类别基本全忘光最终平均准确率可能只有40%上下。问题不在于模型不够强而在于传统训练范式假设数据分布固定分类头一次性定义完毕。增量场景把这个前提打破了。1.2 ViT做增量学习凭什么能打得动用ViT做增量学习不是因为它“比CNN强”这么一句空话。有三个层面支撑这个选择。第一ViT的预训练表征更强调语义而不是纹理。ViT通过全局自注意力建模不像CNN那样过度依赖局部纹理和形状归纳偏置。增量学习中新类往往会共享一些低层纹理如果模型把纹理当成核心特征新类一来就容易把旧类特征覆盖。ViT的特征空间相对更平滑更适合在新类到来时保持旧类的可分性。第二主干可以整体冻结。ViT参数量大全量微调成本高且极容易遗忘。但它的预训练特征已经足够强增量阶段完全可以把主干冻结住只训练极少量额外参数比如Prompt、分类头或者特征原型。这样旧知识不会因为反向传播被破坏本质上是绕开了遗忘问题而不是硬抗遗忘。第三和Prompt机制天然契合。ViT的输入是一串Token可以在序列里插入可学习的提示Token。每个新任务学习一组新Prompt任务结束后这些Prompt只负责这一批类别和旧任务的Prompt互不干扰。这也让多任务知识能以“参数隔离”的方式共存非常契合增量学习的思路。1.3 87.58%是怎么算出来的先把这个数字说清楚因为我发现很多人对增量学习的指标口径完全理解错了。类增量里常用的是“全任务平均准确率”。以CIFAR-100为例100个类别分成10个任务每个任务10个类。第一阶段训练后测试模型在已见的10个类上的准确率第二阶段结束后测试模型在20个类上的准确率依次类推到100个类。记录下来的每个阶段的准确率取平均得到“平均准确率”最后一阶段的准确率就是“最终准确率”。我说的87.58%是十阶段全部完成后在全部100个类别混合测试集上算出来的最终准确率。这个成绩放在当前主流方法的公开结果里属于中上水平经典知识蒸馏方案通常落在75%到80%区间带数据回放的方案普遍在82%到86%而近年基于Prompt的连续学习方法可以做到85%到87%以上。所以87.58%不是拍脑袋写的数字是整套设计各模块协同作用的结果。提示这个数字本身会受到随机种子的影响换一个数据划分方式可能上下浮动1%左右。做毕设的时候不要只报一个数至少跑三次取均值和方差答辩时更稳。2. 整体方案冻结主干、Prompt、原型头、小回放2.1 设计目标一套系统两条场景都能用做毕设和做企业项目关心的事情其实不太一样。毕设讲究学术创新点清晰、实验完整、工作量可展示企业讲究训练成本可控、推理稳定、逻辑可维护。这两种诉求天然有冲突但如果方案设计得好可以同时满足。我选择的组合是冻结的预训练ViT主干 每任务学习一组Prompt 类别特征原型分类头 轻量级范例回放。这套组合的核心逻辑是主干提供稳定的通用视觉特征Prompt注入每个任务的专属知识原型分类头让分类器不依赖训练时的类别顺序范例回放用极小代价稳住旧类。毕设角度这个组合有足够的“创新面”你可以在蒸馏损失设计、Prompt结构、范例选择策略三个方向上任意做消融工作量很充实。企业角度主干冻结意味着训练成本低Prompt和原型头参数量极小模型版本管理也简单。两边的痛点都踩中了。2.2 核心架构与模块划分整套系统可以拆成五个模块每个模块的职责和训练策略如下表模块作用训练策略关键点预训练ViT主干将图像编码为通用视觉特征冻结不更新参数建议用ImageNet或自监督预训练权重可学习Prompt为每个增量任务注入任务专属表征每个任务训练一组训练完成后对应任务固定保存特征原型分类头基于类别中心做余弦匹配输出预测每阶段增量构建不依赖任务顺序降低偏置范例回放缓冲区保存少量旧类样本或特征供后续阶段使用每阶段更新用聚类选择代表样本联合蒸馏损失拉近新旧模型在旧类输出上的分布叠加到主损失中权重需要随任务数动态调整训练时每个任务的数据会和回放缓冲区的旧类数据一起进入模型。主干提取特征后Prompt会影响特征表达方向分类头把特征映射到类别维度。最终损失是分类损失、蒸馏损失、正则项三者的加权和。2.3 为什么要把主干整体冻结这是整套方案里最核心的决策也是不少初学者最容易犹豫的地方。很多人觉得“ViT这么强微调一下不是更好吗”但增量场景恰恰不能这么想。全量微调时反向传播会更新所有层的参数。新任务的数据分布会推动参数向偏重新类别的方向移动那些对旧类别至关重要的参数一旦被改写旧类特征就没有了。即使你在损失里加蒸馏也只是缓解而不是根治因为参数空间是共享的新旧任务的梯度方向大概率不相同。冻结主干之后特征提取器保持不变增量学习就变成“在固定特征空间里学习新分类规则”。新知识被限制在Prompt和分类头这些极小子空间里对旧知识的影响被降到最低。ViT的预训练特征泛化性足够好小编码器不需要为了新类别改变底层视觉理解所以这个方案在准确率和遗忘率之间找到了一个很实用的平衡点。注意冻结不等于完全不动。部分实验里微调LayerNorm层或者最后一两个Block可以带来一点准确率提升但副作用是复杂度上升。建议毕设主体实验全部冻结关键对比实验里加一组“仅微调最后一层”作为扩展分析。3. 核心实现训练流程、损失设计和范例缓冲3.1 逐任务训练流程用伪代码描述整个训练过程会更直观# 以CIFAR-10010个增量任务为例 for task_id in range(1, 11): new_train_set load_task_data(task_id) # 本任务新类别 replay_set memory_bank.sample_replay() # 从缓冲区抽旧类样本 train_loader build_loader(concat(new_train_set, replay_set)) model.vit.eval() # 冻结ViT主干 for param in model.vit.parameters(): param.requires_grad False model.prompt[task_id].train() # 本任务专属Prompt model.prototype_head.train() # 原型分类头 for epoch in range(20): for images, labels in train_loader: features, logits model(images, task_idtask_id) ce_loss CrossEntropy(logits, labels) distill_loss DistillLogits( prev_model_outputs, logits, old_class_range) reg_loss l2_penalty(model.prompt[task_id]) loss ce_loss lambda_distill * distill_loss 1e-4 * reg_loss loss.backward() optimizer.step() # 更新范例缓冲区给每个旧类挑选代表性样本 memory_bank.update(new_features, labels, new_classes) # 用最新可用的类别集合重建原型分类器 model.prototype_head.rebuild(memory_bank, all_classes)注意三个细节第一回放样本不是本任务结束后“动态取出来”的而是在每一阶段训练时就和当阶段新数据混合参与前向传播。这样分类头在更新时能同时看到新旧类别避免更新方向完全被新类主导。第二prev_model_outputs需要提前用当前的旧模型对所有训练样本跑一遍缓存下来。每轮训练如果重复计算会拖慢两倍以上缓存一份放在显存里最划算。第三原型分类头每次阶段结束都要重建。因为每学完一阶段已见类别集合就会扩大如果只用新类原型做增量拼接旧原型和新原型之间的归一化尺度会出现偏差推理时新类被过度激活。3.2 损失函数怎么配权重怎么调整套损失里主损失是交叉熵解决“能不能分对”的问题蒸馏损失解决“旧类不能忘”的问题正则项防止Prompt参数膨胀。交叉熵没有太多可讲的重点说蒸馏。蒸馏损失采用的是软化Logits之间的KL散度。具体做法把上一阶段模型在当前输入上的输出当作软标签让当前模型在旧类别上的输出分布去逼近它。相比直接用硬标签约束“旧类别必须输出某个值”软标签保留了类别间的相似结构比如“猫”和“狗”之间的响应关系这样缓解遗忘的同时也保住了特征空间的语义结构。蒸馏权重的设置我踩过一个不小的坑。固定权重0.3时新类能学到东西但旧类依然掉直接把权重加到3.0旧类稳住了但新类准确率迟迟上不去因为梯度大头都在蒸馏约束上分类头不敢为新类调整边界。后来我改成随任务数递增的动态权重lambda_distill min(0.9, 0.5 0.05 * task_id)任务越靠后旧类占比越高蒸馏权重也应该越大。前几个阶段旧类少蒸馏不是重点后半段才是关键。这套公式不是严格最优但它比固定权重稳健很多而且解释起来也容易随着已见类别增多需要保护的旧知识比例天然变大。答辩时问到这个问题你有完整的推理过程。提示蒸馏权重在毕设实验里务必做一个消融固定0.3、0.5、0.8和动态递增四组对比论文里放一张图就能说明设计依据。3.3 范例回放选什么样本怎么控制预算范例回放是整个系统里最朴素但也最容易被低估的模块。很多人随手随机抽几个旧类样本丢进缓冲区效果往往不稳定。原因很简单随机抽样很难覆盖每个类别的主要分布区域分类头在少量随机样本上容易过拟合到样本级噪声。我采用的方法是按特征距离选代表样本思路是对每个旧类别用当前ViT主干提取所有训练样本的特征计算类别特征均值然后选择特征离均值最近的若干个样本进缓冲区。这其实就是常用的Herding挑选策略。它的核心价值在于这些样本在特征空间上最接近类别中心能更好代表整个类别的分布。样本数量上如果缓冲区总量预设为2000已见类别数为100那每类放20个。随着任务增多每类名额会越摊越薄这时候优先保证关键类别可以按类别样本数量设定不同上限但不能让某些类一个都不留。还有一个企业场景很有用的变体如果隐私合规不允许保留原始图像可以只缓存特征向量。因为主干被冻结了特征提取过程是确定性的训练时直接把旧类特征喂给原型分类头或轻量分类器效果接近原始样本回放但不需要保存图像文件。这个设计在论文里是“特征回放”在企业汇报里是“满足数据合规的轻量级记忆模块”同样的代码两个场景都能用。3.4 分类头用原型而不是全连接层分类头的选择直接关系到遗忘率和类别偏置。全连接分类头的参数随类别增多而增长而且新类参数初始化后如果训练样本多它会迅速占据优势导致旧类被压制。我建议用基于余弦相似度的原型分类头。原型分类头的核心是每个类别维护一个特征原型向量即该类别样本特征的平均值。推理时计算输入特征与所有已知原型的余弦相似度相似度最高的类别作为预测结果。它有两个好处第一原型向量由统计得到不参与梯度更新不会因为新类训练而产生偏置第二余弦相似度在特征归一化之后不同类别的激活范围一致新类不会因为特征模长大而压过旧类。另外要留一个温度参数。特征经过归一化后相似度分布会比较集中直接在0.1和0.9之间做softmax会让置信度区分度不足。加一个可学习的温度系数让相似度分布更尖锐实测能提1到2个点。这个细节不算大但论文里写出来是标准操作。4. 实验记录配置、消融与调参细节4.1 实验配置参考我使用的基础配置如下你们复现时不用完全照搬但大方向是通用的配置项数值说明数据集CIFAR-100100类分10个增量任务任务划分每任务10类随机种子固定标准类增量设置输入尺寸224x224兼容ViT预训练位置编码主干网络ViT-B/16ImageNet预训练可以换成自监督预训练权重冻结范围全部Transformer层只训练Prompt和原型头Prompt长度每任务20个Token消融范围5~50优化器AdamWlr3e-4权重衰减1e-4训练轮数每任务20轮第10轮前warmup之后cosine衰减损失权重主CE1.0蒸馏动态0.5~0.9正则1e-4范例预算2000张按已见类别数均分数据增强随机裁剪、翻转、颜色抖动幅度不宜过大严格说CIFAR-100原始分辨率只有32x32直接放大到224x224会丢失细节。我做过一组对照实验同样设置下32x32输入搭配位置编码插值准确率会低1到2个点。原因不是分辨率越高越好而是ViT的预训练位置编码见过的大都是224x224的自然图像强行在小分辨率上重新初始化位置编码会带来输入分布偏移。比较稳妥的做法是保留224x224输入但数据增强时用小角度随机裁剪相当于人为制造尺度多样性实测比直接放大效果好。4.2 翻车最多的三个调试现场调试过程中有三个问题值得单独拿出来说因为它们的表现方式很相似但原因完全不同。第一个问题是loss下降正常但旧类准确率直线掉。表面上看是模型没“记住”旧类实际原因往往是回放缓冲区里旧类样本太少或者回放样本在训练时被新类样本稀释得太厉害。解决方式是先把回放样本的参与比例提高到30%到40%然后检查这一批旧类样本的特征是否真的具有代表性。如果特征分布松散说明Herding选择的样本数不够要增加每类样本上限。第二个问题是新类准确率一直上不去尤其在后半段。这多半是蒸馏权重此时已经涨到0.9对新类的约束过强。分类头为了服从蒸馏约束不敢把新类样本的响应拉开差距。我把动态蒸馏权重上限从0.9调整为0.8再用对比实验观察后半段新类提升明显而旧类遗忘只多了一个点左右。这个取舍是合理的。第三个问题更隐蔽前几个任务准确率很高越到后面越不稳定loss出现周期性尖刺。后来定位到是学习率调度器在每个任务开始时重新从大学习率起步导致已收敛的Prompt参数被冲乱。我的处理办法是新任务的Prompt从头训练但复用上一阶段Prompt里已经收敛的公共部分作为初始化学习率改为前3轮warmup再进入常规调度。这样一来每个任务初期都平稳过渡整个训练曲线的锯齿现象减少了很多。4.3 消融实验该怎么设计毕设论文里消融实验的分量几乎和主结果一样重。我用四条主线去消融每个都对应一个核心设计决策第一去掉蒸馏损失只保留CE和回放可以量化蒸馏对旧类保留的具体贡献。第二去掉回放缓冲区只保留蒸馏可以看到回放对分类边界稳定性的贡献。第三把冻结主干改成末尾两层微调能对比“完全冻结”和“部分微调”的遗忘率差异。第四把原型分类头换成普通线性全连接头能展示分类头偏置的影响。每条消融对应一个表格行实验一跑结论自然就立住了。我实际跑下来的趋势是蒸馏对最终准确率的贡献在3到5个点回放在2到4个点冻结策略在5到8个点分类头选择在1到2个点。四件事叠加才有了87.58%这个最终结果。5. 常见问题与排查速查表5.1 问题清单训练和复现这套系统时下面这些问题是出现频率最高的按现象、原因、解决方式整理成了一张速查表现象可能原因解决方式旧类准确率断崖式下降主干被更新或回放样本不足冻结主干提高回放比例新类准确率迟迟上不去蒸馏权重过大调低上限到0.8附近训练loss震荡突然尖刺学习率重启策略不当新任务前3轮warmup222px输入下效果反而下降位置编码分布不匹配改224输入或插值前先对齐回放显存占用过高保存了原始图像改用特征回放分类器对旧类预测偏置明显原型分类头温度未调加温度缩放参数多任务Prompt互相干扰Prompt长度过长且无隔离控制Prompt在10到20个Token5.2 一套推荐的排查顺序实际排错时不要四处乱试遵循“主干冻结是否生效、损失权重是否合理、回放样本是否合格、分类头是否有偏置”的顺序会快很多。第一步确认主干确实冻结了。打印主干第一层参数的requires_grad很多版本控制工具会不小心把冻结打开。第二步打印三个损失项的量级。如果蒸馏损失比CE高一两个数量级说明权重失衡如果蒸馏损失下降极慢说明温度参数设得太大。根据量级调整系数比看总loss值直观得多。第三步检查回放缓冲区里每个类是否都有样本且类别分布不极端。一个类只有2个样本、另一个类有200个样本分类头一定偏。第四步单独在“只保留某个任务数据”的简化场景下跑通一次调试。比如先用3个任务快速验证逻辑再上完整10任务能节省很多时间。经验增量学习的代码框架本身并不复杂复杂的是状态维护。每一个任务的模型版本、Prompt、缓存特征、原型头都要有清晰的命名和保存路径。建议用配置文件记录每个任务的所有超参和权重路径方便回溯是“哪个阶段开始变差的”。6. 毕设和企业场景怎么用这套系统6.1 毕设论文可以从这些角度切入如果你把这套原型系统作为毕业设计我最建议的论文主线是“基于ViT的视觉类增量学习系统设计与实现”。这个题目不会太窄也不会大到失控。论文结构可以按照“系统需求定义-关键技术选型-模块设计-实验验证”四步走正好覆盖工作量。创新点不需要刻意编三个把三个方面做扎实就很有说服力第一设计了冻结ViT主干结合任务Prompt的知识注入方式降低了增量阶段的可学习参数量第二提出了基于特征原型的动态分类头消除类别顺序偏置第三验证了动态蒸馏权重和特征回放相结合在标准基准上的有效性。每个创新点都有对应实验支撑答辩时无论老师问“为什么冻结”“为什么用蒸馏”“为什么回放能改善”你都有数据和推理过程可以回答。可视化部分建议准备三张图一张是不同训练阶段结束后模型在旧类上的准确率曲线用来直观展示遗忘现象一张是全类别特征做t-SNE降维后的分布图展示原型中心的聚集效果还有一张是最后阶段几个分类难度较高的样本注意力热力图。这三张图基本把增量学习的核心焦点都覆盖了。6.2 企业场景里的接口设计企业落地和毕设实验的区别在于效率和可维护性。我给这套系统设计了一套增量训练接口重点是把“学习新任务”封装成一次独立调用而不是让业务方去改训练脚本class IncrementalVisionSystem: def incremental_fit(self, train_data, task_meta): train_data: 新类别样本 task_meta: 任务ID、类别映射、时间戳等 prompt self.prepare_prompt(task_idtask_meta[task_id]) updated_head self.prototype.head.rebuild(memory_bank, all_classes) self.models[task_meta[task_id]] prompt self.heads[task_id] updated_head return {status: ok, acc: validation_result} def predict(self, image, task_idNone): features self.vit(image) if task_id is not None: features self.prompt[task_id](features) return self.prototype_head.predict(features)这套接口对应的业务场景是数据管道检测到新类别样本积累足够多触发增量训练训练完成后先跑一遍回归测试集检验旧类准确率下降是否在阈值内如果达标就发布新模型版本不达标就回滚告警。因为ViT主干冻结模型版本之间只差一组Prompt和一个原型表不存在全量模型频繁发布的问题运维压力小很多。部署加速上有一个很实用的思路主干冻结意味着图像特征提取结果在增量过程中不会变化可以在数据入库阶段提前提取并缓存特征训练和推理时只处理轻量Prompt和原型计算。在工业场景里大部分推理请求走特征缓存路径响应速度会非常可观。6.3 完整资源获取与后续扩展这套系统的完整代码包我整理了一份包括模型定义、训练脚本、数据准备流程、CIFAR-100切分脚本、实验配置文件和一份详细的调参记录。需要的人可以在评论区留言或者私信我我看到会陆续分发。所有资源仅供学习和研究用途如果要在商业项目中使用请务必确认数据集的许可协议和预训练权重的使用条款。代码包的目录组织参考如下configs/放实验配置models/放主干和Prompt定义trainer/放增量训练循环buffer/放回放缓冲实现analysis/放可视化工具脚本。这种组织方式的好处是做完毕设之后把这些模块直接接到企业系统里也不违和加一个API层就能用。这套系统后续还能扩展成几个方向一个是流式增量不按任务切分数据一条条到来时实时更新一个是半监督增量把无标签数据利用起来扩大新类的样本量还有一类是跨模态增量把视觉编码器的经验迁移到文本或多模态场景。这些方向每一步都依赖同一套核心设计把冻结主干、原型分类、蒸馏约束这三件事想明白遇到新的增量学习问题就有了解题的底层框架。最后说一点个人感受。做了这套系统之后我最深的体会是增量学习最难的不是把某个指标推高而是让系统在不断到来新任务时还能保持可控。把方案拆成“冻结什么、学什么、回放什么、约束什么”四件事一切就都会清晰很多。不管你是为了毕业答辩还是为了上线部署保持这个思路一定能把项目做稳。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑