简介这份资源面向计算机、人工智能、自动化、通信工程等专业的在校学生与教师以及从事轨道交通信号或工业设备监测的工程人员提供一套基于LSTM多时间序列特征提取的道岔故障诊断完整实现方案。项目以Python为开发语言围绕道岔动作电流等时序信号展开特征提取与故障分类建模适合作为毕业设计、课程设计、项目立项演示或进阶学习案例。压缩包共9个文件包含7个py源码文件、1个md说明文档和1个docx实验报告整体约872KB源码覆盖特征工程、LSTM模型构建、多线程处理与测试脚本等模块实验报告则系统梳理了方法原理与实验流程。目前已有200人学习关注。读者可据此掌握多时间序列特征提取与LSTM故障诊断的完整链路理解模型搭建、训练与评估细节并可在现有代码基础上修改扩展用于自身课题或工程场景。1. 从一份道岔故障诊断源码说起LSTM 多时间序列特征提取能解决什么道岔是铁路信号系统里故障率最高的室外设备之一它的动作电流曲线、功率曲线、锁闭时间这些时序信号直接反映转辙机的工作状态。传统做法靠人工看曲线、设阈值报警漏报误报都不少。这份railway_fault_detect-master资源核心就是用 LSTM 对多路时间序列做特征提取再完成道岔故障分类诊断附带一份实验报告文档。它适合做毕设、课设、信号专业课题的在校生也适合想上手 LSTM 时序分类的 Python 学习者。代码文件不多features.py管特征工程LSTM.py是模型主体xiecheng.py和thread.py涉及多线程数据加载test.py、pythontest.py是测试入口。下面按「数据怎么进、特征怎么提、模型怎么搭、坑在哪」拆开讲。2. 数据管线拆解从原始动作曲线到 LSTM 可吃的三维张量2.1 道岔时序信号的特点与预处理逻辑道岔动作电流曲线是一条典型的短时非平稳信号一次完整动作大概 5 到 8 秒采样率常见 25Hz 到 100Hz 不等。原始数据往往是一堆 CSV 或 Excel每行一个采样点列包括时间戳、电流值、功率值、道岔编号、动作方向。直接丢给 LSTM 是不行的因为 LSTM 要求输入是(样本数, 时间步, 特征数)的三维张量而原始数据是二维表。常见做法是先把每条动作曲线切出来按道岔编号和动作时间分组然后统一重采样到固定长度。比如原始 6 秒 100Hz 是 600 个点可以线性插值到 200 个点这样每个样本的时间步就是 200。特征维度可以是 1只用电流也可以是 3电流、功率、时间差分。这一步在features.py里通常有对应的重采样和归一化函数。归一化用 Min-Max 还是 Z-Score取决于你的数据分布。道岔电流在启动瞬间有冲击Z-Score 对异常值更敏感Min-Max 会把冲击压到 0-1 之间LSTM 收敛更稳。我一般先用 Min-Max如果发现某类故障被淹没再换 Z-Score 试。import numpy as np import pandas as pd from scipy.signal import resample def load_and_segment(csv_path, target_len200): 读取原始CSV按道岔编号和动作时间分组重采样到固定长度 csv_path: 原始数据路径 target_len: 统一时间步长度 df pd.read_csv(csv_path) # 假设列名: switch_id, action_time, current, power df[action_time] pd.to_datetime(df[action_time]) # 按道岔编号和动作时间分组每组是一条完整动作曲线 groups df.groupby([switch_id, pd.Grouper(keyaction_time, freq10S)]) samples [] labels [] for (sid, atime), g in groups: if len(g) 10: # 过滤掉不完整的片段 continue cur g[current].values.astype(np.float32) # 重采样到固定长度 cur_resampled resample(cur, target_len) # Min-Max 归一化 cur_norm (cur_resampled - cur_resampled.min()) / (cur_resampled.max() - cur_resampled.min() 1e-8) samples.append(cur_norm) # 标签从文件名或额外列取这里假设有 label 列 labels.append(g[label].iloc[0]) X np.array(samples).reshape(-1, target_len, 1) y np.array(labels) return X, y这段代码的关键参数是target_len它决定 LSTM 的时间步数。设太小会丢失曲线形态设太大会增加计算量。200 是一个经验值对应 2 秒左右的窗口能覆盖道岔启动、转换、锁闭三个阶段。freq10S是分组窗口如果你的动作间隔更短改成5S或3S。归一化里的1e-8是防止除零血泪经验不加的话遇到恒定电流段直接出 NaN。2.2 多时间序列特征提取的两种落地方式「多时间序列特征提取」这个说法容易让人懵。在这份资源里它其实有两层意思一是输入本身就是多路时序电流、功率、时间差分二是 LSTM 在时间维度上自动提取特征不需要手工设计 FFT 或小波。手工特征和 LSTM 自动特征不是对立的。我一般会先算几个统计量作为辅助输入均值、方差、峰值、过零率。这些在features.py里可以用几行搞定然后和原始归一化曲线拼在一起形成多通道输入。这样 LSTM 既有原始形态又有统计摘要收敛更快。def extract_stat_features(signal): 对单条时序信号提取统计特征 signal: 一维数组长度 target_len 返回: 4维统计特征 mean np.mean(signal) std np.std(signal) peak np.max(np.abs(signal)) zcr np.sum(np.abs(np.diff(np.sign(signal)))) / (2 * len(signal)) return np.array([mean, std, peak, zcr], dtypenp.float32) # 在 load_and_segment 里对每条曲线调用 # stat_feat extract_stat_features(cur_norm) # 然后把 stat_feat 复制到每个时间步或者只在最后一步拼接统计特征怎么用取决于你的模型结构。如果 LSTM 后面接全连接可以把统计特征单独走一个分支再 concat。如果嫌麻烦直接把统计特征广播到每个时间步变成(target_len, 5)的输入LSTM 也能学到。注意过零率对道岔电流不太敏感因为电流基本是正的换成「电流上升沿数量」更合适。多线程加载在thread.py里主要是为了加速大批量 CSV 读取。Python 的 GIL 对 IO 密集任务影响不大用ThreadPoolExecutor读文件是划算的。但别在数据增强阶段用多线程numpy 操作本身释放 GIL多线程反而可能因为竞争变慢。3. LSTM 模型搭建与训练层数、隐藏单元、过拟合的取舍3.1 模型结构设计与参数含义LSTM.py是核心文件里面大概率是一个nn.LSTM加全连接分类头。LSTM 的关键参数就三个input_size、hidden_size、num_layers。input_size等于你的特征维度如果只用电流就是 1加了统计特征就是 5。hidden_size是隐藏状态维度太小欠拟合太大过拟合道岔数据量通常几千条128 或 64 够用。num_layers一般 1 到 2 层2 层以上容易梯度消失除非你数据量上万。import torch import torch.nn as nn class LSTMClassifier(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, num_classes4): super().__init__() self.hidden_size hidden_size self.num_layers num_layers # batch_firstTrue 表示输入维度是 (batch, seq, feature) self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue, dropout0.3) self.fc nn.Linear(hidden_size, num_classes) def forward(self, x): # x: (batch, seq_len, input_size) h0 torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device) c0 torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device) out, _ self.lstm(x, (h0, c0)) # 取最后一个时间步的输出做分类 out out[:, -1, :] out self.fc(out) return outdropout0.3是防过拟合的如果训练集准确率远高于验证集调到 0.5。batch_firstTrue一定要设否则输入维度是(seq, batch, feature)调试时容易搞混。取最后一个时间步out[:, -1, :]是常见做法也可以用平均池化out.mean(dim1)后者对长序列更稳。训练循环里优化器选 Adam学习率 1e-3 起步损失函数用 CrossEntropyLoss。如果类别不均衡比如正常样本远多于故障样本加weight参数或者用 Focal Loss。早停策略看验证集 loss连续 10 个 epoch 不降就停。3.2 训练流程与验证指标训练脚本test.py或pythontest.py里应该有完整的 train/val/test 划分。常见坑是随机划分导致同一动作曲线的不同片段同时出现在训练集和验证集造成数据泄露。正确做法是按道岔编号或按时间段划分确保验证集里的道岔在训练集里没出现过。from sklearn.model_selection import train_test_split from torch.utils.data import DataLoader, TensorDataset # 假设 X, y 已经准备好 # 按道岔编号划分避免同一道岔的样本泄露 switch_ids np.array([...]) # 每条样本对应的道岔编号 unique_switches np.unique(switch_ids) train_switches, val_switches train_test_split(unique_switches, test_size0.3, random_state42) train_mask np.isin(switch_ids, train_switches) val_mask np.isin(switch_ids, val_switches) X_train, y_train X[train_mask], y[train_mask] X_val, y_val X[val_mask], y[val_mask] train_ds TensorDataset(torch.FloatTensor(X_train), torch.LongTensor(y_train)) val_ds TensorDataset(torch.FloatTensor(X_val), torch.LongTensor(y_val)) train_loader DataLoader(train_ds, batch_size32, shuffleTrue) val_loader DataLoader(val_ds, batch_size32, shuffleFalse)验证指标别只看准确率。道岔故障诊断里漏报把故障判成正常比误报代价高得多所以召回率比精确率重要。实验报告里如果只给了 accuracy自己补一个 confusion matrix 和 F1。sklearn.metrics.classification_report一行就能出。训练轮数一般 50 到 100配合早停。如果 loss 震荡厉害把 batch_size 调大或者学习率降到 5e-4。GPU 不是必须的道岔数据量小CPU 跑几百个 epoch 也就几分钟。4. 避坑与排查道岔 LSTM 诊断里最容易翻车的五个地方4.1 现象训练 loss 不降准确率卡在 25%原因通常是输入维度搞错了。LSTM 要求(batch, seq, feature)如果你把(batch, feature, seq)丢进去模型学不到时间依赖。另一个可能是标签没转成 0 到 num_classes-1 的整数CrossEntropyLoss 要求 LongTensor 且从 0 开始。解决在 forward 里打印x.shape确认第二维是时间步。标签用LabelEncoder转一遍或者手动映射。4.2 现象验证集准确率很高但测试集一塌糊涂这是典型的数据泄露。同一动作曲线的相邻片段被分到了训练和验证集模型只是记住了曲线形状。按道岔编号划分能解决大部分问题。如果数据里同一道岔只有一种故障那按编号划分会导致验证集缺少某些类别这时候按时间段划分更合适。解决划分前先检查每个道岔的故障类型分布确保训练集和验证集的类别比例接近。4.3 现象模型对正常样本全判对对故障样本全判错类别不均衡。正常样本可能占 90%模型只要全判正常就有 90% 准确率。解决损失函数加weighttorch.FloatTensor([1, 5, 5, 5])或者用重采样把故障样本过采样到和正常一样多。过采样时注意别简单复制加一点高斯噪声或时间偏移。4.4 现象重采样后曲线形态失真锁闭段被拉平线性插值对短时冲击不友好。道岔锁闭瞬间的电流跌落如果被插值平滑掉模型就学不到关键特征。解决改用保形插值PCHIP或者直接截断/填充到固定长度不做重采样。如果必须重采样先做峰值检测确保峰值位置不变。4.5 现象多线程读取时内存暴涨程序被 killthread.py里如果一次性把所有 CSV 读进内存再处理几千条曲线就是几个 G。解决用生成器逐批读取或者用pd.read_csv的chunksize参数分块。多线程只用于 IO处理完一批就释放。提示道岔数据里经常有缺失值用df.interpolate()补之前先确认缺失是设备离线还是传感器故障后者补了反而引入噪声。5. 从跑通到改出花迁移到其他时序分类任务的三个技巧这份资源的代码结构其实不限于道岔。任何「多路传感器时序 分类标签」的场景都能套电机故障诊断、轴承振动分类、心电图异常检测。我拿它改过一个轴承数据集换了features.py里的重采样长度和LSTM.py的input_size其他基本没动。第一个技巧是特征维度的弹性扩展。原始代码如果只用了电流你可以把功率、温度、振动拼进去input_size从 1 改成 4。但别一次性加太多先用相关性分析筛一遍相关性低于 0.1 的通道直接丢掉否则 LSTM 会被噪声带偏。第二个技巧是时间步的滑动窗口增强。如果原始数据只有几百条可以用滑动窗口切出更多样本。比如一条 600 点的曲线窗口 200步长 50能切出 9 条。标签继承原曲线标签。这样数据量翻几倍LSTM 的泛化会好很多。注意窗口不能跨曲线切否则标签就乱了。def sliding_window_augment(X, y, window200, stride50): 对长时序做滑动窗口增强 X: (n_samples, seq_len, n_features) y: (n_samples,) aug_X, aug_y [], [] for i in range(len(X)): seq X[i] for start in range(0, seq.shape[0] - window 1, stride): aug_X.append(seq[start:startwindow]) aug_y.append(y[i]) return np.array(aug_X), np.array(aug_y)第三个技巧是模型集成。单层 LSTM 方差大跑三次不同随机种子取平均准确率能稳 2 到 3 个点。如果嫌麻烦至少把torch.manual_seed固定住保证实验可复现。我一般会在test.py开头加torch.manual_seed(42)和np.random.seed(42)不然每次跑出来的 confusion matrix 都不一样写报告时对不上号。实验报告文档基于LSTM多时间序列特征提取的道岔故障诊断方法new.docx里应该有完整的实验设置和结果分析建议先看一遍再跑代码能省不少调试时间。README.md 里有运行说明按它走基本能跑通。从那以后我每次拿到时序分类的源码都强制先跑一遍数据形状检查确认(batch, seq, feature)没搞反再动模型。希望帮到你。本文还有配套的精品资源点击获取