SensorFM 不做微调能直接用吗——自监督学到的表示到底够不够

文章导读
SensorFM 这类自监督传感器基础模型,通常可以直接拿来接一个轻量任务头,但“够不够”不能凭感觉判断,它取决于你的标签量、下游任务与预训练语料的分布距离、以及你能接受的指标底线。较为稳妥的做法是先不重训大模型,用三组最小对照把现成表示称一称:一组从零训练的浅层基线、一组冻结编码器只训线性探针、一组少量参数参与梯度更新的少样本微调。三组用同一份切分、同一个指标、同一批随机种子跑出来,才谈得上冻结
📋 目录
  1. 一 先确认当前场景和目标
  2. 二 按这个顺序处理
  3. 三 改完后怎么验证
  4. 四 容易忽略的边界
A A

SensorFM 这类自监督传感器基础模型,通常可以直接拿来接一个轻量任务头,但“够不够”不能凭感觉判断,它取决于你的标签量、下游任务与预训练语料的分布距离、以及你能接受的指标底线。较为稳妥的做法是先不重训大模型,用三组最小对照把现成表示称一称:一组从零训练的浅层基线、一组冻结编码器只训线性探针、一组少量参数参与梯度更新的少样本微调。三组用同一份切分、同一个指标、同一批随机种子跑出来,才谈得上冻结、微调还是换数据。

如果线性探针在验证集上不低于从零训练的浅层基线,且跨随机种子的波动小于组间差距,通常可以先冻结编码器只训任务头;若探针明显落后、而解冻末一两层或加低秩适配后能追平,再考虑小范围梯度更新。前提是采样率、通道顺序、归一化口径与预训练阶段基本对齐,否则三组对照会同时失真,此时应先对齐数据口径或退回从零训练,而不是靠加大微调力度硬撑。

先确认当前场景和目标

要让上面的对照有意义,先确认几件事,任何一项不成立,后面得到的比较都可能是在比数据预处理而不是比表示质量。

  • 任务形态:分类、回归还是异常检测。分类可以先用线性探针;回归和异常检测需要换对应的头,并用 MAE、AUC 之类的指标另设基线。
  • 标签规模:每类或每个工况大概多少条带标签样本。只有个位数样本时,单次实验的方差往往盖过真实差距,必须多组重复。
  • 数据口径:采样率、序列长度、通道数量与顺序、物理量纲、缺失值处理方式,是否和 SensorFM 预训练语料接近。需要结合环境确认,不能默认一致。
  • 评测协议:验证集怎么切,是按时间切还是随机切,是否需要防止同一段信号同时出现在训练和验证里。
  • 资源约束:能不能把编码器留在显存里跑完整个验证集,推理侧是否接受冻结编码器的额外开销。

把这些写成一份简短配置,后面所有实验都引用同一份,避免不同对照组用了不同切分或不同归一化,那样比出来的差距没有意义。

按这个顺序处理

顺序很重要:先立基线,再做探针,最后才动梯度。直接上微调容易把“表示有问题”和“训练超参没调好”搅在一起。

第一步:固定评估协议,写出从零训练的浅层基线

浅层基线的参数规模不要比任务头大太多,否则它赢不了会说明是模型容量不够,而不是预训练表示不行。下面是一段通用骨架,接口名按你实际使用的框架替换。

# baseline.py —— 从零训练的浅层基线,仅用于对照
model = SmallCNN(in_ch=CONFIG.in_ch, n_class=CONFIG.n_class)
opt = torch.optim.AdamW(model.parameters(), lr=3e-4)
for epoch in range(CONFIG.epochs):
    for x, y in train_loader:
        loss = cross_entropy(model(x), y)
        loss.backward()
        opt.step()
        opt.zero_grad()
    log_metrics(epoch, evaluate(model, val_loader))

第二步:冻结编码器,跑线性探针

线性探针只训练一个线性头,编码器权重全程不变。它能回答“现成表示是否已经把类别信息摆在了容易分开的位置”。探针配置建议单独一份文件,便于和微调配置对照。

# probe.yaml —— 线性探针配置,占位符按你的仓库替换
backbone:
  name: sensorfm
  ckpt: /path/to/sensorfm_weights.ckpt
  freeze: true
