简介:设备传感器温度信号异常检测数据集是一套专为时间序列异常检测算法验证与工业设备状态监控设计的小型数据集,面向机器学习初学者、算法工程师及设备运维人员,可用于温度信号异常识别、故障预警与健康管理相关实验。资源包共10个文件,包含9个JSON与1个CSV文件,其中CSV文件存储原始温度信号序列,JSON文件分别对应窗口化数据切片、组合窗口、标签标注结果及辅助信息,便于用户直接按窗口或按时间点进行训练与评估。数据集覆盖正常与异常温度状态,提供细致的标签划分,可直接加载至深度学习或统计模型进行二分异常检测,也可用于特征工程、阈值选择及多模型对比测试。整体压缩包约35KB,文件结构清晰,适合快速上手。目前已有378人学习下载,对于希望低成本获取标准异常检测数据、快速搭建实验流程的研究者与工程师来说,是一份实用的小型资源。
1. 设备传感器温度信号异常检测数据集:先把超限报警思维放一边
设备传感器温度信号异常检测数据集这类资源,很多工程师拿到手第一反应是解压、跑个模型、盯一眼 AUC。但温度信号跟图像完全不一样:相邻两个采样点高度相关,真正的异常往往不是单点超限,而是几十秒到几分钟内的趋势翻转、缓慢漂移或周期形态破坏。如果还用「超过 80°C 就报警」的老思路去处理,大概率会把最贵的故障漏掉。这份 rar 资源的价值在于:它把传感器温度信号和对应的异常标签打包在一起,让你能直接动手做趋势异常检测、滑窗特征提取和在线预警,而不是对着空白需求文档发呆。适合正在做数控机床状态监测、传感器课程设计、工业异常检测算法落地的从业者和学生。接下来的所有步骤,都按「拿到手怎么拆、怎么变成训练样本、怎么上线不回退」来推。
2. 温度信号异常的三种形态:从突变、漂移到周期破裂
2.1 三种最常见的异常形态
温度信号里的异常,按形态大致可以分为三类,这三类对应完全不同的检测特征和算法策略。
第一类是突变异常,表现为某个时刻温度瞬间跳变,形成明显尖峰。产生原因一般是传感器受电磁干扰、接线松动、电机启停瞬间的冲击,少数情况才是真正的过热。这类异常最容易检测,统计特征上峰峰值和相邻点差分会异常突出,但也要小心:它最容易造成误报,因为一个工业现场每天都会有正常的启停冲击。
第二类是持续漂移,温度以很小但持续的斜率向上或向下爬。单点看完全正常,拉长到 5 分钟窗口才能看出趋势。这是轴承磨损、散热通道堵塞、润滑油性能衰退的典型前兆。这类异常最坑人,因为固定阈值和单点超限报警完全拿它没办法,必须用滑窗斜率或者序列级特征才能抓出来。
第三类是周期破裂。正常工况下温度会跟着设备节拍呈现周期性波动,比如机床切削、压缩机往复运动。当周期性被打乱,主频漂移或者幅值衰减,说明机械结构可能出了问题。这类异常在时域里很难定义,通常要加频域特征,用 FFT 去看主频分量是否还稳定。
拿到数据集后,第一步不是直接建模,而是先看标签对应的是哪一类异常,这决定了后续的特征设计和模型选型。如果标签里全是尖峰突变,却用慢漂移的思路去设计特征,结果一定很难看。
2.2 解压后的第一件事:吃透文件清单与字段定义
这份资源是 rar 压缩包格式。解压这一步看起来简单,但值得说清楚:不建议用 Windows 系统自带的压缩文件夹向导去处理,它对中文路径和特殊字符的处理不够稳。我一般用 7-Zip,右键解压到独立目录,路径里尽量不要带中文和空格,减少后续脚本读取时的编码麻烦。
解压之后,先打开目录结构,找 README 或说明文档,再列出所有文件清单。常见的组织方式有两种:一种是一个 CSV 文件里包含所有传感器的数据,靠 sensor_id 字段区分;另一种是每个传感器一个独立 CSV,文件名带编号。无论哪种,核心字段大同小异。
| 字段名 | 含义 | 常见类型 |
|---|---|---|
| timestamp | 采样时间戳 | Unix 秒或 ISO 格式字符串 |
| sensor_id | 传感器编号 | 整数,多传感器时出现 |
| temp_value | 温度值 | float,单位一般为 °C |
| label | 异常标记 | 0 正常,1 异常 |
| phase | 工况阶段 | 整数编码,部分数据集提供 |
读取过程建议直接写成脚本,避免每次用 Excel 打开看,不仅慢,还可能因为文件太大卡死。我常用的开头是这样:
import pandas as pd df = pd.read_csv('temperature_signal.csv', encoding='utf-8-sig') df['timestamp'] = pd.to_datetime(df['timestamp']) df = df.sort_values('timestamp').reset_index(drop=True) print(df.info()) print(df['label'].value_counts())这里的 encoding='utf-8-sig' 是为了兼容 Excel 保存 CSV 时常见的 BOM 头,如果是早期数据集用 GBK 编码,则会乱码,这段后面在坑里再细说。sort_values 按时间排序很关键,后面所有滑窗操作都依赖时间有序,如果原始文件本身就是乱序的,这一步会直接决定后面的特征能不能用。reset_index 则是把排序后的索引重排,避免后续按位置切窗口时索引错位。
2.3 标签平衡性:先判断该走有监督还是无监督
把 label 的分布打出来之后,第一件事是看异常占比。温度信号的异常检测数据集里,异常样本通常占很小比例,常见的情况是 1% 到 15%。这个数字直接决定了算法路线。
如果异常占比低于 5%,这意味着有监督二分类会非常吃力。类别极度不平衡时,模型学到的往往是「全预测正常」这种偷懒解,即使精确率、召回率看起来不错,换一段新数据就可能全线崩溃。这时候更适合走无监督路线,比如孤立森林、Autoencoder 这类只依赖正常模式的方法。
如果异常占比在 10% 以上,并且标签是按时间片段标注的,那么可以考虑有监督模型,比如 LightGBM 或者随机森林,配合滑窗特征。
另外要确认 label 的标注粒度是逐点还是逐段。逐点标注意味着每个时间戳都有独立标签,这种处理起来更灵活,但噪声也更大;逐段标注则是某一段时间整体标为异常,这种情况生成滑窗样本时要额外小心,后面避坑章节会专门提。
还有一点容易被忽略:检查 label 在时间轴上的分布是否连续。如果异常全部集中在数据集的最后 20% 时间段,那么做随机划分时,训练集和验证集都可能被异常片段污染,后面 3.3 会讲到时间序列必须按时间顺序切分。先把这个分布画像画出来,再谈建模不迟。
3. 把温度序列变成训练样本:滑窗、特征与标准化
3.1 滑窗切分:窗口长度与步长的选型逻辑
温度信号是一条一维时间序列,不能像图像那样直接把每个像素点当特征。温度变化是慢变量,相邻点高度相关,单点独立性很差。所以要先把原始信号切成长度固定的窗口,每个窗口变为一个样本。
窗口长度怎么选,是第一步的关键。温度信号采样率通常不高,常见的工业数据采集系统落地频率在 1 Hz 到 10 Hz 之间。如果采样率是 1 Hz,窗口取 256 点,大约对应 4 分钟多的信号,这个长度足够看出趋势性变化。窗口太短,比如 64 点,窗口内可能只是一段平滑曲线,斜率和方差都没区分度;窗口太长,比如 2048 点,异常发生的那一小段会被平均掉,检出延迟也会增大,现场根本等不起。
步长决定样本重叠率。步长为窗口一半,也就是 128 点,相邻窗口有 50% 重叠,样本量翻倍,模型训练更稳定,但要注意重叠会造成样本间的强相关性,评估时数据泄漏的风险会变大。我一般先按 50% 重叠跑基线,确认效果后再考虑减少重叠来降低相关性。
切窗的代码逻辑不复杂,但有几个细节值得注意:
def sliding_window(series, window=256, step=128): """切分一维温度序列为固定长度窗口""" for start in range(0, len(series) - window + 1, step): yield series[start:start + window] samples = [] labels = [] window_size = 256 step_size = 128 for idx, win in enumerate(sliding_window(df['temp_value'].values, window_size, step_size)): samples.append(win) seg_label = df['label'].values[idx * step_size: idx * step_size + window_size] labels.append(1 if (seg_label.sum() / window_size) >= 0.3 else 0)这里的 seg_label 是从原始标签序列中取出当前窗口对应的标签段。异常阈值 0.3 的含义是:窗口内超过 30% 的点被标记为异常,才把整个窗口视为异常样本。这个比例可以调,如果追求召回率就往下调到 0.1,如果怕误报就往上调到 0.5。出现 NaN 标签时要用 fillna 处理,否则 seg_label.sum() 会直接返回 NaN,导致整条样本丢失。
3.2 特征工程:时域五个特征加频域两个特征
切出来的窗口是 256 维原始数值,直接喂给模型在理论上是可行的,但实际效果很差。原因有两个:一是维度太高,孤立森林这类基于距离或密度的模型在 256 维空间里区分度会被稀释;二是温度信号相邻点强相关,256 个原始值里有效信息其实很少,大部分是冗余。
所以要把每个窗口压缩成一组特征。我常用的特征集是七个,覆盖时域和频域:
import numpy as np import pandas as pd def extract_features(win): n = len(win) features = { 'mean': float(win.mean()), # 窗口平均温度 'std': float(win.std()), # 波动幅度 'slope': float(np.polyfit(np.arange(n), win, 1)[0]), # 整体漂移趋势 'ptp': float(np.ptp(win)), # 峰峰值 'rms': float(np.sqrt(np.mean(win ** 2))), # 有效值 } fft_amp = np.abs(np.fft.rfft(win - win.mean())) / n features['fft_max'] = float(fft_amp.max()) features['fft_argmax'] = float(fft_amp.argmax()) return features X_raw = [extract_features(w) for w in samples] X = pd.DataFrame(X_raw)这些特征的物理含义对应 2.1 里的异常类型。slope 是捕获持续漂移的核心特征,轴承磨损导致的温度缓慢上升,在 256 点窗口内会呈现出一个稳定的正斜率。ptp 和 std 则对突变异常敏感,瞬时尖峰会让峰峰值立刻变大。fft_max 和 fft_argmax 是针对周期破裂的,正常工况下主频位置稳定,异常时幅值会下降或主频位置发生偏移。减去均值再去做 FFT 是为了去掉直流分量,否则 0 Hz 的幅值会占据主峰,把真正的周期特征掩盖掉。
提取特征后建议顺手看一眼特征之间的相关性。温度数据的 mean 和 rms 往往高度相关,保留其中一个也可以;slope 和 mean 有时候也会联动,因为温度整体上升时均值自然抬高。如果有明显的共线性特征,可以在后续建模时观察特征重要性再决定去留。
3.3 标准化与数据划分:时间序列不能随机洗牌
特征提取完成后是标准化。温度传感器不同安装位置、不同设备的基准温度差异很大,A 设备 40°C 是正常,B 设备 40°C 可能已经是异常。标准化可以消除这种绝对温度水平的差异,让模型更关注形态变化而不是绝对值。
标准化最容易被忽视的是泄漏问题。StandardScaler 的 fit 阶段会计算均值和标准差,这里只能使用训练集的数据,如果对全量数据做 fit_transform,验证集和测试集的信息已经泄漏进了模型,回测结果会虚高,这一点后面避坑章节单独展开。
时间序列的数据划分必须按时间顺序,不能用 train_test_split 默认的随机洗牌。温度信号有很强的自相关性,相邻窗口之间存在重叠,随机洗牌会把同一段信号的前后窗口拆到训练集和验证集,导致验证集几乎就是训练集的变形,分数虚高到没有任何参考价值。
from sklearn.preprocessing import StandardScaler split = int(len(X) * 0.7) X_train, X_val = X.iloc[:split], X.iloc[split:] y_train, y_val = np.array(labels[:split]), np.array(labels[split:]) scaler = StandardScaler() X_train_s = scaler.fit_transform(X_train) X_val_s = scaler.transform(X_val)这里按前 70% 训练、后 30% 验证的划分方式,是为了模拟真实场景:用过去的数据训练模型,预测未来的数据。如果原始数据时间跨度足够长,还可以留出最后一段完全不参与任何调参的测试集,只在最终评估时用一次,这样更接近现场的真实表现。
4. 异常检测建模:孤立森林与 Autoencoder 的阈值标定
4.1 孤立森林基线:三个参数决定结果走向
拿到特征和标签下的第一步建模对象,我建议先跑孤立森林。理由很简单:不需要假设数据分布,内置的异常分数机制天然适配「正常样本多、异常样本少」的场景,而且训练速度快到可以忽略不计。
三个参数需要重点关注。contamination 代表数据集中异常比例的预估,如果你已经按 2.3 统计过标签分布,直接把该值设为异常占比,比如 10% 就写 0.1;n_estimators 是树的数量,默认 100 够用,但数据量大或者特征维度多时可以提到 200 让分数更稳定;max_samples 是每棵树的随机采样量,默认 256,对温度信号这种特征维度只有个位数的场景,256 已经足够,再大只是徒增计算时间。
from sklearn.ensemble import IsolationForest iso = IsolationForest( contamination=0.1, n_estimators=200, max_samples=256, random_state=42 ) iso.fit(X_train_s) pred_val = iso.predict(X_val_s) # 1 为正常,-1 为异常孤立森林的输出是 predict 的 1 和 -1,但实际调参时我更建议用 decision_function 得到连续的异常分数,这样可以通过分数分位数调整阈值,而不需要每次改 contamination 重训。另外要特别提示:孤立森林对特征尺度敏感,前面 StandardScaler 的处理绝非可选项,必须做。
4.2 Autoencoder:只拿正常样本训练是前提
Autoencoder 是温度信号异常检测里非常实用的方案。它的核心思想是:只用正常数据训练一个压缩还原网络,模型学习的是正常温度模式的潜在表达。当输入异常样本时,因为模型没见过这种模式,还原误差会显著增大。在温度信号场景下,Autoencoder 对持续漂移和周期破裂的感知能力通常优于孤立森林。
关键前提是训练数据只能包含正常样本。如果有监督分类可以用正常+异常一起训练,Autoencoder 这条路一旦混入异常样本,模型会学到异常模式,等于自废武功。
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense X_train_normal = X_train_s[np.array(y_train) == 0] ae = Sequential([ Dense(16, activation='relu', input_dim=X_train_s.shape[1]), Dense(8, activation='relu'), Dense(16, activation='relu'), Dense(X_train_s.shape[1], activation='linear') ]) ae.compile(optimizer='adam', loss='mse') ae.fit(X_train_normal, X_train_normal, epochs=40, batch_size=32, validation_split=0.1, verbose=0) recon_error_val = np.mean((ae.predict(X_val_s) - X_val_s) ** 2, axis=1)网络结构从 7 维特征压缩到 8 维再还原回 7 维,瓶颈设计得比较宽,是因为温度信号特征本身不多,压得太狠会丢失正常模式的细节。loss 用的是均方误差,逐特征计算还原差异然后取平均。如果发现训练后的重构误差分布没有出现预期的双峰,一般是训练集里混入了异常样本,回 2.3 重新检查标签过滤逻辑。
4.3 阈值标定:用分位数而不是肉眼
模型输出的是连续分数或误差值,最后都要转换成异常判定,阈值怎么定决定了整个系统的误报率。最忌讳的做法是拿验证集画误差分布图,然后肉眼选一个「看起来分开」的临界点,这种做法换个数据段就不稳定。
做法是这样的:如果验证集中有正常样本,取正常样本重构误差的 95 分位作为阈值,意味着正常样本里只有 5% 会被误判为异常。如果更看重召回率,压到 90 分位;如果现场对误报零容忍,提到 98 甚至 99 分位。
from sklearn.metrics import classification_report normal_val_mask = np.array(y_val) == 0 threshold_ae = np.percentile(recon_error_val[normal_val_mask], 95) pred_ae = (recon_error_val > threshold_ae).astype(int) print(classification_report(y_val, pred_ae))这里分位数选择的本质是在误报率和漏报率之间做权衡。温度信号异常检测的特殊之处在于,漏报的直接代价可能是设备损坏,误报的代价则是现场人员会关掉报警。一般我建议先从 95 分位起步,然后根据 classification_report 里的 recall 和 precision 做一次调整,优先保证异常样本的召回率不低于 90%。孤立森林也可以用同样的思路,对 decision_function 的输出取分位数,而不是直接用默认的 0 分界。
5. 避坑与常见问题:从解压到上线的五个高频坑
5.1 解压 rar 后 CSV 列名乱码或路径找不到
现象:在 Windows 上解压完成后,用 pandas 读取 CSV,发现列名是乱码,或者文件路径报错找不到文件。
原因:这份数据集里的文件名和列名是中文,且编码为 GBK,而 pandas 默认按 UTF-8 读取;另一个原因是解压时用了系统自带的压缩文件夹向导,中文路径被转义,导致后续脚本路径不匹配。
解决:解压用 7-Zip 这类专业工具,右键解压到纯英文路径下,不要用系统自带向导。读取时先指定 encoding='utf-8-sig',如果仍然乱码,改成 encoding='gbk'。读进来以后立刻把列名改写成英文,比如 temp_value、label,后面所有处理代码都用英文列名,避免每个脚本都要处理编码问题。
5.2 多传感器采样率不一致导致时间轴错位
现象:把多个传感器文件合并成一张表后,温度突变点对不上,同一时刻两个传感器的读数看起来像是隔了一段时间。
原因:不同传感器的落库周期不一样,一个 1 Hz 采样,一个 0.5 Hz 采样。直接按时间戳做内连接会丢失采样点,做外连接会出现大量 NaN,合并出的表没法直接用。
解决:统一先按秒重采样到同一个时间基准,再合并或拼接。pandas 的 resample 可以做到,空值用插值填充而不是直接删除,温度信号是慢变量,线性插值完全够用:
df_a = df_a.set_index('timestamp').resample('1S').interpolate() df_b = df_b.set_index('timestamp').resample('1S').interpolate() merged = df_a.join(df_b, how='inner')5.3 随机切分训练集导致验证分数虚高
现象:AUC 跑到 0.99 以上,分类报告里各项指标都接近完美,上线后效果完全不是那么回事。
原因:训练集和验证集是用随机洗牌切分的。温度信号相邻窗口高度相关,甚至直接有重叠,随机切分意味着验证集里混进了训练集窗口的邻居样本,模型相当于看到过答案。这是时间序列建模里最容易翻车的一步。
解决:改用时间顺序切分或者 sklearn 的 TimeSeriesSplit。不要用默认的 train_test_split,严格按时间索引划分,保证训练集只包含验证集之前的数据:
from sklearn.model_selection import TimeSeriesSplit tscv = TimeSeriesSplit(n_splits=3) for train_idx, val_idx in tscv.split(X): pass5.4 标准化泄漏:验证集分数好得反常
现象:验证集上的表现比训练集还要好,模型部署到现场后输入特征的分布对不上,分数断崖式下跌。
原因:StandardScaler 在数据划分之前就对全量数据做了 fit_transform,均值和标准差里混入了验证集的信息。验证集的标准化是「看着答案」完成的,分数的参考价值归零。
解决:严格分离 fit 和 transform。只 fit 训练集,验证集和测试集都用训练集的均值与标准差去转换,这一点在任何传感器数据处理里都是红线,踩过一次就再也不会忘。如果发现之前已经对全量数据做了标准化,不要试图修复,重新从原始特征开始走一遍流程。
5.5 告警阈值太灵敏导致误报刷屏
现象:模型上线后一天报几十次异常,现场工程师直接把告警功能关了。
原因:阈值定得太低,比如取正常样本 90 分位,温度信号本身的正常波动就会频繁越过阈值。更常见的情况是单点判定:只要当前重构误差超了阈值就立即告警,忽略温度信号的短时噪声。
解决:把单点判定改成连续 N 点超阈值才算真正报警。温度信号异常是持续行为,单点噪声不构成故障:
def is_alarm(recon_errors, threshold, n=5): count = 0 for err in recon_errors: if err > threshold: count += 1 if count >= n: return True else: count = 0 return False这里 n 的取值取决于采样频率和现场的容忍度,一般 1 Hz 数据下取 5 到 10 点,对应 5 到 10 秒的持续异常才触发,误报率会明显下降。
6. 部署前最后一步:回测矩阵与在线预警的延迟控制
6.1 回测矩阵:三个指标比 AUC 更有用
模型评估阶段,AUC 只是一个总体区分度的参考值,现场真正要看的三个数字是:正常误报率、异常检出率和平均检出延迟。一张回测矩阵表能把模型的真实水平说清楚。
| 指标 | 含义 | 我常用的期望范围 |
|---|---|---|
| 正常误报率 | 正常样本中被报为异常的比例 | < 5% |
| 异常检出率 | 异常样本中真正被识别出来的比例 | > 90% |
| 平均检出延迟 | 异常开始到首次触发告警的时间差 | 小于 1 个滑窗周期 |
延迟是温度信号检测里最容易被忽略的。有的模型把异常片段内部的大部分点都识别出来了,但异常开始的头几十秒没有触发告警,回测只能看到检出率数字很漂亮。这类故障如果处理慢,损坏已经造成了。我会要求每个候选模型额外输出最早检出点的位置,对比异常标签的实际起始时间,把这个数字写进评估结论里。
6.2 在线预警:滑动窗口斜率告警的实现
离线模型跑通之后,上线阶段建议做一层轻量级的实时守卫,不必每次调用深度模型,而是用斜率做快速预筛。温度慢漂移的早期特征是窗口内斜率持续为正,这个判断逻辑可以拆成服务:
from collections import deque def make_alert_slope(window_points=60, slope_threshold=0.05): buf = deque(maxlen=window_points) def check(value): buf.append(value) if len(buf) < window_points: return False x = np.arange(len(buf)) slope = np.polyfit(x, np.array(buf), 1)[0] return slope > slope_threshold return checkwindow_points 取 60 对应 1 Hz 采样下的 1 分钟观察窗口;slope_threshold 先用正常工况数据跑一周,取正常窗口斜率的 95 分位再乘 1.5 余量作为初值。部署第一周告警日志只记录不通知,用真实的斜率分布重新校准阈值。
我有一次在注塑机项目里,把 Autoencoder 阈值调到 98 分位,结果伺服阀卡滞的温升捕捉到了,但真正烧轴的慢漂移没有报——因为正常波动里有相当一部分样本落在了 98 分位以上,模型被整体噪声干扰了。从那以后我每次部署前都强制走一遍回测矩阵,并且要求每个模型必须给出「最早可检出时间」这个数字才允许上线。阈值不是越紧越好,是要拿现场停机代价去换。希望帮到你。
本文还有配套的精品资源,点击获取