習(xí)1DCNN的軸承故障診斷:從振動信號到端到端分類實(shí)踐)
簡介基于深度學(xué)習(xí)的1DCNN軸承故障診斷源碼包面向機(jī)械故障診斷、工業(yè)預(yù)測性維護(hù)領(lǐng)域的工程師與研究人員提供從振動信號預(yù)處理、1DCNN模型構(gòu)建、訓(xùn)練優(yōu)化到故障分類的完整實(shí)現(xiàn)方案。資源共50個文件包體僅3.64MB以Python源碼.py為主涵蓋數(shù)據(jù)讀取與預(yù)處理、網(wǎng)絡(luò)結(jié)構(gòu)定義、訓(xùn)練測試以及t-SNE特征可視化另有17張PNG結(jié)果圖、配置XML、說明TXT和混淆矩陣Excel表格便于對照實(shí)驗(yàn)結(jié)果與評估指標(biāo)。源碼基于TensorFlow/Keras編寫支持CWRU數(shù)據(jù)集通過滑動窗口采樣、歸一化等操作對原始振動信號進(jìn)行預(yù)處理可識別滾動體缺陷、內(nèi)圈故障、外圈故障等典型模式同時提供多種優(yōu)化器、損失函數(shù)與評估指標(biāo)配置并包含模型保存與加載功能。目錄包含data、models、utils等模塊結(jié)構(gòu)清晰適合入門學(xué)習(xí)和在此基礎(chǔ)上進(jìn)行二次開發(fā)。已有424人學(xué)習(xí)下載內(nèi)容覆蓋從數(shù)據(jù)準(zhǔn)備到結(jié)果分析的完整鏈路是快速掌握深度學(xué)習(xí)驅(qū)動軸承故障診斷實(shí)踐流程的實(shí)用資料。1. 基于深度學(xué)習(xí)1DCNN軸承故障診斷為什么直接把振動信號送進(jìn)卷積網(wǎng)絡(luò)遇到軸承故障診斷很多人的第一反應(yīng)是FFT頻譜、小波包分解或者時頻圖先做特征工程再訓(xùn)練分類器。但在公開的CWRU軸承數(shù)據(jù)集上跑過幾組實(shí)驗(yàn)后會發(fā)現(xiàn)這些預(yù)處理環(huán)節(jié)引入的參數(shù)比網(wǎng)絡(luò)本身還多窗函數(shù)選什么、頻帶切多寬、小波基用哪個每換一個數(shù)據(jù)集就得重新調(diào)一輪。這套基于深度學(xué)習(xí)1DCNN軸承故障診斷源碼走的是另一條路把傳感器采集到的原始振動加速度信號直接當(dāng)作一維時間序列通過一維卷積網(wǎng)絡(luò)自動提取沖擊特征并完成故障分類。它解決的是預(yù)測性維護(hù)里最核心的問題——只有振動數(shù)據(jù)時如何區(qū)分正常、內(nèi)圈故障、外圈故障和滾動體故障。適合正在復(fù)現(xiàn)論文、做設(shè)備健康管理平臺或者在邊緣設(shè)備上跑推理的工程師閱讀從數(shù)據(jù)切分到網(wǎng)絡(luò)落地都有可直接復(fù)用的代碼。2. 數(shù)據(jù)準(zhǔn)備CWRU數(shù)據(jù)加載、滑窗切分與防泄漏劃分2.1 CWRU數(shù)據(jù)集的類別定義與文件組織CWRU是軸承故障診斷領(lǐng)域使用最廣泛的公開數(shù)據(jù)集用SKF6205軸承在驅(qū)動端做故障注入采集采樣率常用12kHz。故障由電火花加工產(chǎn)生分為內(nèi)圈IR、外圈OR、滾動體B三種位置每種位置有0.007、0.014、0.021英寸三個損傷尺寸加上正常狀態(tài)一共構(gòu)成10個分類。這是絕大多數(shù)1DCNN復(fù)現(xiàn)實(shí)驗(yàn)采用的類別口徑源碼里也建議按10類做。實(shí)驗(yàn)臺還帶0到3馬力四檔負(fù)載對應(yīng)轉(zhuǎn)速從1797r/min降為1730r/min這個轉(zhuǎn)速信息在后面驗(yàn)證跨工況泛化時很有用。數(shù)據(jù)以mat格式存儲文件名中包含了故障位置和尺寸信息常見的讀取方式是通過字段名過濾出驅(qū)動端振動信號。下面這個函數(shù)把mat文件讀成一維數(shù)組并做基本的均值方差標(biāo)準(zhǔn)化import numpy as np from scipy.io import loadmat def load_cwru_signal(mat_path, sensorDE): mat loadmat(mat_path) key [k for k in mat.keys() if k.endswith(f_{sensor}_time)][0] sig mat[key].flatten().astype(np.float32) # 按整個文件做z-score消除不同實(shí)驗(yàn)間的基線漂移 sig (sig - sig.mean()) / (sig.std() 1e-8) return sig這里用endswith(f_{sensor}_time)匹配字段名而不是寫死完整字段名是因?yàn)榇蠖鄶?shù)CWRU版本的mat文件字段形如X097_DE_time但不同下載源頭存在小差異。按歸一化處理說明對整個連續(xù)信號計(jì)算均值和標(biāo)準(zhǔn)差相當(dāng)于把每個實(shí)驗(yàn)的直流分量和幅值差異消掉。如果后續(xù)按滑動窗口切分且沒有重疊這個操作不會引入標(biāo)簽泄漏但如果做的是窗口內(nèi)歸一化則下面一節(jié)還要再處理。2.2 滑窗切分窗口長度與重疊率怎么定連續(xù)振動信號不能整段塞進(jìn)網(wǎng)絡(luò)需要滑窗切成固定長度樣本。窗口長度直接影響模型能觀察到的物理范圍。12kHz采樣率下主軸轉(zhuǎn)一圈大約400個采樣點(diǎn)窗口取2048相當(dāng)于連續(xù)觀察約5個旋轉(zhuǎn)周期足以覆蓋一次故障沖擊從發(fā)生到衰減的完整序列。窗口過短抓不全周期沖擊過長則單個樣本包含太多轉(zhuǎn)速波動和無關(guān)噪聲同時樣本數(shù)量變少。所以2048是這個任務(wù)里比較穩(wěn)妥的起點(diǎn)。重疊率解決的是樣本量問題。0%重疊時每個文件只能切出幾十個樣本10類數(shù)據(jù)全部算上也不過幾千條訓(xùn)練一個帶BatchNorm的CNN顯得捉襟見肘。50%重疊可以把樣本量提升近一倍同時相鄰窗口的相關(guān)性還處在可控范圍內(nèi)75%重疊會帶來嚴(yán)重的樣本冗余訓(xùn)練速度變慢且測試集容易出現(xiàn)虛高。以下函數(shù)實(shí)現(xiàn)滑窗切分返回一個二維數(shù)組每一行是一個窗口def sliding_window(sig, win_len2048, overlap0.5): step int(win_len * (1 - overlap)) if len(sig) win_len: return np.empty((0, win_len), dtypenp.float32) starts np.arange(0, len(sig) - win_len 1, step) return np.stack([sig[s:s win_len] for s in starts])參數(shù)說明win_len控制網(wǎng)絡(luò)輸入的序列長度overlap0.5表示相鄰窗口重疊一半實(shí)際步長為1024個采樣點(diǎn)。切分完成后一個長度約50萬點(diǎn)的原始文件可以產(chǎn)出約490個窗口10類數(shù)據(jù)全量處理后在2萬到4萬樣本量級訓(xùn)練一個1DCNN模型在普通GPU上幾分鐘就能跑完一個epoch。切分時注意保留每個樣本對應(yīng)的原始文件ID后續(xù)劃分訓(xùn)練集和測試集時要用。2.3 歸一化與防數(shù)據(jù)泄漏的數(shù)據(jù)集劃分這一步是決定最終準(zhǔn)確率是否可信的關(guān)鍵。兩種常見做法有本質(zhì)區(qū)別按窗口做歸一化每個樣本減去自身均值再除以自身標(biāo)準(zhǔn)差按全局統(tǒng)計(jì)歸一化用整個文件或整個訓(xùn)練集的均值和標(biāo)準(zhǔn)差統(tǒng)一處理。推薦前者因?yàn)楝F(xiàn)場采集的振動信號經(jīng)常存在基線漂移和幅值波動窗口內(nèi)歸一化可以消除這些設(shè)備相關(guān)變量讓模型更關(guān)注沖擊波形本身。def normalize_window(win): return (win - win.mean()) / (win.std() 1e-8)直接調(diào)用train_test_split隨機(jī)打亂所有窗口是一個隱蔽但高頻出現(xiàn)的數(shù)據(jù)泄漏問題。由于相鄰窗口重疊且來自同一段連續(xù)記錄隨機(jī)劃分后訓(xùn)練集里某幾個窗口和測試集里另幾個窗口在時間上幾乎重合模型相當(dāng)于“見過”測試數(shù)據(jù)的相鄰片段測試準(zhǔn)確率會虛高到很有誤導(dǎo)性。正確做法是按原始文件分組保證同一段連續(xù)記錄的全部窗口只出現(xiàn)在訓(xùn)練集或只出現(xiàn)在測試集中。一個更嚴(yán)格的驗(yàn)證方式是跨負(fù)載劃分用0、1、2馬力數(shù)據(jù)訓(xùn)練3馬力數(shù)據(jù)測試此時轉(zhuǎn)速和載荷都變了得到的準(zhǔn)確率才是模型真實(shí)泛化能力的體現(xiàn)。源碼里建議至少保留一組這樣的劃分結(jié)果。3. 1DCNN網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)卷積核、批量歸一化與參數(shù)推算3.1 為什么一維卷積適合振動信號從濾波器組到感受野從頻域視角看一維卷積核本質(zhì)上是一組可學(xué)習(xí)的帶通濾波器卷積過程就是在原始信號上滑動計(jì)算局部加權(quán)和。軸承故障的特征頻率如內(nèi)圈BPFI、外圈BPFO在時域表現(xiàn)為周期性沖擊這些沖擊在卷積層看來就是局部波形的重復(fù)模式符合卷積權(quán)值共享的特性。相比把信號轉(zhuǎn)成時頻圖再用二維CNN處理1DCNN省去了短時傅里葉變換的窗長、重疊和頻帶選擇參數(shù)端到端直接從原始信號學(xué)習(xí)推理時也更適合部署在資源受限的采集設(shè)備上。卷積核尺寸選擇是1DCNN設(shè)計(jì)里最重要的決定。第一層卷積感受野太小看到的只是幾個采樣點(diǎn)的瞬時幅值無法捕捉一個完整的沖擊波形太大又增加參數(shù)和計(jì)算量。常見的做法是第一層用大卷積核配合大步長快速壓縮序列長度后面幾層換回小卷積核提煉局部細(xì)節(jié)。典型配置是第一層kernel為64、stride為2后面幾層kernel為5、stride為2。設(shè)置第一層kernel大小為64對應(yīng)約5.3毫秒的時間窗覆蓋的物理范圍足夠辨認(rèn)一次沖擊。3.2 網(wǎng)絡(luò)結(jié)構(gòu)與PyTorch源碼實(shí)現(xiàn)下面是一份可以直接用的PyTorch實(shí)現(xiàn)4層一維卷積加自適應(yīng)池化最后接兩層全連接分類器。輸入張量形狀為(batch_size, 1, 2048)輸出形狀為(batch_size, 10)。import torch import torch.nn as nn class Bearing1DCNN(nn.Module): def __init__(self, num_classes10): super().__init__() self.features nn.Sequential( nn.Conv1d(1, 32, kernel_size64, stride2, padding32), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.Conv1d(32, 64, kernel_size5, stride2, padding2), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.Conv1d(64, 128, kernel_size5, stride2, padding2), nn.BatchNorm1d(128), nn.ReLU(inplaceTrue), nn.Conv1d(128, 128, kernel_size5, stride2, padding2), nn.BatchNorm1d(128), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool1d(1), ) self.classifier nn.Sequential( nn.Dropout(0.5), nn.Linear(128, 64), nn.ReLU(inplaceTrue), nn.Linear(64, num_classes), ) def forward(self, x): h self.features(x).squeeze(-1) return self.classifier(h)3.2.1 參數(shù)量與輸出維度推算各層輸出序列長度變化如下表計(jì)算公式為L_out floor((L_in 2 * padding - kernel_size) / stride 1)。層操作kernelstride輸入長度輸出長度conv11→32通道64220481025conv232→64通道521025513conv364→128通道52513257conv4128→128通道52257129poolAdaptiveAvgPool1d--1291選第4層卷積輸出為128維特征是因?yàn)檫@個維度對10類故障已經(jīng)足夠表達(dá)繼續(xù)加寬到256收益有限且全連接層參數(shù)翻倍。全連接部分先壓到64維再加Dropout最后映射到10個類別num_classes參數(shù)可以改成4做正常加三種故障的粗分也可以改成6不區(qū)分故障尺寸改的時候只需要換標(biāo)簽映射網(wǎng)絡(luò)主體不用動。3.2.2 可選給特征層加SE通道注意力模塊如果還想在準(zhǔn)確率上再擠一點(diǎn)可以在每層卷積和BN之后插入一個SE模塊用通道維度的注意力重新加權(quán)各個特征圖。實(shí)現(xiàn)只有十行左右參數(shù)增加很少但對小樣本故障特征有正向作用class SEBlock(nn.Module): def __init__(self, channels, reduction16): super().__init__() self.pool nn.AdaptiveAvgPool1d(1) self.fc nn.Sequential( nn.Conv1d(channels, channels // reduction, 1), nn.ReLU(inplaceTrue), nn.Conv1d(channels // reduction, channels, 1), nn.Sigmoid(), ) def forward(self, x): return x * self.fc(self.pool(x))使用方式是在每個ReLU后插入x se_block(x)通道數(shù)減半時SE的reduction注意不要整除出錯比如32通道時reduction16會得到2個中間通道仍然合法。加了這個模塊后模型參數(shù)會增加約幾千個訓(xùn)練時間影響忽略不計(jì)推薦在最終實(shí)驗(yàn)版本里保留。4. 訓(xùn)練與調(diào)參損失函數(shù)、優(yōu)化器、數(shù)據(jù)增強(qiáng)與過擬合防治4.1 損失函數(shù)、優(yōu)化器與學(xué)習(xí)率策略10分類問題直接使用nn.CrossEntropyLoss()它內(nèi)部已經(jīng)把LogSoftmax和NLLLoss合并所以模型最后一層不需要手動加Softmax。訓(xùn)練時如果某個類別樣本特別少比如現(xiàn)場只采集到正常數(shù)據(jù)和內(nèi)圈故障數(shù)據(jù)可以給CrossEntropyLoss傳weight參數(shù)權(quán)重與樣本數(shù)成反比避免模型把所有樣本都判成多數(shù)類。優(yōu)化器選Adam學(xué)習(xí)率初始化為0.001設(shè)置weight_decay1e-4做L2正則。對這個小規(guī)模分類任務(wù)Adam比SGD收斂快對學(xué)習(xí)率的敏感度低更適合快速復(fù)現(xiàn)和調(diào)參。訓(xùn)練后期常見的做法是切換學(xué)習(xí)率調(diào)度器這里用ReduceLROnPlateau當(dāng)驗(yàn)證集loss連續(xù)5個epoch不下降時把學(xué)習(xí)率乘0.5optimizer torch.optim.Adam(model.parameters(), lr0.001, weight_decay1e-4) criterion nn.CrossEntropyLoss() scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, factor0.5, patience5)學(xué)習(xí)率過大表現(xiàn)為loss劇烈震蕩不收斂過小則loss下降非常緩慢。確認(rèn)優(yōu)化器和調(diào)度器配置后建議記錄的指標(biāo)是訓(xùn)練loss、驗(yàn)證loss和驗(yàn)證準(zhǔn)確率其中驗(yàn)證loss是調(diào)度器判斷的依據(jù)不能用訓(xùn)練loss替代。4.2 訓(xùn)練循環(huán)、檢查點(diǎn)保存與早停訓(xùn)練循環(huán)的要點(diǎn)是每一輪結(jié)束時跑一次驗(yàn)證集同時把驗(yàn)證loss最小的權(quán)重單獨(dú)保存。只保存state_dict而不是整個模型因?yàn)槟P徒Y(jié)構(gòu)可能調(diào)整但權(quán)重文件在相同結(jié)構(gòu)下不會失效。完整訓(xùn)練流程如下def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss, correct, total 0.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() * y.size(0) correct (out.argmax(1) y).sum().item() total y.size(0) return total_loss / total, correct / total torch.no_grad() def evaluate(model, loader, criterion, device): model.eval() total_loss, correct, total 0.0, 0, 0 for x, y in loader: x, y x.to(device), y.to(device) out model(x) loss criterion(out, y) total_loss loss.item() * y.size(0) correct (out.argmax(1) y).sum().item() total y.size(0) return total_loss / total, correct / total訓(xùn)練主循環(huán)里記住三點(diǎn)每個epoch結(jié)束調(diào)用scheduler.step(val_loss)驗(yàn)證準(zhǔn)確率不再提升時保存檢查點(diǎn)跑夠預(yù)定epoch數(shù)或連續(xù)15個epoch驗(yàn)證loss沒刷新紀(jì)錄就提前停止。檢查點(diǎn)里同時保存epoch、模型權(quán)重、優(yōu)化器狀態(tài)便于中斷后恢復(fù)訓(xùn)練best_loss float(inf) for epoch in range(50): train_loss, train_acc train_one_epoch( model, train_loader, optimizer, criterion, device) val_loss, val_acc evaluate(model, val_loader, criterion, device) scheduler.step(val_loss) if val_loss best_loss: best_loss val_loss torch.save({epoch: epoch, model_state: model.state_dict(), optimizer_state: optimizer.state_dict()}, best_ckpt.pt)注意模型在訓(xùn)練和驗(yàn)證之間的狀態(tài)切換。model.eval()必須調(diào)用否則BatchNorm仍在使用訓(xùn)練批次統(tǒng)計(jì)量驗(yàn)證輸出會不準(zhǔn)。訓(xùn)練完成后加載檢查點(diǎn)再評估一次確認(rèn)保存的權(quán)重復(fù)現(xiàn)了驗(yàn)證準(zhǔn)確率。4.3 數(shù)據(jù)增強(qiáng)、Dropout與常見調(diào)參坑CWRU數(shù)據(jù)干凈且類別均衡模型可能很快在訓(xùn)練集上拿到99%以上準(zhǔn)確率但真實(shí)振動信號含有噪聲和轉(zhuǎn)速波動。給訓(xùn)練集加高斯噪聲是最貼近物理現(xiàn)實(shí)的數(shù)據(jù)增強(qiáng)手段按信噪比加噪比直接加隨機(jī)擾動更容易控制強(qiáng)度。常見配置是信噪比20dB到10dB數(shù)值越小噪聲越強(qiáng)默認(rèn)取20dBdef add_noise(x, snr_db20.0): # x為窗口內(nèi)信號, 先計(jì)算原始信號功率再生成對應(yīng)噪聲 sig_power x.pow(2).mean() noise_power sig_power / (10 ** (snr_db / 10.0)) noise torch.randn_like(x) * torch.sqrt(noise_power) return x noise另一個有效增強(qiáng)是隨機(jī)時間平移。故障沖擊的起始位置在每個窗口內(nèi)不一定對齊用torch.roll把信號左右平移幾十個采樣點(diǎn)可以模擬觸發(fā)位置偏移。注意驗(yàn)證集和測試集不要加任何增強(qiáng)只在訓(xùn)練循環(huán)內(nèi)對輸入張量調(diào)用。常見調(diào)參問題集中在三個現(xiàn)象。第一訓(xùn)練準(zhǔn)確率接近100%但驗(yàn)證集只有七八成屬于過擬合優(yōu)先加大Dropout到0.6、增強(qiáng)噪聲強(qiáng)度、把batch size從64降到32。第二loss完全不下降檢查數(shù)據(jù)是否做了窗口內(nèi)歸一化、標(biāo)簽是否從0開始連續(xù)編碼、學(xué)習(xí)率是否設(shè)置在合理區(qū)間。第三訓(xùn)練集和測試集同時出現(xiàn)某個類別準(zhǔn)確率特別低通常是對應(yīng)故障尺寸的數(shù)據(jù)量少或者和其他類別在波形上相似比如滾動體故障的沖擊信號容易被噪聲掩蓋此時單獨(dú)增加該類別的滑窗重疊率比改網(wǎng)絡(luò)更有效。5. 從源碼到現(xiàn)場混淆矩陣、TorchScript導(dǎo)出與跨負(fù)載驗(yàn)證5.1 用混淆矩陣與t-SNE驗(yàn)證診斷效果準(zhǔn)確率只能說明整體水平工程上更關(guān)注哪些類別互相混淆。內(nèi)圈故障和外圈故障在載荷變化時容易混淆滾動體故障誤判成正常的比例通常最高這個現(xiàn)象需要在驗(yàn)證階段用混淆矩陣確認(rèn)。from sklearn.metrics import confusion_matrix torch.no_grad() def predict_labels(model, loader, device): model.eval() preds, gts [], [] for x, y in loader: x x.to(device) preds.extend(model(x).argmax(1).cpu().tolist()) gts.extend(y.tolist()) return confusion_matrix(gts, preds)t-SNE可以直觀展示倒數(shù)第二層特征是否按類別聚攏。把model.features的輸出作為特征向量輸入T-SNEperplexity取30看10個類別在二維平面上是否明顯分簇。如果正常類和某個故障類重疊說明該故障沖擊微弱優(yōu)先考慮數(shù)據(jù)增強(qiáng)而不是加深網(wǎng)絡(luò)。5.2 跨負(fù)載驗(yàn)證從CWRU到現(xiàn)場數(shù)據(jù)的一道坎模型在同一負(fù)載內(nèi)部劃分訓(xùn)練集和測試集時準(zhǔn)確率通常很高但用0馬力數(shù)據(jù)訓(xùn)練、3馬力數(shù)據(jù)測試時轉(zhuǎn)速和載荷變化會讓準(zhǔn)確率明顯下降這是1DCNN軸承故障診斷從論文走向落地時最大的問題。緩解的通用做法是在目標(biāo)工況收集少量數(shù)據(jù)凍結(jié)前兩層卷積只微調(diào)后面特征層和分類器。凍結(jié)參數(shù)數(shù)量少幾百條樣本就能做效果比重新訓(xùn)練整個網(wǎng)絡(luò)穩(wěn)定得多。導(dǎo)出推理模型用TorchScript方便脫離Python環(huán)境部署。注意導(dǎo)出前確定輸入尺寸是固定的(1, 1, 2048)否則trace會失敗model.eval() example_input torch.randn(1, 1, 2048) scripted torch.jit.trace(model, example_input) scripted.save(bearing_1dcnn.pt)現(xiàn)場推理時預(yù)處理必須和訓(xùn)練完全一致特別是窗口內(nèi)歸一化里的均值方差要從當(dāng)前窗口獨(dú)立計(jì)算不能拿訓(xùn)練集的統(tǒng)計(jì)量套用。把滑窗參數(shù)、歸一化邏輯、類別字典和模型文件一起打包現(xiàn)場換人也能直接接手。本文還有配套的精品資源點(diǎn)擊獲取