零基础也能看懂:用PyTorch从零搭建Transformer工业传感器故障检测系统
面向读者:长期从事传统工业软件(PLC/SCADA/DCS)的工程师,希望理解AI模型在工业场景中的实际意义和应用方式。
阅读收获:理解Transformer模型的核心原理,能读懂、运行、修改一段完整的PyTorch故障检测代码。
环境要求:Python 3.8+,PyTorch 2.0+,Matplotlib。不需要GPU,普通笔记本即可运行。
一、写给传统工程师的AI入门
1.1 你已经懂了50%的AI
如果你做过以下任何一件事情,你已经理解了AI的核心思想:
- 在PLC里写
IF 传感器电压 > 4.0V THEN 触发报警 - 在SCADA趋势图上画一条红线,超出就报警
- 用振动分析软件设定阈值,超限就停机
这些做法的本质完全一样:输入数据 → 按规则判断 → 输出结论。
AI做的事情也一样。唯一的区别是:规则怎么来。
| 方式 | 规则来源 | 举例 |
|---|---|---|
| 传统方法 | 工程师根据自己的经验手动编写 | if voltage > 4.0: alarm() |
| 统计方法 | 从历史数据中计算统计量 | if value > mean + 3*sigma: alarm() |
| AI方法 | 机器从大量标注数据中自动学习 | 模型看了9000个案例后自己总结出判别规则 |
1.2 一个类比帮你快速理解
传统工程师像一位经验丰富的老师傅:他见过几百台设备的运行数据,知道"电压超过4V大概率是故障",于是把这个经验写成规则。
AI模型像一个勤奋的新手徒弟:他一开始什么都不懂(参数全是随机数),但他愿意反复看9000个历史案例(训练数据),每次做错就记下教训(反向传播),看多了就总结出了自己的判别规则。
徒弟和老师傅的关键区别:
- 老师傅的规则是显式的:你可以直接看代码理解为什么报故障
- 徒弟的规则是隐式的:判断依据分散在17000多个参数中,无法直接阅读
但徒弟有一个优势:他能发现老师傅看不到的复杂模式——比如"第5个点和第12个点同时偏高,即使都没超过4V,也可能是故障"。这种多变量关联模式,人工规则极难穷举。
1.3 本文要做什么
我们将用一段纯PyTorch代码,从零构建一个基于Transformer的传感器故障检测模型。代码覆盖完整流程:
不需要GPU,不需要云服务器,不需要任何深度学习框架以外的依赖。 你的办公笔记本就能跑通全部代码。
二、代码要解决什么问题
2.1 工业场景
一台工业电机上安装了电压传感器,每秒采集16个连续读数,形成一个时序序列:
[1.8V, 2.1V, 2.5V, 1.9V, 2.2V, 1.5V, 2.8V, 2.3V,
1.9V, 2.0V, 1.4V, 2.6V, 1.7V, 2.2V, 1.3V, 2.4V]
需要判断:设备运行正常(标签0)还是存在故障异常(标签1)。
2.2 传统方法的困境
固定阈值法 if voltage > 4.0: alarm() 有三个致命缺陷:
- 无法识别渐变故障:如果电压从2V缓慢升到3.8V(绝缘老化),每个时刻都没超阈值,但整体趋势已经异常
- 误报临界工况:设备满载运行时电压3.5V是正常的,但接近阈值容易误报
- 忽略时序关联:只看单个数值,不考虑"哪些位置同时异常"
统计方法(均值±3倍标准差)有改进,但仍假设数据服从高斯分布,且只能检测"偏离统计分布"的单点异常,无法捕获多点协同的时序模式。
2.3 AI的思路
不写规则,直接给模型看大量已标注的历史案例。模型从案例中自动学习判别模式:
- 不仅看单点数值大小,还看16个时间步之间的关联关系
- 能识别"第5位突增+第12位突增"这种需要多变量协同才能发现的故障模式
- 能识别"后半段持续升高"这种渐变趋势,无需任何人工阈值
三、数据从哪来——模拟真实传感器信号
3.1 真实传感器信号的构成
真实工业传感器的读数不是纯随机数,而是多个物理因素叠加的结果:
信号 = 基线电压 + 设备老化漂移 + 工况周期波动 + 电磁随机噪声
我们的代码用数学公式模拟了这四个成分:
| 成分 | 代码实现 | 物理含义 |
|---|---|---|
| 基线电压 | baseline = 2.0 + 0.4 * randn() | 不同设备个体的基准差异 |
| 线性漂移 | trend = 0.3 * (t - 0.5) | 设备缓慢老化导致的趋势变化 |
| 周期波动 | periodic = 0.25 * sin(2π*t*2) | 电机旋转等周期性工况 |
| 高斯噪声 | noise = randn() * 0.12 | 电磁干扰等随机误差 |
关键设计:用高斯噪声(randn)而非均匀噪声(uniform),因为真实传感器的随机误差更接近正态分布。
3.2 六类工况波形
代码生成6类波形(3类正常+3类故障),覆盖多种实际工况:
| 波形类型 | 标签 | 对应真实工况 | 设计目的 |
|---|---|---|---|
| 标准正常 | 0 | 设备正常运行 | 基准参照 |
| 临界高值正常 | 0 | 满载高负载运行 | 让模型不把"高值"等同于"故障" |
| 低幅波动正常 | 0 | 低功耗待机模式 | 让模型适应不同工况的基线差异 |
| 标准故障 | 1 | 瞬时短路/电磁冲击 | 正常波形+随机3个点位突发尖峰 |
| 弱故障 | 1 | 早期异常征兆 | 仅1个点位轻微超标,增加检测难度 |
| 渐变故障 | 1 | 设备过热/绝缘老化 | 后半段数值持续升高,无法用单点阈值检测 |
每类波形对应的核心代码:
# 标准正常:基线 + 趋势 + 周期 + 噪声
baseline = 2.0 + 0.4 * torch.randn(1).item() # 设备个体差异
trend = 0.3 * (t - 0.5) # 缓慢漂移
periodic = 0.25 * torch.sin(2 * math.pi * t * 2) # 工况周期
noise = torch.randn(SEQ_LEN) * 0.12 # 电磁干扰
normal_sequence = baseline + trend + periodic + noise
# 标准故障:在正常波形基础上,随机3个位置叠加突发尖峰
fault_pos = torch.randint(0, SEQ_LEN, (3,))
fault_seq[fault_pos] += torch.FloatTensor(3).uniform_(2.5, 4.0)
# 渐变故障:后半段叠加线性上升
gradual_fault[SEQ_LEN // 2:] += torch.linspace(1.0, 2.5, SEQ_LEN // 2)
3.3 波形可视化
下面是代码自动生成的6类波形示意图:

可以看到:正常波形(青色)在阈值线以下波动,故障波形(红色)出现了明显的尖峰或持续上升趋势。但临界高值正常虽然整体偏高却没有超过4V——这正是模型需要学习的"边界判别能力"。
3.4 数据集生成结果
运行代码后,控制台输出:
【数据集初始化中...】基础样本组数 1500 组(每组生成6条样本)
数据集生成进度:已完成 100/1500 组基础样本配套波形
...
数据集生成进度:已完成 1500/1500 组基础样本配套波形
【数据集生成完成】总样本数量:9000 条
包含6类波形:标准正常、标准故障、弱故障、渐变故障、临界高值正常、低幅波动正常
信号构成:基线偏移 + 线性趋势 + 周期正弦波 + 高斯随机噪声
四、Transformer模型——把电压变成"判断"
这是本文的核心章节。Transformer模型把16个浮点电压值,经过5层变换,最终输出"正常/故障"的判定。
4.1 整体数据流
4.2 第1步:线性投影——为什么不用Embedding?
self.input_proj = nn.Linear(1, EMBED_DIM) # 将每个标量映射为32维向量
问题:PyTorch处理文本时通常用 nn.Embedding,把每个词当作一个离散ID查表。如果对传感器数值也这样做——把2.3V查表为ID=23,把2.4V查表为ID=24——这两个几乎等价的电压值会被当作两个完全不同的"单词"。
解决方案:用 nn.Linear(1, 32) 做线性投影。Linear是连续映射——输入值相近,输出向量也相近。2.3V和2.4V的投影向量会非常相似,这符合传感器数据的物理本质。
类比:
Embedding像查字典——离散查表,2.3和2.4是两个完全不同的词条Linear像温度计刻度——连续映射,30°C和31°C的读数非常接近
代码中通过 unsqueeze(-1) 把一维标量扩展为特征维度,再送入Linear:
x = x.unsqueeze(-1) # [batch, 16] → [batch, 16, 1]
x = self.input_proj(x) # [batch, 16, 1] → [batch, 16, 32]
4.3 第2步:位置编码——为什么必须告诉模型"时间"?
Transformer的自注意力机制有一个关键特性:位置无关。把16个电压值打乱顺序再输入,输出不会变。但传感器数据的核心价值恰恰在于时序顺序——第3个采样点异常和第13个采样点异常,含义完全不同。
正弦位置编码为每个时间步注入一个唯一的"位置指纹":
pe[:, 0::2] = torch.sin(position * div_term) # 偶数维度用sin
pe[:, 1::2] = torch.cos(position * div_term) # 奇数维度用cos
数学直觉:不同频率的三角函数组合,使得每个位置都有一个唯一的编码向量。而且三角函数的相位差天然编码了"距离信息"——模型能隐式学到"第5步和第8步相隔3步"这个相对位置关系。
# pe形状 [1, max_len, d_model] 与 x [batch, seq_len, d_model] 相加
# 通过Broadcasting自动扩展到batch_size
x = x + self.pe[:, :x.size(1), :]
4.4 第3步:Transformer编码器——模型"在看什么"
encoder_layer = nn.TransformerEncoderLayer(
d_model=EMBED_DIM, nhead=N_HEAD,
dim_feedforward=FFN_HIDDEN,
batch_first=True, dropout=DROPOUT
)
self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=LAYERS)
这是模型最核心的部分。多头自注意力的直观理解:
想象16个传感器读数排成一排,每个读数都要"看"其他所有读数,判断"谁和自己有关"。注意力机制为每一对位置计算一个相关性权重。
4个注意力头可以同时关注不同的模式维度:
| 头 | 关注的模式 | 类比 |
|---|---|---|
| 头1 | 局部突变——哪个位置突然偏离了周围值 | “有没有突然跳一下?” |
| 头2 | 全局趋势——整体是在上升还是下降 | “整体趋势正常吗?” |
| 头3 | 周期性异常——周期波动是否被打破 | “工况循环有干扰吗?” |
| 头4 | 多点关联——多个异常位置是否同时出现 | “好几个位置都不对劲?” |
"故障共识"机制:当输入是故障样本时(比如第5、9、13位有尖峰),注意力矩阵会呈现这样的模式——异常位置之间互相建立强注意力连接。模型学到的规则是:单个高值可能是噪声,但多个高值同时出现极大概率是故障。
每层Transformer内部还包含一个前馈网络(FFN):对注意力提取的特征做非线性变换,把线性不可分的模式映射到线性可分空间——相当于在特征空间中"扭曲"数据分布,让分类边界更容易划定。
2层堆叠的设计:第一层提取局部模式(单点突变),第二层在第一层的基础上提取更全局的模式(多点关联)。
4.4+ 模型内部状态可视化
下面这张图展示了模型收敛后,面对故障样本和正常样本时,内部各层的状态差异:

