測模型的底層機(jī)制與統(tǒng)一流水線解析)
簡介這套機(jī)器學(xué)習(xí)預(yù)測系統(tǒng)匯總面向希望系統(tǒng)掌握預(yù)測建模的學(xué)習(xí)者與開發(fā)者圍繞貝葉斯網(wǎng)絡(luò)、馬爾科夫模型、線性回歸、嶺回歸、多項(xiàng)式回歸、決策樹回歸和深度神經(jīng)網(wǎng)絡(luò)七類方法展開可用于課程設(shè)計(jì)、模型對比實(shí)驗(yàn)與預(yù)測類項(xiàng)目入門。壓縮包共12個(gè)文件約1.3MB以Python腳本為主體配合Excel與CSV示例數(shù)據(jù)、UI界面文件及docx、md說明文檔既能查看各算法的實(shí)現(xiàn)思路也便于在統(tǒng)一數(shù)據(jù)上運(yùn)行和比較結(jié)果。已有136人學(xué)習(xí)說明其具備一定的參考與復(fù)用價(jià)值。讀者可從中獲得從概率圖模型、統(tǒng)計(jì)回歸到深度學(xué)習(xí)的完整示例鏈路借助數(shù)據(jù)與界面代碼快速搭建演示系統(tǒng)并依據(jù)文檔理解參數(shù)設(shè)置、模型差異與適用場景為后續(xù)調(diào)參與集成學(xué)習(xí)打下基礎(chǔ)。1. 七個(gè)預(yù)測模型塞進(jìn)一個(gè)壓縮包它們共享的其實(shí)只有一條流水線同一份回歸數(shù)據(jù)喂給線性回歸和深度神經(jīng)網(wǎng)絡(luò)預(yù)測誤差可能差出三倍同一份序列數(shù)據(jù)喂給馬爾科夫模型和決策樹回歸結(jié)論甚至方向相反。這種分歧往往不是調(diào)參沒調(diào)好而是每個(gè)模型的歸納偏置inductive bias在數(shù)據(jù)上留下的指紋。一份把線性回歸、嶺回歸、多項(xiàng)式回歸、決策樹回歸、貝葉斯網(wǎng)絡(luò)、馬爾科夫模型、深度神經(jīng)網(wǎng)絡(luò)預(yù)測收攏在一起的機(jī)器學(xué)習(xí)預(yù)測系統(tǒng)價(jià)值從來不在于模型數(shù)量多而在于它把七種前提假設(shè)擺在同一張桌子上哪些誤差來自模型結(jié)構(gòu)哪些來自數(shù)據(jù)本身一眼就能對上號。認(rèn)清這套底層機(jī)制等于拿到一張「模型為什么會(huì)錯(cuò)」的地圖。后面幾章沿著這張地圖逐層往下拆先看最小二乘框架下的三種變體如何在數(shù)值層面分化再看兩種非線性路線在幾何上的本質(zhì)差別然后是概率圖模型怎么把預(yù)測變成一次條件概率查詢最后落到七個(gè)模型接進(jìn)同一套評估流程時(shí)最容易出問題的五個(gè)位置。2. 最小二乘的三副面孔正規(guī)方程的病態(tài)、嶺回歸的 λI 與多項(xiàng)式升維的代價(jià)2.1 正規(guī)方程解的到底是什么線性回歸只寫了一句話的假設(shè)目標(biāo)值與特征之間是線性關(guān)系誤差獨(dú)立同分布且方差恒定。在這句假設(shè)下最小化殘差平方和 ||y ? Xw||2對 w 求梯度令其為零得到 X?Xw X?y。這個(gè)式子叫正規(guī)方程X?X 是 d×d 的 Gram 矩陣。關(guān)鍵在于Gram 矩陣的條件數(shù)是設(shè)計(jì)矩陣條件數(shù)的平方。設(shè)計(jì)矩陣條件數(shù) 103 在一堆量綱不一致的特征里非常常見平方之后 Gram 矩陣就到 10?float64 的有效十進(jìn)制位大概 15 到 16 位平方一下只剩 7 到 8 位可信。所以教科書里那句w inv(X.T X) X.T y在真實(shí)數(shù)據(jù)上會(huì)給出離譜結(jié)果不是公式推錯(cuò)了是數(shù)值路徑選錯(cuò)了。工程上的做法是繞開顯式求逆。SVD 把 X 分解成 UΣV?代入可得 w VΣ?1U?y條件數(shù)只被放大一次而不是平方。np.linalg.lstsq走的就是這條路。還有一件容易被忽略的事當(dāng)特征數(shù) d 大于樣本數(shù) n 時(shí) Gram 矩陣必然奇異正規(guī)方程有無窮多組解lstsq 返回的是其中范數(shù)最小的一組——這個(gè)細(xì)節(jié)正是理解嶺回歸為什么「一定有唯一解」的入口。2.2 嶺回歸λ 加的不是噪聲是給奇異值墊了個(gè)地板把 SVD 代進(jìn)嶺回歸的目標(biāo)函數(shù) ||y ? Xw||2 λ||w||2解出來是這樣w_ridge Σ? [σ?2 / (σ?2 λ)] · (u??y / σ?) · v?方括號里那項(xiàng)是收縮因子。σ? 遠(yuǎn)大于 √λ 的方向因子接近 1系數(shù)幾乎不動(dòng)σ? 遠(yuǎn)小于 √λ 的方向因子接近 0那個(gè)方向承載的信息被整體丟掉。這說明 λ 并不是「把所有權(quán)系數(shù)均勻壓小」而是按每個(gè)奇異值方向的信噪比做選擇性放棄。這就解釋了嶺回歸為什么在共線性嚴(yán)重的數(shù)據(jù)上格外好用。共線性意味著存在幾個(gè)極小的奇異值那些方向上的系數(shù)估計(jì)方差極大普通最小二乘會(huì)把噪聲放大成巨大的系數(shù)符號還會(huì)隨機(jī)翻轉(zhuǎn)嶺回歸直接按比例把這些方向壓掉代價(jià)是引入偏差。偏差換方差這就是它的全部邏輯沒有什么玄學(xué)成分。還有一個(gè)實(shí)現(xiàn)層面的細(xì)節(jié)截距項(xiàng)不參與懲罰。如果懲罰截距模型對 y 的整體平移就不再等變——目標(biāo)值加一個(gè)常數(shù) c預(yù)測本該也加 c但被懲罰的截距做不到。所以手寫嶺回歸時(shí)要把 Gram 矩陣的第 0 個(gè)對角元置零用現(xiàn)成庫且開啟截距估計(jì)時(shí)內(nèi)部一般已經(jīng)這么處理了。λ 怎么定留一交叉驗(yàn)證在嶺回歸上存在閉式解不需要真的訓(xùn)練 n 次。另一個(gè)好用的量是有效自由度 trace(X(X?XλI)?1X?) Σ σ?2/(σ?2λ)它隨 λ 單調(diào)下降可以當(dāng)成「模型實(shí)際用掉了幾參數(shù)」來讀比盯著 λ 的絕對值更有意義。2.3 多項(xiàng)式回歸換了基沒換模型多項(xiàng)式回歸經(jīng)常被當(dāng)成另一類模型來講其實(shí)它還是線性回歸。參數(shù) w 仍然線性地進(jìn)入預(yù)測變的只是設(shè)計(jì)矩陣的列從 [1, x] 換成 [1, x, x2, …, x^d]。所以它有閉式解也能直接套嶺回歸前面兩節(jié)的所有結(jié)論原樣成立。真正的麻煩在數(shù)值上。x 的各次冪之間高度相關(guān)拼出來的設(shè)計(jì)矩陣接近 Vandermonde 矩陣條件數(shù)隨階數(shù)快速上升。在 [-1, 1] 區(qū)間還算溫和一旦特征量綱是 [0, 100]degree 到 6 時(shí) Gram 矩陣條件數(shù)能沖到 1012 量級解出來的系數(shù)符號在正負(fù)之間亂跳曲線在樣本點(diǎn)之間劇烈震蕩。這是踩坑重災(zāi)區(qū)訓(xùn)練集 R2 是 0.99測試集一塌糊涂。解決辦法有兩條。一條是把 x 中心化再標(biāo)準(zhǔn)化把數(shù)值范圍壓回 [-1, 1] 附近簡單但有效另一條是換正交多項(xiàng)式基Chebyshev、Legendre讓基函數(shù)在樣本上的內(nèi)積近似對角Gram 矩陣接近對角陣。第二條更徹底因?yàn)檎换南禂?shù)之間幾乎不相關(guān)每個(gè)系數(shù)都有相對獨(dú)立的含義不會(huì)出現(xiàn)「改一個(gè)系數(shù)整條曲線全變」的連鎖反應(yīng)。數(shù)值分析里做函數(shù)逼近默認(rèn)用正交基原因就在這里。2.4 三條路徑寫在同一套接口下import numpy as np def design_linear(x): # 截距列固定放第 0 列后續(xù)所有正則化都跳過它 x np.asarray(x, dtypefloat).reshape(len(x), -1) return np.hstack([np.ones((x.shape[0], 1)), x]) def fit_ols(X, y): # lstsq 走 SVD不顯式構(gòu)造 X^T X條件數(shù)只被放大一次而不是平方 w, *_ np.linalg.lstsq(X, y, rcondNone) return w def fit_ridge(X, y, lam): d X.shape[1] reg lam * np.eye(d) reg[0, 0] 0.0 # 截距不懲罰保證對 y 的平移等變 # 用 solve 而不是 inv內(nèi)部走 Cholesky/LU數(shù)值上穩(wěn)一個(gè)量級 return np.linalg.solve(X.T X reg, X.T y) def design_poly(x, degree): x np.asarray(x, dtypefloat) # 先中心化再縮放不做這一步degree6 時(shí) Gram 矩陣條件數(shù)可達(dá) 1e12 z (x - x.mean()) / (x.std() 1e-12) cols [np.ones_like(z)] [z ** k for k in range(1, degree 1)] return np.vstack(cols).Tdesign_linear和design_poly返回的都是設(shè)計(jì)矩陣形狀統(tǒng)一為 (n, d1)所以fit_ols/fit_ridge可以直接復(fù)用切換模型只是換一個(gè)建矩陣的函數(shù)。lam建議在 1e-3 到 1e3 之間取對數(shù)網(wǎng)格掃配合交叉驗(yàn)證選點(diǎn)degree和lam必須聯(lián)合調(diào)單獨(dú)把 degree 往上推、λ 卻固定不變等于只放開了容量沒加任何約束。想驗(yàn)證數(shù)值問題是否真的存在加一行np.linalg.cond(X.T X)打印出來看條件數(shù)過了 10? 就該考慮換基或者加正則過了 1012 基本可以判定結(jié)果不可信。3. 兩種非線性決策樹的軸對齊分裂與神經(jīng)網(wǎng)絡(luò)的表示學(xué)習(xí)差在哪3.1 回歸樹的不純度方差縮減就是平方誤差的再分配回歸樹在每個(gè)節(jié)點(diǎn)上只做一件事找一個(gè)特征和一個(gè)閾值把樣本切成兩半讓兩半內(nèi)部的平方誤差之和最小。父節(jié)點(diǎn)的平方誤差是 Σ(y? ? ?)2分裂后是 SSE_L SSE_R兩者之差就是這次分裂帶來的方差縮減。為什么回歸用方差而不是基尼系數(shù)或信息熵那兩者衡量的是類別分布的純度前提是標(biāo)簽離散?;貧w里「多數(shù)類」這個(gè)概念沒有意義能讓平方誤差最小的常數(shù)預(yù)測是均值所以不純度自然定義成方差。由此推出一條常被忽略的結(jié)論葉節(jié)點(diǎn)的輸出是葉內(nèi)樣本的均值這個(gè)值只在平方損失下才最優(yōu)。如果業(yè)務(wù)上更在意絕對誤差葉節(jié)點(diǎn)該輸出中位數(shù)樹對離群點(diǎn)的敏感度會(huì)立刻降一個(gè)檔次。很多實(shí)現(xiàn)把這兩件事混著用——用 MSE 訓(xùn)練、用 MAE 匯報(bào)——然后困惑為什么一個(gè)離群點(diǎn)能把整棵樹的預(yù)測拽偏。3.2 分裂點(diǎn)搜索排一次序前綴和把代價(jià)壓到 O(1)對某個(gè)特征而言候選切點(diǎn)只有 n?1 個(gè)排序后相鄰樣本的中點(diǎn)。暴力做法是對每個(gè)候選重新算左右兩邊的均值再算 SSE復(fù)雜度 O(n2)。實(shí)際實(shí)現(xiàn)用前綴和。核心恒等式是一段樣本的平方誤差和 Σy2 ? (Σy)2/n。只要預(yù)處理出 Σy 和 Σy2 的前綴和任意一段區(qū)間的 SSE 都是 O(1)。整體流程于是變成對特征排序 O(n log n)掃描一遍候選切點(diǎn) O(n)單個(gè)特征 O(n log n)d 個(gè)特征 O(d·n log n)。import numpy as np def best_split(X, y): n, d X.shape # 父節(jié)點(diǎn) SSE作為「這次分裂必須讓誤差下降」的初始門檻 best_sse float(((y - y.mean()) ** 2).sum()) best (None, None, best_sse) # (特征下標(biāo), 閾值, 子節(jié)點(diǎn) SSE 之和) for j in range(d): order np.argsort(X[:, j]) xs, ys X[order, j], y[order] csum np.cumsum(ys) # Σy 的前綴和 csq np.cumsum(ys ** 2) # Σy2 的前綴和 k np.arange(n - 1) # 候選切點(diǎn)左子節(jié)點(diǎn)為 [0..k] nl, nr k 1, n - k - 1 sl, sr csum[:-1], csum[-1] - csum[:-1] ql, qr csq[:-1], csq[-1] - csq[:-1] # SSE Σy2 - (Σy)2/n左右兩邊各算一次再相加 sse (ql - sl ** 2 / nl) (qr - sr ** 2 / nr) i int(np.argmin(sse)) if sse[i] best[2]: best (j, (xs[i] xs[i 1]) / 2.0, sse[i]) return bestbest_sse的初值取父節(jié)點(diǎn) SSE作用是讓「不降低誤差的分裂」直接被拒絕天然形成一個(gè)極簡的預(yù)剪枝。xs[i]和xs[i1]是排序后相鄰的兩個(gè)樣本取中點(diǎn)作為閾值是最穩(wěn)妥的做法——取其中任何一個(gè)都會(huì)讓邊界條件變得依賴浮點(diǎn)比較。返回值里帶上 SSE是為了讓上層遞歸能順便算出這次分裂帶來的方差縮減做后續(xù)的代價(jià)復(fù)雜度剪枝。3.3 軸對齊分裂的硬邊界分段常數(shù)函數(shù)的局限樹的預(yù)測函數(shù)是分段常數(shù)折痕永遠(yuǎn)平行于坐標(biāo)軸。真實(shí)關(guān)系如果是 y x?·x?等值線是一條條雙曲線樹只能用一堆小矩形去拼想達(dá)到固定精度所需的葉子數(shù)隨維度指數(shù)增長。這就解釋了兩個(gè)常見現(xiàn)象。一是樹對特征的單調(diào)變換免疫把 x 換成 log x 結(jié)果完全不變因?yàn)榉至阎豢错樞虿豢磾?shù)值二是樹對特征的線性組合極其敏感把 x? 換成 x? x? 之后整棵樹要推倒重學(xué)。這也是特征工程在樹模型上依然有效的原因只是形式和線性模型完全不同——線性模型需要的是讓關(guān)系變線性樹需要的是讓關(guān)系變軸對齊。隨機(jī)森林和梯度提升能緩解這個(gè)限制但不能消除它們靠集成降低方差折痕方向依然釘死在坐標(biāo)軸上。理解了這一點(diǎn)就不會(huì)在明明需要做特征旋轉(zhuǎn)的問題上死磕樹模型。3.4 神經(jīng)網(wǎng)絡(luò)把軸對齊的折痕換成學(xué)出來的方向一個(gè)帶 ReLU 的多層感知機(jī)輸出是輸入空間上的分段線性函數(shù)。每個(gè) ReLU 單元定義一張超平面激活就是在超平面兩側(cè)取不同的線性片。折痕的法向量由權(quán)重矩陣學(xué)出來不再綁定在坐標(biāo)軸上所以 y x?·x? 這種對角結(jié)構(gòu)可以用很少的參數(shù)表達(dá)。但「分段線性」這四個(gè)字值得停下來想一想它不是萬能光滑函數(shù)逼近器它是一族折痕方向可學(xué)習(xí)的分段線性函數(shù)。import numpy as np def mlp_forward(x, params): # x: (n, d)params: [(W1, b p a hrefhttps://download.csdn.net/download/qq_51320133/89279594 stylecolor:#ec7500;font-size:14px; 本文還有配套的精品資源點(diǎn)擊獲取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p