習(xí)的基石與實(shí)踐)
1. 為什么線性回歸是深度學(xué)習(xí)的必修第一課先拋個(gè)觀點(diǎn)如果你真的想把深度學(xué)習(xí)搞明白線性回歸是繞不過(guò)去的第一塊基石。我見(jiàn)過(guò)太多人一上來(lái)就抱著《動(dòng)手學(xué)深度學(xué)習(xí)》啃CNN、Transformer結(jié)果連損失函數(shù)下降曲線都看不懂更別提調(diào)參了。原因很簡(jiǎn)單——深度學(xué)習(xí)里那些看似高深的東西本質(zhì)上大部分都是線性回歸的“套娃”版本。線性回歸要做的事情非常樸素給定一堆數(shù)據(jù)點(diǎn)找到一條直線或者一個(gè)超平面來(lái)擬合它們之間的關(guān)系。放在深度學(xué)習(xí)的語(yǔ)境里這條直線就變成了神經(jīng)網(wǎng)絡(luò)的第一層權(quán)重?cái)M合的過(guò)程就變成了梯度下降更新參數(shù)的過(guò)程。換句話說(shuō)線性回歸是深度學(xué)習(xí)里最基礎(chǔ)、最簡(jiǎn)單、最容易理解的一個(gè)完整閉環(huán)數(shù)據(jù)、模型、損失函數(shù)、優(yōu)化器、訓(xùn)練、評(píng)估你能在這個(gè)簡(jiǎn)單模型里跑通整套流程后面再看神經(jīng)網(wǎng)絡(luò)就不會(huì)發(fā)怵。適合誰(shuí)來(lái)學(xué)兩類人。一類是剛?cè)腴T機(jī)器學(xué)習(xí)的新手想找一個(gè)能動(dòng)手跑的模型練手另一類是準(zhǔn)備進(jìn)入深度學(xué)習(xí)、但被復(fù)雜的框架和術(shù)語(yǔ)嚇到的人先用線性回歸把“訓(xùn)練”這個(gè)核心動(dòng)作刻進(jìn)腦子里。這篇文章我會(huì)直接用Python把線性回歸從數(shù)學(xué)原理到代碼實(shí)現(xiàn)、再到和深度學(xué)習(xí)的聯(lián)系完整走一遍最后還會(huì)聊聊我實(shí)際踩過(guò)的坑。2. 線性回歸的核心思路與設(shè)計(jì)拆解2.1 線性回歸到底在求解什么我們先從最直覺(jué)的場(chǎng)景說(shuō)起。假設(shè)你開(kāi)了一家奶茶店想研究“溫度”和“奶茶銷量”之間的關(guān)系。你記錄了10天的數(shù)據(jù)溫度30度時(shí)賣了80杯溫度25度時(shí)賣了65杯溫度20度時(shí)賣了50杯……你心里大概有個(gè)感覺(jué)溫度越高賣得越多但具體多多少能不能用一個(gè)公式表達(dá)線性回歸就是干這個(gè)事的。它假設(shè)自變量x溫度和因變量y銷量之間是線性關(guān)系也就是y w * x b這里的w叫權(quán)重weightb叫偏置bias。在這個(gè)例子里w可以理解成“溫度每升高1度銷量增加多少杯”b可以理解成“溫度是0度時(shí)銷量是多少”。你可能覺(jué)得這太簡(jiǎn)單了但稍微擴(kuò)展一下就有意思了。如果x不止一個(gè)比如溫度、濕度、是否是周末三個(gè)因素都影響銷量那就變成多元線性回歸y w1 * x1 w2 * x2 w3 * x3 b寫成向量形式就是 y X * W b。到了這一步它和神經(jīng)網(wǎng)絡(luò)里的“全連接層”就已經(jīng)非常接近了——神經(jīng)網(wǎng)絡(luò)的一層本質(zhì)上就是一個(gè)多元線性回歸只不過(guò)后面再疊加一個(gè)非線性激活函數(shù)而已。2.2 怎么判斷一條直線好不好損失函數(shù)有了模型表達(dá)式下一個(gè)問(wèn)題就是w和b取什么值最好這時(shí)候就需要一個(gè)衡量標(biāo)準(zhǔn)也就是損失函數(shù)。最常用的損失函數(shù)叫均方誤差Mean Squared ErrorMSE。它的計(jì)算方式特別直觀把所有樣本的真實(shí)值y_true和預(yù)測(cè)值y_pred做差把差值平方消除正負(fù)號(hào)的影響同時(shí)放大大誤差的懲罰對(duì)所有樣本求平均。公式如下MSE (1/n) * Σ(y_true - y_pred)^2生活化理解就像你扔飛鏢每次扔完量一下飛鏢離靶心多遠(yuǎn)把所有的誤差距離平方加總再求平均。誤差越小說(shuō)明你的投擲水平越高。同理MSE越小說(shuō)明這條直線對(duì)數(shù)據(jù)的擬合越好。那么為什么用平方而不是絕對(duì)值因?yàn)槠椒胶瘮?shù)是可導(dǎo)的后續(xù)做梯度下降需要用到導(dǎo)數(shù)絕對(duì)值在0點(diǎn)處不可導(dǎo)優(yōu)化起來(lái)會(huì)麻煩。另一個(gè)原因是平方對(duì)大誤差的懲罰更重這既有好處也有壞處——好處是模型會(huì)更努力地去擬合那些偏離大的點(diǎn)壞處是如果數(shù)據(jù)里有異常值模型會(huì)被帶偏這個(gè)我在后面排查部分會(huì)講。2.3 優(yōu)化器選型梯度下降是核心引擎有了損失函數(shù)理論上我們可以用“暴力試錯(cuò)”來(lái)找最好的w和b隨機(jī)生成一堆(w, b)組合計(jì)算每個(gè)組合對(duì)應(yīng)的MSE取最小的那個(gè)。但這樣效率太低尤其是參數(shù)多了以后組合呈指數(shù)爆炸。正確的做法是梯度下降Gradient Descent。核心思路是損失函數(shù)J(w, b)是一個(gè)關(guān)于w和b的曲面我們要沿著“地勢(shì)最陡峭的方向”往下走直到走到最低點(diǎn)。每次更新參數(shù)的規(guī)則是w w - learning_rate * ?J/?w b b - learning_rate * ?J/?b這里的learning_rate學(xué)習(xí)率是步長(zhǎng)決定每次走多大一步。步子太大容易在山谷兩邊來(lái)回震蕩甚至直接沖出去步子太小則要很多步才能走到終點(diǎn)訓(xùn)練時(shí)間拉長(zhǎng)。在線性回歸里頭我們的損失函數(shù)是一個(gè)凸函數(shù)也就是說(shuō)這個(gè)曲面只有一個(gè)全局最低點(diǎn)沒(méi)有局部最優(yōu)的困擾。這比深度學(xué)習(xí)的非凸優(yōu)化問(wèn)題要簡(jiǎn)單得多但正因?yàn)樗?jiǎn)單非常適合用來(lái)理解“梯度下降是在干嘛”這件事。等你理解了手動(dòng)計(jì)算梯度再去理解深度學(xué)習(xí)里的反向傳播會(huì)發(fā)現(xiàn)原理其實(shí)是同一個(gè)。2.4 閉式解 vs 迭代優(yōu)化為什么深度學(xué)習(xí)只用迭代線性回歸其實(shí)還有一個(gè)不需要迭代的“直接解法”——最小二乘法的解析解公式W (X^T * X)^(-1) * X^T * y也就是說(shuō)只要數(shù)據(jù)量不大直接套這個(gè)公式一步到位就能求出最優(yōu)參數(shù)。那為什么深度學(xué)習(xí)中從來(lái)不用這種解法兩個(gè)原因。第一矩陣求逆的復(fù)雜度是O(n^3)當(dāng)數(shù)據(jù)維度很大深度學(xué)習(xí)里動(dòng)輒上百萬(wàn)參數(shù)時(shí)這個(gè)計(jì)算量完全不可行。第二深度學(xué)習(xí)模型不是線性的是層層嵌套的非線性函數(shù)根本不存在閉式解。所以必須用梯度下降這種迭代方式一步一步逼近最優(yōu)解。但在線性回歸這個(gè)環(huán)節(jié)兩種方法我都建議你親手跑一遍。跑閉式解能讓你理解“數(shù)學(xué)公式是怎么變成代碼的”跑梯度下降能讓你理解“深度學(xué)習(xí)訓(xùn)練的核心循環(huán)是怎么運(yùn)轉(zhuǎn)的”。兩條腿走路基礎(chǔ)才扎實(shí)。3. 完整實(shí)操?gòu)牧闶謱懢€性回歸3.1 環(huán)境準(zhǔn)備與數(shù)據(jù)構(gòu)造我用的環(huán)境非常普通Python 3.9、NumPy、Matplotlib這幾個(gè)就夠了。不依賴任何深度學(xué)習(xí)框架這樣才能看清楚核心邏輯。我們先造一組帶線性關(guān)系的數(shù)據(jù)作為實(shí)驗(yàn)樣本import numpy as np import matplotlib.pyplot as plt # 生成100個(gè)隨機(jī)點(diǎn)x在0到10之間均勻分布 np.random.seed(42) X np.linspace(0, 10, 100) # 真實(shí)關(guān)系是 y 2*x 5加上高斯噪聲模擬真實(shí)數(shù)據(jù) true_w 2.0 true_b 5.0 y true_w * X true_b np.random.randn(100) * 2 # 可視化 plt.scatter(X, y, alpha0.7) plt.xlabel(x) plt.ylabel(y) plt.show()這里有個(gè)細(xì)節(jié)要注意我用了np.random.seed(42)來(lái)固定隨機(jī)種子。這樣每次運(yùn)行生成的數(shù)據(jù)是一樣的方便你復(fù)現(xiàn)、排查問(wèn)題。實(shí)際工作中做實(shí)驗(yàn)也建議固定隨機(jī)種子不然模型的結(jié)果每次跑都不一樣你根本沒(méi)法判斷改動(dòng)是有效還是隨機(jī)波動(dòng)。3.2 手寫梯度下降訓(xùn)練循環(huán)下面是核心代碼。我故意不用任何封裝好的庫(kù)完全用NumPy手動(dòng)實(shí)現(xiàn)def compute_loss(w, b, X, y): y_pred w * X b loss np.mean((y - y_pred) ** 2) return loss def compute_gradient(w, b, X, y): n len(X) # 損失函數(shù)對(duì)w求導(dǎo) dw (2 / n) * np.sum(X * (w * X b - y)) # 損失函數(shù)對(duì)b求導(dǎo) db (2 / n) * np.sum(w * X b - y) return dw, db def train(X, y, learning_rate0.01, epochs1000): w 0.0 b 0.0 for epoch in range(epochs): dw, db compute_gradient(w, b, X, y) w w - learning_rate * dw b b - learning_rate * db if epoch % 100 0: loss compute_loss(w, b, X, y) print(fEpoch {epoch}: loss{loss:.4f}, w{w:.4f}, b{b:.4f}) return w, b運(yùn)行train(X, y)你會(huì)看到loss從幾千一路下降最后w落在2.0附近b落在5.0附近。這個(gè)過(guò)程本質(zhì)上就是深度學(xué)習(xí)訓(xùn)練的縮影前向計(jì)算算出預(yù)測(cè)值、計(jì)算損失MSE、反向求梯度這里因?yàn)槟P秃?jiǎn)單梯度是顯式推導(dǎo)出來(lái)的、更新參數(shù)梯度下降。等后面用PyTorch或TensorFlow的時(shí)候這個(gè)循環(huán)里的“反向求梯度”會(huì)被自動(dòng)微分的框架替掉其他環(huán)節(jié)完全沒(méi)有區(qū)別。3.3 閉式解驗(yàn)證再來(lái)看看閉式解法。把X擴(kuò)展成矩陣形式在左邊拼一列1對(duì)應(yīng)偏置b然后直接套公式# 構(gòu)造矩陣 [X, 1] X_b np.c_[X, np.ones(len(X))] # 最小二乘公式 theta np.linalg.inv(X_b.T X_b) X_b.T y w_closed, b_closed theta print(fClosed-form solution: w{w_closed:.4f}, b{b_closed:.4f})你跑出來(lái)會(huì)發(fā)現(xiàn)閉式解和梯度下降迭代出來(lái)的結(jié)果幾乎一致。這說(shuō)明兩種路線最終收斂到同一個(gè)最優(yōu)解——在凸優(yōu)化問(wèn)題里這是數(shù)學(xué)保證的。用一個(gè)小模型把這兩個(gè)解法打通你對(duì)“優(yōu)化”的理解會(huì)完全不一樣。3.4 學(xué)習(xí)率調(diào)多大會(huì)出事我強(qiáng)烈建議你做一個(gè)實(shí)驗(yàn)把learning_rate改成0.5再跑一遍。你會(huì)看到loss不但不下降反而逐漸變大最后直接變成inf。這就是學(xué)習(xí)率過(guò)大導(dǎo)致的“梯度爆炸”效應(yīng)。這個(gè)現(xiàn)象的原因可以用一個(gè)簡(jiǎn)單類比說(shuō)明你在下山每一步都邁得太大直接跨過(guò)谷底跳到了對(duì)面的半山腰接著又跨回來(lái)越蹦越高最終徹底掉出山體。而學(xué)習(xí)率太小的時(shí)候loss下降得非常緩慢就像挪螞蟻步跑幾百個(gè)epoch還在山腰上磨蹭。實(shí)踐中我總結(jié)出一個(gè)經(jīng)驗(yàn)學(xué)習(xí)率的選擇通常從0.01到0.001這個(gè)區(qū)間開(kāi)始試然后觀察loss曲線。如果loss振蕩不降就調(diào)小學(xué)習(xí)率如果loss下降得非常慢就適當(dāng)調(diào)大。這種“玄學(xué)”其實(shí)背后都是幾何直覺(jué)。4. 從線性回歸無(wú)縫過(guò)渡到深度學(xué)習(xí)4.1 把線性模型“翻譯”成神經(jīng)網(wǎng)絡(luò)語(yǔ)言現(xiàn)在到了最關(guān)鍵的環(huán)節(jié)把線性回歸和深度學(xué)習(xí)框架徹底打通。如果你去看PyTorch或TensorFlow里定義的全連接層Linear層你會(huì)發(fā)現(xiàn)它做的事情就是output input W.T b這不就是線性回歸的向量形式嗎對(duì)完全一樣。區(qū)別只在于神經(jīng)網(wǎng)絡(luò)會(huì)在這個(gè)線性變換后面緊跟一個(gè)非線性的激活函數(shù)比如ReLU、Sigmoid讓模型能夠擬合非線性關(guān)系。如果沒(méi)有激活函數(shù)無(wú)論疊多少層線性層整個(gè)網(wǎng)絡(luò)仍然是線性的“深度”就失去了意義。所以你可以把線性回歸理解成一個(gè)只有一層、沒(méi)有激活函數(shù)的神經(jīng)網(wǎng)絡(luò)。這也是為什么很多教材的目錄是“線性回歸 - Softmax回歸 - 多層感知機(jī) - CNN - RNN”一層一層往上加復(fù)雜度。4.2 用PyTorch重寫一遍學(xué)完手寫版本我們用PyTorch把同一個(gè)模型實(shí)現(xiàn)一遍對(duì)比一下深度框架幫你做了什么import torch import torch.nn as nn import torch.optim as optim # 將數(shù)據(jù)轉(zhuǎn)為Tensor X_t torch.tensor(X, dtypetorch.float32).view(-1, 1) y_t torch.tensor(y, dtypetorch.float32).view(-1, 1) # 定義模型一個(gè)線性層輸入維度1輸出維度1 model nn.Linear(1, 1) # 損失函數(shù)均方誤差 criterion nn.MSELoss() # 優(yōu)化器隨機(jī)梯度下降SGD學(xué)習(xí)率0.01 optimizer optim.SGD(model.parameters(), lr0.01) # 訓(xùn)練循環(huán) for epoch in range(1000): optimizer.zero_grad() y_pred model(X_t) loss criterion(y_pred, y_t) loss.backward() optimizer.step() if epoch % 100 0: print(fEpoch {epoch}: loss{loss.item():.4f})你沒(méi)看錯(cuò)訓(xùn)練的核心就這幾行前向計(jì)算y_pred、算loss、反向傳播backward()、更新參數(shù)step()。至于梯度是怎么算的PyTorch的自動(dòng)微分機(jī)制幫你做了。但如果你沒(méi)有親手手寫過(guò)梯度推導(dǎo)你會(huì)覺(jué)得backward()是一個(gè)黑盒出了問(wèn)題完全不知道從哪兒排查。這也是我堅(jiān)持先帶大家手寫一遍的原因。4.3 損失函數(shù)曲線判斷訓(xùn)練是否正常的核心工具訓(xùn)練深度學(xué)習(xí)模型最重要的指標(biāo)之一就是loss曲線。線性回歸里你打印每一輪的loss會(huì)看到一條平滑下降的曲線。但實(shí)際訓(xùn)練中我發(fā)現(xiàn)一個(gè)極其普遍的新手誤區(qū)只看最終loss不看loss曲線形態(tài)。正常的loss曲線應(yīng)該呈現(xiàn)“快速下降到平緩收斂”的形狀。如果loss曲線像過(guò)山車一樣劇烈震蕩大概率是學(xué)習(xí)率太大如果loss曲線幾乎是一條水平線且數(shù)值很大可能是學(xué)習(xí)率太小、特征沒(méi)有歸一化或者梯度消失了。舉個(gè)例子在標(biāo)準(zhǔn)化的數(shù)據(jù)上做線性回歸learning_rate0.01loss從幾千下降到幾十大概在300個(gè)epoch后趨于平緩但如果數(shù)據(jù)不做標(biāo)準(zhǔn)化比如x的取值是0到10000同樣的學(xué)習(xí)率會(huì)導(dǎo)致loss爆裂。原因在于x取值范圍大的時(shí)候X * w那部分的梯度會(huì)非常大放大了參數(shù)更新的步長(zhǎng)。在線性回歸里我們就應(yīng)該培養(yǎng)這個(gè)習(xí)慣每次訓(xùn)練都畫出loss曲線觀察它的收斂形態(tài)而不是只看最終數(shù)字。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 梯度爆炸從數(shù)值溢出到NaN這是我訓(xùn)練中遇到的第一個(gè)經(jīng)典坑。當(dāng)我把學(xué)習(xí)率從0.01改成0.1loss在前幾個(gè)epoch直接變成NaN。排查思路非常簡(jiǎn)單把學(xué)習(xí)率調(diào)回0.01程序恢復(fù)正常說(shuō)明是學(xué)習(xí)率過(guò)大打印每一輪更新后的w和b發(fā)現(xiàn)w和b在幾十輪內(nèi)膨脹到幾百上千說(shuō)明是參數(shù)更新步長(zhǎng)太大數(shù)值溢出。解決方案也不難降低學(xué)習(xí)率、對(duì)輸入特征做標(biāo)準(zhǔn)化、必要時(shí)使用梯度裁剪gradient clipping。在線性回歸中梯度爆炸主要是因?yàn)閿?shù)據(jù)沒(méi)標(biāo)準(zhǔn)化和學(xué)習(xí)率過(guò)大但深度學(xué)習(xí)里梯度爆炸是家常便飯?zhí)崆梆B(yǎng)成檢查梯度的習(xí)慣會(huì)受益匪淺。5.2 特征工程的重要性x的取值范圍決定了收斂速度在線性回歸中x的取值范圍直接影響收斂速度。同樣是y 2x 5這個(gè)關(guān)系如果x在0到1之間learning_rate0.1也能很快收斂如果x在0到10000之間learning_rate0.1直接爆炸。原因在于損失函數(shù)對(duì)w的偏導(dǎo)里包含一個(gè)乘以x的項(xiàng)x越大梯度越大參數(shù)更新步長(zhǎng)就會(huì)異常大。所以數(shù)據(jù)預(yù)處理的標(biāo)準(zhǔn)化標(biāo)準(zhǔn)化為均值0、方差1不是錦上添花而是優(yōu)化能夠正常進(jìn)行的前提。這個(gè)經(jīng)驗(yàn)在深度學(xué)習(xí)中更加重要圖像數(shù)據(jù)歸一化到0-1區(qū)間、文本特征做歸一化都是為了讓不同尺度的特征在梯度下降過(guò)程中“公平競(jìng)爭(zhēng)”。5.3 為什么你的損失函數(shù)不再下降我見(jiàn)過(guò)不少人在線性回歸實(shí)驗(yàn)里卡在loss不再下降這個(gè)現(xiàn)象上以為代碼寫錯(cuò)了。第一種可能是數(shù)據(jù)本身含有噪聲loss下降到某個(gè)值后繼續(xù)減小反而可能把噪聲也擬合進(jìn)去也就是過(guò)擬合第二種可能是學(xué)習(xí)率太小模型還在一個(gè)非常平緩的區(qū)域慢慢挪理論上可以繼續(xù)下降但需要極長(zhǎng)的時(shí)間第三種可能是模型結(jié)構(gòu)不夠比如真實(shí)關(guān)系是二次的你偏用線性模型來(lái)擬合loss自然會(huì)卡在一個(gè)較高的水平。排查方法很簡(jiǎn)單畫出擬合直線和原始數(shù)據(jù)散點(diǎn)圖。如果直線明顯偏離數(shù)據(jù)的整體走勢(shì)說(shuō)明模型容量不夠如果直線很好地穿過(guò)數(shù)據(jù)中心但每個(gè)點(diǎn)都有一定偏離那基本是數(shù)據(jù)噪聲的極限了。順嘴提一個(gè)非常有價(jià)值的實(shí)操技巧記錄不同學(xué)習(xí)率下的loss曲線放在同一張圖上對(duì)比。這會(huì)讓你非常直觀地看到學(xué)習(xí)率從0.0001、0.001、0.01、0.1的變化對(duì)收斂速度和最終loss的影響。我自己當(dāng)年做完這個(gè)對(duì)比實(shí)驗(yàn)對(duì)梯度下降的理解直接上了一個(gè)臺(tái)階。5.4 模型評(píng)估訓(xùn)練損失低不等于模型好還有一個(gè)很容易被忽略的問(wèn)題怎么評(píng)估你的線性回歸模型好壞最簡(jiǎn)單的方法是劃分訓(xùn)練集和測(cè)試集。用訓(xùn)練集擬合出w和b然后在測(cè)試集上計(jì)算MSE。如果測(cè)試集loss明顯高于訓(xùn)練集loss說(shuō)明模型在訓(xùn)練集上“死記硬背”了噪聲這就是過(guò)擬合。對(duì)線性回歸來(lái)說(shuō)因?yàn)槟P秃?jiǎn)單過(guò)擬合風(fēng)險(xiǎn)相對(duì)較小但當(dāng)你加入多項(xiàng)式特征比如x^2之后過(guò)擬合很快就會(huì)出現(xiàn)。做個(gè)簡(jiǎn)單版本from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split(X_b, y, test_size0.2, random_state42) # 在訓(xùn)練集上擬合 theta np.linalg.inv(X_train.T X_train) X_train.T y_train # 在測(cè)試集上評(píng)估 y_pred X_test theta test_loss np.mean((y_test - y_pred) ** 2) print(fTest loss: {test_loss:.4f})這個(gè)流程以后做深度學(xué)習(xí)也是一模一樣的train set訓(xùn)練、validation set調(diào)參、test set最終驗(yàn)證三者不可混用。錯(cuò)誤的做法是反復(fù)用測(cè)試集調(diào)參這相當(dāng)于獲得答案后再做練習(xí)題測(cè)試集的評(píng)估意義就被徹底摧毀了。6. 從線性回歸向外拓展的幾條路線6.1 邏輯回歸從回歸到分類的跳板線性回歸解決的是“預(yù)測(cè)連續(xù)數(shù)值”的問(wèn)題但現(xiàn)實(shí)里還有一堆“分類”問(wèn)題郵件是不是垃圾郵件是/否、圖片里是貓還是狗貓/狗。線性回歸的輸出是一個(gè)無(wú)界的實(shí)數(shù)沒(méi)法直接當(dāng)作概率使用。解決辦法是在線性輸出上套一個(gè)Sigmoid函數(shù)把結(jié)果壓縮到0到1之間這就是邏輯回歸Logistic Regression。邏輯回歸是深度學(xué)習(xí)中二分類問(wèn)題的原型也可以看成從線性回歸到神經(jīng)網(wǎng)絡(luò)的一個(gè)自然橋梁。理解了線性回歸的損失函數(shù)是MSE再去看邏輯回歸的交叉熵?fù)p失你會(huì)發(fā)現(xiàn)它們都是在衡量“預(yù)測(cè)分布和真實(shí)分布的差異”只是換了一個(gè)更適合分類場(chǎng)景的函數(shù)。6.2 多項(xiàng)式回歸線性模型的非線性擴(kuò)展如果數(shù)據(jù)不是直線關(guān)系而是拋物線怎么辦一個(gè)巧妙的辦法是“把非線性變成線性”給模型增加一個(gè)x^2的特征。這樣原來(lái)的y ax b就變成了y ax bx^2 c看起來(lái)還是線性回歸但x換成了更高維度的特征。這就是特征工程Feature Engineering的雛形。這個(gè)方法顯示了線性回歸的局限性也反映了深度學(xué)習(xí)的一個(gè)核心思想特征表示決定了模型能力上限。深度學(xué)習(xí)之所以強(qiáng)大是因?yàn)樗ㄟ^(guò)層層特征提取自動(dòng)學(xué)習(xí)到合適的表示而不需要人工設(shè)計(jì)x^2、x^3這樣的特征。6.3 深度神經(jīng)網(wǎng)絡(luò)線性層 激活函數(shù) 多層堆疊最關(guān)鍵的路線就是你已經(jīng)猜到的將單個(gè)線性層換成多個(gè)線性層每層后面加激活函數(shù)。結(jié)構(gòu)變成輸入 - 線性層1 - ReLU - 線性層2 - ReLU - ... - 線性層N - 輸出這個(gè)過(guò)程就是多層感知機(jī)MLP。雖然看起來(lái)只是做了線性變換加非線性變換的反復(fù)操作但它有理論上的保證只要層數(shù)足夠多、寬度足夠大神經(jīng)網(wǎng)絡(luò)可以以任意精度逼近任意連續(xù)函數(shù)。這就是通用近似定理。線性回歸是這條大路的第0公里而你已經(jīng)跑完了這一程。7. 寫在最后的個(gè)人體會(huì)我?guī)н^(guò)很多完全零基礎(chǔ)的朋友學(xué)習(xí)機(jī)器學(xué)習(xí)和深度學(xué)習(xí)發(fā)現(xiàn)每個(gè)人幾乎都會(huì)經(jīng)歷同一個(gè)拐點(diǎn)手推完線性回歸的梯度再用框架跑通第一個(gè)訓(xùn)練循環(huán)之后原本模糊的概念突然就清晰了。因?yàn)椤坝?xùn)練”這件事本質(zhì)上就是反復(fù)執(zhí)行“計(jì)算預(yù)測(cè) - 計(jì)算損失 - 求梯度 - 更新參數(shù)”這個(gè)循環(huán)線性回歸里它是顯式的、透明的到了深度學(xué)習(xí)中它被框架封裝了但內(nèi)核完全沒(méi)有變。所以我一直堅(jiān)信深度學(xué)習(xí)入門不要急著上GPU、云平臺(tái)或者復(fù)雜框架先把線性回歸在NumPy里手動(dòng)實(shí)現(xiàn)一遍再看框架怎么封裝效率遠(yuǎn)高于一上來(lái)就用高級(jí)API?;A(chǔ)不牢的時(shí)候你無(wú)法判斷訓(xùn)練結(jié)果到底是模型問(wèn)題、數(shù)據(jù)問(wèn)題還是代碼問(wèn)題只能靠瞎猜。另外分享一個(gè)小技巧我每次調(diào)試代碼習(xí)慣在訓(xùn)練循環(huán)里每隔一定epoch打印訓(xùn)練集和驗(yàn)證集的loss同時(shí)畫出一條擬合曲線的動(dòng)畫。這樣做能捕捉到訓(xùn)練過(guò)程中的每一個(gè)異常比如某個(gè)epoch之后loss突然反彈能快速定位到是數(shù)據(jù)擾動(dòng)還是學(xué)習(xí)率半路需要調(diào)整。這個(gè)習(xí)慣看起來(lái)不起眼但確實(shí)幫我排查了無(wú)數(shù)次問(wèn)題。線性回歸只是一個(gè)開(kāi)始但這“開(kāi)始”的價(jià)值值得你花足夠的時(shí)間去打磨。后面的路還很長(zhǎng)但每一步都建立在同一塊基石之上。