故障样本(左列):
- 输入层:第5、9、13位出现明显尖峰(红色柱)
- 注意力层:异常位置之间形成强注意力连接(深紫色区域),实现"故障共识"
- 特征激活:"高电压检测"和"突变检测"维度被显著激活(>0.6)
- 分类输出:故障得分2.87远高于正常得分0.23 → 判定为故障
正常样本(右列):
- 输入层:数值平稳波动,无突变
- 注意力层:注意力均匀分布,无明显聚焦点
- 特征激活:"中电压基准"和"周期性波动"维度主导
- 分类输出:正常得分高于故障得分 → 判定为正常
4.5 第4步+第5步:平均池化 + 分类输出
# 平均池化:将16个时间步的特征取平均,得到全局向量
global_feature = torch.mean(seq_feature, dim=1) # [batch, 16, 32] → [batch, 32]
# 分类输出:32维全局向量 → 正常/故障两个得分
self.classify_head = nn.Sequential(
nn.Dropout(p=DROPOUT),
nn.Linear(EMBED_DIM, 2)
)
平均池化的设计动机:不依赖特定位置。无论故障发生在序列的开头、中间还是结尾,都能被等权重地捕捉到。
输出2维得分(logits),通过 torch.argmax 取最高分索引作为预测标签。
重要细节:模型输出的是原始得分(logits),不需要手动加softmax。因为 CrossEntropyLoss 内部已经自动包含了 softmax + 取对数操作。
4.6 模型初始化输出
【AI模型网络组件初始化】
组件1:线性投影层 Linear(1→32) 初始化完成
组件2:正弦位置编码 PositionalEncoding 初始化完成
组件3:2层Transformer编码器(4头注意力,FFN=64)初始化完成
组件4:二分类输出全连接层(含Dropout)初始化完成
【AI模型所有组件组装完毕】
模型总可训练参数量:17,218 个
4.7 数据集三分集划分
代码将9000条数据按 70/15/15 划分为训练集、验证集、测试集:
为什么不直接用二分集(训练+验证)? 因为验证集既用于早停又用于最终评估时,模型已经"看过"验证集的表现并据此做了决策——这就引入了信息泄露。独立的测试集给出的是模型在完全未见数据上的真实泛化能力。
五、训练——模型如何从"什么都不懂"到"判断准确"
5.1 初始状态:17,218个随机数
模型创建时,PyTorch用随机数填充全部17,218个参数。此时的模型对"正常"和"故障"没有任何概念,输出接近随机猜测——准确率约50%(二分类瞎猜的理论值)。
5.2 标准训练五步流程
每轮训练中,6300条训练数据被分成262个批次(每批24条),每个批次执行5步标准流程:
每一步的含义:
optimizer.zero_grad() # 1. 清零上一批次残留的梯度
batch_pred_logits = model(batch_seq) # 2. 24条样本送入模型,输出24×2得分
batch_loss = loss_function(...) # 3. 对比预测和真实标签,计算差距
batch_loss.backward() # 4. 从输出层往输入层逐层求导
optimizer.step() # 5. 按梯度方向微调17,218个参数
损失值(loss)的含义:CrossEntropyLoss计算预测与真实的差距。随机猜测的理论损失约 ln(2) ≈ 0.693,损失越小表示预测越准。
关键:model.train() 和 model.eval()
model.train() # 训练模式:启用Dropout随机屏蔽,保证正则效果
# ... 训练代码 ...
model.eval() # 评估模式:关闭Dropout,保证推理结果确定性
with torch.no_grad(): # 关闭梯度追踪,节省显存,加速约30%~50%
# ... 验证代码 ...
5.2+ 训练循环全景图
下面这张图展示了从随机初始化到收敛保存的完整训练流程:

外层循环(Epoch):每轮遍历全部6300条训练数据,分为262个批次。
内层循环(Batch):每个批次执行5步标准训练流程,更新17,218个参数。
验证阶段:每轮结束后用验证集评估,计算Accuracy/Precision/Recall/F1四项指标。
早停决策:验证集连续5轮无提升则终止训练,保留历史上表现最好的模型权重。
5.3 验证集评估——四项指标
每轮训练结束后,用验证集(模型没见过的数据)计算完整评估指标:
| 指标 | 公式 | 含义 | 工业关注点 |
|---|---|---|---|
| Accuracy | (TP+TN)/总数 | 整体正确率 | 总体概览 |
| Precision | TP/(TP+FP) | 预测为故障中真正故障的比例 | 关注误报率——频繁误报导致"狼来了" |
| Recall | TP/(TP+FN) | 真实故障中被正确检出的比例 | 关注漏报率——漏报可能导致安全事故 |
| F1 | 2×P×R/(P+R) | Precision和Recall的调和平均 | 综合评价 |
混淆矩阵四个基础量:
TP(真阳性):预测故障,实际故障 —— 正确检出
FP(假阳性):预测故障,实际正常 —— 误报
FN(假阴性):预测正常,实际故障 —— 漏报
TN(真阴性):预测正常,实际正常 —— 正确判定
工业场景中Recall尤其重要:漏报一个故障可能导致设备损坏甚至安全事故。
5.4 早停策略——防止过拟合的"刹车"
类比:做模拟试卷时,如果连续5次成绩没有进步,说明你已经在"刷题但没学到新东西"了——此时应该停止刷题,回顾之前状态最好的时候。
5.5 执行日志解读
以下是程序的实际运行输出(为节约篇幅,每轮只展示关键批次):
第1轮:模型从随机状态开始学习
===== 进入第 1/40 轮完整训练 =====
[轮次1 | 批次 0/262] 批次损失=0.7123 | 批次准确率=41.67%
[轮次1 | 批次30/262] 批次损失=0.6021 | 批次准确率=70.83%
[轮次1 | 批次90/262] 批次损失=0.2107 | 批次准确率=91.67%
[轮次1 | 批次120/262] 批次损失=0.0404 | 批次准确率=100.00%
[轮次1 | 批次240/262] 批次损失=0.0094 | 批次准确率=100.00%
★ 验证集准确率创新高!当前最佳:100.00%(已保存模型快照)
---------- 第1轮 训练+验证完整汇总报告 ----------
【训练集】平均损失:0.2080 | 准确率:90.10%
【验证集】平均损失:0.0019 | 准确率:100.00% | Precision:100.00% | Recall:100.00% | F1:100.00%
【混淆矩阵】TP=685 FP=0 FN=0 TN=665
观察关键变化:
- 批次0:损失0.7123(接近随机),准确率41.67%——模型还没学到东西
- 批次30:损失降到0.6,准确率跳到70.83%——模型开始学到"高值=故障"这个粗略模式
- 批次90:准确率91.67%——模型开始区分"临界正常"和"真实故障"
- 批次120以后:准确率稳定在100%——模型已经掌握了主要判别模式
第2~6轮:模型持续优化,最终触发早停
===== 进入第 2/40 轮完整训练 =====
[轮次2 | 批次 0/262] 批次损失=0.0062 | 批次准确率=100.00%
验证集无提升,早停等待计数:1/5
---------- 第2轮汇总 ----------
【训练集】平均损失:0.0166 | 准确率:99.59%
【验证集】平均损失:0.0008 | 准确率:100.00%
...(第3、4、5轮类似,验证集持续100%但无提升)...
===== 进入第 6/40 轮完整训练 =====
验证集无提升,早停等待计数:5/5
===== 触发早停!验证集连续5轮无提升,停止训练 =====
【训练流程全部结束】
全程最佳验证集准确率:100.00%
5.6 防过拟合策略总结
代码在多个层面构建了完整的防过拟合防线:
| 层面 | 策略 | 作用 |
|---|---|---|
| 数据层 | 6类波形+高斯噪声 | 让模型学判别规则,而非记忆固定数字 |
| 数据层 | 训练/验证/测试三分集 | 测试集全程封印,保证评估无偏 |
| 模型层 | Dropout 0.25 | 训练时随机屏蔽25%神经元 |
| 模型层 | L2权重衰减 | 约束参数不要过大 |
| 训练层 | 早停(5轮) | 验证集无提升时提前终止 |
六、评估与推理
6.1 测试集最终结果
训练结束后,加载最佳模型权重,在全程完全没见过的1350条测试数据上评估:
========== 测试集最终评估(模型全程未见过的数据) ==========
【测试集最终结果】
平均损失:0.0001
准确率 Accuracy:100.00%
精确率 Precision:100.00%(预测故障中真正故障的比例)
召回率 Recall:100.00%(真实故障中被正确检出的比例)
F1 分数:100.00%(Precision和Recall的调和平均)
混淆矩阵:TP=673 FP=0 FN=0 TN=677
测试集与验证集的区别:验证集参与了早停决策(模型"间接看过"),测试集全程封印。测试集的100%准确率才是模型真实泛化能力的无偏估计。
6.2 训练曲线可视化
代码自动生成4张训练曲线图,直观展示模型的学习过程:

