简介围绕2016年PHM数据集性能特征展开的预测性维护学习资料面向希望掌握设备故障预测与多方法数据分析的个人学习者集中提供完整赛题数据、分析脚本与可视化结果。压缩包共759个文件、17.84MB以563个csv数据文件为主体辅以126个zbak数据备份、55张png图表、py与pyc脚本及npy数据并含md说明、license授权与pdf文档整体结构清晰便于按模块还原实验流程。资料内重点呈现三模态与双模态划分实验思路可借助脚本与图表理解不同特征组合对模型性能的影响同时通过多方法对比分析能够学习数据预处理、特征筛选、结果可视化等预测性维护常见环节深入了解PHM竞赛中设备故障预测的完整分析路径。配合README研究背景说明与版权声明既能复现分析过程也能结合内容预览中的典型训练样本快速定位核心数据。已有75人学习。1. PHM 2016轴承数据集同一批CSV为什么不同人算出的寿命曲线差了一倍做预测性维护的同行手里大概率存着一套2016年PHM数据集的复现材料。这份资源的核心是10份CMP-training系列CSV文件每个文件记录一台轴承从正常运转到失效的完整振动过程。所谓性能特征分析就是把这些原始采样拆成RMS、峭度、峰值因子等指标再研究它们随寿命变化的规律。它能直接回答两个问题轴承走到哪一步了还能撑多久。适合正在复现PHM竞赛、想对比特征工程方案、或者刚开始接触剩余寿命RUL预测的从业者。注意这套数据来自网络分享只用于个人学习交流LICENSE文件里明确限制了商业使用。下面的内容我按自己实际拆这套数据时的顺序来讲尽量少讲空理论。2. 把CMP-training CSV变成特征矩阵三模态与双模态划分实验的落地2.1 先读一遍原始CSV文件结构与目录组成拿到压缩包解压之后先别急着跑代码。我习惯先用表格把目录里到底有什么清点一遍避免后面跑了一半发现某个文件缺列或者根本是空文件。文件项用途说明CMP-training-*.csv10份轴承训练样本每份是一段连续的振动采样序列README.md项目背景、实验设计、结果摘要通常写清了数据来源与预处理方式LICENSE版权归属与使用许可明确禁止商用个人学习可复现.gitattributesGit配置规范换行符和二进制文件识别不影响分析结果.zip备份数据备份防止源文件损坏或误删CSV文件没有列名这是PHM竞赛数据最常见的情况。读取时我一般带上headerNone先把前几行和整体形状打印出来确认是“一行一个采样时刻、多列多通道”还是别的排列方式。这一步不做后面所有窗口切分都可能基于错误的假设。import pandas as pd df pd.read_csv(CMP-training-128.csv, headerNone, nrows5) print(shape of first 5 rows:, df.shape) print(df.head()) full pd.read_csv(CMP-training-128.csv, headerNone) print(full shape:, full.shape)第一次跑的时候full shape大概率是一个行数很大的二维表格。行数对应采样时刻列数对应不同的测量通道。如果你发现行数只有几千、列数有几百那这个文件的存储方式就是一行一个样本片段处理逻辑要完全反过来先按列展开再切窗口。判断标准很简单看full.shape里哪个维度大。数据规模不算小读取时用nrows先抽样是稳妥做法避免一次加载一个几十万行的文件把内存直接占满。2.2 滑动窗口特征提取RMS、峭度、峰值因子怎么算才稳原始振动信号直接拿来训练模型基本不行。轴承振动是典型的非平稳序列瞬时幅值波动很大直接输入原始波形模型学到的是噪点而不是退化趋势。我一般先把每个文件切成长度相同的窗口在每个窗口内算统计特征用特征序列代替原始波形参与后续分析。理论上任何能刻画幅值和冲击的特征都可以用但PHM这类轴承退化场景最稳的组合是这几个RMS反映整体振动能量峭度反映冲击性故障峰值因子反映幅值尖峰程度裕度因子和波形因子在某些退化阶段也敏感。我先用最常用的三个把流程跑通后面三模态实验就是在这三个特征里做组合变化。import numpy as np import pandas as pd def extract_window_features(series, window2000, step500): rows [] for i in range(0, len(series) - window, step): seg series.iloc[i:i window].astype(float).values seg seg[~np.isnan(seg)] if len(seg) window * 0.9: continue rms float(np.sqrt(np.mean(seg ** 2))) kurt float(pd.Series(seg).kurtosis()) peak float(np.max(np.abs(seg))) peak_factor peak / rms if rms 1e-8 else 0.0 rows.append({start_idx: i, rms: rms, kurt: kurt, peak_factor: peak_factor}) return pd.DataFrame(rows) feat_df extract_window_features(full.iloc[:, 0], window2000, step500) print(feat_df.head()) print(feature rows:, len(feat_df))窗口和步长的选择直接决定特征曲线的平滑度。窗口太短比如500点RMS会被瞬时冲击主导曲线毛刺多到没法看窗口太长比如10000点退化起点的位置会被平均掉。我一般从2000点窗口、500点步长开始调先画出RMS曲线观察毛刺程度毛刺大就加大窗口曲线太钝就减小步长。这一段是整个流程里最费时间的部分也是后面所有模型效果的基石。2.3 三模态划分 vs 双模态划分实验设计到底在改什么变量资源包的README里提到了三模态划分实验和双模态划分实验。一开始我以为“模态”指的是模型或者传感器类型跑完才发现这里所谓的模态指的是参与划分的特征维度个数。三模态划分就是拿RMS、峭度、峰值因子三个特征组成三维特征空间把每个窗口划分成不同退化阶段双模态划分则只取RMS和峭度两个特征组成二维特征空间。两者对外的控制变量是特征数量对内的状态数量保持一致都划分成三个退化阶段正常期、早期退化期、晚期退化期。from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans feat feat_df[[rms, kurt, peak_factor]].values scaler StandardScaler() # 三模态使用三个特征构成特征子空间 X3 scaler.fit_transform(feat) labels3 KMeans(n_clusters3, n_init10, random_state42).fit_predict(X3) # 双模态只保留RMS和峭度两个特征 X2 scaler.fit_transform(feat[:, [0, 1]]) labels2 KMeans(n_clusters3, n_init10, random_state42).fit_predict(X2) feat_df[stage_3mode] labels3 feat_df[stage_2mode] labels2 print(feat_df.groupby(stage_3mode).size())这里有一个关键设计点聚类数固定为3因为轴承退化过程大致对应三个阶段。三模态和双模态的差异只在特征子空间的维度。如果聚类数不同实验就失去了对比意义。KMeans本身是无监督方法划分结果只代表“数值空间上的相近”不一定代表真实的物理退化阶段。所以做这一步时我内心是把它当作伪标签来用的后续必须回到原始波形验证每个簇的形态是否真的有区别。3. 多方法分析性能特征分类、RUL回归与频谱能量迁移3.1 退化阶段分类用树模型验证特征子空间能不能分开状态拿到三模态和双模态的划分结果后下一步是验证一件事这套特征到底能不能把不同退化阶段分开。常见做法是把KMeans得到的簇标签当作伪标签训练一个随机森林分类器看交叉验证准确率。这里容易陷入循环论证聚类结果喂给分类器分类器当然能学到聚类边界。所以我的判断标准不是准确率绝对值而是三模态和双模态的对比差异以及特征重要性排名是否与物理直觉一致。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_val_score clf RandomForestClassifier(n_estimators200, max_depth8, random_state42) scores cross_val_score(clf, X3, labels3, cv5, scoringaccuracy) print(3-mode CV accuracy: %.3f (/- %.3f) % (scores.mean(), scores.std())) clf.fit(X3, labels3) importance pd.Series(clf.feature_importances_, index[rms, kurt, peak_factor]) print(importance.sort_values(ascendingFalse))特征重要性结果往往很直接RMS通常排第一因为振动能量随退化持续上升峭度排第二它只在出现冲击性故障时跳变峰值因子对早期点蚀比较敏感但到晚期反而下降。如果你跑出来的重要性排名完全反过来大概率是数据加载时列顺序弄错了特征名和实际列对不上号。3.2 剩余寿命回归把特征矩阵对齐到RUL标签分类只能判断“当前属于哪个阶段”工程上更关心“还能撑多久”也就是RUL回归。构建RUL标签时假设每个CSV文件是一个完整的寿命周期最后一行的剩余寿命记为1往前递增到整个序列长度。这样每个窗口的特征向量就对应一个RUL值。total_life len(full) rul np.array([total_life - idx for idx in feat_df[start_idx]]) feat_df[rul] rul from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error X feat_df[[rms, kurt, peak_factor]].values y feat_df[rul].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42 ) reg RandomForestRegressor(n_estimators200, max_depth10, random_state42) reg.fit(X_train, y_train) y_pred reg.predict(X_test) print(MAE (sampling points):, mean_absolute_error(y_test, y_pred))这里要特别注意单位问题。start_idx是窗口起点在原始序列中的位置所以RUL也是采样点数量不是秒、不是分钟。如果你需要用实际时间单位必须知道原始数据的采样率再乘对应系数。很多人跑完直接说“预测误差500秒”其实那只是500个采样点。这个问题在资源包的README里大概率没有细讲属于典型的默认坑。3.3 频域补充FFT能量迁移如何解释退化过程时域特征能说明“能量变大了”但说不清“能量跑到哪个频段去了”。轴承早期损伤往往表现为某个特征频率附近的边带能量升高这个信息在RMS曲线上几乎看不到。我一般会在时域特征之外补一段FFT频谱能量分布的分析。from scipy.fft import rfft, rfftfreq def band_energy_ratio(segment, fs1.0): n len(segment) spec np.abs(rfft(segment - np.mean(segment), n4096)) freqs rfftfreq(4096, d1.0 / fs) total_energy np.sum(spec ** 2) low np.sum(spec[(freqs 0.1) (freqs 0)] ** 2) return low / total_energy if total_energy 0 else 0.0这里的fs1.0是占位实际必须替换成原始数据的真实采样率。如果资源包没提供采样率一个变通做法是不用绝对频率改用相对频带比如把FFT结果按频率排序后均分成三段分别计算能量占比。这个方法对采样率不敏感能从趋势上看出退化过程中低频能量逐渐占据主导、高频冲击成分消失的典型模式。3.4 多方法结果怎么对照特征增却不涨分先检查标签质量把分类、回归、频域三种方法的结果摆在一起才能得出可信结论。我这次跑下来的典型输出形态是三模态的分类准确率比双模态高两三个百分点RUL回归的MAE却反而差一些频域能量比显示早期退化阶段在冲击频率附近有明显的能量迁移。这种情况并不矛盾。分类任务看的是“能不能分开”特征越多、信息越丰富分开的概率越大回归任务看的是“能不能精确估计剩余寿命”峰值因子这类冲击敏感特征在晚期剧烈波动反而给回归器引入噪声。所以特征数量不是越多越好要针对任务选特征。如果三模态的RUL回归MAE明显变差第一反应不是换模型而是回头检查RUL标签有没有对齐错位尤其是窗口起始索引和标签长度是不是一致。4. PHM 2016实战避坑指南五个必踩的坑与根因4.1 坑1全样本混训导致模型预测像水平线现象把10个CSV文件全部拼接在一起直接做特征提取和模型训练结果预测曲线几乎是一条水平线RUL全部落在平均值附近。原因不同轴承的退化速度差异极大。有的样本寿命几千个采样点有的跑了几十万点才失效。混在一起后特征和RUL的对应关系被不同寿命长度的样本平均化了模型学到的是“平均寿命”而不是“退化规律”。解决每个文件单独提取特征、单独构造RUL标签。如果要跨样本建模先把每个样本的时间轴归一化到0到1再对齐特征和标签。归一时以“窗口索引除以该文件总长度”作为退化阶段位置而不是直接用原始索引。4.2 坑2窗口和步长拍脑袋特征曲线毛刺多到没法看现象RMS曲线锯齿感严重看起来不像退化趋势像随机噪声换一个窗口长度后退化起点又完全变了位置。原因窗口太短时瞬时冲击振动在窗口内占比过高RMS被异常尖峰主导步长太大时退化过程被欠采样关键转折点落在两个窗口之间。解决先用2000点窗口、500点步长跑一遍画出RMS曲线观察毛刺。毛刺明显就把窗口加到5000点曲线太平就把窗口减到1000点。这个调节过程没有固定公式以可视化结果为准属于典型的“调参玄学”但调完你会对这份数据的退化形态有非常直观的理解。4.3 坑3全局归一化把退化信息揉掉了现象对所有样本的所有特征做了一次全局MinMaxScaler归一化后健康段特征值全部压在0.0到0.1之间退化段的差异也被压缩模型训练效果反而不如不归一化。原因健康段数据在全局范围内占比大且原始数值范围远宽于退化段。全局归一化后退化段的微小变化被缩放得不明显模型失去了区分能力。解决不要对整个数据集做一次性归一到0到1。要么用StandardScaler做标准化保留退化特征的相对差异要么先按文件分别归一化再拼接。回归任务里原始量纲往往比归一化后更好用。4.4 坑4只盯RMS把正常波动当退化前兆现象某个样本的RMS曲线在中段平稳上升你以为进入了退化期结果后面又恢复了平稳另一个样本RMS大幅跃升你判断为严重故障结果确实很快失效了。RMS判断常常误报。原因RMS对整体振动能量的变化敏感但轴承早期损伤先以冲击形式出现RMS还没明显上升时峭度和峰值因子已经先动了。只看RMS会把正常的负载波动和早期点蚀混为一谈。解决把峭度变化作为退化起点判据RMS作为退化趋势判据。两个指标都超过各自阈值才标记为进入退化期。单独一个指标的跳变先不要急着下结论。4.5 坑5三模态/双模态聚类结果看着干净换个样本就完全对不上现象KMeans聚成3类后轮廓系数很漂亮样本A的三个簇物理含义清晰但同样的特征组合跑到样本B上聚类结果完全乱了退化早期和晚期混在一个簇里。原因特征多了以后数值分布更复杂的特征主导了聚类距离。峰值因子在有的样本上退化规律明显在另一些样本上则完全是噪声聚类结构就跟着变了。解决对每个簇回看原始波形确认簇对应的振动形态是否真的有差别。更严格的做法是持有一部分窗口人工标注退化阶段再用标注结果评估聚类的正确率。这个正确率比轮廓系数可靠得多因为轮廓系数衡量的是“数值距离”不是“退化阶段”。5. 用滑窗变点检测校准退化起点RUL标签质量提升技巧RUL回归的误差来源很多时候不是模型不够强而是训练标签里的退化起点标偏了。轴承从正常到失效中间有一个转折点之前RMS缓慢上升之后RMS进入加速上升通道。这个点就是退化起点。起点标早模型会把大量正常段当成退化段起点标晚模型会把退化早期丢掉。我后来养成了一个习惯对每个文件的RMS曲线独立跑一次变点检测用检测结果校准RUL标签的零点。def find_degradation_start(rms_series, window30, ratio1.2): for i in range(window, len(rms_series) - window): mean_a np.mean(rms_series[i - window:i]) mean_b np.mean(rms_series[i:i window]) if mean_b ratio * mean_a and (mean_b - mean_a) 1e-6: return i return None start find_degradation_start(feat_df[rms].values) print(degradation start index:, start)这个方法的逻辑很朴素滑动窗口比较前后两段的均值后段均值超过前段一定比例就认为退化开始。window控制平滑程度ratio控制触发灵敏度。注意ratio1.2只是一个初始值不同样本的RMS基数差异很大有的样本正常段RMS就很高这个阈值需要逐个文件微调。跑完所有文件后把所有起点画在一张图上看是否落在RMS曲线开始加速的位置。如果起点普遍偏前或偏后说明ratio需要整体调大或调小。校准退化起点之后RUL标签应该从起点处开始计算起点之前的窗口不参与回归训练。这样做的直接收益是测试集MAE明显下降尤其在样本数量有限的场景下标签质量对结果的影响比模型选择更大。我最开始做RUL预测时退化起点随手选在“RMS曲线看起来开始翘”的位置结果模型误差一大半来自标签错位。后来强制对每个文件先跑一遍变点检测再标RULMAE降了将近三分之一。从那以后我每次拿到新的振动样本第一件事不是调模型而是先把退化起点标出来看分布。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?