基于Python与深度学习的滚动轴承智能故障诊断系统实战 简介在工业设备维护从事后维修向预测性维护转型的背景下如何利用振动信号提前识别旋转机械的健康状态成为关键课题。滚动轴承作为电机、泵、风机等设备中最易受损的部件其故障诊断长期依赖人工经验与频谱分析面对复杂工况往往力不从心。深度学习的引入改变了这一局面通过卷积神经网络自动提取振动信号中的故障特征无需人工设计特征即可实现高精度分类。本文以CWRU公开数据集为例详细讲解一维信号预处理、滑窗采样、CNN模型设计、训练评估及部署的完整链路并探讨样本不平衡、域偏移等工业落地中的典型问题。无论是运维工程师还是深度学习实践者都能从中获得可直接复用的工程方案与代码思路。 制造业里的设备维护这几年变化真的很大。以前是坏了再修后来是定时保养现在大家都在往预测性维护走——核心就一句话能不能在设备真正坏掉之前提前知道它要出问题。而滚动轴承恰恰是旋转机械里最容易受伤、也最关键的那个零件。我最早接触这个方向是因为一个做产线监控的朋友抱怨说他们厂里电机轴承烧了好几次每次都是突然停机换一套下来一天产能就没了。当时我就在想能不能用深度学习直接拿振动信号把轴承的健康状态判断出来后来这个想法一步步变成了一个完整的系统也就是这篇文章要聊的东西基于Python的滚动轴承智能故障诊断系统。它不是一个简单的模型Demo而是把数据集处理、深度学习算法、诊断结果可视化串起来的一整套方案。无论你是做工业设备运维的工程师还是正在找深度学习落地项目练手的学生这套东西都有可以直接复用的价值。1. 为什么轴承故障诊断必须换一种玩法从人工经验到数据驱动先说个背景。滚动轴承虽然是个小零件但它在内圈、外圈、滚动体、保持架上出的故障占了旋转机械总故障率的30%到40%——这个数字在电机、泵、风机、机床主轴上基本都成立。早些年做诊断靠的是老师傅拿听音棒贴上去听再用手摸振动经验成分太重而且异常往往要发展到比较明显的程度才能被察觉。后来有了振动传感器和频谱分析行业里开始用特征频率来判断故障外圈故障频率BPFO、内圈故障频率BPFI、滚动体故障频率BSF这些公式至今有效但实际工况可比书本复杂得多。麻烦之处在于轴承在工作时它的振动信号不是干净的单一频率成分。齿轮啮合、轴弯曲、负载变化、环境噪声都会叠加进去你想要的故障特征频率往往被淹没在一片复杂的信号里。传统方法需要人工提取特征——时域的均值、峰峰值、峭度频域的幅值谱、功率谱或者时频域的小波包能量——然后拿这些特征去套阈值或者做浅层分类。这一步极度依赖分析人员的专业水平而且一旦工况变了之前调好的阈值可能立刻失效。深度学习出来之后这个逻辑被改写了。你把原始振动信号或者简单的时频图喂给模型让它自己学习“什么特征代表正常”、“什么特征代表内圈故障”不再需要人工设计特征。这听起来像是把活推给了模型但好处非常明显一是特征提取的自动化二是模型对复杂非线性模式的拟合能力三是当数据量足够时诊断准确率往往能超过传统方法。做工业落地的朋友别急着追求什么花哨的新网络先把CNN这条路走通你就能解决80%的问题。我选滚动轴承作为切入点还有一个务实的原因它的数据集相对成熟有公开的基准数据可以验证算法。西储大学CWRU的轴承数据中心是全球这个领域用得最多的公开数据集电机驱动端和风扇端的振动数据包含了正常、内圈故障、外圈故障、滚动体故障几种状态每种故障还分了不同损伤直径。这给开发调试带来了极大的便利——算法在这套数据上跑不通就别提回到现场去应对更复杂的工况。2. 数据集与预处理一维振动信号如何变成模型能学懂的输入2.1 CWRU数据集的关键细节与下载方式很多初学者第一步就卡在数据集上。CWRU的数据可以从其官网直接下载是.mat格式用SciPy的loadmat就能读。但我要提醒你几个坑第一数据集里信号采样频率有12kHz和48kHz两套不要混用——同一批训练数据里混合不同采样率的信号模型会学到一堆没用的“频率偏移”信息第二数据文件命名要仔细看比如“内圈故障”会有多种负载档位如果你是做故障类型识别建议把不同负载的数据都放进来让模型学到对负载不敏感的特征第三CWRU的故障是用电火花加工出来的单点损伤跟真实磨损有一定差距这是公开数据集的通病跑通流程没问题但别指望它100%等于现场情况。如果你手里有现场采集的数据也完全可以替换这套流程里的数据源。只要确定好采样率、传感器位置、故障标签代码框架不用大改。2.2 滑窗采样把长信号切成模型能吃的小样本原始振动信号是一长串时间序列几秒钟甚至几分钟的波形直接扔进网络计算量大、效果也不理想。标准做法是做滑窗采样设定一个窗口长度比如每段1024个点按照一定步长比如重叠50%在原始信号上滑动切分切成若干个小样本。这样做的目的有两个一是数据增强——同一条信号能生成大量样本缓解标注数据不足的问题二是让每个样本对应一个明确的健康状态标签方便做监督学习。窗口长度怎么定这个没有绝对标准但我的经验是至少覆盖轴承旋转周期的几倍。假设转速是1800rpm也就是每秒30转采样率12kHz下每转对应400个点窗口取1024个点大约是2.5圈信息量已经比较充分。窗口取太短可能截不到一个完整的故障冲击周期取太长样本量变少且一个窗口内可能包含工况的剧烈变化。1024是个很常用的值可以当成默认起点。代码实现非常直接import numpy as np def sliding_window(data, labels, window_size1024, step_size512): samples [] sample_labels [] for signal, label in zip(data, labels): num_windows (len(signal) - window_size) // step_size 1 for i in range(num_windows): start i * step_size end start window_size samples.append(signal[start:end]) sample_labels.append(label) return np.array(samples), np.array(sample_labels)我习惯把窗口重叠设成50%也就是步长为窗口长度的一半。这样样本量翻倍模型见过的变化更多实测对收敛稳定性和准确率都有帮助。但注意训练集和测试集的切分要在切窗口之前完成——先按信号段划分再做滑窗否则同一个原始片段既出现在训练集又出现在测试集评估结果会虚高。2.3 要不要做特征变换时域、频域和时频图的选择这个问题被问得很多直接把一维波形丢给CNN行不行行。但做特征变换有时能让模型学得更快、更稳。我做过的对比实验里有三条路线路线一纯时域波形。输入就是原始振动幅值序列模型自己学特征。这是最省事的方案一维CNN直接处理端到端效果不差。缺点是模型需要自己学会从原始波形里提取频域信息训练时间相对长一些。路线二频域特征。对每个窗口做FFT取幅值谱作为输入。好处是特征更紧凑一维CNN输入长度可以大幅缩短计算量下降。缺点是相位信息丢失了而且FFT的前提是信号平稳轴承振动里的瞬态冲击信息会被平均掉一部分。路线三时频图如短时傅里叶STFT、小波变换。把一维信号转成二维图像用二维CNN、ResNet甚至VGG来分类。这是很多论文的标配做法可视化效果好也方便迁移预训练模型。缺点是把问题复杂化了——你得处理图像尺寸、颜色映射、预训练权重这些额外问题而且原本一维信号里的时间对齐信息在二维变换中也会被重新分布。我个人的建议是先走路线一把baseline跑出来如果有余力再对比路线三。工业场景里实时性往往比那1%的准确率更值钱一维CNN在推理速度上的优势是实打实的。3. 模型选型与网络设计为什么一维CNN比“先转图像再分类”更贴合振动信号3.1 从MLP到CNN振动信号的空间局部性早期有不少人用多层感知机MLP做故障分类把1024个点全部拉平作为输入。MLP的毛病在于它的每个输入节点跟下一层每个节点都是全连接的参数数量爆炸而且没有利用信号本身的局部结构。你可以把振动信号想象成一篇文章——MLP是一个一个字读而CNN是先看几个相邻的词的词组。振动信号有个重要特性故障冲击在时间上是局部的相邻几个采样点之间有强相关性而相隔很远的两段波形之间关联很弱。CNN的卷积核天然就在做“局部连接”这件事——一个小卷积核滑过整个信号提取的是局部波形的模式比如一个冲击尖峰、一段调制波形。多个卷积核叠起来就能学到从低级边缘到高级语义的特征这跟图像分类里CNN学到的层级特征是一个道理。所以一维CNN这个选择不是“我随便选了个网络”而是“我选了一个结构先验与信号本质匹配的网络”。3.2 一个够用且容易训练的一维CNN结构我这里给出一个结构清晰、参数量适中、在CWRU数据上准确率能到99%以上的网络设计。参考了这个领域的常见做法也结合了我自己调试的经验import torch import torch.nn as nn class BearingCNN(nn.Module): def __init__(self, num_classes4, input_length1024): super().__init__() self.features nn.Sequential( nn.Conv1d(1, 16, kernel_size64, stride8, padding28), nn.BatchNorm1d(16), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2, stride2), nn.Conv1d(16, 32, kernel_size3, stride1, padding1), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2, stride2), nn.Conv1d(32, 64, kernel_size3, stride1, padding1), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2, stride2), nn.Conv1d(64, 64, kernel_size3, stride1, padding1), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool1d(1), ) self.classifier nn.Sequential( nn.Flatten(), nn.Dropout(0.3), nn.Linear(64, num_classes), ) def forward(self, x): x x.unsqueeze(1) # [B, 1, input_length] x self.features(x) x self.classifier(x) return x第一层卷积核特别大kernel_size64这是有意为之。振动信号的第一个卷积层相当于在学习一个“可学习的带通滤波器组”大卷积核可以覆盖一个完整的冲击周期直接捕捉低频调制包络。后续的小卷积核3×1在这个基础上细化。这比第一层就用3×1的小卷积核效果要好因为小卷积核的感受野一层层扩得太慢模型需要更多层才能看到全局信息。BatchNorm放在卷积之后、ReLU之前是常规操作。Dropout放在全连接层之前用来抑制过拟合。AdaptiveAvgPool1d(1)把最后一个卷积层的输出从时间维度上池化成1个点这样不管输入序列长度是多少全连接层的输入维度都是固定的——这个设计也让模型稍微具备了一点处理不同长度输入的能力虽然我们在实际训练时还是固定1024点。3.3 为什么不用更复杂的网络关于“够用就好”的工程判断你可能看到过很多论文把ResNet、注意力机制、Transformer搬到轴承故障诊断里。作为学术研究这没问题但作为工程落地我会先泼盆冷水。CWRU那个数据集的难度说实话没有高到需要ResNet这种量级的网络——4分类任务、不同故障模式差异明显、信号质量好一个小CNN就能达到99%以上。凡事看性价比。小模型训练快CPU都能轻松推理部署到边缘设备树莓派、工业控制器都不费劲大模型参数量多推理慢对部署环境的算力要求高而换来的准确率提升可能只有0.1到0.2个百分点在工业现场这0.1%根本不构成决定性差异。所以我的建议一直很明确先用小模型跑通、跑稳再根据实际场景的需要决定要不要上更复杂的结构。如果你想知道自己场景的“天花板”在哪可以用原本设计给图像的ResNet做一个对比实验——但不必把它作为生产方案。4. 训练与评估的完整代码链路从数据加载到混淆矩阵4.1 数据划分别把评估结果做成“开卷考试”训练之前最重要的一件事是把数据划分想清楚。很多初学者直接在滑窗之后随机打乱再划分训练/测试集这在轴承诊断里是个严重错误——因为同一个原始信号段切出的相邻窗口高度相似它们同时出现在训练集和测试集里模型相当于提前看到了答案测试准确率虚高到没有参考意义。正确做法是先把原始信号按“段”划分比如CWRU数据里每个.mat文件就是一段连续采集的信号把文件级别的列表按7:2:1划成训练、验证、测试三份再对每一份分别做滑窗。这样保证训练集和测试集里的样本来自完全不同的原始信号段评估结果才可信。如果你在现场采集数据还要注意一条更隐蔽的规则不同时间段采集的数据要同时覆盖训练集和测试集。工业设备的信号有很强的时变性——温度变了、润滑状态变了、环境噪声变了——如果训练集全是一个月前采的、测试集全是这个月采的那测试准确率会大打折扣。这是数据划分里的一个常见陷阱。4.2 训练脚本构造Dataset、训练循环与模型保存这里给出一个完整的训练脚本框架基于PyTorch。为了不把篇幅拉到太长省去了部分常规样板代码但核心逻辑都在。import os import torch from torch.utils.data import Dataset, DataLoader from sklearn.metrics import classification_report, confusion_matrix import numpy as np class VibrationDataset(Dataset): def __init__(self, samples, labels): self.samples torch.FloatTensor(samples) self.labels torch.LongTensor(labels) def __len__(self): return len(self.labels) def __getitem__(self, idx): return self.samples[idx], self.labels[idx] def train_one_epoch(model, loader, criterion, optimizer, device): model.train() total_loss, correct, total 0, 0, 0 for x, y in loader: x, y x.to(device), y.to(device) optimizer.zero_grad() out model(x) loss criterion(out, y) loss.backward() optimizer.step() total_loss loss.item() * x.size(0) correct (out.argmax(dim1) y).sum().item() total x.size(0) return total_loss / total, correct / total def evaluate(model, loader, criterion, device): model.eval() total_loss, correct, total 0, 0, 0 all_preds, all_labels [], [] with torch.no_grad(): for x, y in loader: x, y x.to(device), y.to(device) out model(x) loss criterion(out, y) total_loss loss.item() * x.size(0) correct (out.argmax(dim1) y).sum().item() total y.size(0) all_preds.extend(out.argmax(dim1).cpu().numpy()) all_labels.extend(y.cpu().numpy()) return total_loss / total, correct / total, all_preds, all_labels训练主循环通常就是几十行定义模型结构定义交叉熵损失定义Adam优化器学习率我常用1e-3起步训练过程中如果验证集loss不降就阶梯式下降然后循环若干个epoch。有个小技巧保存模型的时候先别急着只存最后一轮可以按验证集准确率保存最优快照——如果后面跑测试发现效果不好还能回溯到最好的中间状态。def main(): device torch.device(cuda if torch.cuda.is_available() else cpu) # 假设 data_dict 是预处理好的字典: {train_samples: ..., train_labels: ..., ...} train_ds VibrationDataset(data_dict[train_samples], data_dict[train_labels]) val_ds VibrationDataset(data_dict[val_samples], data_dict[val_labels]) test_ds VibrationDataset(data_dict[test_samples], data_dict[test_labels]) train_loader DataLoader(train_ds, batch_size64, shuffleTrue) val_loader DataLoader(val_ds, batch_size128, shuffleFalse) test_loader DataLoader(test_ds, batch_size128, shuffleFalse) model BearingCNN(num_classes4, input_length1024).to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size30, gamma0.1) best_val_acc 0.0 for epoch in range(1, 61): train_loss, train_acc train_one_epoch(model, train_loader, criterion, optimizer, device) val_loss, val_acc, _, _ evaluate(model, val_loader, criterion, device) scheduler.step() print(fEpoch {epoch:03d} | train_loss {train_loss:.4f} | train_acc {train_acc:.4f} | val_loss {val_loss:.4f} | val_acc {val_acc:.4f}) if val_acc best_val_acc: best_val_acc val_acc torch.save(model.state_dict(), best_bearing_cnn.pth) model.load_state_dict(torch.load(best_bearing_cnn.pth)) test_loss, test_acc, y_pred, y_true evaluate(model, test_loader, criterion, device) print(fTest accuracy: {test_acc:.4f}) print(classification_report(y_true, y_pred, target_names[normal, inner, outer, ball]))4.3 评估维度准确率是远远不够的要看混淆矩阵和类别召回率分类准确率是个很粗糙的指标尤其当类别不平衡时。做过工业诊断的人都知道你最关心的是“漏报”——把有故障的轴承判断成正常这在产线上意味着可能错过一次故障停机前的最后预警。所以必须看混淆矩阵和每个类别的精确率、召回率。import matplotlib.pyplot as plt import seaborn as sns cm confusion_matrix(y_true, y_pred) plt.figure(figsize(8, 6)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabels[normal, inner, outer, ball], yticklabels[normal, inner, outer, ball]) plt.xlabel(Predicted Label) plt.ylabel(True Label) plt.title(Confusion Matrix on Test Set) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi150)理想情况下混淆矩阵应该是一个对角阵——所有类别都分对。如果发现某两类的相互误判较多比如内圈故障和外圈故障经常混淆先别急着加模型复杂度而是回到数据层面这两类的振动特征是不是本来就相似是不是某个负载条件下两者的频谱特征重合度高这时候用t-SNE对中间层特征做可视化往往能直观看到两类在特征空间里的分布情况。这个排查链路比盲目调模型有效得多。5. 实践中躲不开的坑样本不平衡、域偏移与模型落地5.1 类别不平衡少数类故障样本不够怎么办真实工业场景里正常数据占绝大多数故障样本非常稀少——可能一个月的监测数据里故障样本只有几十条而正常数据有几十万条。直接拿这种不均衡数据训练模型会把所有样本都判成“正常”因为这样“准确率”就已经很高了。解决办法有几个层面。最简单的对少数类做过采样从已有的故障样本里滑窗切出更多样本或者用数据增强方式生成合成样本。另一个思路是调整损失函数——给少数类更高的类别权重让模型在梯度更新时更重视这些样本。PyTorch里直接用CrossEntropyLoss的weight参数就行class_weights torch.tensor([1.0, 5.0, 5.0, 5.0]).to(device) criterion nn.CrossEntropyLoss(weightclass_weights)还要注意评估指标的选取不要只看准确率。工业场景里我会更关注故障类的召回率——所有真实故障中被模型抓到多少比例。这个指标直接对应“误停机”和“漏报”这对矛盾中的安全底线。宁可多一些误报也不能让故障从眼皮底下溜走。5.2 域偏移为什么实验室数据跑得很好到现场就不行了这是从论文到产品之间最大的一道坎。你在CWRU数据上训练出的模型拿到自己工厂的轴承上准确率很可能跌掉一大截——不是模型坏了而是训练数据和测试数据来自不同的分布传感器型号不同、安装位置不同、转速负载不同、环境噪声不同这些都导致数据分布发生偏移术语叫“域偏移”。应对方法从易到难排列收集现场数据微调fine-tuning把预训练模型在目标设备的一小部分标注数据上做微调这是最直接有效的手段。哪怕只有几十条现场故障样本也能显著拉回准确率。数据混合训练训练时就混合多个来源的数据CWRU、自己实验台、现场试采增加数据多样性模型学到的特征更泛化。对抗式域适应用领域对抗网络DANN这类方法让特征提取器在训练时同时“骗过”领域分类器逼迫模型学到与设备无关的通用特征。这个方法实现复杂度高一些但一旦通吃效果很惊艳。我在实际项目里的体验是对大多数中小企业场景第一条路性价比最高第二条路是常规操作第三条路一般来说有点杀鸡用牛刀了。5.3 推理部署把训练好的模型变成能用的诊断服务模型训练完成之后部署方式取决于使用场景。如果是在实验室里做验证直接调PyTorch加载权重即可如果是要上产线一般有两种路径路径一是把模型导出成TorchScript或ONNX格式用LibTorch或ONNX Runtime做推理。这样部署环境可以完全脱离Python训练环境C或者Java都能调用对产线集成更友好。dummy_input torch.randn(1, 1, 1024) traced_model torch.jit.trace(model.cpu(), dummy_input) traced_model.save(bearing_cnn_scripted.pt)路径二是把模型封装成一个Web服务用FastAPI或Flask提供HTTP接口。振动传感器数据通过采集卡上传到服务器服务器跑推理返回诊断结果。这种方案适合已有物联网平台的工厂后端直接对接数据中台。下面是一个极简的FastAPI推理服务示例from fastapi import FastAPI, UploadFile, File import numpy as np import torch import io app FastAPI() model BearingCNN(num_classes4) model.load_state_dict(torch.load(best_bearing_cnn.pth, map_locationcpu)) model.eval() LABELS [normal, inner_race_fault, outer_race_fault, ball_fault] app.post(/predict) async def predict(file: UploadFile File(...)): data np.load(io.BytesIO(await file.read())) signal data[signal].astype(np.float32)[:1024] if len(signal) 1024: signal np.pad(signal, (0, 1024 - len(signal))) tensor torch.FloatTensor(signal).unsqueeze(0).unsqueeze(0) with torch.no_grad(): out model(tensor) prob torch.softmax(out, dim1) pred int(out.argmax(dim1)) return {label: LABELS[pred], confidence: float(prob[0][pred])}部署时有个容易忽略的细节训练时的预处理归一化、去均值、滤波必须一模一样地在推理端复现。很多人在这一步翻车是因为训练时对数据做了标准化部署时忘了减均值除标准差导致推理结果完全不可用。建议把预处理逻辑单独封装成函数训练和推理共用不要各写一份。5.4 离线诊断与在线监测的不同策略离线诊断是指定期采集一段数据进行分析比如巡检人员每周采几秒钟振动数据跑一次模型看当前轴承状态是否正常在线监测则是传感器实时采集模型以固定频率比如每秒钟一次跑推理连续输出健康状态。这两种模式对模型推理延迟的要求完全不同。离线诊断用CPU跑这个小模型单次推理时间在毫秒级完全没问题在线监测就要考虑数据缓冲、推理频率、结果存储的工程设计了。我在项目里常用的做法是在线监测不直接输出“某一个窗口的类别”而是做一个简单的滑动投票机制——连续10个窗口中如果有7个以上判为同一类故障才触发报警。这个策略能有效避免单窗口的随机误报代价只是报警延迟了10个窗口的时间如果每窗口1秒就是延迟10秒。6. 从这套系统还能延伸出什么时频图、多传感器融合与迁移学习6.1 如果追求更高的准确率尝试把一维信号转成时频图前面说过一维CNN已经够用。但如果你做的是比较难的场景——比如故障早期、信号极其微弱、噪声水平很高——一维CNN的特征提取能力可能不再够用。这时可以考虑把信号转成二维时频图用二维CNN来分类。这相当于人为地把“频率随时间变化”的信息在输入层就显式地展开给模型看省去了模型自己去学频域特征的功夫。短时傅里叶变换STFT是最容易上手的方式from scipy.signal import stft f, t, Zxx stft(signal, fs12000, nperseg256, noverlap128) spectrogram np.abs(Zxx) # 取幅值把这个幅值矩阵当成单通道灰度图喂给二维CNN。如果你用PyTorch可以用torch.stft直接完成。小波变换CWT在低频段的分辨率更好实现上稍微麻烦一点但原理类似。个人经验是对轴承信号两者的分类效果差异没有想象中大选一个顺手的就是了。6.2 多传感器融合不是所有问题都能靠单点振动解决实际产线里一个轴承座旁边往往不止一个振动传感器——轴承座垂直方向、水平方向各装一个加速度计甚至还有温度、声发射传感器。多传感器融合可以显著提升诊断鲁棒性。最简单的做法是每个传感器通道各自跑一遍滑窗把多个通道的窗口数据拼接成多通道输入比如两个振动传感器就是2通道输入模型的第一层Conv1d的in_channels改成2即可。更复杂一点的做法是每个传感器单独训练一个模型最后做决策级投票。这种方案的好处是各传感器之间有独立的故障检测能力一个传感器掉线了系统还能靠其他传感器工作。缺点是部署负担变重。我见过不少失败的“多传感器融合”项目多数是败在传感器之间的时间同步问题上——如果两个通道的数据没对齐融合进去的不只是特征还有噪声。所以做融合之前先把时间同步的问题解决掉。6.3 迁移学习的两种用法迁移学习在这个领域有两个常见用法。第一种是跨数据集迁移在CWRU上先把模型训好然后拿到自己的数据上微调。大多数情况下哪怕源域和目标域的设备类型不完全一样前几层卷积学到的“边缘检测器”依然通用微调时只需要重新训练后面几层。第二种是跨模态迁移拿ImageNet上预训练的ResNet权重把输入换成轴承信号的时频图用预训练权重的低层特征来初始化模型。这在数据量特别小的时候很管用。但有一点要泼冷水如果目标数据量和源域差异都适中从头训练一个小CNN的效果可能比微调一个大预训练模型更好。预训练模型的归纳偏置是针对自然图像的振动时频图在统计特性上跟它差别不小强搬不一定有好处。迁移学习是个工具箱不是万应灵丹。6.4 跟传统信号处理方法结合深度学习不是要取代FFT而是合作最后想聊一个容易被忽略的点。深度学习的强项是端到端学习但它并不意味着传统信号处理就完全没用了。实际上把FFT、包络谱、峭度这些传统特征作为辅助信息拼接到模型输入里可以给模型提供“先验引导”尤其在小样本场景下效果立竿见影。我在一个风电齿轮箱轴承项目里试过只喂原始波形准确率92%把包络谱特征拼进去后准确率直接到96%。那个项目的数据量很少传统特征相当于帮模型绕过了“从零开始学频谱”这个难题。这种做法在工程上也不难——在模型输入部分增加一个特征分支把手工特征向量拼接进卷积层输出的特征向量再接全连接层分类。代码上比纯CNN多不了多少但收益有时非常可观。写在最后关于轴承故障诊断落地的一点个人体会做这套系统的过程中我最大的感受是深度学习模型的代码反而是整个项目里最简单的一环真正花时间的是数据怎么划分、预处理怎么做、现场数据跟实验室数据不一致怎么办。如果非要给后来者一个建议那就是——先别急着堆模型先把数据层面的问题想清楚。数据干净了、划分合理了一个不复杂的CNN就能给你惊喜数据没搞清楚再大的模型也只是在垃圾上建模。这套系统的下一步我打算把模型部署到一块嵌入式板子上放在实验台旁边做实时监测——到时候再写一篇分享边缘端推理的实测数据。本文还有配套的精品资源点击获取