图表包含4个子图:
| 子图 | 横轴 | 纵轴 | 看什么 |
|---|---|---|---|
| 左上:损失曲线 | 训练轮次 | 损失值 | 训练损失↓验证损失↓ = 正常收敛;训练↓验证↑ = 过拟合 |
| 右上:准确率曲线 | 训练轮次 | 准确率 | 两者差距越大 = 过拟合越严重 |
| 左下:P/R/F1曲线 | 训练轮次 | 分数 | 观察精确率和召回率的权衡变化 |
| 右下:混淆矩阵 | 类别 | 样本数 | FP多=误报多,FN多=漏报多 |
6.3 交互式推理演示
训练完成后进入交互模式,输入16个逗号分隔的电压值即可实时检测:
请输入16个传感器电压数值:1.8,2.1,2.5,1.9,2.2,1.5,2.8,2.3,1.9,2.0,1.4,2.6,1.7,2.2,1.3,2.4
------------ AI故障检测详细报告 ------------
输入传感器完整序列:[1.8, 2.1, 2.5, 1.9, 2.2, 1.5, 2.8, 2.3, 1.9, 2.0, 1.4, 2.6, 1.7, 2.2, 1.3, 2.4]
AI对【设备正常】打分:3.2105
AI对【设备故障】打分:-3.0762
最终判定:【设备运行状态良好,各项指标正常】
--------------------------------------------
请输入16个传感器电压数值:1.1,2.3,5.2,2.0,1.8,4.6,2.1,2.5,1.7,2.2,1.5,2.7,4.9,2.3,1.6,2.1
------------ AI故障检测详细报告 ------------
输入传感器完整序列:[1.1, 2.3, 5.2, 2.0, 1.8, 4.6, 2.1, 2.5, 1.7, 2.2, 1.5, 2.7, 4.9, 2.3, 1.6, 2.1]
AI对【设备正常】打分:-8.5921
AI对【设备故障】打分:10.2910
最终判定:【设备存在异常,判定为故障,建议停机检修!】
--------------------------------------------
七、AI vs 传统方法:深度对比
7.1 三种方法的核心差异