head:
  type: linear
  out_dim: NUM_CLASSES
data:
  train: /path/to/train
  val: /path/to/val
  norm: per_channel_zscore   # 与预训练阶段统计口径保持一致
train:
  epochs: 50
  lr: 1e-3
  seeds: [0, 1, 2, 3, 4]

跑完后确认日志里编码器的梯度范数为零或参数哈希不变,确保它确实处于冻结状态。

第三步:少样本梯度对照

如果探针落后,不要立刻全量微调。先解冻末一到两层,或改用低秩适配,把可训练参数压住,学习率比头部的学习率再小一个量级。注意少样本重复实验方案:每个样本量档位至少换几组随机种子和几组不同的支撑集抽样,跑完取中位数与极差,而不是只报最好的一次。

# finetune.yaml —— 少样本梯度对照
finetune:
  mode: last_n_layers      # 也可改为 lora,替换为对应的秩与缩放系数
  unfreeze_layers: 2
  lr_backbone: 1e-5
  lr_head: 1e-3
  n_shot: [4, 8, 16]
  repeats: 5
  seed_split: [0, 1, 2, 3, 4]

评估入口保持一条命令,方便三组用同样的参数复跑。

python eval.py `--ckpt` runs/probe/seed0/best.ckpt `--split` val `--metric` acc,f1,mae

第四步:按结果选冻结、微调还是换数据

把三组结果放进同一张表,再按下表判断。表里的“明显”指超过跨种子波动范围,而不是单次跑分高一点。

观察到的现象建议动作
线性探针不小于从零浅层基线,跨种子波动小先冻结编码器,只训任务头,把精力花在数据和标签上
探针落后,但解冻末一两层或低秩适配后追平采用小范围梯度更新,保持可训练参数尽量少
探针与从零基线都差,换随机初始化的编码器也同样差优先排查标注质量、切分泄漏和归一化,再考虑更换表示或补充预训练数据
微调后训练集指标高、验证集不涨回退到冻结方案,减少可训练参数或增加正则,不要继续加大学习率

改完后怎么验证

验证动作要能独立于训练过程复查。建议至少做以下几项,并把输出落到日志文件而不是只留在终端。

  1. 用相同切分、相同种子把三组各跑一遍,比较同一个指标。组间差距小于跨种子极差时,说明证据不足,先增加重复次数再下判断。
  2. 看线性探针的收敛曲线:训练损失趋于平稳、验证指标不再上升即可停,不需要追求训练损失更低。
  3. 抽一批验证样本,按类别计算编码器输出的余弦相似度或距离分布。如果不同类别的嵌入几乎重合,说明现成表示对当前任务区分度不足,冻结方案很难达标。
  4. 确认冻结设置真的生效:编码器参数哈希、梯度范数、可训练参数量都应与配置相符;可训练参数量意外变大,说明解冻范围写错了。
  5. 回归任务额外看残差分布是否随工况漂移,漂移明显时不能只用整体 MAE 下结论。

预期信号是:探针方案能给出稳定且可复现的验证指标,且不劣于浅层基线;少样本梯度方案在增加重复后仍能稳定超过探针。若始终看不到这个信号,就按回退顺序处理。

容易忽略的边界

  • 数据口径不一致:目标传感器的采样率、通道顺序或量纲与预训练阶段差别较大时,冻结表示往往明显退化。先做重采样、通道对齐和归一化,并记录统计量的来源。
  • 归一化统计不可复现:如果找不到预训练阶段使用的均值方差,用少量带标签数据估计并在配置里写死,不要让训练脚本每次重新计算。
  • 版本与权重格式:SensorFM 的权重文件格式、加载代码和框架版本需要相互匹配。版本不一致时先固定版本或转换权重,不要一边排查兼容性一边解冻层数。
  • 任务不适用:分类探针不适用于回归和异常检测;类别极不平衡或正样本极少时,探针的验证指标波动大,不宜作为唯一依据。
  • 切分泄漏:同一段连续信号被切进训练和验证两侧时,指标会虚高,三组对照都会失真。按时间或按设备切分更稳。
  • 回退建议:结果不达标时,按冻结、少样本梯度、从零训练、更换数据或表示的顺序退,每一步只改一个变量,并保留可复跑的配置。