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
第四步:按结果选冻结、微调还是换数据
把三组结果放进同一张表,再按下表判断。表里的“明显”指超过跨种子波动范围,而不是单次跑分高一点。
| 观察到的现象 | 建议动作 |
|---|---|
| 线性探针不小于从零浅层基线,跨种子波动小 | 先冻结编码器,只训任务头,把精力花在数据和标签上 |
| 探针落后,但解冻末一两层或低秩适配后追平 | 采用小范围梯度更新,保持可训练参数尽量少 |
| 探针与从零基线都差,换随机初始化的编码器也同样差 | 优先排查标注质量、切分泄漏和归一化,再考虑更换表示或补充预训练数据 |
| 微调后训练集指标高、验证集不涨 | 回退到冻结方案,减少可训练参数或增加正则,不要继续加大学习率 |
改完后怎么验证
验证动作要能独立于训练过程复查。建议至少做以下几项,并把输出落到日志文件而不是只留在终端。
- 用相同切分、相同种子把三组各跑一遍,比较同一个指标。组间差距小于跨种子极差时,说明证据不足,先增加重复次数再下判断。
- 看线性探针的收敛曲线:训练损失趋于平稳、验证指标不再上升即可停,不需要追求训练损失更低。
- 抽一批验证样本,按类别计算编码器输出的余弦相似度或距离分布。如果不同类别的嵌入几乎重合,说明现成表示对当前任务区分度不足,冻结方案很难达标。
- 确认冻结设置真的生效:编码器参数哈希、梯度范数、可训练参数量都应与配置相符;可训练参数量意外变大,说明解冻范围写错了。
- 回归任务额外看残差分布是否随工况漂移,漂移明显时不能只用整体 MAE 下结论。
预期信号是:探针方案能给出稳定且可复现的验证指标,且不劣于浅层基线;少样本梯度方案在增加重复后仍能稳定超过探针。若始终看不到这个信号,就按回退顺序处理。
容易忽略的边界
- 数据口径不一致:目标传感器的采样率、通道顺序或量纲与预训练阶段差别较大时,冻结表示往往明显退化。先做重采样、通道对齐和归一化,并记录统计量的来源。
- 归一化统计不可复现:如果找不到预训练阶段使用的均值方差,用少量带标签数据估计并在配置里写死,不要让训练脚本每次重新计算。
- 版本与权重格式:SensorFM 的权重文件格式、加载代码和框架版本需要相互匹配。版本不一致时先固定版本或转换权重,不要一边排查兼容性一边解冻层数。
- 任务不适用:分类探针不适用于回归和异常检测;类别极不平衡或正样本极少时,探针的验证指标波动大,不宜作为唯一依据。
- 切分泄漏:同一段连续信号被切进训练和验证两侧时,指标会虚高,三组对照都会失真。按时间或按设备切分更稳。
- 回退建议:结果不达标时,按冻结、少样本梯度、从零训练、更换数据或表示的顺序退,每一步只改一个变量,并保留可复跑的配置。