三种工业故障检测方案在五个维度上的直观对比:
| 维度 | 固定阈值法 | 统计方法(3-sigma) | Transformer AI |
|---|---|---|---|
| 决策依据 | 单点数值是否超限 | 单点是否偏离统计分布 | 16个时间步的全局关联模式 |
| 时序建模 | 无 | 无 | 多头自注意力捕获任意两点间关系 |
| 模式类型 | 仅"数值过大" | “统计异常” | 尖峰、渐变、周期性异常、复合模式 |
| 渐变故障 | 无法检测 | 缓慢漂移可检 | 序列趋势建模 |
| 可解释性 | 极高(一条规则) | 高(均值和标准差) | 中(注意力权重可部分解释) |
| 工况适应 | 需人工重写规则 | 需重新计算统计量 | 用新数据重新训练即可 |
| 维护成本 | 规则频繁调整 | 窗口参数需调 | 需标注数据,但一劳永逸 |
7.2 什么时候用传统方法就够了
- 规则极简:简单的过温保护,一个
if语句就够 - 工况固定:设备永远在相同条件下运行,数据分布不随时间变化
- 成本敏感:没有算力资源,也不需要处理复杂模式
7.3 什么时候必须上AI
- 故障模式复杂:渐变老化、多点关联、工况切换等无法用单一阈值描述
- 工况多变:设备在不同负载、不同季节下运行,基线持续漂移
- 需要提前预警:想在故障发生前就识别"异常趋势",而不是等超限了才报警
7.4 给传统工程师的转型建议
- 不需要从数学博士开始。先理解"输入→模型→输出"这个黑盒,把Transformer当作一个"更聪明的规则引擎"
- 用合成数据验证想法。先用代码模拟数据跑通全流程,再接入真实传感器数据
- 从"规则辅助AI"过渡到"AI替代规则"。先让AI和传统规则并行运行,对比两者结果,逐步建立信任
八、距离工业部署还有多远
8.1 当前代码的定位
技术可行性验证 + 团队学习参考
它能证明"用Transformer做传感器故障检测是可行的",也能帮助团队理解自注意力在时序数据上的工作原理。但不是可以直接上产线的工业系统。
8.2 缺少的工业级能力
| 维度 | 当前状态 | 工业要求 |
|---|---|---|
| 数据来源 | 纯代码合成,6类固定模式 | 真实产线传感器采集,含未知异常 |
| 输入规模 | 固定16个时间步 | 每秒数百到数千采样点,连续流式输入 |
| 模型部署 | Python交互式命令行 | 模型服务化(REST/gRPC)、边缘设备推理 |
| 实时性 | 无延迟要求 | 毫秒级响应,确定性延迟上限 |
| 可靠性 | 训练完跑一次 | 7×24小时持续运行,故障自愈 |
| 数据漂移 | 无监测 | 自动检测分布漂移,触发重训练 |
| 可解释性 | 仅输出分数 | 需知道"为什么报故障"(注意力可视化、SHAP值) |
8.3 从原型到产线的改进路径
附录A:环境配置
# 1. 创建虚拟环境(推荐)
python -m venv venv
# 2. 激活虚拟环境
# Windows:
venv\Scripts\activate
# Linux/Mac:
source venv/bin/activate
# 3. 安装依赖
pip install torch matplotlib
# 4. 运行
python main.py
硬件要求:普通办公笔记本即可,不需要GPU。在CPU上完整训练约需1~3分钟。
附录B:超参数配置清单
| 参数 | 值 | 含义 |
|---|---|---|
| SEQ_LEN | 16 | 每次采集16个传感器读数 |
| EMBED_DIM | 32 | 投影向量维度 |
| N_HEAD | 4 | 多头注意力头数 |
| FFN_HIDDEN | 64 | 前馈网络隐藏层大小 |
| LAYERS | 2 | Transformer编码器层数 |
| BATCH_SIZE | 24 | 每批训练样本数 |
| EPOCHS | 40 | 最大训练轮数 |
| LR | 0.0005 | 学习率 |
| DROPOUT | 0.25 | Dropout概率 |
| SAMPLE_NUM_BASE | 1500 | 基础样本组数(总计9000条) |
| PATIENT_EARLY_STOP | 5 | 早停耐心值 |
附录C:完整代码
代码约700行,带详细中文注释。
# ===================== 程序功能说明 =====================
# 场景:工业设备传感器时序故障检测AI
# 输入:连续16个设备电压传感器数值序列
# 输出:0=设备运行正常 1=设备存在故障异常
# 模型:2层Transformer + 正弦位置编码 + 线性投影输入层
# 核心设计:
# 1. 用nn.Linear处理连续浮点数值,保留数值的大小关系语义
# 2. 正弦位置编码为序列注入时序位置信息,让模型区分"开头异常"和"尾部异常"
# 3. 合成数据模拟真实传感器特征:基线漂移、周期性波动、线性趋势、高斯噪声
# 4. 训练/验证/测试 三分集划分,测试集全程不参与训练和调参,保证评估无偏
# 5. 早停策略防止过拟合,Dropout + L2权重衰减双重正则约束
# 6. Precision / Recall / F1 / 混淆矩阵 全维度评估指标
# 7. 模型自动保存最佳权重,训练曲线可视化
# 依赖:PyTorch + Matplotlib
# ========================================================
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader, random_split
import math
# import os # 当前版本未使用,预留用于后续模型路径管理、环境变量读取等扩展
# ===================== 1. 硬件设备与全局超参数配置区 =====================
# 自动判断运行硬件:有NVIDIA显卡用GPU加速,无显卡自动切换CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("=" * 80)
print("【设备故障检测AI程序 - 启动自检日志】")
print(f"程序运行硬件设备:{device}")
print(f"当前PyTorch框架版本:{torch.__version__}")
if torch.cuda.is_available():
print(f"检测到NVIDIA显卡,显卡型号:{torch.cuda.get_device_name(0)}")
else:
print("未检测到NVIDIA独立显卡,本次全程使用CPU训练模型")
print("=" * 80)
# ---------------------- 模型超参数(集中顶部,方便调参) ----------------------
SEQ_LEN = 16 # 一次采集16个数据点,对应16个连续时间步的传感器读数
EMBED_DIM = 32 # 投影向量维度:每个传感器数值被映射为32维特征向量
N_HEAD = 4 # 多头注意力头数:4个头可同时关注不同尺度的时间模式
FFN_HIDDEN = 64 # 前馈网络隐藏层神经元数,负责非线性特征变换
LAYERS = 2 # Transformer编码器层数,2层可提取更抽象的时序特征
BATCH_SIZE = 24 # 每次训练送入模型的样本批次数
EPOCHS = 40 # 最大训练轮次上限,早停机制通常会在之前终止
LR = 0.0005 # 学习率:控制每步参数更新的幅度,偏小使训练更稳定
SAMPLE_NUM_BASE = 1500 # 基础样本组数(每组生成6条样本,总计9000条)
PATIENT_EARLY_STOP = 5 # 早停耐心值:验证集连续5轮无提升则提前终止训练
DROPOUT = 0.25 # Dropout概率:训练时随机屏蔽25%的神经元,防止过拟合
MODEL_SAVE_PATH = "sensor_model_best.pth" # 训练好的模型权重保存路径
# 打印全部超参数配置,方便对照和复现
print("\n【模型全部超参数配置清单】")
print(f"传感器序列固定长度 SEQ_LEN = {SEQ_LEN}")
print(f"投影向量维度 EMBED_DIM = {EMBED_DIM}")
print(f"多头注意力头数 N_HEAD = {N_HEAD}")
print(f"前馈网络隐藏层 FFN_HIDDEN = {FFN_HIDDEN}")
print(f"Transformer编码器层数 LAYERS = {LAYERS}")
print(f"单次训练批次大小 BATCH_SIZE = {BATCH_SIZE}")
print(f"最大训练轮数 EPOCHS = {EPOCHS}")
print(f"基础样本组数 SAMPLE_NUM_BASE = {SAMPLE_NUM_BASE}")
print(f"优化器学习率 LR = {LR}")
print(f"早停等待轮数 PATIENT_EARLY_STOP = {PATIENT_EARLY_STOP}")
print(f"Dropout概率 = {DROPOUT}")
print(f"模型保存路径 = {MODEL_SAVE_PATH}")
print("=" * 80)
# ===================== 2. 传感器数据集:模拟真实工业传感器信号 =====================
class SensorDataset(Dataset):
"""
工业传感器时序数据集生成器
核心思路:真实传感器信号不是纯随机数,而是由多种物理因素叠加而成:
信号 = 基线电压 + 设备老化漂移 + 工况周期波动 + 电磁随机噪声
生成6类样本(3类正常 + 3类故障),模拟多种实际工况:
正常类:
1. 标准正常:2V基准附近波动,叠加微弱趋势和周期性(设备正常运行)
2. 临界高值正常:3.2~3.95V,高负载但未超限(满载运行)
3. 低幅波动正常:1.2V附近平稳波动(低功耗待机模式)
故障类:
4. 标准故障:正常波形上随机3个点位突发尖峰(瞬时短路/冲击)
5. 弱故障:仅1个点位轻微超标(早期异常,值得关注)
6. 渐变故障:后半段数值持续升高(设备过热/绝缘老化)
"""
def __init__(self, sample_num=800):
print(f"\n【数据集初始化中...】基础样本组数 {sample_num} 组(每组生成6条样本)")
self.data = []
self.label = []
for sample_idx in range(sample_num):
# 生成归一化时间轴 [0, 1],用于构造趋势和周期波形
t = torch.linspace(0, 1, SEQ_LEN)
# ---------------- 类型1:标准正常 — 带基线漂移 + 周期性波动 + 噪声 ----------------
baseline = 2.0 + 0.4 * torch.randn(1).item() # 随机基线偏移,模拟设备个体差异
trend = 0.3 * (t - 0.5) # 微弱线性趋势,模拟缓慢漂移
periodic = 0.25 * torch.sin(2 * math.pi * t * 2) # 周期性正弦波,模拟工况循环
noise = torch.randn(SEQ_LEN) * 0.12 # 高斯噪声,模拟电磁干扰
normal_sequence = baseline + trend + periodic + noise
normal_sequence = torch.clamp(normal_sequence, min=0.1, max=6.0)
self.data.append(normal_sequence)
self.label.append(0)
# ---------------- 类型2:标准故障 — 正常波形 + 多点突发尖峰 ----------------
baseline2 = 2.0 + 0.3 * torch.randn(1).item()
trend2 = 0.2 * (t - 0.5)
periodic2 = 0.2 * torch.sin(2 * math.pi * t * 2)
fault_seq = baseline2 + trend2 + periodic2
# 随机选3个位置叠加突发尖峰,模拟瞬时短路/电磁冲击
fault_pos = torch.randint(0, SEQ_LEN, (3,))
fault_seq[fault_pos] = fault_seq[fault_pos] + torch.FloatTensor(3).uniform_(2.5, 4.0)
fault_noise = torch.randn(SEQ_LEN) * 0.12
fault_seq = torch.clamp(fault_seq + fault_noise, min=0.1, max=6.0)
self.data.append(fault_seq)
self.label.append(1)
# ---------------- 类型3:弱故障 — 仅1个轻微超标点位 ----------------
baseline3 = 2.0 + 0.3 * torch.randn(1).item()
periodic3 = 0.15 * torch.sin(2 * math.pi * t * 3)
weak_fault = baseline3 + periodic3
# 仅1个位置出现轻微异常,模拟早期故障征兆
single_pos = torch.randint(0, SEQ_LEN, (1,))
weak_fault[single_pos] = weak_fault[single_pos] + torch.FloatTensor(1).uniform_(2.5, 3.8)
weak_fault = torch.clamp(weak_fault + torch.randn(SEQ_LEN) * 0.12, min=0.1, max=6.0)
self.data.append(weak_fault)
self.label.append(1)
# ---------------- 类型4:渐变故障 — 后半段数值持续升高 ----------------
baseline4 = 2.0 + 0.2 * torch.randn(1).item()
gradual_fault = baseline4 + 0.15 * torch.randn(SEQ_LEN)
# 后半段叠加线性上升,模拟设备持续升温或绝缘逐渐老化
gradual_fault[SEQ_LEN // 2:] = gradual_fault[SEQ_LEN // 2:] + torch.linspace(1.0, 2.5, SEQ_LEN // 2)
gradual_fault = torch.clamp(gradual_fault, min=0.1, max=6.0)
self.data.append(gradual_fault)
self.label.append(1)
# ---------------- 类型5:临界高值正常 — 高负载运行但未超限 ----------------
edge_normal = 3.2 + 0.3 * torch.randn(SEQ_LEN) * 0.5
edge_normal = edge_normal + 0.15 * torch.sin(2 * math.pi * t * 2)
# 强制钳位到3.95以下,属于正常工况中的满载运行
edge_normal = torch.clamp(edge_normal, min=0.1, max=3.95)
self.data.append(edge_normal)
self.label.append(0)
# ---------------- 类型6:低幅波动正常 — 低功耗待机模式 ----------------
low_normal = 1.2 + 0.3 * torch.randn(1).item()
low_normal_seq = low_normal + 0.2 * torch.sin(2 * math.pi * t * 3) + torch.randn(SEQ_LEN) * 0.1
low_normal_seq = torch.clamp(low_normal_seq, min=0.1, max=2.5)
self.data.append(low_normal_seq)
self.label.append(0)
if (sample_idx + 1) % 100 == 0:
print(f"数据集生成进度:已完成 {sample_idx + 1}/{sample_num} 组基础样本配套波形")
print(f"【数据集生成完成】总样本数量:{len(self.data)} 条")
print(f"包含6类波形:标准正常、标准故障、弱故障、渐变故障、临界高值正常、低幅波动正常")
print("信号构成:基线偏移 + 线性趋势 + 周期正弦波 + 高斯随机噪声")
print("=" * 80)
def __len__(self):
"""返回数据集总样本数。PyTorch的DataLoader靠它知道有多少样本可遍历。"""
return len(self.data)
def __getitem__(self, idx):
"""根据索引取出单条样本。
DataLoader通过它按批次抽取数据,返回 (样本张量, 标签张量) 的元组。
这是PyTorch Dataset抽象接口的标准约定,必须实现。
"""
# 返回原始浮点数序列,不做离散化
# 模型入口使用 nn.Linear 投影层,能正确处理连续数值的大小关系
raw_float_seq = self.data[idx]
raw_label = self.label[idx]
float_tensor = raw_float_seq.float()
label_tensor = torch.tensor(raw_label, dtype=torch.long)
return float_tensor, label_tensor
# ===================== 3. 正弦位置编码 =====================
class PositionalEncoding(nn.Module):
"""
正弦位置编码(Sinusoidal Positional Encoding)
为什么需要位置编码?
Transformer的自注意力机制是"位置无关"的——打乱输入序列的顺序,输出不变。
但传感器数据的核心价值在于时序顺序:第3个采样点和第13个采样点含义完全不同。
因此必须显式告诉模型"当前是第几个时间步"。
原理:
使用不同频率的正弦/余弦函数为每个位置生成唯一编码向量。
公式:PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
其中 pos 是位置索引,i 是维度索引。
三角函数的数学性质使得模型能隐式学到相对位置关系
(相位差天然编码了两个位置之间的距离),这对时序建模非常有价值。
"""
def __init__(self, d_model, max_len=100, dropout=0.1):
super().__init__()
self.dropout = nn.Dropout(p=dropout)
# 预计算所有位置的位置编码矩阵
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) # [max_len, 1]
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term) # 偶数维度:sin编码
pe[:, 1::2] = torch.cos(position * div_term) # 奇数维度:cos编码
pe = pe.unsqueeze(0) # 扩展为 [1, max_len, d_model],方便广播
# register_buffer:标记为非参数张量,不参与梯度更新但会随模型一起保存/加载
self.register_buffer('pe', pe)
def forward(self, x):
"""
:param x: [batch_size, seq_len, d_model] 输入特征
:return: x + 对应位置的位置编码
"""
# self.pe 形状为 [1, max_len, d_model],x 形状为 [batch, seq_len, d_model]
# 切片 [:, :seq_len, :] 取前 seq_len 个位置,然后利用 Broadcasting 自动扩展到 batch_size
x = x + self.pe[:, :x.size(1), :]
return self.dropout(x)
# ===================== 4. Transformer 故障检测模型 =====================
class SensorTransformer(nn.Module):
"""
时序故障检测 Transformer 模型
完整数据流:
浮点数值 [batch, 16]
→ 线性投影 [batch, 16, 32] 将每个标量数值映射为32维特征向量
→ 位置编码 [batch, 16, 32] 注入时序位置信息
→ Transformer编码器 [batch, 16, 32] 多头自注意力提取全局时序特征
→ 平均池化 [batch, 32] 将16个时间步的特征压缩为1个全局向量
→ 分类输出 [batch, 2] 输出正常/故障两个类别的得分
为什么用 Linear 投影而不是 Embedding?
Embedding 把每个数值当作离散ID处理,2.3V和2.4V会被当作两个完全不同的"单词"。
但在传感器场景中,2.3V和2.4V在语义上几乎等价(都是正常电压)。
Linear 是连续映射,数值相近的输入会映射到相近的向量,保留了数值的大小关系语义。
"""
def __init__(self):
super().__init__()
print("\n【AI模型网络组件初始化】")
# 组件1:线性投影层,将标量数值映射为高维特征向量
self.input_proj = nn.Linear(1, EMBED_DIM)
print(f"组件1:线性投影层 Linear(1→{EMBED_DIM}) 初始化完成")
# 组件2:正弦位置编码,注入时序位置信息
self.pos_encoder = PositionalEncoding(d_model=EMBED_DIM, max_len=SEQ_LEN, dropout=DROPOUT)
print(f"组件2:正弦位置编码 PositionalEncoding 初始化完成")
# 组件3:Transformer编码器,多头自注意力提取全局时序特征
encoder_layer = nn.TransformerEncoderLayer(
d_model=EMBED_DIM,
nhead=N_HEAD,
dim_feedforward=FFN_HIDDEN,
batch_first=True,
dropout=DROPOUT
)
self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=LAYERS)
print(f"组件3:{LAYERS}层Transformer编码器({N_HEAD}头注意力,FFN={FFN_HIDDEN})初始化完成")
# 组件4:分类输出头,带Dropout正则
self.classify_head = nn.Sequential(
nn.Dropout(p=DROPOUT),
nn.Linear(EMBED_DIM, 2)
)
print("组件4:二分类输出全连接层(含Dropout)初始化完成")
print("【AI模型所有组件组装完毕】")
print("=" * 80)
def forward(self, x):
"""
:param x: [batch_size, SEQ_LEN] 传感器浮点数值序列
:return logits: [batch_size, 2] 正常/故障两个类别的原始得分
"""
# unsqueeze(-1) 在最后一个维度插入大小为1的新维度
# 原因:nn.Linear 期望输入的最后一维是特征数,这里每个时间步只有一个标量值
# [batch, seq_len] → [batch, seq_len, 1] 才能适配 Linear(1, 32)
x = x.unsqueeze(-1)
x = self.input_proj(x)
# 注入时序位置信息
x = self.pos_encoder(x)
# 多头自注意力编码
seq_feature = self.encoder(x)
# 平均池化:将16个时间步的特征向量取平均,得到一个全局特征向量
# 这样做的好处是不依赖特定位置,无论故障发生在序列开头还是结尾都能被捕捉
global_feature = torch.mean(seq_feature, dim=1)
# 输出分类得分
logits = self.classify_head(global_feature)
return logits
# ===================== 5. 数据集划分:训练 / 验证 / 测试 三分集 =====================
#
# 三分集各自的角色和隔离原则:
# 训练集(70%):直接用于更新模型参数,模型唯一的"学习材料"
# 验证集(15%):不参与梯度更新,用于早停判断和超参数调优
# 测试集(15%):训练和调参期间完全不可见,最终评估时才揭晓,保证泛化性能评估无偏
#
# 为什么不直接用二分集(训练+验证)?
# 如果验证集既用于早停又用于最终评估,模型在训练过程中已经"看过"验证集的表现
# 并据此做了决策(何时停止训练),这就引入了信息泄露,导致评估指标虚高。
# 独立的测试集给出的是模型在"完全未见数据"上的真实泛化能力。
# =========================================================================
full_dataset = SensorDataset(sample_num=SAMPLE_NUM_BASE)
total_size = len(full_dataset)
train_size = int(0.7 * total_size)
val_size = int(0.15 * total_size)
test_size = total_size - train_size - val_size
train_dataset, val_dataset, test_dataset = random_split(
full_dataset, [train_size, val_size, test_size]
)
train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False)
test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False)
total_batch_per_epoch = len(train_dataset) // BATCH_SIZE
print(f"\n【数据集三分集划分结果】")
print(f"训练集:{train_size} 条(70%)| 验证集:{val_size} 条(15%)| 测试集:{test_size} 条(15%)")
print(f"每完整一轮训练处理批次总数:{total_batch_per_epoch} 批")
print("=" * 80)
# ===================== 6. 初始化训练三件套:损失函数 + 优化器 + 学习率调度 =====================
model = SensorTransformer().to(device)
# 交叉熵损失:二分类标准损失函数,衡量预测概率分布与真实标签的差距
# 注意:CrossEntropyLoss 内部会自动对输入做 softmax + 取对数
# 因此模型 forward 返回的 logits(原始得分)直接传入即可,不需要手动加 softmax
loss_function = nn.CrossEntropyLoss()
# Adam优化器:自适应学习率,配合L2权重衰减(weight_decay)约束模型参数不要过大
# L2正则通过在损失函数中添加参数平方和的惩罚项,抑制模型对训练数据的过度拟合
optimizer = optim.Adam(model.parameters(), lr=LR, weight_decay=1e-4)
total_trainable_params = sum(p.numel() for p in model.parameters())
print("\n【训练工具初始化信息】")
print(f"模型总可训练参数量:{total_trainable_params:,} 个")
print(f"损失函数:CrossEntropyLoss 二分类交叉熵")
print(f"优化器:Adam,学习率={LR},L2权重衰减 weight_decay=1e-4")
print("=" * 80)
print("========== 正式开始AI模型训练流程(训练+验证+早停) ==========")
print("=" * 80)
# ===================== 7. 评估指标计算函数 =====================
def compute_metrics(preds, labels):
"""
计算分类任务的完整评估指标
四个基础量(混淆矩阵):
TP(真阳性):预测为故障,实际也是故障 —— 正确检出的故障
FP(假阳性):预测为故障,实际是正常 —— 误报
FN(假阴性):预测为正常,实际是故障 —— 漏报
TN(真阴性):预测为正常,实际也是正常 —— 正确判定的正常
衍生指标:
Accuracy = (TP+TN) / 总数 整体正确率
Precision = TP / (TP+FP) 预测为故障中,真正故障的比例(关注误报率)
Recall = TP / (TP+FN) 真实故障中,被正确检出的比例(关注漏报率)
F1 = 2*P*R / (P+R) Precision和Recall的调和平均(综合评价)
工业场景中 Recall 尤其重要:漏报一个故障可能导致安全事故。
"""
preds = torch.tensor(preds)
labels = torch.tensor(labels)
tp = ((preds == 1) & (labels == 1)).sum().item()
fp = ((preds == 1) & (labels == 0)).sum().item()
fn = ((preds == 0) & (labels == 1)).sum().item()
tn = ((preds == 0) & (labels == 0)).sum().item()
accuracy = (tp + tn) / max(tp + fp + fn + tn, 1)
precision = tp / max(tp + fp, 1)
recall = tp / max(tp + fn, 1)
f1 = 2 * precision * recall / max(precision + recall, 1e-8)
return {
"accuracy": accuracy,
"precision": precision,
"recall": recall,
"f1": f1,
"tp": tp, "fp": fp, "fn": fn, "tn": tn
}
# ===================== 8. 完整训练循环 =====================
#
# 训练流程:
# 每轮训练包含两个阶段:
# 阶段1 - 训练:用训练集数据更新模型参数(标准五步流程:清零→前向→算损失→反向→更新)
# 阶段2 - 验证:用验证集评估当前模型(不更新参数),计算完整评估指标
#
# 早停策略:
# 如果验证集准确率连续 PATIENT_EARLY_STOP 轮没有提升,说明模型已经收敛或开始过拟合,
# 提前终止训练,避免浪费算力并保留最佳状态的模型权重。
# =========================================================================
best_val_acc = 0.0 # 记录历史最高验证集准确率
early_stop_count = 0 # 连续无提升的轮次计数器
best_model_state = None # 保存最佳模型参数的快照
# 记录每轮训练指标,用于绘制训练曲线
history = {
"train_loss": [], "train_acc": [],
"val_loss": [], "val_acc": [],
"val_precision": [], "val_recall": [], "val_f1": []
}
for current_epoch in range(EPOCHS):
model.train() # 切换到训练模式:启用Dropout随机屏蔽,保证训练时的正则效果
epoch_total_loss = 0.0
epoch_correct_count = 0
epoch_total_samples = 0
print(f"\n===== 进入第 {current_epoch + 1}/{EPOCHS} 轮完整训练 =====")
# ---------------------- 阶段1:训练集参数更新 ----------------------
for batch_index, (batch_seq, batch_label) in enumerate(train_loader):
batch_seq = batch_seq.to(device)
batch_label = batch_label.to(device)
# 标准训练五步流程
optimizer.zero_grad() # 1. 清零梯度
batch_pred_logits = model(batch_seq) # 2. 前向传播
batch_loss = loss_function(batch_pred_logits, batch_label) # 3. 计算损失
batch_loss.backward() # 4. 反向传播求梯度
optimizer.step() # 5. 更新参数
# 统计批次准确率
batch_pred_class = torch.argmax(batch_pred_logits, dim=1)
batch_correct_num = (batch_pred_class == batch_label).sum().item()
batch_sample_count = batch_seq.shape[0]
epoch_total_loss += batch_loss.item()
epoch_correct_count += batch_correct_num
epoch_total_samples += batch_sample_count
# 每30批次打印实时训练日志
if batch_index % 30 == 0:
print(f"[轮次{current_epoch + 1} | 批次{batch_index:2d}/{total_batch_per_epoch}] "
f"批次损失={batch_loss.item():.4f} | "
f"批次准确率={batch_correct_num / batch_sample_count:.2%}")
# 训练集本轮汇总
epoch_avg_loss = epoch_total_loss / total_batch_per_epoch
epoch_acc = epoch_correct_count / epoch_total_samples
# ---------------------- 阶段2:验证集评估(不更新参数) ----------------------
model.eval() # 切换到评估模式:关闭Dropout,使用全局统计量,保证推理结果确定性
val_total_loss = 0.0
val_all_preds = []
val_all_labels = []
with torch.no_grad():
# 推理阶段只进行前向传播,不需要反向传播求梯度
# 关闭梯度追踪可大幅节省显存,并将推理速度提升约30%~50%
for val_seq, val_lab in val_loader:
val_seq = val_seq.to(device)
val_lab = val_lab.to(device)
val_logits = model(val_seq)
val_loss = loss_function(val_logits, val_lab)
val_pred = torch.argmax(val_logits, dim=1)
val_total_loss += val_loss.item()
val_all_preds.extend(val_pred.cpu().tolist())
val_all_labels.extend(val_lab.cpu().tolist())
val_avg_loss = val_total_loss / len(val_loader)
val_metrics = compute_metrics(val_all_preds, val_all_labels)
# 记录本轮历史数据
history["train_loss"].append(epoch_avg_loss)
history["train_acc"].append(epoch_acc)
history["val_loss"].append(val_avg_loss)
history["val_acc"].append(val_metrics["accuracy"])
history["val_precision"].append(val_metrics["precision"])
history["val_recall"].append(val_metrics["recall"])
history["val_f1"].append(val_metrics["f1"])
# ---------------------- 早停逻辑 ----------------------
if val_metrics["accuracy"] > best_val_acc:
best_val_acc = val_metrics["accuracy"]
early_stop_count = 0
best_model_state = model.state_dict() # 保存当前最佳模型参数快照
print(f"★ 验证集准确率创新高!当前最佳:{best_val_acc:.2%}(已保存模型快照)")
else:
early_stop_count += 1
print(f"验证集无提升,早停等待计数:{early_stop_count}/{PATIENT_EARLY_STOP}")
# 打印本轮完整汇总报告
print(f"\n---------- 第{current_epoch + 1}轮 训练+验证完整汇总报告 ----------")
print(f"【训练集】平均损失:{epoch_avg_loss:.4f} | 准确率:{epoch_acc:.2%}")
print(f"【验证集】平均损失:{val_avg_loss:.4f} | 准确率:{val_metrics['accuracy']:.2%} | "
f"Precision:{val_metrics['precision']:.2%} | Recall:{val_metrics['recall']:.2%} | "
f"F1:{val_metrics['f1']:.2%}")
print(f"【混淆矩阵】TP={val_metrics['tp']} FP={val_metrics['fp']} FN={val_metrics['fn']} TN={val_metrics['tn']}")
print("-" * 70)
# 触发早停则终止训练
if early_stop_count >= PATIENT_EARLY_STOP:
print(f"\n===== 触发早停!验证集连续{PATIENT_EARLY_STOP}轮无提升,停止训练 =====")
break
# 训练结束收尾
print("=" * 80)
print("【训练流程全部结束】")
print(f"全程最佳验证集准确率:{best_val_acc:.2%}")
print("=" * 80)
# ===================== 9. 保存最佳模型权重 =====================
# 保存内容包含:模型参数 + 模型配置 + 最佳验证准确率
# 这样加载时可以自动校验架构是否匹配,避免加载不兼容的权重
if best_model_state is not None:
torch.save({
"model_state_dict": best_model_state,
"config": {
"seq_len": SEQ_LEN,
"embed_dim": EMBED_DIM,
"n_head": N_HEAD,
"ffn_hidden": FFN_HIDDEN,
"layers": LAYERS,
"dropout": DROPOUT,
},
"best_val_acc": best_val_acc,
}, MODEL_SAVE_PATH)
print(f"\n【模型已保存】最佳模型权重 → {MODEL_SAVE_PATH}")
print("=" * 80)
# ===================== 10. 测试集最终评估 =====================
# 测试集在训练全程中完全不可见,用于给出模型真实泛化能力的无偏估计
print("\n========== 测试集最终评估(模型全程未见过的数据) ==========")
if best_model_state is not None:
model.load_state_dict(best_model_state)
model.eval()
test_all_preds = []
test_all_labels = []
test_total_loss = 0.0
with torch.no_grad():
for test_seq, test_lab in test_loader:
test_seq = test_seq.to(device)
test_lab = test_lab.to(device)
test_logits = model(test_seq)
test_loss = loss_function(test_logits, test_lab)
test_pred = torch.argmax(test_logits, dim=1)
test_total_loss += test_loss.item()
test_all_preds.extend(test_pred.cpu().tolist())
test_all_labels.extend(test_lab.cpu().tolist())
test_metrics = compute_metrics(test_all_preds, test_all_labels)
test_avg_loss = test_total_loss / len(test_loader)
print(f"【测试集最终结果】")
print(f"平均损失:{test_avg_loss:.4f}")
print(f"准确率 Accuracy:{test_metrics['accuracy']:.2%}")
print(f"精确率 Precision:{test_metrics['precision']:.2%}(预测故障中真正故障的比例)")
print(f"召回率 Recall:{test_metrics['recall']:.2%}(真实故障中被正确检出的比例)")
print(f"F1 分数:{test_metrics['f1']:.2%}(Precision和Recall的调和平均)")
print(f"混淆矩阵:TP={test_metrics['tp']} FP={test_metrics['fp']} FN={test_metrics['fn']} TN={test_metrics['tn']}")
print("=" * 80)
# ===================== 11. 训练过程可视化 =====================
# 生成4张子图,直观展示模型的训练状态:
# 左上:损失曲线(训练 vs 验证)—— 两者差距大则过拟合
# 右上:准确率曲线(训练 vs 验证)—— 验证准确率停滞则模型已收敛
# 左下:P/R/F1 曲线 —— 观察精确率和召回率的权衡变化
# 右下:混淆矩阵柱状图 —— 一眼看出误报和漏报的数量分布
try:
import matplotlib
matplotlib.use("Agg") # 非交互式后端,适合服务器环境
import matplotlib.pyplot as plt
# 设置中文字体,避免图表中的中文显示为方块
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "WenQuanYi Micro Hei"]
plt.rcParams["axes.unicode_minus"] = False
epochs_range = range(1, len(history["train_loss"]) + 1)
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 子图1:损失曲线
axes[0, 0].plot(epochs_range, history["train_loss"], "b-o", markersize=3, label="训练集损失")
axes[0, 0].plot(epochs_range, history["val_loss"], "r-o", markersize=3, label="验证集损失")
axes[0, 0].set_xlabel("训练轮次")
axes[0, 0].set_ylabel("损失值")
axes[0, 0].set_title("训练集 vs 验证集 损失曲线")
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# 子图2:准确率曲线
axes[0, 1].plot(epochs_range, history["train_acc"], "b-o", markersize=3, label="训练集准确率")
axes[0, 1].plot(epochs_range, history["val_acc"], "r-o", markersize=3, label="验证集准确率")
axes[0, 1].set_xlabel("训练轮次")
axes[0, 1].set_ylabel("准确率")
axes[0, 1].set_title("训练集 vs 验证集 准确率曲线")
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
# 子图3:Precision / Recall / F1 曲线
axes[1, 0].plot(epochs_range, history["val_precision"], "g-o", markersize=3, label="Precision")
axes[1, 0].plot(epochs_range, history["val_recall"], "m-o", markersize=3, label="Recall")
axes[1, 0].plot(epochs_range, history["val_f1"], "c-o", markersize=3, label="F1")
axes[1, 0].set_xlabel("训练轮次")
axes[1, 0].set_ylabel("分数")
axes[1, 0].set_title("验证集 Precision / Recall / F1 曲线")
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)
# 子图4:混淆矩阵柱状图
cm_labels = ["TP(真阳性)", "FP(假阳性)", "FN(假阴性)", "TN(真阴性)"]
cm_values = [test_metrics["tp"], test_metrics["fp"], test_metrics["fn"], test_metrics["tn"]]
cm_colors = ["#4CAF50", "#FF9800", "#F44336", "#2196F3"]
bars = axes[1, 1].bar(cm_labels, cm_values, color=cm_colors, edgecolor="black")
axes[1, 1].set_ylabel("样本数量")
axes[1, 1].set_title("测试集混淆矩阵")
for bar, val in zip(bars, cm_values):
axes[1, 1].text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 5,
str(val), ha="center", va="bottom")
plt.tight_layout()
plt.savefig("training_curves.png", dpi=150)
print(f"\n【训练曲线图已保存】→ training_curves.png")
print(f"图表包含4个子图:损失曲线、准确率曲线、P/R/F1曲线、混淆矩阵")
except ImportError:
print("\n【提示】未安装 Matplotlib,跳过训练曲线可视化。")
print("如需可视化,请运行:pip install matplotlib")
print("=" * 80)
# ===================== 12. 推理预测函数 =====================
def detect_equipment_fault(input_float_sequence):
"""
推理预测:接收一组传感器数值,返回AI的故障判定结果和置信度
:param input_float_sequence: list,长度固定SEQ_LEN个浮点数
:return: (pred_result, score_normal, score_fault)
pred_result: 0=正常, 1=故障
score_normal: 模型对"正常"类别的打分
score_fault: 模型对"故障"类别的打分
"""
model.eval() # 推理前必须切到评估模式,确保 Dropout 不随机屏蔽神经元
with torch.no_grad():
input_tensor = torch.FloatTensor(input_float_sequence).unsqueeze(0).to(device)
output_scores = model(input_tensor)
score_normal = output_scores[0][0].item()
score_fault = output_scores[0][1].item()
pred_result = torch.argmax(output_scores, dim=1).item()
return pred_result, score_normal, score_fault
# ===================== 13. 交互式控制台测试面板 =====================
# 训练完成后可在此手动输入传感器数据进行实时故障检测
print("\n===================== 设备故障检测交互测试面板 =====================")
print("使用说明:")
print(f"1. 输入{SEQ_LEN}个数字,英文逗号 , 分隔,代表{SEQ_LEN}个连续传感器电压值")
print("2. 输入 exit 即可退出检测程序")
print(f"3. 正常波形示例:1.8,2.1,2.5,1.9,2.2,1.5,2.8,2.3,1.9,2.0,1.4,2.6,1.7,2.2,1.3,2.4")
print(f"4. 故障波形示例:1.1,2.3,5.2,2.0,1.8,4.6,2.1,2.5,1.7,2.2,1.5,2.7,4.9,2.3,1.6,2.1")
print("====================================================================")
while True:
user_input_text = input(f"\n请输入{SEQ_LEN}个传感器电压数值:")
user_command = user_input_text.strip().lower()
if user_command == "exit":
print("\n收到退出指令,故障检测程序结束!")
break
try:
num_value_list = [float(num_str.strip()) for num_str in user_input_text.split(",")]
if len(num_value_list) != SEQ_LEN:
print(f"【输入错误】必须输入恰好{SEQ_LEN}个数值,你输入了{len(num_value_list)}个,请重新输入!")
continue
predict_label, score_norm, score_fault = detect_equipment_fault(num_value_list)
print("------------ AI故障检测详细报告 ------------")
print(f"输入传感器完整序列:{[round(v, 2) for v in num_value_list]}")
print(f"AI对【设备正常】打分:{score_norm:.4f}")
print(f"AI对【设备故障】打分:{score_fault:.4f}")
if predict_label == 1:
print("最终判定:【设备存在异常,判定为故障,建议停机检修!】")
else:
print("最终判定:【设备运行状态良好,各项指标正常】")
print("--------------------------------------------")
except Exception as error_info:
print(f"【输入格式解析失败】请输入纯数字并用英文逗号分隔,错误详情:{error_info}")
你也可以直接复制本文涉及的核心代码片段到本地 .py 文件中运行。
一次运行日志:
C:\Users\lenovo\PycharmProjects\pythonProject_AI\venv\Scripts\python.exe C:\Users\lenovo\PycharmProjects\pythonProject_AI\main.py
================================================================================
【设备故障检测AI程序 - 启动自检日志】
程序运行硬件设备:cpu
当前PyTorch框架版本:2.13.0+cpu
未检测到NVIDIA独立显卡,本次全程使用CPU训练模型
================================================================================
【模型全部超参数配置清单】
传感器序列固定长度 SEQ_LEN = 16
投影向量维度 EMBED_DIM = 32
多头注意力头数 N_HEAD = 4
前馈网络隐藏层 FFN_HIDDEN = 64
Transformer编码器层数 LAYERS = 2
单次训练批次大小 BATCH_SIZE = 24
最大训练轮数 EPOCHS = 40
基础样本组数 SAMPLE_NUM_BASE = 1500
优化器学习率 LR = 0.0005
早停等待轮数 PATIENT_EARLY_STOP = 5
Dropout概率 = 0.25
模型保存路径 = sensor_model_best.pth
================================================================================
【数据集初始化中...】基础样本组数 1500 组(每组生成6条样本)
数据集生成进度:已完成 100/1500 组基础样本配套波形
数据集生成进度:已完成 200/1500 组基础样本配套波形
数据集生成进度:已完成 300/1500 组基础样本配套波形
数据集生成进度:已完成 400/1500 组基础样本配套波形
数据集生成进度:已完成 500/1500 组基础样本配套波形
数据集生成进度:已完成 600/1500 组基础样本配套波形
数据集生成进度:已完成 700/1500 组基础样本配套波形
数据集生成进度:已完成 800/1500 组基础样本配套波形
数据集生成进度:已完成 900/1500 组基础样本配套波形
数据集生成进度:已完成 1000/1500 组基础样本配套波形
数据集生成进度:已完成 1100/1500 组基础样本配套波形
数据集生成进度:已完成 1200/1500 组基础样本配套波形
数据集生成进度:已完成 1300/1500 组基础样本配套波形
数据集生成进度:已完成 1400/1500 组基础样本配套波形
数据集生成进度:已完成 1500/1500 组基础样本配套波形
【数据集生成完成】总样本数量:9000 条
包含6类波形:标准正常、标准故障、弱故障、渐变故障、临界高值正常、低幅波动正常
信号构成:基线偏移 + 线性趋势 + 周期正弦波 + 高斯随机噪声
================================================================================
【数据集三分集划分结果】
训练集:6300 条(70%)| 验证集:1350 条(15%)| 测试集:1350 条(15%)
每完整一轮训练处理批次总数:262 批
================================================================================
【AI模型网络组件初始化】
组件1:线性投影层 Linear(1→32) 初始化完成
组件2:正弦位置编码 PositionalEncoding 初始化完成
组件3:2层Transformer编码器(4头注意力,FFN=64)初始化完成
组件4:二分类输出全连接层(含Dropout)初始化完成
【AI模型所有组件组装完毕】
================================================================================
【训练工具初始化信息】
模型总可训练参数量:17,218 个
损失函数:CrossEntropyLoss 二分类交叉熵
优化器:Adam,学习率=0.0005,L2权重衰减 weight_decay=1e-4
================================================================================
========== 正式开始AI模型训练流程(训练+验证+早停) ==========
================================================================================
===== 进入第 1/40 轮完整训练 =====
[轮次1 | 批次 0/262] 批次损失=0.6848 | 批次准确率=50.00%
[轮次1 | 批次30/262] 批次损失=0.6382 | 批次准确率=58.33%
[轮次1 | 批次60/262] 批次损失=0.4756 | 批次准确率=79.17%
[轮次1 | 批次90/262] 批次损失=0.1328 | 批次准确率=100.00%
[轮次1 | 批次120/262] 批次损失=0.1899 | 批次准确率=95.83%
[轮次1 | 批次150/262] 批次损失=0.0996 | 批次准确率=95.83%
[轮次1 | 批次180/262] 批次损失=0.1348 | 批次准确率=95.83%
[轮次1 | 批次210/262] 批次损失=0.0711 | 批次准确率=95.83%
[轮次1 | 批次240/262] 批次损失=0.0306 | 批次准确率=100.00%
★ 验证集准确率创新高!当前最佳:100.00%(已保存模型快照)
---------- 第1轮 训练+验证完整汇总报告 ----------
【训练集】平均损失:0.2382 | 准确率:89.14%
【验证集】平均损失:0.0026 | 准确率:100.00% | Precision:100.00% | Recall:100.00% | F1:100.00%
【混淆矩阵】TP=677 FP=0 FN=0 TN=673
----------------------------------------------------------------------
===== 进入第 2/40 轮完整训练 =====
[轮次2 | 批次 0/262] 批次损失=0.1066 | 批次准确率=95.83%
[轮次2 | 批次30/262] 批次损失=0.0096 | 批次准确率=100.00%
[轮次2 | 批次60/262] 批次损失=0.0330 | 批次准确率=100.00%
[轮次2 | 批次90/262] 批次损失=0.0081 | 批次准确率=100.00%
[轮次2 | 批次120/262] 批次损失=0.0072 | 批次准确率=100.00%
[轮次2 | 批次150/262] 批次损失=0.0058 | 批次准确率=100.00%
[轮次2 | 批次180/262] 批次损失=0.0099 | 批次准确率=100.00%
[轮次2 | 批次210/262] 批次损失=0.0049 | 批次准确率=100.00%
[轮次2 | 批次240/262] 批次损失=0.0046 | 批次准确率=100.00%
验证集无提升,早停等待计数:1/5
---------- 第2轮 训练+验证完整汇总报告 ----------
【训练集】平均损失:0.0300 | 准确率:99.14%
【验证集】平均损失:0.0008 | 准确率:100.00% | Precision:100.00% | Recall:100.00% | F1:100.00%
【混淆矩阵】TP=677 FP=0 FN=0 TN=673
----------------------------------------------------------------------
===== 进入第 3/40 轮完整训练 =====
[轮次3 | 批次 0/262] 批次损失=0.0106 | 批次准确率=100.00%
[轮次3 | 批次30/262] 批次损失=0.0092 | 批次准确率=100.00%
[轮次3 | 批次60/262] 批次损失=0.0098 | 批次准确率=100.00%
[轮次3 | 批次90/262] 批次损失=0.0090 | 批次准确率=100.00%
[轮次3 | 批次120/262] 批次损失=0.0027 | 批次准确率=100.00%
[轮次3 | 批次150/262] 批次损失=0.1143 | 批次准确率=95.83%
[轮次3 | 批次180/262] 批次损失=0.0078 | 批次准确率=100.00%
[轮次3 | 批次210/262] 批次损失=0.0019 | 批次准确率=100.00%
[轮次3 | 批次240/262] 批次损失=0.0061 | 批次准确率=100.00%
验证集无提升,早停等待计数:2/5
---------- 第3轮 训练+验证完整汇总报告 ----------
【训练集】平均损失:0.0208 | 准确率:99.44%
【验证集】平均损失:0.0005 | 准确率:100.00% | Precision:100.00% | Recall:100.00% | F1:100.00%
【混淆矩阵】TP=677 FP=0 FN=0 TN=673
----------------------------------------------------------------------
===== 进入第 4/40 轮完整训练 =====
[轮次4 | 批次 0/262] 批次损失=0.0038 | 批次准确率=100.00%
[轮次4 | 批次30/262] 批次损失=0.0036 | 批次准确率=100.00%
[轮次4 | 批次60/262] 批次损失=0.0030 | 批次准确率=100.00%
[轮次4 | 批次90/262] 批次损失=0.0041 | 批次准确率=100.00%
[轮次4 | 批次120/262] 批次损失=0.0014 | 批次准确率=100.00%
[轮次4 | 批次150/262] 批次损失=0.0092 | 批次准确率=100.00%
[轮次4 | 批次180/262] 批次损失=0.0733 | 批次准确率=95.83%
[轮次4 | 批次210/262] 批次损失=0.0020 | 批次准确率=100.00%
[轮次4 | 批次240/262] 批次损失=0.0018 | 批次准确率=100.00%
验证集无提升,早停等待计数:3/5
---------- 第4轮 训练+验证完整汇总报告 ----------
【训练集】平均损失:0.0200 | 准确率:99.44%
【验证集】平均损失:0.0004 | 准确率:100.00% | Precision:100.00% | Recall:100.00% | F1:100.00%
【混淆矩阵】TP=677 FP=0 FN=0 TN=673
----------------------------------------------------------------------
===== 进入第 5/40 轮完整训练 =====
[轮次5 | 批次 0/262] 批次损失=0.0018 | 批次准确率=100.00%
[轮次5 | 批次30/262] 批次损失=0.0355 | 批次准确率=95.83%
[轮次5 | 批次60/262] 批次损失=0.0012 | 批次准确率=100.00%
[轮次5 | 批次90/262] 批次损失=0.0036 | 批次准确率=100.00%
[轮次5 | 批次120/262] 批次损失=0.0012 | 批次准确率=100.00%
[轮次5 | 批次150/262] 批次损失=0.0016 | 批次准确率=100.00%
[轮次5 | 批次180/262] 批次损失=0.0019 | 批次准确率=100.00%
[轮次5 | 批次210/262] 批次损失=0.0138 | 批次准确率=100.00%
[轮次5 | 批次240/262] 批次损失=0.0011 | 批次准确率=100.00%
验证集无提升,早停等待计数:4/5
---------- 第5轮 训练+验证完整汇总报告 ----------
【训练集】平均损失:0.0092 | 准确率:99.79%
【验证集】平均损失:0.0002 | 准确率:100.00% | Precision:100.00% | Recall:100.00% | F1:100.00%
【混淆矩阵】TP=677 FP=0 FN=0 TN=673
----------------------------------------------------------------------
===== 进入第 6/40 轮完整训练 =====
[轮次6 | 批次 0/262] 批次损失=0.0036 | 批次准确率=100.00%
[轮次6 | 批次30/262] 批次损失=0.0163 | 批次准确率=100.00%
[轮次6 | 批次60/262] 批次损失=0.0020 | 批次准确率=100.00%
[轮次6 | 批次90/262] 批次损失=0.0019 | 批次准确率=100.00%
[轮次6 | 批次120/262] 批次损失=0.0014 | 批次准确率=100.00%
[轮次6 | 批次150/262] 批次损失=0.0008 | 批次准确率=100.00%
[轮次6 | 批次180/262] 批次损失=0.0015 | 批次准确率=100.00%
[轮次6 | 批次210/262] 批次损失=0.0080 | 批次准确率=100.00%
[轮次6 | 批次240/262] 批次损失=0.0019 | 批次准确率=100.00%
验证集无提升,早停等待计数:5/5
---------- 第6轮 训练+验证完整汇总报告 ----------
【训练集】平均损失:0.0089 | 准确率:99.75%
【验证集】平均损失:0.0002 | 准确率:100.00% | Precision:100.00% | Recall:100.00% | F1:100.00%
【混淆矩阵】TP=677 FP=0 FN=0 TN=673
----------------------------------------------------------------------
===== 触发早停!验证集连续5轮无提升,停止训练 =====
================================================================================
【训练流程全部结束】
全程最佳验证集准确率:100.00%
================================================================================
【模型已保存】最佳模型权重 → sensor_model_best.pth
================================================================================
========== 测试集最终评估(模型全程未见过的数据) ==========
【测试集最终结果】
平均损失:0.0002
准确率 Accuracy:100.00%
精确率 Precision:100.00%(预测故障中真正故障的比例)
召回率 Recall:100.00%(真实故障中被正确检出的比例)
F1 分数:100.00%(Precision和Recall的调和平均)
混淆矩阵:TP=683 FP=0 FN=0 TN=667
================================================================================
【训练曲线图已保存】→ training_curves.png
图表包含4个子图:损失曲线、准确率曲线、P/R/F1曲线、混淆矩阵
================================================================================
===================== 设备故障检测交互测试面板 =====================
使用说明:
1. 输入16个数字,英文逗号 , 分隔,代表16个连续传感器电压值
2. 输入 exit 即可退出检测程序
3. 正常波形示例:1.8,2.1,2.5,1.9,2.2,1.5,2.8,2.3,1.9,2.0,1.4,2.6,1.7,2.2,1.3,2.4
4. 故障波形示例:1.1,2.3,5.2,2.0,1.8,4.6,2.1,2.5,1.7,2.2,1.5,2.7,4.9,2.3,1.6,2.1
====================================================================
请输入16个传感器电压数值:1.8,2.1,2.5,1.9,2.2,1.5,2.8,2.3,1.9,2.0,1.4,2.6,1.7,2.2,1.3,2.4
------------ AI故障检测详细报告 ------------
输入传感器完整序列:[1.8, 2.1, 2.5, 1.9, 2.2, 1.5, 2.8, 2.3, 1.9, 2.0, 1.4, 2.6, 1.7, 2.2, 1.3, 2.4]
AI对【设备正常】打分:4.2948
AI对【设备故障】打分:-3.9801
最终判定:【设备运行状态良好,各项指标正常】
--------------------------------------------
请输入16个传感器电压数值:1.1,2.3,5.2,2.0,1.8,4.6,2.1,2.5,1.7,2.2,1.5,2.7,4.9,2.3,1.6,2.1
------------ AI故障检测详细报告 ------------
输入传感器完整序列:[1.1, 2.3, 5.2, 2.0, 1.8, 4.6, 2.1, 2.5, 1.7, 2.2, 1.5, 2.7, 4.9, 2.3, 1.6, 2.1]
AI对【设备正常】打分:-4.5312
AI对【设备故障】打分:4.3450
最终判定:【设备存在异常,判定为故障,建议停机检修!】
--------------------------------------------
总结
本文从传统工业工程师的视角出发,用一段完整的PyTorch代码演示了Transformer在传感器故障检测中的应用。核心要点:
- AI不是魔法,是"从数据中学规则"——和传统阈值法的本质区别仅在于规则来源
- Transformer的核心优势是"看全局关联"——不是判断单个数值大小,而是理解16个时间步之间的协同模式
- 完整流程:数据→模型→训练→评估→推理——每一步都有明确的工程动机
- 当前代码是学习原型,不是工业系统——从原型到产线还需要数据真实化、工程化、可靠性三个层面的补充
希望这篇文章能帮助传统工业工程师建立对AI模型的直觉理解。理解"为什么"比记住"怎么做"重要得多。
更多推荐




所有评论(0)