優(yōu)化到CNN分類的完整實(shí)戰(zhàn))
簡介這份資源面向軸承故障診斷方向的研究生、算法工程師與信號處理學(xué)習(xí)者提供一套將高階變分模態(tài)分解與卷積神經(jīng)網(wǎng)絡(luò)結(jié)合的完整Matlab實(shí)現(xiàn)方案用于解決西儲大學(xué)驅(qū)動端振動信號中故障特征提取難、噪聲干擾強(qiáng)、分類準(zhǔn)確率不穩(wěn)定等問題。壓縮包共15個文件約60.73MB以9個m腳本為核心配套4個xlsx數(shù)據(jù)集、1個pdf算法論文與1個txt環(huán)境說明覆蓋從信號分解、特征提取到模型訓(xùn)練與評估的全流程。資源中HO-VMD負(fù)責(zé)多層次分解并去除噪聲CNN完成故障模式識別另附五種適應(yīng)度函數(shù)與空間Dropout層等模塊便于讀者復(fù)現(xiàn)實(shí)驗、替換數(shù)據(jù)或?qū)Ρ炔煌刂抵笜?biāo)。目前已有203人學(xué)習(xí)下載適合希望快速搭建故障診斷基線、理解分解與深度學(xué)習(xí)融合思路的讀者參考。1. 從「HO-VMD-CNN」說起為什么這套組合拳在軸承故障診斷里值得一試如果你手頭有一臺旋轉(zhuǎn)機(jī)械的振動監(jiān)測數(shù)據(jù)想從一堆噪聲里把早期故障特征摳出來大概率繞不開三個詞VMD、CNN、以及一個負(fù)責(zé)調(diào)參的優(yōu)化器。HO-VMD-CNN 這套組合本質(zhì)上是用河馬優(yōu)化算法Hippopotamus OptimizationHO去自動搜索變分模態(tài)分解VMD的關(guān)鍵參數(shù)再把分解后的模態(tài)分量喂給卷積神經(jīng)網(wǎng)絡(luò)CNN做故障分類。西儲大學(xué)軸承數(shù)據(jù)集則是這個方向里被跑爛了的公開基準(zhǔn)——不是因為它簡單而是因為它足夠干凈、標(biāo)注明確適合驗證方法本身是否成立。我見過太多人卡在同一個地方VMD 的模態(tài)數(shù) K 和懲罰因子 α 靠手調(diào)調(diào)一天也不一定找到最優(yōu)組合換一組工況就崩。HO 這類群智能優(yōu)化算法的價值就在這兒——把玄學(xué)調(diào)參變成可復(fù)現(xiàn)的搜索過程。這套方案適合誰適合已經(jīng)跑通過基礎(chǔ) CNN 分類、想往「信號分解 深度學(xué)習(xí)」方向深入的人也適合做設(shè)備健康管理、想找一個能寫進(jìn)論文或落地原型的完整鏈路的人。下面我按實(shí)際動手順序把這條鏈路拆開講。2. VMD 與 HO 的配合邏輯參數(shù)搜索到底在搜什么2.1 VMD 的兩個命門參數(shù)K 和 αVMD 的核心思想是把一個實(shí)值信號分解成 K 個有限帶寬的模態(tài)分量IMF每個分量圍繞一個中心頻率。它的數(shù)學(xué)骨架是變分問題最小化各模態(tài)的帶寬之和約束條件是所有模態(tài)加起來能重構(gòu)原信號。求解過程用交替方向乘子法ADMM迭代最終得到 K 個模態(tài)。問題在于K 和 α 直接決定分解質(zhì)量。K 太小模態(tài)混疊故障沖擊特征被淹沒K 太大過分解同一個故障特征被拆到多個模態(tài)里反而增加后續(xù)分類的干擾。α 控制帶寬太小則模態(tài)帶寬過寬噪聲混入太大則帶寬過窄模態(tài)中心頻率漂移。常見做法是固定 α 調(diào) K或者用中心頻率觀察法手動試——但這不是可復(fù)現(xiàn)的路子。我一般會這樣設(shè)定搜索范圍K 取 2 到 10α 取 200 到 3000。這個范圍覆蓋了西儲大學(xué)數(shù)據(jù)在 12kHz 采樣率下大多數(shù)工況的合理分解區(qū)間。搜索空間不大但手動遍歷依然費(fèi)時所以交給 HO。2.2 HO 優(yōu)化算法為什么適合這個場景河馬優(yōu)化算法是近年提出的一種群智能算法模擬河馬在自然界中的覓食、防御和逃逸行為。它的位置更新公式里包含了對最優(yōu)個體的跟隨和對隨機(jī)個體的排斥平衡了探索和開發(fā)。相比粒子群PSO和遺傳算法GAHO 在低維參數(shù)搜索上收斂更快而且參數(shù)少——主要就是種群規(guī)模和迭代次數(shù)。用 HO 搜 VMD 參數(shù)適應(yīng)度函數(shù)怎么定是關(guān)鍵。常見做法是用包絡(luò)熵對每個模態(tài)分量算包絡(luò)熵取最小值作為適應(yīng)度。包絡(luò)熵越小說明該模態(tài)包含的周期性沖擊成分越明顯噪聲越少。也有用樣本熵或排列熵的但包絡(luò)熵在軸承故障診斷里更直接因為它對沖擊衰減敏感。注意適應(yīng)度函數(shù)的選擇直接決定 HO 往哪個方向搜。如果你用包絡(luò)熵HO 會傾向于找到讓某個模態(tài)最「干凈」的 K 和 α但這個模態(tài)不一定對分類最有利。穩(wěn)妥做法是分解后取前幾個模態(tài)一起送 CNN而不是只取適應(yīng)度最優(yōu)的那一個。2.3 用 Python 跑通 HO 優(yōu)化 VMD 的最小閉環(huán)下面這段代碼是我常用的骨架依賴vmdpy和numpy。HO 部分我手寫了一個簡化版核心邏輯保留方便你直接改適應(yīng)度函數(shù)。import numpy as np from vmdpy import VMD # 包絡(luò)熵計算 def envelope_entropy(imf): # 希爾伯特變換取包絡(luò) analytic np.abs(np.fft.ifft(np.fft.fft(imf) * 2)) # 歸一化 p analytic / (np.sum(analytic) 1e-12) # 避免log0 p p[p 0] return -np.sum(p * np.log(p)) # HO 適應(yīng)度函數(shù)輸入 [K, alpha]輸出包絡(luò)熵最小值 def fitness(params, signal): K int(np.round(params[0])) alpha params[1] # 邊界保護(hù) if K 2 or K 10 or alpha 200 or alpha 3000: return 1e6 try: u, u_hat, omega VMD(signal, alpha, 0, K, 0, 1, 1e-7) # 取所有模態(tài)中包絡(luò)熵最小的值作為適應(yīng)度 return min(envelope_entropy(u[i]) for i in range(K)) except Exception: return 1e6 # 簡化 HO種群規(guī)模 N迭代 T def HO_optimize(signal, N20, T30): dim 2 lb np.array([2, 200]) ub np.array([10, 3000]) # 初始化 X lb (ub - lb) * np.random.rand(N, dim) fit np.array([fitness(x, signal) for x in X]) best_idx np.argmin(fit) best_X X[best_idx].copy() best_fit fit[best_idx] for t in range(T): for i in range(N): # 河馬覓食行為向最優(yōu)靠近同時受隨機(jī)個體排斥 r1, r2 np.random.rand(), np.random.rand() X_new X[i] r1 * (best_X - X[i]) r2 * (X[np.random.randint(N)] - X[i]) # 邊界裁剪 X_new np.clip(X_new, lb, ub) f_new fitness(X_new, signal) if f_new fit[i]: X[i] X_new fit[i] f_new if f_new best_fit: best_fit f_new best_X X_new.copy() return best_X, best_fit # 示例讀取一段西儲大學(xué)數(shù)據(jù)假設(shè)已加載為 signal # signal np.loadtxt(bearing_signal.txt) # best_params, best_score HO_optimize(signal) # print(最優(yōu) K:, int(np.round(best_params[0])), 最優(yōu) alpha:, best_params[1])這段代碼的邏輯說明fitness函數(shù)接收 K 和 α調(diào)用 VMD 分解然后計算每個模態(tài)的包絡(luò)熵返回最小值。HO 主循環(huán)里每個個體向當(dāng)前最優(yōu)靠近同時被隨機(jī)個體推離這是河馬算法里「覓食 防御」的簡化表達(dá)。參數(shù)說明種群規(guī)模 N 取 20 足夠迭代 T 取 30 在西儲數(shù)據(jù)上基本收斂K 的搜索范圍 2 到 10α 的搜索范圍 200 到 3000這兩個邊界是根據(jù) 12kHz 采樣率下軸承故障沖擊的典型頻率范圍定的。如果你換采樣率或換設(shè)備邊界要重新估。跑完這一步你會拿到一組 K 和 α以及對應(yīng)的分解模態(tài)。接下來才是 CNN 的事。3. 從 VMD 模態(tài)到 CNN 輸入數(shù)據(jù)組織與模型搭建3.1 模態(tài)分量怎么變成 CNN 能吃的張量VMD 輸出的是 K 個一維時間序列每個長度和原信號一樣。CNN 處理一維信號有兩種常見做法一是直接把每個模態(tài)當(dāng)作一個通道組成 (K, L) 的二維輸入用一維卷積在 L 方向滑動二是把模態(tài)做時頻變換轉(zhuǎn)成二維譜圖用二維卷積。我傾向于第一種因為保留了一維時序結(jié)構(gòu)計算量小而且西儲大學(xué)數(shù)據(jù)在 12kHz 下每段 1024 或 2048 點(diǎn)直接做一維卷積足夠。具體操作對每段振動信號先用 HO 搜出的 K 和 α 做 VMD得到 K 個模態(tài)。然后對每個模態(tài)做歸一化減均值除標(biāo)準(zhǔn)差再堆疊成 (K, L) 的矩陣。標(biāo)簽沿用原信號的故障類別。這樣一條樣本就是一個二維數(shù)組CNN 的輸入通道數(shù)等于 K。注意K 是 HO 搜出來的不同工況下 K 可能不同。如果你要做跨工況遷移要么固定 K要么在遷移時重新搜。我一般會在目標(biāo)域上重新跑一次 HO因為 VMD 參數(shù)對信號本身的頻率結(jié)構(gòu)很敏感。3.2 一維 CNN 的結(jié)構(gòu)設(shè)計與參數(shù)選擇CNN 部分不需要太深。西儲大學(xué)數(shù)據(jù)類別少通常 10 類正常 3 種故障尺寸 × 3 種故障位置樣本量在幾千到一萬左右太深的網(wǎng)絡(luò)容易過擬合。我常用的結(jié)構(gòu)是兩層一維卷積 兩層池化 全局平均池化 全連接分類。import torch import torch.nn as nn class VMD_CNN(nn.Module): def __init__(self, in_channels, num_classes10): super().__init__() # 第一層卷積提取局部沖擊特征 self.conv1 nn.Conv1d(in_channels, 32, kernel_size7, padding3) self.bn1 nn.BatchNorm1d(32) self.pool1 nn.MaxPool1d(2) # 第二層卷積組合特征 self.conv2 nn.Conv1d(32, 64, kernel_size5, padding2) self.bn2 nn.BatchNorm1d(64) self.pool2 nn.MaxPool1d(2) # 全局平均池化替代展平減少參數(shù)量 self.gap nn.AdaptiveAvgPool1d(1) self.fc nn.Linear(64, num_classes) self.relu nn.ReLU() def forward(self, x): # x: (batch, K, L) x self.relu(self.bn1(self.conv1(x))) x self.pool1(x) x self.relu(self.bn2(self.conv2(x))) x self.pool2(x) x self.gap(x).squeeze(-1) return self.fc(x)邏輯說明in_channels就是 VMD 的 K。第一層卷積核大小 7是為了覆蓋軸承故障沖擊的典型衰減周期第二層 5進(jìn)一步組合。每層后接 BatchNorm 和 ReLU池化用 MaxPool1d(2) 降采樣。全局平均池化把 (batch, 64, L) 壓成 (batch, 64)再接全連接。參數(shù)說明卷積核數(shù)量 32 和 64 是經(jīng)驗值數(shù)據(jù)量再大可以加到 64 和 128kernel_size 不要小于 3否則感受野不夠覆蓋沖擊。學(xué)習(xí)率我一般用 1e-3優(yōu)化器 Adambatch size 64訓(xùn)練 50 到 100 epoch早??打炞C集損失。3.3 訓(xùn)練流程與驗證集劃分的坑西儲大學(xué)數(shù)據(jù)有一個經(jīng)典陷阱如果按隨機(jī)劃分同一段信號切出來的樣本可能同時出現(xiàn)在訓(xùn)練集和驗證集里導(dǎo)致驗證準(zhǔn)確率虛高。正確做法是按「記錄」劃分而不是按「樣本」劃分。也就是說先把原始記錄分成訓(xùn)練記錄和驗證記錄再從各自記錄里切樣本。我一般會這樣做對每個故障類別取 70% 的記錄做訓(xùn)練30% 做驗證。切樣本時用重疊采樣增加樣本量但重疊只發(fā)生在同一記錄內(nèi)部。這樣驗證集才能真正反映模型對未見記錄的泛化能力。# 偽代碼按記錄劃分 records load_all_records() # 每個記錄是一個長信號 train_records, val_records [], [] for cls in records: n len(records[cls]) idx np.random.permutation(n) split int(0.7 * n) train_records [records[cls][i] for i in idx[:split]] val_records [records[cls][i] for i in idx[split:]] # 再從記錄切樣本 def slice_samples(record, window1024, step512): samples [] for start in range(0, len(record) - window, step): samples.append(record[start:startwindow]) return samples這段邏輯的關(guān)鍵是step小于window時產(chǎn)生重疊但重疊只在同一記錄內(nèi)。參數(shù)說明window 取 1024 或 2048取決于你的采樣率和故障沖擊周期step 取 window 的一半既能增加樣本又不會過度冗余。4. 避坑與排查HO-VMD-CNN 鏈路上最容易翻車的五個地方4.1 現(xiàn)象HO 搜出來的 K 每次都不一樣原因HO 是隨機(jī)初始化種群每次運(yùn)行結(jié)果有波動。如果適應(yīng)度函數(shù)對 K 的變化不敏感搜索就會在多個 K 值之間跳。解決固定隨機(jī)種子并且把 HO 跑多次取適應(yīng)度最優(yōu)的那組參數(shù)。我一般跑 5 次取包絡(luò)熵最小的。如果 5 次結(jié)果差異很大說明適應(yīng)度函數(shù)區(qū)分度不夠考慮換樣本熵或增加懲罰項。4.2 現(xiàn)象VMD 分解報錯或模態(tài)全為零原因α 太小或 K 太大時ADMM 迭代可能不收斂或者某個模態(tài)能量趨近于零。西儲大學(xué)數(shù)據(jù)里正常軸承的信號能量集中在低頻如果 α 設(shè)得過大高頻模態(tài)會被壓沒。解決在 fitness 函數(shù)里加異常捕獲返回一個大懲罰值讓 HO 避開這些參數(shù)組合。同時檢查信號是否做了去均值處理VMD 對直流分量敏感。4.3 現(xiàn)象CNN 訓(xùn)練準(zhǔn)確率很高但驗證準(zhǔn)確率上不去原因最常見的就是 3.3 里說的按樣本隨機(jī)劃分導(dǎo)致數(shù)據(jù)泄漏。另一個原因是 VMD 分解時用了整段信號的信息而訓(xùn)練集和驗證集的信號如果有重疊分解結(jié)果也會泄漏。解決嚴(yán)格按記錄劃分并且 VMD 分解只在各自記錄內(nèi)部做。不要對整段長信號做一次 VMD 再切樣本那樣模態(tài)里混入了驗證集的信息。4.4 現(xiàn)象換一個工況模型直接崩原因VMD 參數(shù)是在源域上搜的目標(biāo)域的頻率結(jié)構(gòu)不同分解出來的模態(tài)物理意義變了。CNN 學(xué)到的是源域的模態(tài)模式遷移不過去。解決目標(biāo)域上重新跑 HO 搜參數(shù)或者用域適應(yīng)方法。我一般會先看目標(biāo)域信號的中心頻率分布如果和源域差得遠(yuǎn)就重新搜。如果差得近可以凍結(jié) CNN 前幾層只微調(diào)全連接。4.5 現(xiàn)象訓(xùn)練 loss 震蕩不收斂原因?qū)W習(xí)率太大或者 BatchNorm 在 batch size 太小時不穩(wěn)定。一維卷積對輸入尺度敏感如果 VMD 模態(tài)沒歸一化各通道量級差異大BN 也救不回來。解決每個模態(tài)單獨(dú)做 z-score 歸一化學(xué)習(xí)率降到 1e-4 試試batch size 不要小于 32。如果還震蕩檢查標(biāo)簽有沒有錯位——我見過有人把標(biāo)簽和樣本的對應(yīng)關(guān)系搞反了loss 直接飛。5. 進(jìn)階技巧用包絡(luò)譜峰值比做二次驗證跑通 HO-VMD-CNN 之后你拿到的是一個分類準(zhǔn)確率。但準(zhǔn)確率不能告訴你模型到底學(xué)到了什么。我習(xí)慣在訓(xùn)練完之后對每個類別的驗證樣本做一次包絡(luò)譜分析看故障特征頻率處的峰值比是否和標(biāo)簽一致。這一步能幫你判斷模型是學(xué)到了物理特征還是只是記住了數(shù)據(jù)里的偽相關(guān)。具體做法對 VMD 分解后的模態(tài)取包絡(luò)熵最小的那個模態(tài)做希爾伯特變換得到包絡(luò)再對包絡(luò)做 FFT 得到包絡(luò)譜。軸承故障的特征頻率外圈、內(nèi)圈、滾動體可以根據(jù)軸承幾何參數(shù)和轉(zhuǎn)速算出來。看包絡(luò)譜在特征頻率處有沒有明顯峰值峰值比特征頻率幅值 / 附近頻段均值大于 3 就算顯著。def envelope_spectrum(imf, fs12000): # 希爾伯特變換取包絡(luò) analytic np.abs(np.fft.ifft(np.fft.fft(imf) * 2)) # 去均值 analytic analytic - np.mean(analytic) # FFT spec np.abs(np.fft.fft(analytic)) freq np.fft.fftfreq(len(imf), 1/fs) # 只取正半軸 half len(freq) // 2 return freq[:half], spec[:half] # 計算峰值比 def peak_ratio(freq, spec, target_freq, band50): idx np.argmin(np.abs(freq - target_freq)) peak spec[idx] # 附近頻段均值 mask (freq target_freq - band) (freq target_freq band) mask[idx] False noise np.mean(spec[mask]) return peak / (noise 1e-12)邏輯說明envelope_spectrum返回包絡(luò)譜的頻率軸和幅值。peak_ratio計算目標(biāo)頻率處的幅值與附近頻段均值的比。參數(shù)說明target_freq根據(jù)你的軸承型號和轉(zhuǎn)速算西儲大學(xué)數(shù)據(jù)里常用的是外圈故障頻率約 107Hz取決于具體工況band取 50Hz 覆蓋附近噪聲。如果峰值比低于 3說明這個模態(tài)的故障特征不明顯可能需要重新檢查 VMD 參數(shù)或 CNN 是否真的學(xué)到了東西。我自己的習(xí)慣是每次跑完分類隨機(jī)抽 10 個驗證樣本做包絡(luò)譜人工看一眼。如果峰值比和標(biāo)簽對不上先別急著調(diào) CNN回頭查 VMD 分解和 HO 的適應(yīng)度函數(shù)。這套鏈路里信號分解的質(zhì)量決定了上限CNN 只是逼近這個上限。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取