设备坏了再修,这是最传统的事后维修。设备按固定周期保养,这是预防性维护。但如果你想做到“这台电机未来两周内大概率会出问题,趁还没停机赶紧安排检修”,这就是预测性维护。我最近刚好把一个预测性维护项目从零跑到了落地,数据层接的是开源能源管理系统 MyEMS,算法层用的是 CNN-LSTM 组合模型,最终在验证集上把故障预警准确率做到了 92%。这篇文章就把整个项目的设计思路、数据处理、模型训练、部署踩坑完整梳理一遍。
这套方案适合谁参考?如果你是工厂设备管理人员、工业互联网方向的开发者、或者正在做设备状态监测课题的研究生,会非常有参考价值。MyEMS 本身的主业是能源管理,但它能采集电机、泵、风机等设备的运行参数,天然适合当预测性维护的数据底座。CNN-LSTM 这块我用的是比较标准的组合结构,没有上太重的新奇机制,尽量保证可复现性。我不光会讲模型怎么搭,更会把数据标签怎么打、92% 这个数字是怎么算出来的、上线后遇到哪些坑都交代清楚。
1. 整体设计思路:为什么是“MyEMS + CNN-LSTM”而不是别家组合
1.1 预测性维护解决的是哪一类问题
工业现场的故障目前主要靠三种方式应对。第一种是坏了再修,成本最高,一次非计划停机可能意味着整条产线停摆,损失按小时算。第二种是定期检修,比如每三个月换一次轴承,这比坏了再修好一些,但存在过度维护的问题——很多部件还没到寿命极限就被换掉了,维护成本白白浪费。第三种就是预测性维护,核心目标只有一个:在设备真正失效之前,给出一个合理的提前量预警,“未来 N 天内这台设备故障概率达到 X%”。
我在这个项目里把预测对象限定为“滚动轴承早期故障退化”。轴承是旋转类设备最容易出问题的部件,故障特征明显,数据也相对好获取,适合作为第一个跑通全流程的预测对象。预测目标是剩余使用寿命估计,通俗讲就是“还能撑多少天”,然后把它映射到三级预警级别里:正常、关注、报警。这套逻辑后续迁移到齿轮箱、电机绕组上也不需要推翻重建,换数据、微调标签就行。
1.2 MyEMS 在项目里扮演的角色
很多人对 MyEMS 的认知还停留在“能耗监测报表系统”,实际上它的数据接入层做得相当完善。MyEMS 支持标准的 Modbus TCP、Modbus RTU、BACnet 等工业协议,可以接入 PLC、传感器、智能电表,而且自带点位管理和历史数据库存储。这意味着它不只是抄表工具,更是一个统一的数据采集与存储平台,所有设备的电气参数、温度、振动信号都能汇到同一套系统里。
我选择 MyEMS 有个很实际的理由:项目里前期已经有一批设备接入了 MyEMS 做能耗监测,电机电流、转速、负载率这些点表是现成的。不需要额外部署一套采集系统,直接复用现有采集链路,只是在点位表里新增了振动加速度、轴承温度等几个测点。数据统一从 MyEMS 的 MySQL 历史库里面读出来,拿到算法侧做预处理和特征工程,省掉了一大笔集成的成本。
注意:MyEMS 本身不提供训练模型能力,它负责的是“采数据、存数据、展示数据”。训练和推理这部分需要自己在独立的算法服务里做,再把预测结果回写 MyEMS 的看板或者对接工单系统。搞清楚这个边界,业务架构会清晰很多。
1.3 CNN-LSTM 的组合逻辑:一个提特征,一个看趋势
选型之前我对比了好几个方案,纯 LSTM、纯 CNN、XGBoost、CNN-LSTM 都做了小规模实验。最终选择 CNN-LSTM 组合,是因为它把两种结构的长处拼在了一起。
CNN 擅长提取局部特征。在设备监测场景里,一段短时间窗口里的振动幅值突变、电流尖峰、温度阶跃,这类“局部异常模式”正是 CNN 卷积核擅长捕捉的东西。几个卷积核扫过去,等于自动完成了特征提取,省去了大量手工构造特征的工作。
LSTM 擅长捕捉时间依赖。设备退化不是瞬间发生的,往往有一个缓慢劣化的过程,今天的数据和前两天、前一周的数据有强关联。LSTM 的门控机制可以把这种跨时间步的长程依赖关系记住,用来拟合“退化趋势曲线”。
组合起来就是:先用 CNN 从每个短片段里提炼局部特征,再把一串局部特征按时间顺序送给 LSTM,让它学习退化轨迹的演化规律。对比实验里这个结构比纯 LSTM 在验证集上提升了大约 4.6 个百分点,提升主要来自 CNN 层对早期微弱故障特征的增强。
1.4 一条数据从设备到预警的完整链路
整个系统的数据流可以概括为六步。
第一步,现场传感器和智能仪表通过 Modbus 协议接入 MyEMS。第二步,MyEMS 网关按设定的采集频率(本项目是每分钟一个点)把数据写入 MySQL 历史库。第三步,训练服务定时从 MySQL 批量读取历史数据,完成清洗、滑窗、构造标签后生成训练集。第四步,CNN-LSTM 模型离线训练,评估通过后导出模型文件。第五步,推理服务加载模型,每隔固定周期读取最近一段窗口的数据做预测,输出剩余寿命估计值和预警等级。第六步,预测结果写回 MyEMS 数据库,在 Web 看板上展示,预警信息同步推送到企业微信机器人。
完整的链路里,实时推理的压力并不大,因为预警的粒度是“天”级别,不需要毫秒级响应,所以整个推理服务用 Python 写就行,没有上时序数据库,也没有引入流计算框架。这类项目最忌讳一上来就搭一堆中间件,先把链路跑通才是关键。
2. 数据工程:92% 准确率的地基是数据处理,不是模型结构
2.1 从 MyEMS 里捞哪些数据:多维特征字段与采集频率
模型再强,数据质量不行全部白搭。我从 MyEMS 点位表里选取了 11 个具体测点作为模型输入,涵盖电气参数、运行参数、状态参数三类。
| 数据类型 | 具体测点 | 采集频率 | 用途 |
|---|---|---|---|
| 电气参数 | 三相电流、三相电压、有功功率、功率因数 | 1 min | 反映负载变化与电气异常 |
| 运行参数 | 电机转速、负载率 | 1 min | 反映工况变化 |
| 状态参数 | 轴承温度、驱动端振动加速度、非驱动端振动加速度 | 1 min(振动建议提高频率) | 直接反映机械状态退化 |
实际项目中振动信号最好用更高的采集频率,至少每秒一次,但受限于现场已有的传感器和网关带宽,我这里统一用了一分钟粒度。这是一个妥协方案,实测对轴承这类缓慢退化故障影响不大,但如果要监测的是齿轮点蚀这类发展较快的故障,建议把振动通道的采集频率提升到秒级甚至更高。
数据质量处理上,我做了四件事:对每个测点做缺失值统计,缺失超过 30% 的通道直接废弃;对短时间缺失(几分钟)用前后线性插值补全;用孤立森林算法对每个通道做异常值识别,把传感器掉线、检修停机产生的“假异常”剔除;把所有时间戳对齐到统一的分钟刻度上,避免不同设备的时间偏差。
2.2 滑窗采样:把连续时间序列变成模型输入
模型的输入不是一整条时间序列,而是一段固定长度的滑动窗口。窗口长度的选择很关键,我的做法是结合设备劣化周期和模型可学习能力一起考虑。
以轴承为例,从早期微裂纹发展到明显故障,通常需要 15 到 45 天。窗口太短,模型看不到退化趋势的全局形态;窗口太长,训练数据量爆炸,而且早期的状态和近期状态混在一段窗口里,反而干扰 LSTM 对近端状态的判断。我最终把滑窗长度定为 7 天,也就是 7 × 24 × 60 = 10080 个时间步。特征维度是 11 个测点,所以每个样本的形状是 (10080, 11)。
这个形状直接喂给 CNN 层显然太大了。实际处理时,我在时间维度上先做了一次降采样,把每秒/每分钟的数据按 10 分钟聚合,取均值、标准差、最大值、最小值四类统计量。这样每 10 分钟生成一组特征,7 天窗口变成 1008 个时间步,每个时间步的维度是 11 个测点 × 4 个统计量 = 44 维。经过这样处理后,每个样本的形状是 (1008, 44)。
这里有一个经验可以分享:不要直接对原始 1 分钟数据做归一化就送进模型。工业时间序列里噪声比较多,先做小窗口聚合统计,相当于一次低通滤波,模型输入更干净,训练稳定性也更好。我在实验中对比过,用聚合特征比用原始 1 分钟数据训练出来的模型准确率高约 3%,收敛速度快了近一半。
2.3 故障标签怎么打:剩余寿命估计与阈值区间划分
监督学习必须有标签。轴承没有直接标注“还有几天坏”,所以这里用剩余使用寿命(RUL,Remaining Useful Life)作为回归目标,再把它映射到预警等级。
标签规则的划分逻辑是这样的:把轴承从健康到失效的完整周期定义为“全寿命周期”。定义首次触发故障阈值的时间点为 T_f,即振动加速度的有效值连续超过设定阈值的起始点。对于 T_f 之前的历史数据,RUL 标签定义为 T_f 减去当前时间点,单位为小时。对于 T_f 之后的数据,虽然在真实场景里已经算故障了,但为了训练模型区分“快坏”和“刚坏”,我把 T_f 到最终失效之间的样本 RUL 标签置为 0 到 48 小时的区间值。
在实际工程中,我还用了一个更省事的近似做法:如果在维保记录里能找到轴承的实际更换时间,就把更换时间前 48 小时定义为“故障窗口”,窗口内的样本标记为报警类,窗口之前的样本按时间衰减构造 RUL 标签。不过这里的隐患也很明显:如果轴承还没到寿命就被提前更换了,标签会被污染。所以一定要结合振动趋势曲线确认故障窗口,不能盲目使用更换时间。
最终把 RUL 值映射成三个分类标签,方便现场使用:
| 预警级别 | RUL 范围 | 现场含义 | 处置建议 |
|---|---|---|---|
| 正常 | RUL > 168 小时(7 天以上) | 设备状态健康 | 维持例行巡检 |
| 关注 | 48 小时 < RUL ≤ 168 小时 | 开始出现退化迹象 | 加密监测,准备备件 |
| 报警 | RUL ≤ 48 小时 | 故障风险高,即将失效 | 尽快安排停机检修 |
2.4 数据切分:时间序列的切分方式与类别不平衡处理
训练集、验证集、测试集的切分方式,是这类项目中最容易犯错的点。如果用随机切分,训练集和测试集里会混入同一个设备同一段时间的数据,造成信息泄漏,测试准确率虚高。正确做法是时间序列切分:按照设备维度,把每台设备的前 60% 生命周期作为训练集,之后 20% 作为验证集,最后 20% 作为测试集。这样测试集里的数据严格晚于训练集数据,更能反映模型在真实场景中的泛化表现。
类别不平衡也是一个不可忽略的问题。因为设备大部分时间处于正常状态,报警样本占比可能只有 5% 到 8%。我采用了两种组合处理方式:一是给损失函数里的三个类别设置不同权重,报警类的权重设得最高;二是在训练集中对报警类样本做 SMOTE 过采样,但要注意必须在滑窗采样之后做,不能先过采样再滑窗,否则同一窗口的数据会重复出现在训练集和验证集里。
3. CNN-LSTM 模型搭建与训练细节:92% 准确率是这样炼成的
3.1 网络结构配置:从输入层到输出层的完整参数
模型结构这块我尽量保持简洁可复现。整体是“CNN 特征提取 + LSTM 序列建模 + 全连接回归/分类”的结构,输入层接收形状为 (None, 1008, 44) 的序列数据,None 是批次大小,1008 是时间步数,44 是每个时间步的特征维度。
第一层是 Conv1D 卷积层,卷积核数量 64,卷积核大小 3,padding 设置为 same,激活函数用 ReLU。这里在时间维度上做卷积,每个卷积核相当于检测一种局部时间模式,比如“振动突然上升”“电流连续 3 分钟下降”,64 个卷积核能覆盖多种异常模式。卷积层之后接一个 MaxPooling1D 池化层,池化窗口大小 2,作用是降维,把时间步从 1008 压缩到 504,同时增强平移不变性,防止模型对时间轴上的轻微偏移过于敏感。
第二组是 128 个卷积核的 Conv1D 层,核大小还是 3,再跟一个池化窗口为 2 的池化层,输出时间步变成 252。这种双卷积层叠加的设计,让底层卷积捕捉短周期高频模式,高层卷积捕捉更长周期的组合模式,类似视觉模型里的多层特征抽象。
两层卷积后接两层 LSTM。第一层 LSTM 隐藏单元数 64,return_sequences 设为 True,把完整序列输出给下一层。第二层 LSTM 隐藏单元数 32,return_sequences 设为 False,只输出最后一个时间步的隐藏状态,作为整个序列的压缩表示。为什么用两层 LSTM 而不是一层?多层 LSTM 能建模更复杂的时序依赖,但也不是越深越好,超过两层在工业场景里容易过拟合,训练时间也翻倍,选 64+32 的组合我实测是效果和开销比较平衡的点。
最后接一个 Flatten 层,把 LSTM 输出展开,经过一个 Dropout 层防过拟合,Dropout 率设置为 0.3,再进两个全连接层做输出。第一层全连接有 32 个神经元,激活函数用 ReLU。第二层是全连接输出层,如果做 RUL 回归,输出维度为 1,激活函数用 Linear;如果做预警等级分类,输出维度为 3,激活函数用 Softmax。
模型总参数量大约在 24 万左右。这个规模在 GPU 上训练单轮只要十几秒,在 CPU 上慢一些但也能接受。所有实验我用的是单张 RTX 3060,跑 50 轮的完整流程在半小时左右。
3.2 损失函数与训练策略:先回归再分类,多任务输出更稳定
我在损失函数上做了点小改动。3 分类交叉熵损失直接训练也能用,但收敛慢,而且分类边界附近的样本很难学准。我的方案是采用多任务学习思路:模型同时输出 RUL 回归值和预警等级分类结果,总损失是回归损失和分类损失的加权和。
import tensorflow as tf from tensorflow.keras import layers, models, losses, optimizers model = models.Sequential() model.add(layers.Conv1D(filters=64, kernel_size=3, padding='same', activation='relu', input_shape=(1008, 44))) model.add(layers.MaxPooling1D(pool_size=2)) model.add(layers.Conv1D(filters=128, kernel_size=3, padding='same', activation='relu')) model.add(layers.MaxPooling1D(pool_size=2)) model.add(layers.LSTM(units=64, return_sequences=True)) model.add(layers.LSTM(units=32, return_sequences=False)) model.add(layers.Dropout(0.3)) model.add(layers.Dense(32, activation='relu')) output = layers.Dense(3, activation='softmax')(model.output) model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=0.0005), loss=tf.keras.losses.SparseCategoricalCrossentropy(), metrics=['accuracy'] )训练轮数我一开始设了 100 轮,加了早停机制,patience 设为 10,验证集损失连续 10 轮不下降就停止。batch size 我用的是 32,这个值对序列模型来说不会太占显存,梯度更新也足够平缓。Adam 优化器的初始学习率 0.0005,每 15 轮衰减为原来的 0.1 倍。批量统计 BatchNorm 在这个任务里效果反而一般,建议不放在序列层之前。
注意:如果只做三种状态分类,模型输出的只是一个离散等级。现场实际上更想看到的是“退化趋势有没有在加速”,所以我建议在训练阶段同时让模型学 RUL 回归目标。实践中用这种多任务方式训练,分类准确率不会因增加回归任务而下降,反而比单独的纯分类模型高出 1.5% 左右,因为回归任务提供了更细粒度的状态信号。
3.3 92% 准确率是怎么算出来的:评估口径必须先行
先说结论:91.7%,我对外统一说“约 92%”。在讲这个数字之前,必须先定义清楚“准确率”在这里的准确含义。
我用的评估口径是“预警命中率”。定义一条预警事件为:模型在测试期内,对某个设备样本给出了“报警”级别预测,且该设备在预测时刻之后 48 小时内在真实记录中发生了故障(或 RUL 标签进入了报警区间)。在这种情况下,算一次“命中”。预警准确率 = 命中预警次数 ÷ 模型发出的报警次数。
还有一个重要的配套指标是误报率。如果模型一天发几十条报警,其中只有几次真的坏了,那准确率就虚高没有意义。实验测试集上,模型共发出报警 231 次,其中真实故障或即将进入故障状态的 212 次,误报 19 次,所以命中率 91.7%。同时我统计了召回率:真实故障样本 158 例中,模型至少提前 24 小时给出报警的 143 例,召回率 90.5%。
建议做类似项目的人一定把准确率和召回率放在一起看,误报率太高现场会失去信任、变成“狼来了”,漏报率太高则失去了预测性维护的意义。我当时的验收标准是:准确率不低于 85%,召回率不低于 85%,且误报导致的无效检修不超过 15%。最终这套模型完全达到验收线,所以对标现场业务指标去定模型的评估口径,比单纯追求测试集准确率更实在。
3.4 对比实验:为什么这个数字值得信
为了证明 CNN-LSTM 结构的选择价值,我跑了一组对比实验,统一输入数据处理方式,随机种子固定,评估口径一致。结果如下:
| 模型 | 预警准确率 | 召回率 | 训练耗时(参考) |
|---|---|---|---|
| 纯 CNN(池化后平铺 + 全连接) | 84.2% | 80.1% | 约 20 min |
| 纯 LSTM(单层 64 单元) | 86.5% | 83.3% | 约 25 min |
| XGBoost(手工特征工程后分类) | 82.0% | 78.6% | 约 15 min |
| CNN-LSTM(本文方案) | 91.7% | 90.5% | 约 30 min |
纯 CNN 的问题在于只提取局部特征,对长时间跨度的退化趋势记忆不足,所以召回率偏低,容易漏掉缓慢发展的故障。纯 LSTM 要比纯 CNN 好些,但对局部异常模式的捕捉不如 CNN 灵敏,导致报警不够及时。XGBoost 依赖手工特征工程,特征工程做的好也能接近 85%,但维护成本高,更换设备类型就要重新做特征适配,泛化能力差。CNN-LSTM 把两者的优势合起来,准确率和召回率都明显领先。
说实话,92% 不是一个靠堆模型结构堆出来的数字,更大的功劳在于数据清洗、标签规则设计和评估口径的统一。如果谁上来就说自己用某个模型轻松做到了 99%,你要先问他准确率的定义是什么。
4. 部署上线与工业现场避坑:从“模型能用”到“系统能用”
4.1 推理服务怎么落地:轻量部署 + 结果回写 MyEMS
模型训练完成之后,面临的是把模型接进真实环境的问题。因为我们前期已经通过 MyEMS 做了数据采集,所以推理服务直接部署在同一台内网服务器上,定时从 MySQL 里拉取增量数据,推理完成后再把结果写回 MySQL。
推理流程拆解成五个步骤:第一步读取点位表配置,确认当前采集的测点有哪些;第二步从 MySQL 中查询最近 10080 分钟的测点数据;第三步做和训练时完全一致的数据预处理:缺失值插补、异常值剔除、10 分钟聚合;第四步用训练好的模型推理,得到 RUL 值和预警等级;第五步把结果写入预测结果表,通过 MyEMS 的自定义看板展示。
模型导出用的是 Keras 的 SavedModel 格式,推理环境用 TensorFlow Serving 或者直接 Python 加载模型文件都可以。我最后的方案是 Wrapper Service 的轻量方案:一个 Python FastAPI 服务,内部加载模型,对外暴露一个 HTTP 接口,定时任务模块每 15 分钟调用一次接口完成一轮推理。整个推理耗时在 CPU 环境下大约 2 到 4 秒,作为预测性维护场景完全可以接受。
4.2 上线后无法回避的数据漂移和工况变化问题
模型上线之后面临的最大挑战不是模型本身的问题,而是数据分布的变化。工厂不会永远一成不变地运行,生产工艺调整、季节温度变化、设备负荷波动,这些都会导致特征分布发生偏移。有一个很典型的例子:冬季低温环境下,润滑油粘度增大,振动特征整体上抬,模型在夏季数据上训练的阈值到了冬季就容易误报。
我在这个项目里遇到的最典型漂移场景是周末和节假日。某些设备在周末会进入低频运行模式,振动、电流特征与工作日完全不同,模型把这部分样本全部判成了正常,实际上周末低频运行恰恰是轴承润滑不足故障的诱发时段。解决思路有几个方向可以用:一是对工况做聚类(如高速/中速/低速/停机),不同工况分别训练模型或用工况标签作为模型输入特征;二是建立数据漂移监控,定期比较实时数据分布和训练集分布的差异,比如算 KL 散度,超过阈值就预警需要重新训练;三是设计每周自动重训任务,拉取最近三个月数据增量微调模型。
我在项目里采用的是工况聚类 + 月度重训方案。先把运行数据按负载率聚类成 4 个典型工况,模型输入中增加一个工况编码维度;然后每月第一个周一凌晨自动拉取近 3 个月的新数据,用原来的结构重新训练一次。只重训一个 epoch 或者做增量训练会快很多,但效果不一定好。实测每 3 个月全量重训一轮,配合每月增量微调,误报率同比降低了 40%。
4.3 现场踩过的三个典型坑:标签错位、传感器断流、个体差异
第一个坑是标签错位。最早一版我直接用维保记录里的更换时间来打故障标签,后来在回溯数据时发现振动信号的突变点比更换时间早了将近 5 天,也就是说设备其实已经故障了 5 天才被换下来。标签晚了 5 天,模型学到的“故障前特征”实际是“故障后持续运行特征”,导致推理阶段预警偏晚。后来我改成了人工结合振动趋势标定故障起始点,用均方根值超阈值 + 频谱能量集中度两个条件交叉确认。这个工作很费时间,但直接影响模型上限,值得做。
第二个坑是传感器断流。 project 里有过一次网关故障,某台设备的振动通道连续 3 天没有数据写入。训练时如果整段数据缺失,滑窗会自动跳过;推理时如果缺失时间超过 24 小时,服务直接把当次预测判断为“数据不足”,不输出预警,等待数据恢复。这个“宁可少报,不要胡说”的原则很关键,否则推理服务会在数据缺失时拿插值后的假数据硬算,输出一个看似精确的剩余寿命,反倒误导现场。
第三个坑是不同设备个体的差异性。同一个型号的轴承,安装在两台不同的设备上,由于安装间隙、负载特性不同,振动基线差异很大。拿设备 A 训练好的模型直接去预测设备 B,准确率会掉到 80% 以下。我的解决办法是分组训练加迁移学习:把同型号设备按安装位置和工况分成小组,每组训练一个基础模型,再用该组里每台设备最近两个月的少量数据微调出一个设备专属模型。微调时冻结 CNN 层参数,只更新最后全连接层,这样每台设备只需几十条样本就能完成适配,推起来也不慢。
4.4 提升现场接受度的小技巧:置信度分级和工单联动
技术指标达标只是第一步,现场操作人员和设备主管真正关心的是“报警之后我该干什么”。我在地面部署阶段做了一件很有效的事情:把预警从二值“报警/不报警”改成了三色分级加置信度展示。模型输出的 Softmax 概率分布里,如果“报警”类别概率超过 0.9,标记为红色高置信度预警,直接推送到维修班组长手机。概率在 0.7 到 0.9 之间,标记为黄色关注,只在白班看板显示。概率低于 0.7,虽然 RUL 数值可能小于 48 小时,但按“置信度不足”处理,不触发页面弹窗。
这样调整之后,现场信任度明显提升。过去一个黄色报警去看半天发现设备正常,操作工就会开始质疑模型。有了置信度分级,每一级都有对应的处置标准,高置信度报警严格要求 24 小时内处理。同时我还把预警事件接入了工单系统,点击看板里的红色报警可以一键生成检修工单,自动带上设备名称、点位编号、最近 24 小时关键趋势截图,维修人员拿着工单就能到场,不需要再翻系统查数据,这实际体验差异非常大。
5. 常见问题与排查技巧实录:这份速查表能帮你少熬三个夜
5.1 数据侧常见问题的标准排查步骤
数据问题占了这类项目排查工作量的一大半。我最常遇到的问题按出现频率排序:第一是时间戳对齐问题。MyEMS 不同点位来自不同采集终端,时间不同步是常态,有的快了 3 分钟,有的慢了 10 秒。处理方案是入库时统一按分钟取整,并在写库前做时钟同步检查。第二是重复数据。网关在断线重连时常常会补传历史数据,导致同一分钟出现两条记录,一条实时一条补传。写数据抽取 SQL 时要去重,我用的方式是按点位、时间戳取最新一条。第三是传感器精度漂移,振动传感器用了三四年后零点会偏移。这类问题单纯靠算法很难完全消除,我的处理措施是不定期对传感器的温度输出做基准校准,校准记录存到 MyEMS 配置表里。
第四是数据缺失率的阈值判断。如果某个通道的缺失率超过 15%,我会认为这个通道已经不可信,宁可在特征中剔除它也不要用平均填充。第五是“0 值陷阱”,很多仪表断线后读数会变成 0,但这和实际值“真的是 0”无法区分。处理时我会结合同一设备的有功功率判断,当有功功率 > 0 但电流为 0 且持续 5 分钟以上,就认定为传感器断流,打上缺失标记。
5.2 模型效果不达标时的排查清单与避坑技巧
当模型在测试集上的准确率或召回率达不到预期时,建议按下面这个顺序排查,而不是一上来就换模型结构或调参。
| 排查项 | 判断方法 | 常见结论 |
|---|---|---|
| 标签是否准确 | 随机抽 50 条报警样本,目检振动趋势图 | 标签错位、故障窗口定得不准的情况非常多 |
| 训练/测试切分是否泄漏 | 检查测试集设备是否有训练集时间段完全重叠 | 随机切分场景下准确率虚高 5% 到 8% |
| 输入特征是否包含“未来信息” | 检查特征计算是否用到当前时刻之后的数据,例如滚动统计了整个窗口的平均值包含未来 | 归一化参数用了全局信息是最常见的泄漏来源 |
| 数据不平衡处理是否到位 | 打印每个类别的样本数和类别权重 | 报警类样本过少,模型偏向正常类 |
| 阈值是否合理 | 画 PR 曲线结合现场备件周期定阈值 | 默认 0.5 不是最优,根据现场容忍度微调 |
这里单独提一下归一化泄漏问题。做时间序列预测时很容易踩的坑是:用整段序列的均值和标准差做归一化,但这会让模型在训练阶段就“偷看”了未来数据的信息。正确做法是在每个滑窗内部独立做标准化,或者仅用训练集统计量计算归一化参数,然后用同样的参数去处理验证集和测试集。这个细节我最初也没注意到,后来是在查一个“线下准确率高但线上效果差”的问题时发现的,改完之后线上和线下的差距缩小了很多。
5.3 推理阶段的效果波动与故障特征不明显的边缘情况
推理阶段最常见的现象是模型在线下效果好,线上却出现连续误报。排查思路要看四个方向:线上数据的特征分布是否和训练时一致、推理用的数据预处理流程是否和训练时完全一致、模型有没有被最近的新数据影响、以及设备是否出现了训练数据中从未出现过的工况。
有一个比较有代表性的边缘案例:某设备在测试阶段表现不错,但在某次生产中连续三次预警。排查之后发现是生产订单更换了原料批次,设备的振动特征出现整体升高,但这个升高并不可归因于故障,而只是工况变化。处理方案是给模型加一个“状态变化检测前置模块”的思路——先判断当前工况是否和模型看到过的工况一致,如果不一致就不出预警,而是提示“工况变化,需人工评估”。示例如下,这个模块在工况波动大的工厂里非常实用:
import numpy as np from sklearn.cluster import KMeans def infer_with_condition_check(model, sample): sample_condition = condition_cluster.predict(sample) known_conditions = [0, 1, 2, 3] # 训练时定义过的工况编码 if sample_condition not in known_conditions: return {"level": "unknown", "message": "工况未匹配,跳过预警"} return model.predict(sample)5.4 项目复盘里值得单独记下来的经验
整个项目从开始做到跑通,前后用了大约三个月,其中数据处理和标签标定占了 70% 的时间,模型训练和调参反而只占很小比例。如果重新做一遍,我会提前把“设备故障时间点”的标定规则定得更细致,并且让熟悉设备维保的工程师参与进来,共同确认故障标签,而不是在模型训练完才发现标签有问题。
另一个经验是不要盲目追求模型的复杂程度。在验证集上,把 CNN 卷积核数量从 64 提升到 128,准确率提升只有 0.8%,但训练时间增加了近一倍。对于预测性维护场景,模型效果的天花板其实被数据质量、标签精度、评估口径这几块决定,模型结构只要不是太离谱,差距不会特别大。
最后给想做类似项目的朋友一个建议:从单台设备、单一故障类型开始做,不要一开始就规划一个覆盖全厂所有设备的预测平台。用一台设备跑通“采集-建模-预警-检修联动”的最小闭环,拿到现场认可之后,再横向扩展。这样投入小、见效快,也更容易在过程中打磨出适合自己厂区的经验。踩过几次坑之后,我现在看一个预测性维护项目值不值得做,第一眼看的不是它用了什么高级算法,而是它的数据基础和维护记录靠不靠谱,这两点决定了项目能不能真正落地。