简介:基于LSTM和SVM的设备故障诊断Python源码项目,融合长短期记忆网络提取时序特征与支持向量机分类判别,面向人工智能、自动化、电子信息等专业的毕业设计、课程设计与项目初期演示,也适合故障诊断方向的学习者作为进阶参考。压缩包共63个文件,以53个mat数据文件为主体,辅以m格式运行脚本、txt/md/docx说明文档及xlsx特征向量表,整体53.92MB,数据与代码模块清晰,便于定位和二次修改。该代码经过完整测试并成功运行,答辩评审平均分达96分,已有143人浏览学习;搭配西储大学轴承数据说明与README,可快速复现算法流程、理解特征处理与模型融合思路。对于希望在LSTM+SVM组合模型上快速落地实验的研究者或学生,这套源码提供了可复用的实验数据、特征提取脚本与分类判别流程,能够显著节省从零搭建与调试的时间。
1. 一台设备的“提前报警”,为什么必须同时用LSTM和SVM
设备故障诊断在工业现场的核心诉求很简单:别等设备坏了再停机,最好提前几小时甚至几天告诉你“这台机器不对劲”。但难点在于,传感器采集到的信号(振动、电流、温度)是持续变化的时间序列,故障特征藏在波形和趋势里,不是看一眼数值就能判断的。LSTM擅长从时间序列里提取特征,但它在小样本分类上并不擅长;SVM在小样本、高维特征的分类上非常可靠,但它处理不了原始时序数据。把两者串起来,正好互补——这也是“基于LSTM和SVM实现设备故障诊断python源码”这类项目最常见的核心结构。对于正在做课程设计、毕业设计,或者想给预测性维护方案搭一个初版原型的工程师,这个“LSTM提取特征+SVM分类”的组合是最值得先跑通的结构。本文要讲的就是这个组合的完整落地路径,从原理到代码到调参,再到底层踩过的坑。
2. 先搞清楚套路:把设备故障诊断拆成“时序特征提取+分类”两段式
2.1 为什么单用LSTM或单用SVM都会翻车——从故障数据的三个脾气说起
设备故障数据有三个特点。第一,故障样本少。正常设备占绝大多数,故障可能是偶发的,一个工厂能拿到的故障样本可能只有几十条到几百条。第二,信号是时序的。故障往往表现为某个频段能量升高、某个趋势突变,跟时间上下文强相关。第三,故障类型之间边界模糊。比如轴承早期磨损和润滑不良,在振动信号上可能只有细微差别。
单用LSTM做端到端分类,理论上可行,实际上在小样本场景容易过拟合。LSTM参数量大,需要大量数据才能压住泛化误差,而这个项目标题对应的场景往往拿不到那么多故障样本。单用SVM做分类,它本身是个强大的分类器,但得先把时序数据变成特征向量。常见的做法是手动提取统计特征:均值、方差、峰值、峭度、频谱能量等。这套手工特征工程在工业场景里确实能用,但对人的经验要求太高,换个设备、换个工况,特征可能就要重新试。
所以最稳的套路是:让LSTM自动从原始时序里学特征,把学到的特征向量交给SVM去分类。LSTM不用承担分类压力,SVM不用担心原始序列太长没法处理。两个模型各干各擅长的事。这个设计思路就是整个python源码项目的骨架。
2.2 两段式结构怎么搭:LSTM当特征提取器,SVM当分类器
整体结构可以分为三段。第一段是数据切片:把连续采样的传感器信号按固定窗口切成长度相同的片段,每个片段对应一个标签。第二段是特征提取:把每个窗口的数据送入LSTM,取最后一个时间步的隐藏状态(或者对全部时间步做池化)作为这段数据的特征向量。第三段是分类:把特征向量送入SVM,输出故障类别。
需要注意,LSTM的输出是个三维张量:batch_size、seq_len、hidden_size。如果输入一个窗口的序列,seq_len就是窗口长度,hidden_size是你设定的隐含层维度。我们要的特征向量通常取最后一个时间步的输出,形状是batch_size、hidden_size;或者对所有时间步做均值池化,得到同样形状的向量。这个向量就是“机器自己学出来的特征”,不需要你手动算峭度、峰值这些统计量。
为什么选SVM而不是直接用softmax层?在故障样本少的场景,SVM的间隔最大化思想让它在高维小样本分类上比神经网络末端的softmax更稳。尤其是类别数不多(比如正常加3种故障),SVM配合RBF核,决策边界可以很灵活,也不容易像神经网络那样在小数据上反复震荡。如果你在框架里看到LSTM后面接的是SVM而不是全连接层,设计意图就在这里。
2.3 完整数据流:从原始振动/温度数据到故障标签要过几道关
一个完整的数据流大概是:原始采集数据(连续时间序列)→ 滑窗切片(每个窗口长度例如256或512个采样点)→ 归一化(每个窗口内做z-score)→ 划分训练/验证/测试集(注意按设备或按时间段划分,不要随机打乱跨序列)→ LSTM提取特征 → SVM分类 → 输出故障标签。
这里有三个关键点。
第一,窗口长度怎么选。窗口太短,一个窗口内包含的故障特征不完整;窗口太长,LSTM的序列计算量变大,而且一个窗口里可能混入多个状态变化。常见做法是先看采样频率,比如采样率是1kHz,一个窗口覆盖0.5到1秒,取256或512个点比较常见。
第二,归一化不能全局做。很多人习惯对整个数据集做一次归一化再切窗,这在时序问题上是有风险的:如果训练集和测试集来自不同时间段,全局归一化会把未来数据的统计信息泄露到训练过程里。正确做法是在每个窗口内部做z-score,或者用训练集的统计量去归一化测试集。
第三,数据集划分不能随机打乱。如果同一个设备的连续信号被切成了几百个窗口,随机打乱后训练集和测试集会包含时间上相邻的窗口,导致评估结果虚高。工业场景里应该按“设备”或“时间段”划分,保证模型是在没见过的时段上做预测。后面第5章会展开讲这个坑,因为在故障诊断项目里这是最容易让结果“看起来完美”的陷阱。
3. 在Python里把LSTM+SVM跑通:数据准备、模型定义与训练
3.1 数据准备:窗口切片、归一化、训练集/测试集划分
先给出完整的数据准备代码。这里假设你有一个CSV文件,第一列是timestamp,第二列是振动值,后面可能还有其他传感器通道。
import numpy as np import pandas as pd def load_and_slice(csv_path, window_size=256, stride=128): df = pd.read_csv(csv_path) # 假设第二列以后都是传感器信号 data = df.iloc[:, 1:].values.astype(np.float32) windows = [] for start in range(0, len(data) - window_size, stride): end = start + window_size windows.append(data[start:end]) # 每个窗口形状: (window_size, n_channels) return np.array(windows) def zscore_per_window(windows): # 每个窗口内部做z-score,避免全局归一化泄露未来信息 mean = windows.mean(axis=1, keepdims=True) std = windows.std(axis=1, keepdims=True) + 1e-6 normalized = (windows - mean) / std return normalized windows = load_and_slice("bearing_data.csv", window_size=256, stride=128) windows = zscore_per_window(windows) print(windows.shape) # (样本数, 256, 通道数)逻辑说明:这里用stride=128,意思相邻窗口有50%重叠,可以在样本量少的时候变相扩充数据。每个窗口内部做z-score,是为了让LSTM输入每个通道的数值都在0附近波动,避免某个传感器量纲过大主导梯度更新。加1e-6是为了防止信号在某段恒定导致std为0。
参数说明:window_size=256和stride=128是起步值。如果你的信号采样率很高、故障特征变化比较慢,可以加大window_size到512或1024,stride可以保持50%重叠。注意窗口重叠比例太高会让相邻窗口高度相似,训练集和测试集之间如果切分不当,评估结果会虚高。这一步做完,你应该得到一个形状为(样本数, 窗口长度, 通道数)的三维数组,这就是LSTM的标准输入格式。
3.2 LSTM特征提取层的PyTorch实现
用PyTorch定义LSTM特征提取器,输出每个窗口的特征向量。
import torch import torch.nn as nn class LSTMTFeatureEncoder(nn.Module): def __init__(self, input_size, hidden_size, num_layers=1, dropout=0.2): super().__init__() self.lstm = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=num_layers, batch_first=True, dropout=dropout if num_layers > 1 else 0 ) def forward(self, x): # x: (batch_size, seq_len, input_size) out, (h_n, c_n) = self.lstm(x) # out: (batch_size, seq_len, hidden_size) # 取最后一个时间步输出作为整个窗口的特征 last_step = out[:, -1, :] # (batch_size, hidden_size) return last_step逻辑说明:input_size就是你每个窗口里的传感器通道数,比如只用振动信号就是1,加上温度和电流就是3。batch_first=True让我们传入的数据形状是(batch_size, seq_len, input_size),更符合PyTorch模块里读写数据的习惯。取out[:, -1, :]就是最后一个时间步的隐藏状态,它浓缩了整段序列的信息。
参数说明:hidden_size是核心参数,一般取64、128、256。数据量小就少一些,数据量大可以大一些。num_layers建议从1开始,不要一上来就堆两层,因为设备故障数据量通常不大,两层LSTM的参数量和梯度传播难度会让训练变得不稳定。dropout只在num_layers大于1时生效,单层LSTM加dropout反而会削弱记忆能力,这也是很多初学者容易踩的坑。如果你想用均值池化替代最后一个时间步,把last_step改成out.mean(dim=1)即可,在某些数据集上会更稳。
3.3 把LSTM输出拉成特征向量,喂给SVM
LSTM训练好之后,要把训练集和测试集都过一遍LSTM,拿到特征向量,然后用scikit-learn训练SVM。
from sklearn import svm from sklearn.model_selection import train_test_split def extract_features(model, windows, batch_size=128): model.eval() features = [] with torch.no_grad(): for i in range(0, len(windows), batch_size): batch = torch.tensor(windows[i:i+batch_size], dtype=torch.float32) feat = model(batch) features.append(feat.numpy()) return np.concatenate(features, axis=0) # 假设已有训练窗口和标签 X_train_w, X_test_w, y_train, y_test = train_test_split( windows, labels, test_size=0.2, stratify=labels, random_state=42 ) # 加载训练好的LSTM权重(第3.4节会讲怎么训练) model = LSTMTFeatureEncoder(input_size=1, hidden_size=128) model.load_state_dict(torch.load("lstm_encoder.pth")) train_feat = extract_features(model, X_train_w) test_feat = extract_features(model, X_test_w) # 用RBF核的SVM,C和gamma先给一个宽泛值 clf = svm.SVC(kernel='rbf', C=1.0, gamma='scale', probability=True) clf.fit(train_feat, y_train) print("SVM test acc:", clf.score(test_feat, y_test))逻辑说明:模型切到eval模式后,关闭梯度计算,这一步很关键——如果不加torch.no_grad(),虽然不影响提取结果,但会把特征图存进计算图,内存直接爆掉。SVM不关心梯度,它只需要数值特征。
参数说明:gamma='scale'意味着gamma会自动取1/(n_features * X.var()),比手动设gamma=0.01要稳健,第一次跑就用这个。C=1.0是SVM的正则化强度,越大越容易过拟合,越小越强调间隔最大化。probability=True允许SVM输出概率,后面做可视化诊断时会有用,但会显著增加训练时间,如果你只需要类别标签,可以去掉。如果特征提取时发现内存不足,把batch_size从128降到64或32。
3.4 训练流程:先训LSTM到收敛,再训SVM——还是端到端一起训?
这是新手最容易搞混的问题。LSTM和SVM能不能端到端一起训?严格来说可以,但SVM的损失函数不是平滑的,很难和LSTM的梯度一起反传。常见做法是分阶段训练。
第一阶段,训练LSTM本身。有两种选择:一是用LSTM接一个临时的全连接分类层,用交叉熵损失训练到收敛;二是把LSTM当自编码器,通过重建原始信号来学习特征。设备故障诊断数据量小,我一般用第一种,训完之后把临时的全连接层丢掉,只用LSTM的隐藏状态作为特征。
第二阶段,冻结LSTM,提取特征,训练SVM。这样LSTM的训练和SVM的训练是两个独立过程,互不干扰,也便于排查到底是哪个环节出了问题。
class LSTMTEncoderWithHead(nn.Module): def __init__(self, encoder, num_classes): super().__init__() self.encoder = encoder self.classifier = nn.Linear(encoder.lstm.hidden_size, num_classes) def forward(self, x): feat = self.encoder(x) return self.classifier(feat) # 训练若干epoch后,只保存encoder部分 torch.save(model.encoder.state_dict(), "lstm_encoder.pth")逻辑说明:把临时分类头去掉之后,LSTM就成了一个纯粹的特征提取器。这样做的另一个好处是,你可以在不同的数据集上复用同一个LSTM特征提取器,只要设备的信号特性相似,就不需要重新训练。
参数说明:训练LSTM的优化器我一般用Adam,学习率从1e-3起步,batch_size在64到256之间。注意LSTM输入数据形状必须是(batch_size, seq_len, input_size),如果你的数据是(input_size, seq_len),会在forward里报维度错误,这几乎是高频报错。训练epoch数建议50到100,配合早停策略——验证集loss连续10个epoch不降就停止。这里不推荐直接把SVM的hinge loss接在LSTM后面做端到端训练,原因会在第5章展开。
4. 参数怎么设:LSTM的hidden_size、层数和SVM的核函数
4.1 LSTM侧:hidden_size、num_layers、dropout的常见取值
hidden_size决定特征向量的维度,也是LSTM参数量最大的来源。hidden_size=64在大约几百个样本的场景下足够;hidden_size=128适合千级样本;如果样本超过几千条,可以尝试256。注意hidden_size增大之后,SVM输入特征维度也变高,RBF核gamma='scale'的自动计算会帮你平衡,但特征维度太高、样本量太少,SVM同样会过拟合。
num_layers从1开始。很多教程默认两层LSTM效果更好,但设备故障诊断的数据量通常撑不起两层。如果你发现单层LSTM在训练集上loss降不下来,先调hidden_size和学习率,不要急着加层。dropout对单层LSTM无效,这是因为PyTorch里dropout参数在num_layers=1时不会应用。如果你非要加正则化,可以在LSTM之后加一个单独的Dropout层。另一个容易被忽略的参数是学习率,LSTM的梯度传播路径长,学习率太高容易震荡,太低收敛太慢。我常用1e-3起步,loss不降就减半。
4.2 SVM侧:RBF核的C和gamma怎么调
RBF核有两个关键参数:C和gamma。C控制误分类惩罚,C越大越不愿意放过训练集里的错误,容易过拟合;C越小决策边界越平滑。gamma控制单个样本的影响半径,gamma越大每个样本的影响范围越小,决策边界越复杂。
一个简单有效的调参方法是网格搜索配合交叉验证。scikit-learn里面有GridSearchCV,可以直接用提取好的特征跑。
from sklearn.model_selection import GridSearchCV from sklearn.svm import SVC param_grid = { 'C': [0.1, 1, 10], 'gamma': ['scale', 0.01, 0.001], 'kernel': ['rbf'] } grid = GridSearchCV(SVC(probability=True), param_grid, cv=5, scoring='f1_macro') grid.fit(train_feat, y_train) print(grid.best_params_) print(grid.best_score_)逻辑说明:这里用f1_macro而不是accuracy,是因为设备故障数据里类别通常不平衡,正常样本远多于故障样本,accuracy会被多数类带偏。f1_macro会同时关注每个类别的精确率和召回率,更适合故障诊断场景。
参数说明:C=1、gamma='scale'是起步值。如果交叉验证结果中best_params是C=10、gamma=0.01附近,说明特征空间比较稀疏;如果best_params落在C=0.1、gamma='scale',说明模型倾向更平滑的边界。注意GridSearchCV会重新跑5折交叉验证,特征量大的时候耗时较长,建议先跑小的参数网格,确认趋势再扩充。如果你想更快,可以先用LinearSVC跑一遍作为基线,如果线性核的F1分数已经不错,RBF核的提升空间就有限。
4.3 一个能直接起步的参数表
| 参数 | 起步值 | 调整方向 |
|---|---|---|
| window_size | 256 | 故障特征变化慢就加大到512或1024 |
| stride | 128 | 样本少就减小保持50%重叠 |
| LSTM hidden_size | 128 | 样本多/特征复杂加到256,否则降到64 |
| LSTM num_layers | 1 | 先别加层,除非单层欠拟合明显 |
| LSTM优化器 | Adam | 学习率1e-3,loss震荡就降到5e-4 |
| batch_size | 128 | 显存不够就降到64或32 |
| SVM kernel | RBF | 特征维度特别高时可以试linear |
| SVM C | 1.0 | 过拟合调小,欠拟合调大 |
| SVM gamma | 'scale' | 网格搜索0.01、0.001 |
这个表的思路是先跑通一个“正常”结果,再根据训练集和测试集的表现差异去调。高手和新手的区别不是谁会调大调小,而是谁能从loss曲线和验证集指标判断到底是哪个环节出了问题。下一章就专门讲这些判断。
5. 避坑与排查:从“loss下降但测试集一塌糊涂”到“SVM根本跑不动”
5.1 时序切窗时泄露未来信息,导致评估虚高
现象:训练时loss降得很漂亮,测试集准确率也很高,但一到真实场景就完全失灵。
原因:大多数情况下是数据划分出了问题。如果训练集和测试集来自同一段连续信号,随机打乱之后,测试集里会有大量与训练集时间相邻的窗口,信息重复严重。更隐蔽的是全局归一化——如果先对整个数据集计算均值和标准差,再切窗,测试集的统计信息就已经被模型看过了。
解决:按时间段或按设备划分数据集。比如前80%的时间段作为训练集,后20%作为测试集。归一化参数只能用训练集计算,然后应用到测试集。如果信号有多个设备来源,划分时保证同一个设备不能同时出现在训练集和测试集里。
注意:在时序故障诊断里,“随机划分”基本等于作弊。你要的不是在已见过的信号片段上准确率多高,而是对没见过的故障形态有泛化能力。这也是“高分项目”里最容易拿分的点——把数据划分讲清楚,比模型调参更能体现工程意识。
5.2 LSTM输出特征没有归一化,SVM训练时直接崩
现象:SVM训练耗时极长,或者gamma='scale'计算出来的值异常小,训练完评估准确率只有30%左右。
原因:LSTM的隐藏状态经过tanh或sigmoid激活后,数值范围通常不是标准正态分布,可能集中在某个区间。SVM的RBF核依赖样本间的欧氏距离,特征尺度不一致会让某些维度主导距离计算。gamma='scale'虽然会自动调整,但对分布偏移严重的高维特征,稳定性并不好。
解决:在提取完LSTM特征后,对特征向量再做一次标准化。
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() train_feat_scaled = scaler.fit_transform(train_feat) test_feat_scaled = scaler.transform(test_feat) clf.fit(train_feat_scaled, y_train) print(clf.score(test_feat_scaled, y_test))逻辑说明:这里的StandardScaler用训练集特征拟合,再用同一组均值和标准差去变换测试集特征。如果你直接在全部特征上fit_transform,又犯了信息泄露的错。这一步在故障诊断项目里出现的频率极高,因为LSTM作为黑匣子,很多人默认它输出的特征可以直接用,其实不然。
参数说明:标准化之后的特征均值接近0、方差接近1,RBF核的距离计算会均衡得多。记住:LSTM解决了特征提取问题,但它不负责把特征整理成适合SVM的分布。这一个坑踩下去,SVM的准确率可能直接从60%跳到85%,属于性价比最高的修复操作。
5.3 故障类别不平衡,SVM的决策边界被多数类带偏
现象:测试集整体准确率85%,但看一下每一类的准确率,正常的识别率98%,某个故障的识别率只有40%。
原因:设备故障场景里正常样本数量远大于故障样本是常态。SVM在训练时会优先把多数类分对,以降低整体损失,少数类的边界被压缩。
解决:处理类别不平衡有两条路。第一条是数据层面,给少数类做重采样,比如对少数类窗口做更小的stride,让它产生更多重叠窗口。第二条是算法层面,给SVM设置class_weight='balanced',让scikit-learn自动按类别频率加权。
clf = SVC(C=1.0, kernel='rbf', gamma='scale', class_weight='balanced')逻辑说明:class_weight='balanced'会让每个类别的惩罚权重与该类别样本数量成反比。样本少的类别分错了会被罚得更重,决策边界就不会一边倒。
参数说明:如果你发现class_weight='balanced'之后,多数类准确率有所下降,这是正常现象,总体的F1分数会上升。故障诊断场景里,漏报一次严重故障的代价远高于多报几次正常维护,所以宁可牺牲一点正常类准确率。
5.4 想端到端训练LSTM+SVM,loss要么不更新要么直接NaN
现象:有人想直接把SVM的hinge loss接到LSTM后面做端到端训练,结果发现loss是NaN,或者根本不会更新。
原因:SVM的hinge损失在间隔大于等于1时梯度为0,且决策函数里没有可导的软间隔映射。就算使用hinge loss的次梯度,当样本落在间隔内部时梯度方向也不稳定,和LSTM的时序反向传播叠加后极容易梯度爆炸。
解决:不要端到端训练。先把LSTM用临时softmax头训练到收敛,再提取特征训练SVM。如果你一定想追求端到端,常见做法是把SVM替换成一个带margin loss的全连接层,或者用triplet loss让LSTM学习类间距离,但这已经是另一个方向的改造,超出了这个项目标题的范围。我见过不少人在这一步卡了一周,最后把模型拆开分阶段训练,问题立刻消失。
5.5 SVM训练时概率模式太慢,内存直接爆掉
现象:样本量有几万个窗口,每个窗口提取的hidden_size=128的特征,理论上不大,但如果你用概率型SVC(probability=True),训练时间会从几分钟变成几小时,甚至内存报错。
原因:SVC的probability=True会额外做Platt缩放,需要多跑一次交叉验证,复杂度比不带概率的版本高很多。另外,RBF核要计算所有样本两两之间的核矩阵,样本量上万时,核矩阵大小是1e8级别,内存瞬间吃满。
解决:如果不需要输出概率,去掉probability=True。如果一定要概率,可以改用LinearSVC配合CalibratedClassifierCV。如果特征维度很高且类别数不多,可以先试linear核,训练快很多。
clf = SVC(C=1.0, kernel='rbf', gamma='scale', probability=False)逻辑说明:去掉概率输出后,SVM只需要求解一次二次规划问题。对故障诊断来说,多数场景输出类别就够用,概率只是锦上添花。
参数说明:如果你的样本量超过2万,或者特征维度超过512,建议开始考虑用LinearSVC替代RBF核SVC。LinearSVC在大样本上训练快得多,虽然决策边界不如RBF核灵活,但在高维特征空间里线性边界往往已经够用。这个坑在你把LSTM的hidden_size调大之后特别容易出现,因为特征维度越高,核矩阵计算越慢。
6. 最后的落地技巧:把诊断结果可视化,并验证模型是真的能用
6.1 用混淆矩阵和t-SNE验证特征可分性
模型训练完之后,我习惯先画两个图。一个是SVM在测试集上的混淆矩阵,可以一眼看出哪两类故障最容易混。另一个是t-SNE把LSTM特征降到2D,看不同类别的点在空间里是否自然聚团。
import matplotlib.pyplot as plt from sklearn.manifold import TSNE from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay y_pred = clf.predict(test_feat_scaled) cm = confusion_matrix(y_test, y_pred) ConfusionMatrixDisplay(cm).plot() plt.savefig("confusion_matrix.png", dpi=150) tsne = TSNE(n_components=2, perplexity=30, random_state=42) feat_2d = tsne.fit_transform(test_feat_scaled) plt.scatter(feat_2d[:, 0], feat_2d[:, 1], c=y_test, cmap='tab10', s=10) plt.savefig("tsne_features.png", dpi=150)逻辑说明:混淆矩阵里的混类位置会告诉你下一步该往哪调。如果发现故障A经常被识别成故障B,可以回去看这两个类别的原始信号差异,或者增加对应传感器通道。t-SNE则是一个可视化“体检报告”,如果同类别点没有聚团,说明LSTM提取的特征还没学好,直接调SVM也没用。这两个图放进课程设计或毕业设计的报告里,能直观说明你做了特征层面的验证,而不是只报一个准确率数字。
6.2 把模型封装成“输入一段时序→输出故障类别”的函数
实验做完之后,最终要交付一个能直接用的诊断函数。我的做法是写一个predict_fault函数,输入一段原始信号,内部完成归一化、LSTM特征提取、特征标准化、SVM预测四步。
def predict_fault(raw_signal, encoder, scaler, clf, window_size=256): # raw_signal: 一维numpy数组,长度至少为window_size if len(raw_signal) < window_size: raise ValueError("信号长度不足一个窗口") # 取最后一个窗口 window = raw_signal[-window_size:].astype(np.float32) # 窗口内z-score mean, std = window.mean(), window.std() + 1e-6 window = (window - mean) / std # 输入LSTM x = torch.tensor(window.reshape(1, window_size, 1), dtype=torch.float32) encoder.eval() with torch.no_grad(): feat = encoder(x).numpy() # 标准化 + SVM预测 feat_scaled = scaler.transform(feat) return clf.predict(feat_scaled)[0], clf.predict_proba(feat_scaled)[0] if hasattr(clf, "predict_proba") else None逻辑说明:这个函数把四步串起来,输入是原始信号数组,输出是故障类别和概率。注意窗口取的是最后window_size个点,意味着你只需要采集这么长的实时信号就能触发一次诊断。如果信号维度不是1而是多通道,把reshape里的1改成对应的通道数即可。
6.3 旧习惯:先保存LSTM特征,再调SVM,别每次都从头训
最后一个经验:LSTM训练很贵,SVM调参很快。所以我在提取完特征后,第一件事就是把特征矩阵保存成npy文件,后面调SVM参数的时候直接加载,不用再跑一遍LSTM。这样反复调C和gamma的成本几乎为零,迭代速度大幅提升。
这个习惯帮我省了很多时间,尤其当样本量大或者LSTM层数加深时。如果你已经在做设备故障诊断方向,建议把这个流程固定在代码里:训练LSTM → 提取并保存特征 → 调SVM → 只修改SVM参数重新训练。希望帮到你。
本文还有配套的精品资源,点击获取