測:灰狼算法優(yōu)化最小二乘支持向量機(jī)實(shí)戰(zhàn))
凡是做過回歸預(yù)測的朋友基本都經(jīng)歷過那個(gè)階段模型選了一大堆線性回歸太簡單決策樹又容易過擬合神經(jīng)網(wǎng)絡(luò)調(diào)起來讓人頭皮發(fā)麻。后來用上了支持向量機(jī)結(jié)果勉強(qiáng)能看但光是一個(gè)懲罰系數(shù)和一個(gè)核參數(shù)就能讓你在網(wǎng)格搜索里等到懷疑人生。后來我把目光轉(zhuǎn)向了 LSSVM就是最小二乘支持向量機(jī)再配合灰狼優(yōu)化算法自動(dòng)尋參也就是標(biāo)題里的 GWO-LSSVM整套流程一下子順了很多。這篇博客我就把完整的思路、原理、Python 實(shí)現(xiàn)和踩過的坑一次說清楚希望能給你提供一個(gè)可以直接復(fù)制去用的回歸預(yù)測方案。這套方案適合誰適合那些手頭有中等規(guī)模數(shù)據(jù)集、想要穩(wěn)定回歸精度、又不想在調(diào)參上花太多時(shí)間的工程師和研究者。不管你是做風(fēng)功率預(yù)測、房價(jià)估值、工業(yè)過程軟測量還是用各類傳感器數(shù)據(jù)做狀態(tài)預(yù)測GWO-LSSVM 都是一套值得放進(jìn)工具箱的方案。讀懂這篇文章不需要很強(qiáng)的數(shù)學(xué)基礎(chǔ)我會(huì)盡量用大白話把原理講透代碼也給了最小可用版本你只要會(huì) numpy 和 sklearn 的基本操作就能跑通。1. 項(xiàng)目定位GWO 為什么盯上了 LSSVM 這個(gè)模型1.1 LSSVM 回歸的本質(zhì)與痛點(diǎn)LSSVM 的全稱是 Least Squares Support Vector Machine翻譯過來是“最小二乘支持向量機(jī)”。它和傳統(tǒng)支持向量機(jī)最大的區(qū)別在于傳統(tǒng) SVR 用的是不等式約束求解的是二次規(guī)劃問題數(shù)據(jù)量大一點(diǎn)計(jì)算就會(huì)很慢而 LSSVM 把不等式約束換成了等式約束損失函數(shù)里也不再使用 epsilon 不敏感帶而是直接用誤差平方項(xiàng)。這樣原本的凸優(yōu)化問題就會(huì)退化成一組線性方程的求解速度和穩(wěn)定性都好了很多。這聽起來確實(shí)很香但代價(jià)是模型對超參數(shù)變得更加敏感。LSSVM 的核心參數(shù)有兩個(gè)一個(gè)是懲罰系數(shù) C在 LSSVM 公式里經(jīng)常寫作 gamma控制正則化強(qiáng)度另一個(gè)是 RBF 核的核寬度 sigma也可以換算成 sklearn 里的 gamma。這兩個(gè)參數(shù)一旦設(shè)得不好模型不是過擬合就是欠擬合。更麻煩的是C 和 sigma 對預(yù)測精度的影響是非線性的二維網(wǎng)格搜索雖然能找到大致不錯(cuò)的范圍但計(jì)算量大而且很難精細(xì)定位到最優(yōu)值。我在實(shí)際項(xiàng)目里體會(huì)最深的一點(diǎn)是LSSVM 的訓(xùn)練過程確實(shí)快但調(diào)參過程慢相當(dāng)于“跑步五分鐘找鞋兩小時(shí)”。如果數(shù)據(jù)量只有幾百條網(wǎng)格搜索勉強(qiáng)還能接受一旦樣本上千甚至上萬參數(shù)稍微多一點(diǎn)網(wǎng)格搜索就基本不可行了。所以調(diào)參這個(gè)環(huán)節(jié)必須要自動(dòng)化最好還不依賴梯度信息因?yàn)?C 和 sigma 對目標(biāo)函數(shù)的梯度根本沒法穩(wěn)定計(jì)算。1.2 LSSVM 的兩個(gè)關(guān)鍵旋鈕先說說 C 的作用。如果數(shù)據(jù)里有噪聲或者異常值C 太小模型會(huì)對誤差過于寬容學(xué)到的規(guī)律就很粗糙容易出現(xiàn)欠擬合。如果 C 太大模型會(huì)把每一個(gè)訓(xùn)練點(diǎn)的誤差都當(dāng)成大事拼命去擬合結(jié)果測試集上波動(dòng)很大過擬合風(fēng)險(xiǎn)隨之上升。換句話說C 起到的是一個(gè)“誤差容忍度”的杠桿作用。再說核寬度 sigma。RBF 核的定義是 K(x_i, x_j) exp(-||x_i - x_j||2 / (2 sigma2))。sigma 小的時(shí)候核函數(shù)隨距離衰減很快只有非常近的樣本才會(huì)相互影響模型會(huì)變得非常靈活容易把噪聲也學(xué)進(jìn)去。sigma 大的時(shí)候距離較遠(yuǎn)的樣本也能產(chǎn)生相似度模型變得平滑預(yù)測結(jié)果更穩(wěn)定但可能丟掉細(xì)粒度特征。通俗點(diǎn)說C 決定“模型敢不敢犯錯(cuò)”sigma 決定“模型看問題的視野有多寬”。這兩個(gè)參數(shù)不是獨(dú)立的。同樣的 Csigma 選得不同最優(yōu)規(guī)律也不同同樣 sigmaC 的適應(yīng)區(qū)間也跟著變。所以絕不能簡單地“先定 C 再定 sigma”必須把兩個(gè)參數(shù)放進(jìn)同一個(gè)搜索空間里讓優(yōu)化算法自動(dòng)化去尋優(yōu)。1.3 灰狼優(yōu)化如何解決調(diào)參難題灰狼優(yōu)化算法Grey Wolf OptimizerGWO是 Mirjalili 在 2014 年前后提出的一類群體智能優(yōu)化算法。它的靈感來自灰狼種群的社會(huì)等級(jí)和狩獵行為。算法把候選解分成四個(gè)層級(jí)alpha 狼是當(dāng)前全局最優(yōu)解beta 狼是次優(yōu)解delta 狼是第三優(yōu)解剩下的 omega 狼負(fù)責(zé)在搜索過程中不斷根據(jù)前三者調(diào)整自己的位置。通過迭代更新整個(gè)種群會(huì)慢慢收斂到目標(biāo)函數(shù)最優(yōu)點(diǎn)。用 GWO 調(diào) LSSVM 的思路很簡單把 C 和 sigma 當(dāng)作二維空間中的一個(gè)坐標(biāo)點(diǎn)灰狼的位置就是一組候選參數(shù)目標(biāo)函數(shù)用交叉驗(yàn)證的均方誤差來構(gòu)造?;依钦耀C物就是找“讓驗(yàn)證誤差最小”的那組參數(shù)。相比網(wǎng)格搜索GWO 會(huì)從種群里挑選多個(gè)方向同時(shí)搜索并且在 alpha、beta、delta 三條精英線的引導(dǎo)下動(dòng)態(tài)調(diào)整步長在復(fù)雜參數(shù)空間中往往可以用更少的評估次數(shù)找到更好的結(jié)果。我當(dāng)時(shí)選擇 GWO 而不是粒子群或者遺傳算法主要看中三點(diǎn)一是 GWO 幾乎沒有需要額外調(diào)節(jié)的超參數(shù)初始化非常簡單二是算法結(jié)構(gòu)清晰很容易嵌入模型訓(xùn)練流程三是它在小規(guī)模維度問題上速度很快回歸模型的參數(shù)維度通常只有 2 到 4 個(gè)并不會(huì)陷入高維度危機(jī)。2. GWO-LSSVM 整體設(shè)計(jì)拆解2.1 算法整體流程每次跑 GWO-LSSVM我都習(xí)慣把流程拆成四個(gè)模塊數(shù)據(jù)預(yù)處理模塊、LSSVM 模型模塊、適應(yīng)度評估模塊、GWO 尋優(yōu)模塊。這四個(gè)模塊彼此咬合數(shù)據(jù)預(yù)處理負(fù)責(zé)把原始數(shù)據(jù)變成規(guī)范化矩陣LSSVM 模塊負(fù)責(zé)接收一組參數(shù)并完成訓(xùn)練和預(yù)測適應(yīng)度評估模塊使用 K 折交叉驗(yàn)證計(jì)算誤差指標(biāo)GWO 模塊不斷調(diào)整參數(shù)迭代出最優(yōu)解。數(shù)據(jù)預(yù)處理是最容易被忽略但最關(guān)鍵的模塊。我一般先用StandardScaler或MinMaxScaler對特征做標(biāo)準(zhǔn)化再把目標(biāo)變量也做一個(gè)標(biāo)準(zhǔn)化處理。為什么要做這一步因?yàn)?RBF 核函數(shù)本質(zhì)上依賴樣本之間的距離。如果某個(gè)特征取值范圍是 0 到 100另一個(gè)特征是 0 到 0.01距離計(jì)算就會(huì)被取值范圍大的特征完全主導(dǎo)核函數(shù)學(xué)到的相似度就失真了。標(biāo)準(zhǔn)化之后每個(gè)特征都在大致相同的尺度上LSSVM 才能公平地利用所有特征。整個(gè)尋優(yōu)流程我按照下面的順序執(zhí)行第一步讀取數(shù)據(jù)并劃分訓(xùn)練集和測試集通常按 7:3 或 8:2 劃分第二步對訓(xùn)練集特征做標(biāo)準(zhǔn)化并把標(biāo)準(zhǔn)化參數(shù)保存下來之后應(yīng)用到測試集第三步初始化灰狼種群位置每個(gè)位置對應(yīng)一組 log10(C) 和 log10(sigma)第四步對每個(gè)灰狼個(gè)體解碼出真實(shí)的 C 和 sigma訓(xùn)練 LSSVM 并做 K 折交叉驗(yàn)證第五步用交叉驗(yàn)證誤差更新 alpha、beta、delta 的位置第六步根據(jù)灰狼狩獵策略更新所有個(gè)體位置迭代到最大次數(shù)第七步用最優(yōu)參數(shù)在完整訓(xùn)練集上重新訓(xùn)練 LSSVM對測試集做回歸預(yù)測再計(jì)算 R2、RMSE、MAE 等指標(biāo)。2.2 變量編碼與適應(yīng)度函數(shù)怎么定如果想直接用 GWO 去搜索 C 和 sigma第一個(gè)坑就是搜索范圍不好定。C 的可能最優(yōu)值可能從 0.01 到 1000 跨越五個(gè)數(shù)量級(jí)如果用線性坐標(biāo)直接搜索大量候選解會(huì)集中在很小的數(shù)值區(qū)間邊緣導(dǎo)致搜索效率極低。所以我采用的是對數(shù)編碼灰狼實(shí)際更新的參數(shù)是 p1 log10(C)p2 log10(sigma)。每次計(jì)算適應(yīng)度時(shí)再做一次解碼得到真實(shí)的 C 10^p1sigma 10^p2。采用對數(shù)編碼還有一個(gè)好處就是距離計(jì)算更合理。C10 和 C12 在效果上差異其實(shí)很小但 C0.1 和 C0.3 的差異可能會(huì)非常大。如果用線性尺度灰狼位置之間移動(dòng) 1 個(gè)單位并不能區(qū)分這兩種情況如果用 log10差距體現(xiàn)在同一量級(jí)算法步長才能保持相對穩(wěn)定。適應(yīng)度函數(shù)我常用的是五折交叉驗(yàn)證的均方誤差MSE代碼如下面的思路def fitness_function(params, X_train, y_train): C 10 ** params[0] sigma 10 ** params[1] scores [] kf KFold(n_splits5, shuffleTrue, random_state42) for train_idx, val_idx in kf.split(X_train): model LSSVR(CC, sigmasigma) model.fit(X_train[train_idx], y_train[train_idx]) pred model.predict(X_train[val_idx]) scores.append(mean_squared_error(y_train[val_idx], pred)) return np.mean(scores)注意我這里把 GWO 的目標(biāo)定義為最小化 MSEalpha 狼也就是 MSE 最小的一組參數(shù)。有些教程里會(huì)把適應(yīng)度定義為準(zhǔn)確率或者其他越大越好的指標(biāo)那樣的話 alpha 初始化就應(yīng)該用負(fù)無窮。實(shí)際編碼的時(shí)候一定要分清方向否則整個(gè)進(jìn)化過程會(huì)反著跑那是很讓人摸不著頭腦的問題。2.3 關(guān)鍵參數(shù)推薦GWO-LSSVM 的超參數(shù)并不復(fù)雜但每個(gè)都有講究。我把在實(shí)際實(shí)驗(yàn)中反復(fù)驗(yàn)證過的推薦值整理成了表格參數(shù)常用范圍或取值說明灰狼數(shù)量20 ~ 30數(shù)量太少容易陷入局部最優(yōu)太多會(huì)拖慢速度最大迭代次數(shù)30 ~ 60用于回歸參數(shù)尋優(yōu)時(shí)50 次基本足夠收斂維度2對應(yīng) C 和 sigma如果加上 epsilon 就是 3log10(C) 搜索范圍-3 ~ 3對應(yīng) C 從 0.001 到 1000log10(sigma) 搜索范圍-2 ~ 1對應(yīng) sigma 從 0.01 到 10head 系數(shù) a從 2 線性衰減到 0GWO 原文的標(biāo)準(zhǔn)策略交叉驗(yàn)證折數(shù)5兼顧方差與訓(xùn)練成本比較常用如果你發(fā)現(xiàn)最優(yōu)參數(shù)總貼著搜索邊界比如 C 取到了 1000 這個(gè)最大值說明搜索范圍設(shè)置不合理需要擴(kuò)大邊界或者檢查數(shù)據(jù)標(biāo)準(zhǔn)化是否做到位。這一點(diǎn)值得反復(fù)強(qiáng)調(diào)因?yàn)閷?shí)際項(xiàng)目里出現(xiàn)貼邊最優(yōu)的概率還真不低往往不是算法不行而是邊界把最優(yōu)解給框住了。3. 核心實(shí)操Python 從零跑通 GWO-LSSVM3.1 最小可用的 LSSVM 回歸實(shí)現(xiàn)很多剛接觸 LSSVM 的朋友會(huì)問sklearn 里有現(xiàn)成的 LSSVM 嗎官方?jīng)]有直接提供這個(gè)模型。有一種捷徑是使用 sklearn 的KernelRidge它和 LSSVM 的數(shù)學(xué)本質(zhì)非常接近也是 L2 正則化加核技巧。但在實(shí)際項(xiàng)目中我還是更喜歡寫一個(gè)輕量版的 LSSVM 類既能完全掌控計(jì)算流程也方便替換成自己調(diào)整后的版本。LSSVM 回歸的求解核心其實(shí)很簡潔。給定訓(xùn)練數(shù)據(jù)后需要求解一個(gè)線性方程組[ 0 1^T ] [ b ] [ 0 ] [ 1 Kinv(C) I ] [ alpha ] [ y ]其中 K 是核矩陣I 是單位陣。這個(gè)方程組解出來之后預(yù)測函數(shù)就是核函數(shù)在訓(xùn)練樣本上的線性組合再加一個(gè)偏置項(xiàng)。下面是我在項(xiàng)目里使用的壓縮版實(shí)現(xiàn)總共不到三十行import numpy as np class LSSVR: def __init__(self, C1.0, sigma1.0): self.C C self.sigma sigma def _kernel(self, X1, X2): 計(jì)算 RBF 核矩陣X1 和 X2 都是二維數(shù)組。 用廣播方式計(jì)算兩兩樣本之間的平方距離。 X1_sq np.sum(X1 ** 2, axis1).reshape(-1, 1) X2_sq np.sum(X2 ** 2, axis1).reshape(1, -1) dist_sq X1_sq X2_sq - 2 * np.dot(X1, X2.T) dist_sq np.maximum(dist_sq, 0) return np.exp(-dist_sq / (2 * self.sigma ** 2)) def fit(self, X, y): X np.asarray(X, dtypefloat) y np.asarray(y, dtypefloat).flatten() n len(X) K self._kernel(X, X) A np.zeros((n 1, n 1)) A[0, 0] 0.0 A[0, 1:] 1.0 A[1:, 0] 1.0 A[1:, 1:] K np.eye(n) / self.C Y np.hstack([0.0, y]) self.alpha_b np.linalg.solve(A, Y) self.X_fit X return self def predict(self, X_test): K_test self._kernel(np.asarray(X_test, dtypefloat), self.X_fit) b self.alpha_b[0] alpha self.alpha_b[1:] return np.dot(K_test, alpha) b如果你不是非要用純手寫版本直接使用 sklearn 的KernelRidge(kernelrbf)也能做類似的事。區(qū)別在于 sklearn 內(nèi)部對參數(shù)名和正則項(xiàng)的寫法稍有不同但優(yōu)化目標(biāo)幾乎一致??紤]到篇幅和教學(xué)意義我在項(xiàng)目里往往先用最小版本跑通邏輯再替換成更高效的工程實(shí)現(xiàn)。3.2 GWO 主循環(huán)逐行說明灰狼優(yōu)化的核心代碼寫起來并不復(fù)雜但是更新公式很容易寫錯(cuò)。我建議在實(shí)現(xiàn)之前先把灰狼位置、alpha/beta/delta 的概念在草稿紙上畫一遍。算法中的每個(gè)個(gè)體在每次迭代里都會(huì)根據(jù) alpha、beta、delta 三個(gè)精英個(gè)體計(jì)算新位置然后取平均值這樣既能利用當(dāng)前最優(yōu)信息也能保留一定的探索多樣性。下面是直接可用的灰狼優(yōu)化函數(shù)目標(biāo)函數(shù)是任意一個(gè)輸入二維位置并返回標(biāo)量誤差的函數(shù)import numpy as np import random def gwo_optimize(obj_func, dim2, wolf_num25, max_iter50, lb-3.0, ub3.0): obj_func: 輸入是一維數(shù)組對數(shù)編碼的參數(shù)輸出是越小越好的標(biāo)量 lb, ub: 每個(gè)維度的對數(shù)搜索下界和上界 alpha_pos np.zeros(dim) alpha_score float(inf) beta_pos np.zeros(dim) beta_score float(inf) delta_pos np.zeros(dim) delta_score float(inf) positions np.random.uniform(lb, ub, (wolf_num, dim)) convergence [] for t in range(max_iter): # a 從 2 線性衰減到 0 a 2.0 - t * (2.0 / max_iter) for i in range(wolf_num): fitness obj_func(positions[i]) # 更新三個(gè)精英個(gè)體注意這里是最小化問題 if fitness alpha_score: delta_pos beta_pos.copy() delta_score beta_score beta_pos alpha_pos.copy() beta_score alpha_score alpha_pos positions[i].copy() alpha_score fitness elif fitness beta_score: delta_pos beta_pos.copy() delta_score beta_score beta_pos positions[i].copy() beta_score fitness elif fitness delta_score: delta_pos positions[i].copy() delta_score fitness # 更新每個(gè)灰狼的位置 for i in range(wolf_num): for j in range(dim): r1 random.random() r2 random.random() A1 2 * a * r1 - a C1 2 * r2 D_alpha abs(C1 * alpha_pos[j] - positions[i, j]) X1 alpha_pos[j] - A1 * D_alpha r1 random.random() r2 random.random() A2 2 * a * r1 - a C2 2 * r2 D_beta abs(C2 * beta_pos[j] - positions[i, j]) X2 beta_pos[j] - A2 * D_beta r1 random.random() r2 random.random() A3 2 * a * r1 - a C3 2 * r2 D_delta abs(C3 * delta_pos[j] - positions[i, j]) X3 delta_pos[j] - A3 * D_delta new_pos (X1 X2 X3) / 3.0 positions[i, j] np.clip(new_pos, lb, ub) convergence.append(alpha_score) # 可選打印迭代過程 print(fiter {t1}/{max_iter}, best score {alpha_score:.6f}) best_param alpha_pos best_score alpha_score return best_param, best_score, convergence代碼里最需要留神的是維度問題上統(tǒng)一使用對數(shù)坐標(biāo)。比如更新后的位置是 1.2實(shí)際 C 值就是 10^1.2約等于 15.85。這樣設(shè)計(jì)可以讓搜索從一開始就覆蓋多個(gè)數(shù)量級(jí)不會(huì)在 0 到 1000 里“盲人摸象”。3.3 回歸預(yù)測完整流程為了讓例子有真實(shí)感這里用 sklearn 自帶的加州房價(jià)數(shù)據(jù)集來做演示。這個(gè)數(shù)據(jù)集有八個(gè)特征包括收入中位數(shù)、房齡、房間數(shù)等預(yù)測目標(biāo)是房價(jià)中位數(shù)。我用前五百條樣本做實(shí)驗(yàn)既能體現(xiàn)訓(xùn)練速度又能保持回歸問題的代表性。完整流程如下import numpy as np from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error from sklearn.model_selection import KFold # 讀取數(shù)據(jù) data fetch_california_housing() X, y data.data[:500], data.target[:500] # 劃分?jǐn)?shù)據(jù)集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42 ) # 標(biāo)準(zhǔn)化 scaler_X StandardScaler() X_train scaler_X.fit_transform(X_train) X_test scaler_X.transform(X_test) scaler_y StandardScaler() y_train scaler_y.fit_transform(y_train.reshape(-1, 1)).flatten() y_test_scaled scaler_y.transform(y_test.reshape(-1, 1)).flatten() # 適應(yīng)度函數(shù) def obj_func(log_params): C 10 ** log_params[0] sigma 10 ** log_params[1] kf KFold(n_splits5, shuffleTrue, random_state42) mse_list [] for train_idx, val_idx in kf.split(X_train): model LSSVR(CC, sigmasigma) model.fit(X_train[train_idx], y_train[train_idx]) pred model.predict(X_train[val_idx]) mse_list.append(mean_squared_error(y_train[val_idx], pred)) return np.mean(mse_list) # 執(zhí)行 GWO 尋優(yōu) best_log_params, best_mse, convergence gwo_optimize( obj_func, dim2, wolf_num25, max_iter50, lb-3.0, ub3.0 ) print(f最優(yōu) log10(C){best_log_params[0]:.4f}) print(f最優(yōu) log10(sigma){best_log_params[1]:.4f}) print(f最優(yōu)交叉驗(yàn)證 MSE{best_mse:.6f}) # 用最優(yōu)參數(shù)在完整訓(xùn)練集上重新訓(xùn)練 best_C 10 ** best_log_params[0] best_sigma 10 ** best_log_params[1] final_model LSSVR(Cbest_C, sigmabest_sigma) final_model.fit(X_train, y_train) # 預(yù)測并轉(zhuǎn)換回原始量綱 pred_scaled final_model.predict(X_test) pred scaler_y.inverse_transform(pred_scaled.reshape(-1, 1)).flatten() rmse np.sqrt(mean_squared_error(y_test, pred)) mae mean_absolute_error(y_test, pred) r2 r2_score(y_test, pred) print(f測試集 RMSE{rmse:.4f}) print(f測試集 MAE{mae:.4f}) print(f測試集 R2{r2:.4f})回看這段代碼有兩個(gè)細(xì)節(jié)值得專門提一下。第一是目標(biāo)變量標(biāo)準(zhǔn)化之后交叉驗(yàn)證誤差是基于標(biāo)準(zhǔn)化后的 y 計(jì)算但最終報(bào)告測試集誤差時(shí)你需要把預(yù)測值逆變換回去否則 RMSE 的量綱跟實(shí)際業(yè)務(wù)對不上。第二是最優(yōu)參數(shù)要在完整訓(xùn)練集上重訓(xùn)模型因?yàn)榻徊骝?yàn)證只是用來評估參數(shù)并不會(huì)為你產(chǎn)出最終模型兩者之間不要搞混。3.4 訓(xùn)練表現(xiàn)與收斂記錄在我本機(jī) 8 核 CPU 的環(huán)境下25 只灰狼跑 50 次迭代每次適應(yīng)度需要五折訓(xùn)練總共有 6250 次 LSSVM 訓(xùn)練。由于樣本只有幾百條LSSVM 的求解又是小規(guī)模線性方程組整個(gè)過程大概兩三分鐘跑完。收斂曲線通常表現(xiàn)為前期快速下降前 15 次迭代基本能把 MSE 從 0.03 附近壓到 0.005 以下后期則在小范圍內(nèi)震蕩并逐步穩(wěn)定。一次典型實(shí)驗(yàn)結(jié)果最終得到的最優(yōu)參數(shù)大約是 C 50 左右sigma 0.8 左右。對應(yīng)的測試集 R2 可以到 0.75 以上RMSE 在 0.65 左右。作為對比默認(rèn) C1 和 sigma1 的 LSSVM 測試 R2 可能只有 0.65 上下。當(dāng)然這個(gè)差距會(huì)隨著數(shù)據(jù)分布、樣本規(guī)模、特征噪聲而變化并不代表任何情況下 GWO 都能帶來絕對提升但它確實(shí)表現(xiàn)出一種“穩(wěn)定地把參數(shù)拉到合理區(qū)域”的能力而且找到的參數(shù)往往比人工湊出來的更均衡。4. 實(shí)戰(zhàn)中容易踩的坑與排查手冊4.1 目標(biāo)函數(shù)放縮的坑GWO-LSSVM 項(xiàng)目最常見的問題出現(xiàn)在適應(yīng)度函數(shù)構(gòu)造環(huán)節(jié)。如果直接用原始目標(biāo)變量的 RMSE 作為適應(yīng)度那么當(dāng)目標(biāo)變量本身范圍很大時(shí)MSE 可能達(dá)到幾千甚至上百萬alpha 的初始化如果還是用 float(inf) 倒沒問題但收斂曲線的變化會(huì)顯得非??鋸埐焕谂袛嗨惴ㄊ欠裾嬲M(jìn)步。更好的做法是把目標(biāo)變量標(biāo)準(zhǔn)化到均值 0、方差 1或者用 MAE、R2 這類受尺度影響較小的指標(biāo)。只要統(tǒng)一量綱GWO 的每一步迭代都能更有意義地體現(xiàn)參數(shù)變化帶來的影響。這個(gè)坑我早期犯過數(shù)據(jù)集目標(biāo)變量是百分制的評分在標(biāo)準(zhǔn)化之前適應(yīng)度值從 600 多緩慢下降迭代后期始終停在 120 附近我以為是參數(shù)范圍不夠后來才發(fā)現(xiàn)只是目標(biāo)變量尺度太大把細(xì)微改進(jìn)都淹沒了。標(biāo)準(zhǔn)化之后再觀察MSE 的梯度信息一目了然優(yōu)化效果立刻改善。4.2 核函數(shù)參數(shù)初始范圍另一個(gè)高頻問題是搜索邊界設(shè)得太窄。很多算法教程喜歡給 C 和 sigma 設(shè)成 [0.01, 100] 這類范圍看起來沒什么問題但實(shí)際數(shù)據(jù)分布不同最優(yōu)參數(shù)可能落在完全不同的區(qū)域。把邊界設(shè)窄之后灰狼種群會(huì)被迫擠在邊界附近無法探索更遠(yuǎn)的解空間最后得出的所謂最優(yōu)解可能只是“邊界內(nèi)看著最好”并沒有真實(shí)接近全局最優(yōu)。我推薦先做一次粗略試跑觀察優(yōu)化結(jié)果是否貼近上下界。如果發(fā)現(xiàn)最優(yōu)解貼到邊界就在更寬的范圍內(nèi)再跑一次比如 log10(C) 從 -4 到 4log10(sigma) 從 -3 到 2。這種擴(kuò)展搜索范圍的做法代價(jià)很小卻能顯著降低錯(cuò)過最優(yōu)參數(shù)的概率。4.3 常見問題對照表異?,F(xiàn)象可能原因解決辦法交叉驗(yàn)證 MSE 始終很大不下降特征沒標(biāo)準(zhǔn)化或搜索邊界不合理先做特征標(biāo)準(zhǔn)化擴(kuò)大參數(shù)范圍收斂曲線后期仍有劇烈波動(dòng)灰狼數(shù)量太少或最大迭代次數(shù)不夠增加到 30 只50 迭代起步測試集表現(xiàn)遠(yuǎn)差于驗(yàn)證集LSSVM 過擬合C 過大或數(shù)據(jù)量太少縮小 C 范圍嘗試增加樣本或加噪聲魯棒性每次運(yùn)行結(jié)果高度不一致隨機(jī)種子不確定交叉驗(yàn)證劃分不同固定 random_state多次運(yùn)行取平均參數(shù)明明變化但誤差不變搜索空間在對數(shù)編碼時(shí)過于離散步長過大縮小對數(shù)范圍或?qū)ξ恢酶录舆吔缥漳P陀?xùn)練特別慢樣本量超過幾千核矩陣求解復(fù)雜度高使用更高效的求解方式或先降采樣測試對于最后一條如果你的數(shù)據(jù)集已經(jīng)到上萬條LSSVM 的 O(n^3) 復(fù)雜度會(huì)變得很吃力。我建議先用隨機(jī)抽樣跑通流程觀察精度-耗時(shí)平衡再?zèng)Q定是否使用完整數(shù)據(jù)。若數(shù)據(jù)量真的很大GWO 每次迭代都要做交叉驗(yàn)證計(jì)算負(fù)擔(dān)會(huì)比較重這種情況下可以先在少量樣本上尋優(yōu)再在完整訓(xùn)練集上微調(diào)也算一種務(wù)實(shí)做法。5. 把 GWO-LSSVM 進(jìn)一步用好的細(xì)節(jié)思考5.1 和網(wǎng)格搜索、粒子群做橫向?qū)Ρ茸龌貧w預(yù)測項(xiàng)目時(shí)很多人會(huì)問“為什么不直接用 sklearn 的 GridSearchCV”我的體會(huì)是當(dāng)參數(shù)維度只有兩個(gè)時(shí)網(wǎng)格搜索的優(yōu)勢是穩(wěn)定、可復(fù)現(xiàn)但它的缺點(diǎn)是必須預(yù)設(shè)步長。如果步長太粗容易把最優(yōu)參數(shù)落在格子之間步長太細(xì)計(jì)算量指數(shù)上升。GWO 則不同它沒有固定的網(wǎng)格概念搜索過程更像是連續(xù)空間中的智能逼近所以它能在有限的評估次數(shù)內(nèi)走得比網(wǎng)格搜索更細(xì)。那和粒子群算法相比呢GWO 的優(yōu)勢在于參數(shù)更少。粒子群需要設(shè)置慣性權(quán)重、個(gè)體學(xué)習(xí)因子和社會(huì)學(xué)習(xí)因子每個(gè)參數(shù)都需要人為調(diào)聽起來就像一個(gè)“調(diào)參算法還需要調(diào)參”的冷笑話。GWO 只需要設(shè)置種群數(shù)量和迭代次數(shù)收斂性主要依賴系數(shù) a 的自適應(yīng)線性衰減使用門檻低很多。當(dāng)然 PSO 在特定問題上表現(xiàn)也很強(qiáng)但在 LSSVM 這種低維優(yōu)化任務(wù)上GWO 足夠?qū)嵱谩?.2 是否能擴(kuò)展為多目標(biāo)或更多參數(shù)如果某個(gè) LSSVM 變體需要同時(shí)優(yōu)化懲罰系數(shù) C、核寬度 sigma 和 epsilon-SVR 中的 epsilon 參數(shù)那么只需要把 GWO 的目標(biāo)維度從 2 改到 3。此時(shí)搜索空間確實(shí)變大了但灰狼種群的多樣性優(yōu)勢能繼續(xù)發(fā)揮作用。你只需要小心三點(diǎn)一是種群數(shù)量和迭代次數(shù)最好同步增加二是三個(gè)參數(shù)的搜索范圍差異很大但只要你統(tǒng)一使用 log10 編碼就不會(huì)出現(xiàn)某個(gè)維度變化太快而其他維度變化太慢的問題三是可視化時(shí)不要只看二維收斂曲線可以保存每一代的 alpha 參數(shù)軌跡判斷是否存在異常跳變。如果你想要更精細(xì)的優(yōu)化效果也可以把交叉驗(yàn)證的折數(shù)從 5 改成 10。折數(shù)越多驗(yàn)證評價(jià)越穩(wěn)定但訓(xùn)練次數(shù)也隨之增加。個(gè)人建議先用 5 折做快速篩選最后用 10 折對排名前列的參數(shù)組做精排這樣比直接開 10 折更省時(shí)間效果卻不打折扣。5.3 一些實(shí)在的小技巧這里再分享幾個(gè)我自己常用但不容易寫進(jìn)正式論文里的經(jīng)驗(yàn)。第一個(gè)技巧是在交叉驗(yàn)證之前固定數(shù)據(jù)劃分順序。如果不固定每次 GWO 迭代時(shí)交叉驗(yàn)證的訓(xùn)練集和驗(yàn)證集劃分都不同那適應(yīng)度值包含了額外的劃分噪聲灰狼很難穩(wěn)定收斂。最簡單的做法是在每次交叉驗(yàn)證前設(shè)置KFold(shuffleTrue, random_state42)保證同一參數(shù)得到相同誤差。第二個(gè)技巧是每次記錄最優(yōu)參數(shù)對應(yīng)的驗(yàn)證誤差而不是只記錄最終結(jié)果。GWO 收斂到后期時(shí)alpha、beta、delta 往往已經(jīng)很接近如果目標(biāo)函數(shù)不平滑可能出現(xiàn)微小的性能震蕩。保存最優(yōu)位置可以避免迭代后期偶然變差導(dǎo)致最終模型參數(shù)不如中間某次的結(jié)果這種“保存歷史最優(yōu)”的思路和很多優(yōu)化算法的精英保留策略是相通的。第三個(gè)技巧是關(guān)于數(shù)據(jù)量。如果數(shù)據(jù)集非常小比如只有幾十條樣本LSSVM 的交叉驗(yàn)證誤差波動(dòng)會(huì)很大GWO 很難找到穩(wěn)定最優(yōu)。這種情況下我通常先增加驗(yàn)證折數(shù)或者改用留一交叉驗(yàn)證把評估做得更嚴(yán)格。這雖然會(huì)增加訓(xùn)練量但能更真實(shí)反映參數(shù)對未見過樣本的預(yù)測能力。數(shù)據(jù)量很小又想要穩(wěn)定預(yù)測不如試試更簡單的回歸模型先用線性回歸或嶺回歸建立一個(gè)基線如果 R2 已經(jīng)足夠好沒有必要執(zhí)著于支持向量機(jī)這類復(fù)雜模型。根據(jù)我個(gè)人的經(jīng)驗(yàn)任何創(chuàng)新方法都應(yīng)該服務(wù)于“最終業(yè)務(wù)結(jié)果”如果基線已經(jīng)滿足需求調(diào)參再花哨也沒有意義。