数据不够怎么办?小样本下的预测性维护建模技巧
数据不够怎么办?小样本下的预测性维护建模技巧
说实话,做预测性维护最怕的不是模型调参,不是选什么框架,而是数据不够。
去年Q3我们接了一个江苏做精密齿轮箱的工厂项目。对方有6台关键设备,正常运行了3年,采集的振动数据大概也就200万条——听起来不少对吧?但故障样本呢?7条。就7条。其中3条还是同一个轴承在不同阶段的重复记录。你让深度学习去啃这7条样本,纯属搞笑。
但甲方要的是"能跑起来的模型",不是"等你们再攒两年数据"。所以那段时间我们团队被逼着研究了一套小样本建模的路子,今天把这些实战经验摊开来聊。
一、先搞清楚:你的"不够"是哪种不够
很多人一说数据不够就急着上数据增强,其实得先分类。
我见过的场景大概分三种:
第一种,总量够但类别极度不平衡。比如齿轮箱项目,200万条正常数据,7条故障。这种其实好办,后面要说的SMOTE和代价敏感学习就能解决。
第二种,总量本身就少。比如新上线的高端设备,运行才3个月,总共就5万条数据,其中故障1条。这种最难搞,得靠迁移学习或者预训练模型。
第三种,标注不够。原始振动信号一大堆,但没人标过哪些是故障、故障类型是什么。这种得先从半监督或者主动学习入手。
我们那个齿轮箱项目,其实是第一种+第二种的混合体。总量看着还行,但故障样本稀缺到离谱。
二、SMOTE不是万能药,但确实管用
先说最基础的——SMOTE(Synthetic Minority Over-sampling Technique)。
很多人看不起SMOTE,觉得"过采样嘛,老生常谈"。但说实话,在工业场景里,SMOTE + 一个调得好的随机森林,效果经常比瞎折腾的深度学习强。
我们在齿轮箱项目上用的不是原版SMOTE,而是Borderline-SMOTE。原版SMOTE是在所有少数类样本周围插值,但Borderline-SMOTE只对那些"靠近决策边界"的少数类样本做增强——说白了就是,只给"模棱两可"的样本加戏,远离边界的样本不动。
代码长这样,用的是imbalanced-learn 0.12.0:
from imblearn.over_sampling import BorderlineSMOTE from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import StratifiedKFold import numpy as np # X_train: 振动特征 (时域统计量 + FFT频带能量) # y_train: 0=正常, 1=内圈故障, 2=外圈故障, 3=滚动体故障 # 原始分布: 正常 19993条, 故障合计 7条 print(f"增强前: {np.bincount(y_train)}") # 输出: [19993, 2, 3, 2] # Borderline-SMOTE, 只过采样到 1:10 的比例,不是1:1 # 1:1 在工业场景里容易过拟合,1:10 更稳 bsmote = BorderlineSMOTE( sampling_strategy={1: 500, 2: 500, 3: 500}, # 故障类过采样到500条 k_neighbors=3, # 邻居数设小一点,样本太少了 random_state=42, kind='borderline-1' # borderline-1 比 borderline-2 更保守 ) X_res, y_res = bsmote.fit_resample(X_train, y_train) print(f"增强后: {np.bincount(y_res)}") # 输出: [19993, 500, 500, 500] # 用随机森林,不是XGBoost——小样本下XGBoost容易过拟合 clf = RandomForestClassifier( n_estimators=200, max_depth=8, # 限制深度,防止过拟合 min_samples_leaf=10, class_weight='balanced_subsample', # 内置的代价敏感学习 random_state=42 ) # 5折分层交叉验证,必须分层,否则某折可能全是正常样本 skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)踩坑提醒:SMOTE 一定要在训练集上做,千万别在验证集或者测试集上过采样。我见过有人把整个数据集过采样完再划分,那评估指标全是假的,能到99%但上线就崩。
三、数据增强:给振动信号"加料"
SMOTE 是在特征空间做插值,但振动信号本身也可以做增强。我们在另一个项目里(2024年5月,给一家做数控机床的台企做主轴健康监测)试了几种信号层面的增强方法,效果最好的有两个:
Jitter(加高斯噪声)和Magnitude Warping(幅度扭曲)。
import numpy as np def jitter(signal, sigma=0.03): """给信号加高斯噪声,sigma 控制噪声强度""" noise = np.random.normal(0, sigma, signal.shape) return signal + noise def magnitude_warp(signal, sigma=0.1, num_knots=4): """幅度扭曲:用随机样条曲线扭曲信号幅度""" from scipy.interpolate import CubicSpline orig_steps = np.arange(signal.shape[0]) random_warps = np.random.normal(1.0, sigma, (num_knots + 2,)) knot_points = np.linspace(0, signal.shape[0] - 1, num=num_knots + 2) cs = CubicSpline(knot_points, random_warps) warped = cs(orig_steps) return signal * warped # 一条原始振动信号,采样率 12kHz,长度 2048 点 original = vibration_signal # shape: (2048,) # 增强出 5 条新样本 augmented = [] for _ in range(5): aug = jitter(original, sigma=0.02) aug = magnitude_warp(aug, sigma=0.08) augmented.append(aug)注意这里有个细节:sigma 不能太大。我们一开始设了 0.1,增强出来的信号看着跟原信号差很多,模型学不到东西。后来降到 0.02-0.03,效果反而好。说白了就是,增强要"像",不能太离谱。
更狠的是,我们把信号增强和 SMOTE 结合用——先对原始故障信号做5倍增强,再提取特征,最后用 Borderline-SMOTE 在特征空间二次增强。故障样本从7条变成了35条信号,再变成1500条特征向量。随机森林的F1-score从0.31提到了0.84。
四、迁移学习:借别人的数据,练自己的模型
如果数据真的少到SMOTE都救不了,那就得上迁移学习了。
2024年8月,我们给一个做航空发动机叶片检测的军工项目做支持。对方的数据涉密,不能外传,但训练样本只有12条故障记录。我们用的是预训练模型 + 领域微调的路子。
具体来说,先用公开的轴承故障数据集(CWRU,凯斯西储大学那个)训练一个基础模型,然后用客户的少量数据做微调。这里有个关键决策:
冻结前几层,只微调最后2-3层。
import torch import torch.nn as nn from torchvision.models import resnet18 # 把1D振动信号转成2D时频图(STFT),然后喂给ResNet # 这是工业界常用的"曲线救国"方法 # 加载在CWRU数据集上预训练的模型 pretrained = torch.load('resnet18_cwru_stft.pth') model = resnet18(pretrained=False) model.load_state_dict(pretrained) # 冻结前3层(layer1-layer3),只微调layer4和fc层 for name, param in model.named_parameters(): if 'layer4' not in name and 'fc' not in name: param.requires_grad = False # 替换最后的全连接层,输出3类故障 model.fc = nn.Linear(model.fc.in_features, 3) # 微调:学习率设得很低,epoch 也少 optimizer = torch.optim.Adam( filter(lambda p: p.requires_grad, model.parameters()), lr=1e-4 # 预训练模型微调,学习率必须小 ) # 只训20个epoch,多了就过拟合 for epoch in range(20): ...你可能会问:CWRU是轴承数据,客户是航空叶片,领域不一样啊,迁移有用吗?
别急,往下看。确实,如果源域和目标域差异太大,迁移效果会打折扣。但振动信号的底层特征(频谱结构、谐波模式、冲击响应)在不同旋转机械之间是有共性的。我们实测下来,预训练 + 微调的方案比从头训练好了不止一个档次——F1从0.42提到了0.79。
当然,如果源域和目标域完全八竿子打不着(比如从图像分类模型迁移到振动信号),那就别折腾了,老实找同领域的数据集。
五、一个争议点:小样本下,深度学习到底行不行?
很多人会觉得,样本少就应该用传统机器学习,深度学习是"大数据"的专属。但说实话,这个观点在2024年已经有点过时了。
我们做过一个对比实验:用同样的30条故障样本(来自一个泵阀监测项目),分别训练: - 随机森林(调参到最优) - 1D-CNN(3层卷积,参数量控制在5万以内) - 原型网络(Prototypical Network,专门为小样本设计的)
结果挺有意思的: - 随机森林:F1 = 0.71,训练5秒 - 1D-CNN:F1 = 0.68,训练2分钟——更差,而且不稳定 - 原型网络:F1 = 0.83,训练30秒
说白了,小样本下不是深度学习不行,是你用的架构不对。原型网络、关系网络、MAML 这些元学习架构,就是为"看几个样本就能学会"设计的。2024年我们在3个项目里用了原型网络,效果都稳定优于随机森林。
但反过来想,原型网络也有坑——它对特征提取器的要求很高,如果特征提得不好,原型在嵌入空间里聚不到一起,那就白搭。所以通常的做法是:先用大量无标签数据训练一个自编码器做特征提取,再用原型网络做分类。
写在最后
小样本建模没有银弹。SMOTE 简单但有效,信号增强能锦上添花,迁移学习能救命,原型网络则是前沿武器。
最后说两句实操建议:
- 先试试随机森林 + Borderline-SMOTE,这是性价比最高的方案,很多时候够用了。
- 数据增强的噪声参数要调,别抄个代码就跑,sigma 差0.01结果可能差很多。
- 迁移学习一定要冻结底层,全层微调在小样本下等于从头训练,预训练的优势全浪费了。
- 别迷信深度学习,小样本下一个调好的随机森林经常比瞎搞的神经网络强。
上个月和一个做半导体设备监测的朋友聊,他们厂里有台价值800万的刻蚀机,3年才出了2次故障。我说你们这数据量,上什么深度学习啊,先攒够50条故障样本再说吧。他深以为然。
数据是预测性维护的命根子,模型只是锦上添花。在数据不够的时候,先把能用的技巧都用上,同时想办法多攒数据——这才是正经路子。
