首页 / 资讯中心 / 文章详情

知识蒸馏赋能时间序列异常检测:轻量化落地与工程避坑

知识蒸馏赋能时间序列异常检测:轻量化落地与工程避坑 ★ FEATURED ARTICLE
1. 从一次边缘端部署翻车说起为什么异常检测也要做知识蒸馏时间序列异常检测这件事我在工业设备监控和业务指标巡检两个场景里都做过最难受的阶段从来不是模型指标刷不上去而是指标刷上去了却落不了地。去年有个项目教师侧用的是一套堆了十二层注意力、带多尺度重构头的模型在离线测试集上 AUC-PR 能到 0.87团队里所有人都挺满意。结果一上现场就傻眼采集端是一块算力很紧的工控板单次推理要一百多毫秒还得按滑动窗口每几秒跑一次跑起来之后 CPU 长期 90% 以上连带其它采集线程抖动。这就是知识蒸馏在时间序列异常检测里最朴素的动机——我们已经有了一个知道什么是正常的大模型现在需要把它对正常的理解压缩进一个小模型同时尽量不丢检测能力。这里先说清楚本文要聊的对象。时间序列异常检测指的是在多变量或单变量时序流里把偏离正常模式的点、段或者整个窗口找出来知识蒸馏指的是用一个训练好的大模型教师去指导一个小模型学生训练让学生模仿教师的输出或中间表征。把这两件事拼在一起解决的问题很具体在算力、显存、延迟被卡死的边缘或在线场景里保留大模型的判断力。适合读这篇的人大概是三类手上已经有异常检测模型但部署困难的同学、想尝试蒸馏但发现分类那套搬不过来的同学、以及准备复现类似论文但卡在实验设计上的同学。1.1 异常检测模型到底重在了哪些地方很多人以为模型重是因为参数量其实在时序场景里算力消耗的大头往往不在参数上。我拆过一次推理耗时一个带多尺度金字塔的教师模型参数量只有 1.2M但单窗口推理 130ms拆下来大概是这样多尺度分支重复做了三次卷积占 45%、注意力层的 QKV 投影和 softmax占 30%、重构头逐点输出后再做一次全窗口的误差归一化占 15%、剩下的序列化和张量搬运占 10%。也就是说重在多分支冗余计算和逐点输出而不是单纯的权重数量。这个观察直接决定了后面蒸馏要往哪儿使劲如果只是把通道数剪一半参数量降了但分支数没变延迟几乎不动。还有一层隐性开销容易被忽略异常检测模型通常要输出逐时间点的异常分数而不是一个窗口级的标签。窗口长度 100、通道数 40 的输入重构头就要吐 4000 个数值后续还要算残差、做平滑、跑阈值。这一步在服务器上无所谓在采集端就是实打实的压力。所以我在做蒸馏方案时会把学生要不要保留完整的逐点重构头当成一个独立的设计决策而不是想当然地继承教师结构。1.2 蒸馏真正能省掉的是什么一句话概括蒸馏省的是表示能力的冗余不是简单地砍层数。教师之所以强很大程度是它在高维表征空间里学到了一个相当精细的正常流形学生要做的不是重新学一遍这个流形而是学会用更低的维度、更少的层去逼近它。这跟图像分类里学生模仿教师 logits 是一个道理区别在于时序异常检测通常没有类别标签也就没有那个可以直接软化的分类头。我在实际项目里把可省的部分拆成三块。第一块是编码深度教师十二层注意力学生三层卷积加一层轻量注意力在多数场景下足够第二块是多尺度分支的合并教师的三个尺度分支可以由学生的一个膨胀卷积堆叠近似靠不同的 dilation 覆盖不同感受野第三块是解码器的共享教师每个尺度一个独立解码器学生只保留一个共享解码器加尺度嵌入。这三块加起来在我那次实测里把单窗口推理从 130ms 压到 21msAUC-PR 从 0.87 掉到 0.845。掉 0.025 换来 6 倍加速这个交易在现场是可以接受的但如果掉到 0.80 以下基本就没法交付了——这也是后面所有设计取舍的判断基准。提示先量出延迟瓶颈在哪一层再决定蒸馏策略。盲目按参数量减半设计学生结构很可能延迟一点没降指标先掉一大截。2. 分类蒸馏那套为什么搬不过来三种可行的接法如果你照着 Hinton 那套软标签蒸馏直接往时序异常检测上套大概率第一天就会卡住教师模型没有分类 logits输出的是每个时间点的重构值或者异常分数你拿什么当软标签直接把重构值当软标签做 MSE理论上可行实践里效果往往很差因为重构值本身数值范围大、分布偏斜学生很容易学成平均化的输出——把所有点都往均值附近拉异常点上的残差反而被抹平了。这是我这几年踩过最典型的一个坑值得单独展开讲。所以真正可行的接法是把知识重新定义。我在项目里用过三种分别对应不同的信息层次也对应不同的实现难易度。2.1 输出层蒸馏把重构值换成正常性得分最直接的做法是先对教师的输出做一次单调变换把它变成一个有界的正常性得分再做蒸馏。具体来说教师对每个时间点输出重构值 $\hat{x}_t$我算残差 $e_t |x_t - \hat{x}_t|^2$然后过一个可学习的或者基于训练集统计的归一化得到 $s_t \exp(-e_t / \tau)$$\tau$ 取训练集残差的中位数。这个 $s_t$ 落在 0 到 1 之间含义是这个点有多正常学生的对应输出也过同样变换两边做 KL 或者 BCE。这样做的关键好处是数值尺度被压平了学生不会因为某几个极端残差就把整体输出拉偏。不过这里有个必须提醒的细节温度参数 $\tau$ 不能用验证集上的异常样本去调。异常检测的训练集通常只有正常数据如果你拿异常样本去调 $\tau$等于把测试信息泄漏进训练流程离线指标会虚高得离谱。我一般固定用训练集残差的某个分位数比如 60% 分位或者在正常数据上做交叉验证选宁可保守一点。2.2 中间特征蒸馏对齐哪一层比怎么对齐更重要特征蒸馏是我实际用得最多的一路因为它不依赖输出形式教师和学生只要都有编码器就能做。做法是选教师的某一层或某几层输出用一层线性投影把学生对应层的通道数对齐然后算余弦相似度损失或者归一化后的 MSE。听起来简单但选层这件事讲究不少。我试过三种选层策略实测差别挺明显。第一种是对齐最后一层也就是编码器输出好处是语义层级接近坏处是学生最后一层维度通常压得很低信息已经被挤没了逼着学生用很小的表征去拟合教师的大表征容易过拟合到训练集的正常模式细节上。第二种是对齐中间层比如教师的第 6 层效果一般最好因为这一层的表征既有局部模式又有一定全局性学生的容量刚好够。第三种是每层都对齐听起来信息最全实际上会让学生被多个损失拉扯训练不稳定我只有在教师和学生层数接近时才会考虑。下面是我常用的一个投影加对齐的写法通道对齐用 1x1 卷积损失用归一化 MSE实测比直接 MSE 稳import torch import torch.nn as nn import torch.nn.functional as F class FeatureDistill(nn.Module): def __init__(self, stu_dim, tea_dim): super().__init__() # 1x1 卷积做通道对齐比全连接更保留时序结构 self.proj nn.Conv1d(stu_dim, tea_dim, kernel_size1, biasFalse) # 可学习的层权重让模型自己决定对齐的松紧 self.scale nn.Parameter(torch.ones(1)) def forward(self, stu_feat, tea_feat): # 输入形状均为 (B, C, T) s self.proj(stu_feat) s F.normalize(s, dim1) t F.normalize(tea_feat.detach(), dim1) # 教师梯度必须断掉 loss F.mse_loss(s, t) return self.scale * loss那段代码里有两个点值得说。tea_feat.detach()是硬性要求教师是冻结的不加 detach 会白白多算一遍梯度还可能因为教师参数被微调导致蒸馏目标漂移。F.normalize沿通道维做归一化等于只对齐方向不对齐幅度这样学生在容量不足时不会被强行要求匹配教师的激活强度。这两个小改动在我手上大概带来 0.01 到 0.02 的 AUC-PR 提升属于低成本高回报。2.3 关系型蒸馏把样本之间的相似度结构搬过去第三种是我在小样本场景下的偏好不蒸馏单点表征而是蒸馏样本之间的关系。具体做法是一个 batch 里取 $N$ 个窗口教师算出 $N \times N$ 的相似度矩阵学生也算一个然后对齐两个矩阵。这个思路的价值在于异常检测的核心其实是这个窗口和正常窗口有多不像关系结构比绝对表征更贴近任务本身。实现上有两个坑。一是 batch 内如果混入了异常窗口半监督场景下有可能关系矩阵会被污染所以我会在算关系损失前用一个基于教师分数的软掩码把高异常分数的样本权重压低。二是 $N$ 不能太小$N8$ 的时候关系矩阵太稀疏基本没信息量我一般用 32 以上这也是关系蒸馏显存开销大的原因——$N^2$ 的矩阵在长窗口下很吃内存。如果显存吃紧可以只对一个子集算关系损失比如每个 batch 随机抽 16 个窗口算其余只算特征损失。蒸馏方式依赖教师结构显存开销我实测的收益适用场景输出层正常性得分需要重构或打分头低稳定但有限0.005~0.01学生结构简单、只求稳中间特征对齐需要编码器中最显著0.015~0.03默认首选关系型蒸馏只要有编码器高$N^2$小样本下好0.02正常样本稀少三者组合都需要高收益递减容易互相拖有充足调参预算时3. 教什么比怎么教更关键正常样本建模范式的选择聊完接法得往上一层追问教师到底学到了什么值不值得学这个问题在异常检测里比分类任务尖锐得多。分类任务里教师的软标签蕴含了类别间的相似性结构这个信息天然有价值而异常检测的教师通常只在正常数据上训练它学到的全部东西就是正常长什么样学生的任务本质上不是模仿判断而是模仿对正常的建模方式。这意味着教师范式选错了后面蒸馏做得再精细也是白搭。3.1 重构式、预测式、对比式三条路对蒸馏的友好度我这几年主要用过三条路各自的蒸馏友好度差别挺大。重构式是最常见的教师学一个编码器-解码器最小化正常数据的重构误差。它的优点是教师输出稠密、每个时间点都有监督信号特征蒸馏和输出蒸馏都能做。缺点是重构式模型有个众所周知的倾向容量足够大时它连异常点也能重构得不错残差反而不明显。这时候你蒸馏出来一个学生学生的重构能力被教师带得很好异常点上的残差同样被压小检测能力就废了。我在一个传感器数据集上遇到过这种情况教师重构式的 AUC-PR 是 0.82蒸馏后学生只有 0.71比不用蒸馏直接训练的小模型还差。后来查出来就是这个问题教师太强把异常也重构了。预测式是拿历史窗口预测未来若干步用预测误差做异常分数。它对蒸馏更友好一点因为预测任务本身就限制了模型抄答案的可能——未来值没看到只能靠模式推断。但预测式有个麻烦多步预测的误差会累积教师在某些平稳段预测极准在抖动段误差很大这个误差分布的不均匀性会被学生原样学过去导致学生分数在不同区段尺度不一致阈值很难统一设。对比式是近几年比较流行的路子通过构造正负样本对让模型学会区分表征。它的优势是表征判别性强蒸馏出来的学生即使容量小表征也够锐。缺点是训练流程复杂对数据增强的设计很敏感——时序上的增强抖动、缩放、裁剪如果做过头会把正常的周期性破坏掉模型学到的判别边界就虚了。我一般只在前两种效果不理想时才上对比式。3.2 教师只见过正常样本学生会不会学成过度自信这个问题值得单独拎出来。教师训练时只有正常数据损失只惩罚把正常数据判成异常从不惩罚把异常数据判成正常因为压根没有异常样本。结果是教师的正常性评分在正常区域非常平滑、非常自信一旦离开正常流形它的输出其实是不受约束的——可能很高也可能因为插值效应反而给出一个看起来正常的值。学生如果全盘模仿这种自信就会继承同样的问题。我的应对办法是在蒸馏损失里加一个边界约束项对输入做小幅扰动高斯噪声、随机掩码要求教师和学生在扰动样本上的输出差异也要小。这相当于告诉学生在正常样本附近要保持和教师一致而那些远离正常流形的点扰动后的差异会自然变大异常分数也就有了区分度。实测这一项大概能带来 0.01 到 0.015 的提升代价是训练时间增加三成左右。注意扰动幅度要控制。我用的是相对标准差 0.02 到 0.05 的高斯噪声超过 0.1 就相当于在正常数据里制造伪异常蒸馏目标会自相矛盾。3.3 合成异常做反向监督到底值不值很多论文会提到用合成异常在正常序列里注入尖峰、阶跃、周期破坏来增强训练。我在蒸馏流程里试过结论是谨慎使用。合成异常和真实异常的分布差距太大尤其工业场景里的异常往往是缓慢漂移或者多通道协同失效你注入的尖峰根本不像。用合成异常训练出来的判别边界在现场很容易误报。如果确实想用我建议只把它当作正则项而不是主损失给合成异常样本一个很小的权重比如 0.1作用是防止学生把正常区域学得太宽而不是真的指望它识别异常。另外合成方式最好从真实数据的统计特性出发——比如按训练集每个通道的均值和方差生成尖峰而不是拍脑袋定一个固定幅值。4. 学生骨架与蒸馏插点的工程选型学生网络选什么直接决定了蒸馏能拿到多少收益。我见过不少人直接拿教师的前几层当学生觉得结构一致好对齐实测下来往往不是最优因为教师的前几层是为后面的深结构服务的单独拎出来表征能力很弱。4.1 五种常见骨架的实际表现我在同一个多变量数据集上横向比过五种学生骨架输入窗口 100、通道 40教师固定为十二层注意力的重构模型。结果大致如下表收益指相对于同结构学生不用蒸馏的提升。学生骨架参数量单窗口延迟独立训练 AUC-PR蒸馏后 AUC-PR蒸馏收益三层 1D-CNN约 60K4ms0.7620.8150.053膨胀 TCNdilation 1/2/4约 90K6ms0.7810.8310.050双向 GRU隐藏 64约 110K15ms0.7740.8220.048轻量 Transformer2 层头 4约 150K18ms0.7880.8380.050MLP-Mixer 风格无卷积约 80K5ms0.7510.7960.045几个观察。TCN 在延迟和精度之间平衡最好是我在多数项目里的默认选择GRU 延迟偏高但在强周期性数据上表现不错轻量 Transformer 精度最高可惜 18ms 的延迟在采集端仍然紧张MLP-Mixer 风格最轻但对通道顺序敏感通道数变化时要重训。有意思的是蒸馏收益基本都在 0.045 到 0.053 之间也就是说学生骨架本身的强弱序列和蒸馏后的强弱序列高度一致——蒸馏是锦上添花不是雪中送炭。指望用蒸馏把一个设计糟糕的学生救回来不现实。4.2 感受野和窗口长度必须对得上这一条特别容易被忽略。学生用膨胀卷积时感受野是 $1 \sum (k-1) \cdot d$三层核大小 3、膨胀率 1/2/4 的话感受野只有 15而输入窗口是 100。这意味着学生只能看到窗口内 15 个点剩下 85 个点它完全没感知蒸馏时教师看到全局学生只看到局部特征对齐会非常困难。解决办法是把膨胀率拉大或者直接堆更多层。我一般按窗口长度的 1/2 到 2/3 来设计感受野窗口 100 就至少要覆盖 50 到 65 个点膨胀率用 1/2/4/8/16 五层感受野能到 63。这个改动看起来是超参调优实际上决定了蒸馏能不能对齐——我在一个项目里就是因为感受野只有 15特征蒸馏的损失一直卡在 0.4 下不去改成 63 之后直接降到 0.12AUC-PR 也从 0.79 跳到 0.83。4.3 蒸馏插点末端对齐还是逐层对齐插点位置我做过一轮对比。末端对齐只在编码器最后输出处算蒸馏损失实现最简单训练最稳逐层对齐学生的每一层对应教师的某几层信息量大但需要设计层间映射而且学生的中间层维度和教师差得远投影层本身就要占不少参数。实用建议是学生层数少于四层时用末端对齐多于四层时用分段对齐。分段对齐的意思是把教师层按比例分组学生每层对应教师一组取组内输出的平均做对齐目标。这样既保留了多层次信息又不用逐层一对一硬配实现上也不复杂。5. 训练流程、损失配比和几个必须盯死的超参讲完整套设计落到代码上其实就三件事损失怎么组、权重怎么调、训练怎么排。5.1 两阶段还是联合训练两阶段是先把教师训到收敛、冻结再训学生联合训练是教师学生一起训。我在实际项目里几乎只用两阶段原因很实际教师训练通常要跑很久联合训练意味着每次调学生超参都要重跑教师时间成本受不了。而且教师一旦开始和学生一起更新它的输出分布会漂移蒸馏目标不稳定很容易训崩。两阶段的代价是教师一旦训得不好学生上限就被锁死了。所以我一般在教师阶段会多花时间把学生阶段的预算压到教师的三分之一左右。5.2 损失组合的实际配比我常用的组合是三项学生的自监督任务损失重构或预测、特征蒸馏损失、输出蒸馏损失。权重上我试过很多组合比较稳的一组是自监督损失 1.0、特征蒸馏 0.5、输出蒸馏 0.2。特征蒸馏权重比输出高是有原因的输出层的数值噪声大权重太高反而干扰主任务特征层的信息更结构化可以承担更大的权重。# 训练主循环骨架简化版 for epoch in range(epochs): model.train() for x in train_loader: # x: (B, C, T)全部为正常样本 x x.to(device) # 学生前向 stu_out, stu_feats model(x, return_featsTrue) # 教师前向冻结no_grad with torch.no_grad(): tea_out, tea_feats teacher(x, return_featsTrue) # 1) 自监督任务损失 loss_task F.mse_loss(stu_out, x) # 2) 特征蒸馏只对齐一个中间层 loss_feat feat_distill(stu_feats[-2], tea_feats[5]) # 3) 输出蒸馏先转成正常性得分再对齐 s_stu torch.exp(-((stu_out - x) ** 2).mean(dim1) / tau) s_tea torch.exp(-((tea_out - x) ** 2).mean(dim1) / tau) loss_out F.mse_loss(s_stu, s_tea) loss 1.0 * loss_task 0.5 * loss_feat 0.2 * loss_out optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step()clip_grad_norm_那行不是可选项。蒸馏损失和主任务损失的尺度差异大早期梯度容易爆我吃过一次亏训练到第 3 个 epoch 损失突然变 NaN排查半天才发现是特征蒸馏那一项在某一层激活特别大的时候把梯度带飞了。5.3 温度与权重的调度策略分类蒸馏里温度是固定的时序场景里我更倾向做退火前 30% 的 epoch 用较大的温度$\tau$ 取训练集残差标准差的 2 倍让学生先把整体形状学对后 70% 逐步降到 0.5 倍逼学生去拟合细节。实测比固定温度稳定AUC-PR 大概高 0.008。损失权重也可以调度但我不建议搞太复杂。一个够用的策略是特征蒸馏权重从 0.2 线性升到 0.5理由是训练初期学生表征还很乱强行对齐反而拖慢主任务收敛等主任务稳定了再加大蒸馏力度。这个小技巧在我手上没翻过车。6. 评估环节最容易自欺欺人的三件事模型训完指标一出来最危险的不是指标低是指标虚高你还信了。异常检测这个领域的评估坑特别多我列三个最要命的。6.1 point-adjust 带来的虚高按时间点做平滑调整把一个异常段内所有点都算作检测正确只要段内有一个点被检出这种做法能让 AUC 从 0.7 直接跳到 0.95 以上。我第一次看到这个数字的时候还挺激动后来才发现它把检测延迟这个最关键的工程指标彻底掩盖了——你在异常段最后一个点才报出来也算全对但现场设备早就出事了。我在项目里的做法是双轨报告既报 point-adjust 后的数字用于和文献对比也报不调整的逐点 AUC-PR 和 F1后者才是交付依据。如果两个数字差距超过 0.15我会怀疑模型其实只在异常段末尾才有响应需要进一步查检测延迟分布。6.2 阈值到底怎么定异常分数是连续的落地必须有阈值。我在评审别人的方案时经常看到用测试集的最优 F1 反推阈值这是典型的信息泄漏。正确做法应该是在验证集同样只含正常样本或者含极少量标注异常上按目标误报率定阈值比如要求每小时误报不超过 0.5 次就取验证集正常分数分布的 99.5 分位。这里还有个现实约束误报成本远高于漏报成本的场景比如告警会打扰人阈值要往高分位压反过来漏报代价大比如设备保护阈值就得放宽。这个取舍没有通用答案必须和业务方对齐。6.3 检测延迟需要单独统计我现在的评估脚本里一定会算一个指标从异常真实起点到模型首次连续 $k$ 个点超过阈值的平均滞后步数。这个数字在论文里几乎没人报但在工程上比 AUC 重要得多。曾经有个模型 AUC-PR 0.86看着很好延迟统计出来平均滞后 23 步按采样频率折算快 4 分钟对产线来说完全不可用。后来改小了窗口长度和平滑窗口AUC 掉到 0.83延迟降到 6 步才算能交付。7. 实测中的坑和完整的排查链路最后这部分是我最想写的因为前面那些设计再漂亮实际跑起来总会出问题。7.1 学生分数分布塌缩现象是蒸馏训完之后学生在所有输入上的异常分数都挤在一个很窄的区间里正常和异常的分数几乎分不开AUC-PR 掉到 0.5 附近。我第一次遇到时以为是蒸馏损失写错了查了半天 loss 曲线很平滑没有任何异常。排查链路是这样的。第一步把教师和学生在同一批正常数据上的分数分布画出来发现教师的标准差是 0.11学生只有 0.014学生确实塌缩了。第二步检查学生输出的逐点残差发现残差的方差也很小说明学生把重构做得过于平滑——它学到了一个几乎恒定的输出对所有输入都给相似的结果。第三步回溯损失权重发现特征蒸馏权重设成了 1.0主任务损失被压制学生只顾着对齐教师表征把自己原本的重构能力丢了。第四步验证这个猜测把特征蒸馏权重降到 0.3 重训分数标准差回到 0.08AUC-PR 恢复到 0.82。根因很清楚蒸馏损失和学生自身任务的尺度不匹配时学生会偷懒直接去优化更容易的蒸馏目标。修复方案就是前面说的权重配比另外加一个监控项——训练过程中定期算学生输出分数的标准差低于 0.03 就报警。7.2 蒸馏之后异常反而被抹平这个更隐蔽。整体 AUC 看着还行但某几类异常尤其是幅度较小、持续时间较长的漂移型异常检不出来。我的排查路径是先把测试集里的异常按类型分桶分别算每类的召回发现漂移类召回从 0.62 掉到 0.31其它类基本没变。然后做对照实验同一个学生结构一组用蒸馏训一组不用蒸馏直接训发现不用蒸馏的那组在漂移类上召回是 0.58。也就是说蒸馏把这一类的能力弄丢了。继续查把教师和学生在漂移异常段上的逐点分数打出来看到教师在漂移段前期分数明显上升学生却一直平着直到漂移幅度很大才响应。这说明学生的感受野不够看不到足够长的上下文来判断这是漂移只能等到局部偏差足够大。修复就是把感受野从 15 拉到 63同时把窗口长度从 100 加到 200。改完之后漂移类召回回到 0.55整体 AUC-PR 还涨了 0.01。这个坑的教育意义在于蒸馏会放大学生对教师全局视野的依赖学生视野不够时被牺牲的往往是那些需要长上下文才能判断的异常类型。7.3 训练集里混进了异常点这个问题跟蒸馏本身无关但会彻底毁掉蒸馏。异常检测的无监督范式默认训练集全是正常数据实际拿到的数据里总有没标注的异常。教师容量大对少量脏数据有一定鲁棒性学生容量小几个异常点就能把它的正常流形拉歪。我的处理流程是先用教师给训练集打分取分数最高的 1% 样本做人工复核或者直接剔除再用干净数据训学生。剔的时候要小心别把正常的极端值也剔了比如设备启停阶段的正常剧烈波动这些样本分数也很高。我的经验是结合持续时长判断——单点高分大概率是噪声或真异常连续一段高分更可能是正常的工况切换后者应该保留并单独建模。现象最可能的根因快速验证方式处理方向学生分数标准差 0.03蒸馏损失权重过大打印各损失项量级降蒸馏权重至 0.3 以下漂移类异常召回骤降学生感受野不足按异常类型分桶算召回加大 dilation 或窗口训练 loss 震荡后 NaN梯度爆炸加梯度裁剪后重训clip 阈值设 1.0蒸馏后全面不如不蒸馏教师重构能力过强对比教师异常段残差换预测式或对比式教师离线好线上差阈值来自测试集检查阈值来源改用验证集分位数8. 我个人在这套流程里攒下的几条经验做这类项目做多了会形成一些不太写在论文里但很管用的习惯。第一个是先跑通不蒸馏的基线。很多人一上来就搭蒸馏管线最后指标不好根本分不清是蒸馏的问题还是学生结构的问题。我现在的顺序固定是训教师、训同结构学生不蒸馏、训蒸馏学生三个数字摆在一起谁的问题一目了然。第二个是把教师的异常分数分布存下来。这不只是为了对比更是为了后续排查——学生分数和教师分数的散点图能暴露很多问题比如学生是不是在某些区段系统性偏低是不是对某类通道不敏感。我一般会存成 npz配上时间戳和通道号出问题的时候直接捞出来看。第三个是别迷信蒸馏能救轻量模型。前面表格里那组数字已经说明了蒸馏收益大概就是 0.05 的 AUC-PR如果学生独立训练的基线只有 0.6蒸馏完也就 0.65还是不能用。这种情况下该做的是换特征、换窗口长度、加通道选择而不是在蒸馏上继续折腾。第四个是部署前一定在真实采样频率下测延迟。离线测延迟用的是规整的 batch现场是流式、单样本、还带预处理实际耗时往往是离线测量的 2 到 3 倍。我吃过一次亏离线测 6ms上线变 19ms直接把余量吃光。如果这套东西还要往下扩展我觉得有两个方向值得试一是把蒸馏和在线自适应结合起来让学生在现场用无标注数据做轻量微调缓解分布漂移二是把多尺度蒸馏做成层级化的不同尺度用不同的学生容量而不是一刀切。这两个方向我在小规模实验里都看到了一点苗头但还没有能拿出手的完整结论等跑稳了再单独写。
阅读完成 · 觉得有帮助?
咨询建站