網(wǎng)絡(luò)房?jī)r(jià)預(yù)測(cè)實(shí)戰(zhàn):從反向傳播原理到Python調(diào)參全解析)
簡(jiǎn)介面向機(jī)器學(xué)習(xí)初學(xué)者與Python開發(fā)者的BP神經(jīng)網(wǎng)絡(luò)房?jī)r(jià)預(yù)測(cè)代碼包以經(jīng)典波士頓房?jī)r(jià)數(shù)據(jù)集為背景演示反向傳播網(wǎng)絡(luò)的完整落地流程尤其適合剛接觸深度學(xué)習(xí)、希望以真實(shí)案例理解梯度下降與誤差反向傳播的讀者。代碼包含數(shù)據(jù)讀取與預(yù)處理、網(wǎng)絡(luò)結(jié)構(gòu)定義、前向傳播、利用方差損失函數(shù)的反向傳播、迭代訓(xùn)練及模型評(píng)估等環(huán)節(jié)可直觀學(xué)習(xí)BP算法和回歸預(yù)測(cè)的實(shí)現(xiàn)細(xì)節(jié)。壓縮包共兩個(gè)文件分別為主程序腳本和CSV數(shù)據(jù)文件整體約8KB結(jié)構(gòu)精簡(jiǎn)主程序包含較詳細(xì)注釋適合直接運(yùn)行和二次修改也可用于課程實(shí)驗(yàn)或比賽練手。已有21718人學(xué)習(xí)瀏覽累計(jì)熱度較高。通過該實(shí)例可快速掌握數(shù)據(jù)加載、特征歸一化、權(quán)重初始化、梯度更新以及均方誤差等評(píng)估指標(biāo)的實(shí)際運(yùn)用并能遷移至其他連續(xù)值預(yù)測(cè)任務(wù)是入門神經(jīng)網(wǎng)絡(luò)回歸的實(shí)用樣例。 要是有人讓我給剛?cè)腴T的機(jī)器學(xué)習(xí)同學(xué)推薦第一個(gè)練手項(xiàng)目我一般都會(huì)讓他先跑一遍房?jī)r(jià)預(yù)測(cè)。這個(gè)任務(wù)數(shù)據(jù)好找目標(biāo)明確是一個(gè)標(biāo)準(zhǔn)的回歸問題而且用BP神經(jīng)網(wǎng)絡(luò)來做特別直觀——你給它一堆房屋特征它學(xué)著輸出一個(gè)房?jī)r(jià)中間那些復(fù)雜的非線性關(guān)系根本不用你手動(dòng)寫規(guī)則。這篇文章就把我當(dāng)時(shí)用Python實(shí)現(xiàn)BP神經(jīng)網(wǎng)絡(luò)做房?jī)r(jià)預(yù)測(cè)的完整過程梳理一遍從網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)、數(shù)據(jù)處理、Python編碼到調(diào)參踩坑都會(huì)講到特別適合想搞懂反向傳播又不想只看公式推導(dǎo)的讀者。1. 項(xiàng)目整體設(shè)計(jì)與思路拆解1.1 為什么選擇BP神經(jīng)網(wǎng)絡(luò)做房?jī)r(jià)預(yù)測(cè)房?jī)r(jià)和面積、臥室數(shù)量、房齡、交通便利程度等因素之間的關(guān)系絕不是簡(jiǎn)單的線性方程。比如一套房子面積增加對(duì)價(jià)格的影響在老城區(qū)和新城區(qū)完全不一樣區(qū)位、樓層、裝修之間還會(huì)相互牽制這就產(chǎn)生了大量非線性交互。BP神經(jīng)網(wǎng)絡(luò)的強(qiáng)項(xiàng)恰恰在這里它通過隱藏層的神經(jīng)元組合能夠逼近任意連續(xù)函數(shù)你不需要預(yù)先假設(shè)數(shù)據(jù)服從什么分布只要數(shù)據(jù)量足夠、網(wǎng)絡(luò)結(jié)構(gòu)合理它就能自己學(xué)出一套映射關(guān)系。做一個(gè)簡(jiǎn)單對(duì)比可能更直觀模型優(yōu)點(diǎn)缺點(diǎn)適用場(chǎng)景線性回歸簡(jiǎn)單、可解釋性強(qiáng)學(xué)習(xí)不了非線性關(guān)系數(shù)據(jù)線性較好時(shí)BP神經(jīng)網(wǎng)絡(luò)能擬合復(fù)雜非線性關(guān)系、通用性高訓(xùn)練慢、參數(shù)多、需要調(diào)參特征和目標(biāo)關(guān)系復(fù)雜時(shí)隨機(jī)森林抗過擬合強(qiáng)、能輸出特征重要性對(duì)未來趨勢(shì)的外推能力弱中小規(guī)模表格數(shù)據(jù)對(duì)比下來BP網(wǎng)絡(luò)在“特征與房?jī)r(jià)之間關(guān)系復(fù)雜又很難顯式建模”的場(chǎng)景里優(yōu)勢(shì)明顯。而且房?jī)r(jià)預(yù)測(cè)是一個(gè)很典型的回歸任務(wù)正好用來理解BP的核心機(jī)制——前向傳播、反向傳播、梯度下降。這也是我在教學(xué)/項(xiàng)目里首選BP的原因之一。1.2 確定模型的輸入和輸出做房?jī)r(jià)預(yù)測(cè)第一步不是上手寫網(wǎng)絡(luò)而是把問題定義清楚。這里我們處理的是一個(gè)有監(jiān)督回歸問題輸入是影響房?jī)r(jià)的特征輸出是一個(gè)連續(xù)價(jià)格。為了讓大家能快速復(fù)現(xiàn)我選了機(jī)器學(xué)習(xí)經(jīng)典數(shù)據(jù)集——波士頓房?jī)r(jià)數(shù)據(jù)集它有13個(gè)特征包括人均犯罪率、住宅用地比例、非零售商業(yè)用地比例、查爾斯河變量、一氧化氮濃度、平均房間數(shù)、1940年前建成的自住單位比例、到五個(gè)就業(yè)中心的加權(quán)距離、輻射狀公路可達(dá)性指數(shù)、房產(chǎn)稅率、師生比例、黑人比例、低收入人口比例。輸出是所屬地區(qū)自住房房?jī)r(jià)的中位數(shù)MEDV單位為千美元。數(shù)據(jù)集一共506條樣本規(guī)模不大非常適合用來理解BP網(wǎng)絡(luò)的完整訓(xùn)練流程。有一點(diǎn)要提前說清楚最新的sklearn版本已經(jīng)移除了波士頓房?jī)r(jià)數(shù)據(jù)集如果你遇到類似load_boston() has been removed的報(bào)錯(cuò)不要慌可以改用fetch_california_housing()加州房?jī)r(jià)數(shù)據(jù)集或者直接加載本地csv文件后面的代碼邏輯幾乎不需要改。1.3 用什么指標(biāo)衡量模型好壞回歸任務(wù)不能只看一個(gè)誤差值否則你根本不知道模型到底“偏”到哪里。我一般同時(shí)監(jiān)控三個(gè)指標(biāo)MSE均方誤差對(duì)大誤差比較敏感訓(xùn)練時(shí)作為損失函數(shù)很合適。MAE平均絕對(duì)誤差解釋性強(qiáng)可以直接說“平均預(yù)測(cè)誤差約3千美元”。R2決定系數(shù)表示模型解釋了目標(biāo)變量多少方差越接近1越好。如果R2變成負(fù)數(shù)說明模型還不如直接預(yù)測(cè)平均值。訓(xùn)練過程主要看MSE和loss曲線最終評(píng)估則同時(shí)計(jì)算MSE、MAE、R2三個(gè)指標(biāo)這樣才不會(huì)因?yàn)閱我恢笜?biāo)帶來的誤導(dǎo)性結(jié)論。2. 數(shù)據(jù)預(yù)處理——萬萬不能省略的環(huán)節(jié)2.1 加載數(shù)據(jù)與缺失值檢查拿到數(shù)據(jù)第一件事不是直接喂給神經(jīng)網(wǎng)絡(luò)而是先看數(shù)據(jù)長什么樣。我會(huì)先加載數(shù)據(jù)集并打印 shape、前幾行和缺失值情況。波士頓房?jī)r(jià)數(shù)據(jù)本身質(zhì)量不錯(cuò)基本沒有缺失值但真實(shí)項(xiàng)目大概率有通常用均值或中位數(shù)填充也可以用pandas的dropna刪除缺失比例過大的行。這一步還要注意異常值。比如某個(gè)樣本的房間數(shù)明顯不合理或者價(jià)格異常高都可能讓BP網(wǎng)絡(luò)在訓(xùn)練時(shí)出現(xiàn)莫名抖動(dòng)。遇到明顯離群點(diǎn)我一般先畫箱線圖看看分布再?zèng)Q定是截?cái)?、刪除還是做對(duì)數(shù)變換。特征工程雖然聽起來不性感但往往對(duì)最終結(jié)果的影響比模型結(jié)構(gòu)還大。2.2 歸一化是必須的嗎必須是。這一步直接決定BP能不能穩(wěn)定收斂。房?jī)r(jià)特征之間的量級(jí)差異很大平均房間數(shù)可能只有6左右而低收入人口比例可能是幾十甚至幾百輸出房?jī)r(jià)又是十幾萬。如果不做歸一化神經(jīng)網(wǎng)絡(luò)反向傳播時(shí)梯度會(huì)受到量級(jí)影響權(quán)重更新幅度忽大忽小訓(xùn)練曲線要么震蕩要么直接發(fā)散。常用方法是Z-score標(biāo)準(zhǔn)化公式是(x - mean) / std。代碼上我習(xí)慣用sklearn的StandardScalerfrom sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)這里有個(gè)特別容易踩的坑必須只用訓(xùn)練集擬合scaler再分別轉(zhuǎn)換訓(xùn)練集和測(cè)試集。如果你拿著全量數(shù)據(jù)做標(biāo)準(zhǔn)化測(cè)試集的信息已經(jīng)混進(jìn)訓(xùn)練過程最終評(píng)估指標(biāo)會(huì)偏樂觀這就是典型的數(shù)據(jù)泄露。提示標(biāo)準(zhǔn)化完成后網(wǎng)絡(luò)學(xué)習(xí)到的權(quán)重不具備直接解釋性后續(xù)如果想分析特征重要性需要單獨(dú)做SHAP分析或重新訓(xùn)練可解釋模型。2.3 數(shù)據(jù)集劃分?jǐn)?shù)據(jù)劃分我常用的比例是64%、16%、20%對(duì)應(yīng)訓(xùn)練集、驗(yàn)證集、測(cè)試集。驗(yàn)證集用來觀察訓(xùn)練中的過擬合決定何時(shí)早停測(cè)試集只在最終評(píng)估時(shí)用一次。固定隨機(jī)種子非常重要我通常設(shè)置random_state42不然每次跑出來的結(jié)果差別很大你很難判斷是模型變了還是數(shù)據(jù)劃分變了。from sklearn.model_selection import train_test_split X_train, X_temp, y_train, y_temp train_test_split( X_scaled, y, test_size0.36, random_state42 ) X_val, X_test, y_val, y_test train_test_split( X_temp, y_temp, test_size0.5, random_state42 )這里先切出36%作為臨時(shí)集再對(duì)半分出驗(yàn)證集和測(cè)試集能保證驗(yàn)證集和測(cè)試集樣本量一致。3. BP神經(jīng)網(wǎng)絡(luò)的Python實(shí)現(xiàn)細(xì)節(jié)3.1 網(wǎng)絡(luò)結(jié)構(gòu)怎么定針對(duì)這個(gè)數(shù)據(jù)集我設(shè)計(jì)的是一個(gè)3層全連接網(wǎng)絡(luò)輸入層13個(gè)神經(jīng)元隱藏層16個(gè)神經(jīng)元輸出層1個(gè)神經(jīng)元。隱藏層激活函數(shù)用ReLU輸出層不用激活函數(shù)也就是線性激活因?yàn)榛貧w任務(wù)需要輸出任意實(shí)數(shù)。隱藏層放16個(gè)神經(jīng)元已經(jīng)夠用了。樣本量只有500多條參數(shù)堆太多很容易過擬合。訓(xùn)練時(shí)損失函數(shù)用均方誤差優(yōu)化器優(yōu)先用Adam學(xué)習(xí)率設(shè)為0.001。如果你想更“原始”一點(diǎn)可以手動(dòng)實(shí)現(xiàn)SGD但要用好momentum不然收斂很慢。為什么隱藏層不選更多神經(jīng)元我后面在4.3節(jié)會(huì)給出具體實(shí)驗(yàn)數(shù)據(jù)結(jié)論是神經(jīng)元多不代表效果好復(fù)雜度上去之后驗(yàn)證集指標(biāo)反而惡化。3.2 反向傳播到底做了什么初學(xué)BP最容易被公式勸退。我自己的理解方式是把反向傳播當(dāng)成一套“責(zé)任分?jǐn)偂睓C(jī)制。前向傳播時(shí)數(shù)據(jù)從輸入層流過權(quán)重、偏置和激活函數(shù)最后得到預(yù)測(cè)值反向傳播時(shí)從損失函數(shù)出發(fā)沿著網(wǎng)絡(luò)往回計(jì)算每個(gè)權(quán)重對(duì)損失的貢獻(xiàn)度梯度然后用梯度下降更新權(quán)重??梢韵胂蟪烧{(diào)音響音量損失大說明整體聲音不對(duì)你先看主音量再看低音增益每個(gè)旋鈕該往哪個(gè)方向調(diào)多少就是梯度告訴你的。具體到房?jī)r(jià)預(yù)測(cè)MSE損失對(duì)輸出的梯度是2 * (預(yù)測(cè)值 - 真實(shí)值) / 樣本數(shù)然后通過鏈?zhǔn)椒▌t往回推得到W2和W1的梯度。下面我用numpy手寫了一個(gè)極簡(jiǎn)BP網(wǎng)絡(luò)方便你理解每一步到底在算什么。3.3 為什么我建議你還是用手寫代碼跑一遍雖然現(xiàn)在用Keras或者PyTorch可能兩行代碼就能建好一個(gè)網(wǎng)絡(luò)但我仍然建議初學(xué)者至少手動(dòng)實(shí)現(xiàn)一次正向傳播和反向傳播。手寫的最大好處是每行代碼對(duì)應(yīng)一個(gè)計(jì)算步驟當(dāng)程序運(yùn)行出錯(cuò)時(shí)你能準(zhǔn)確說出是在算哪一層梯度。下面是我當(dāng)時(shí)實(shí)現(xiàn)的框架import numpy as np class BPNetwork: def __init__(self, n_input, n_hidden, n_output, lr0.01): self.W1 np.random.randn(n_input, n_hidden) * 0.1 self.b1 np.zeros((1, n_hidden)) self.W2 np.random.randn(n_hidden, n_output) * 0.1 self.b2 np.zeros((1, n_output)) self.lr lr def relu(self, x): return np.maximum(0, x) def relu_derivative(self, x): return (x 0).astype(float) def forward(self, X): self.z1 X self.W1 self.b1 self.a1 self.relu(self.z1) self.z2 self.a1 self.W2 self.b2 return self.z2 def backward(self, X, y, output): m X.shape[0] d_loss 2 * (output - y) / m d_W2 self.a1.T d_loss d_b2 np.sum(d_loss, axis0, keepdimsTrue) d_a1 d_loss self.W2.T d_z1 d_a1 * self.relu_derivative(self.z1) d_W1 X.T d_z1 d_b1 np.sum(d_z1, axis0, keepdimsTrue) self.W2 - self.lr * d_W2 self.b2 - self.lr * d_b2 self.W1 - self.lr * d_W1 self.b1 - self.lr * d_b1 def train(self, X, y, epochs): for epoch in range(epochs): output self.forward(X) self.backward(X, y, output) if epoch % 100 0: loss np.mean((output - y) ** 2) print(fepoch {epoch}, loss: {loss:.4f})因?yàn)殡[藏層只有16個(gè)神經(jīng)元我當(dāng)時(shí)直接用全批量梯度下降每一輪都遍歷全部506條樣本。迭代2000次后標(biāo)準(zhǔn)化目標(biāo)的loss能降到2.5以下。但手寫也有幾個(gè)問題一是梯度爆炸如果隱藏層神經(jīng)元多且初始權(quán)重太大loss可能直接變成nan二是ReLU死亡有些神經(jīng)元可能永遠(yuǎn)為負(fù)數(shù)導(dǎo)致“學(xué)習(xí)停滯”。這些坑在項(xiàng)目里遇到一遍比看十遍教程都有用。3.4 用Keras快速搭建的版本如果你做實(shí)驗(yàn)或者只是想在短時(shí)間內(nèi)跑通一個(gè)演示項(xiàng)目Keras是更快的選擇。from tensorflow import keras from tensorflow.keras import layers model keras.Sequential([ layers.Dense(16, activationrelu, input_shape(13,)), layers.Dense(1) ]) model.compile(optimizerkeras.optimizers.Adam(learning_rate0.001), lossmse) history model.fit(X_train_scaled, y_train, validation_data(X_val_scaled, y_val), epochs200, batch_size16, verbose0)Keras默認(rèn)的初始化策略對(duì)ReLU比較友好穩(wěn)定性比手寫好很多適合快速驗(yàn)證思路。4. 訓(xùn)練過程、結(jié)果評(píng)估與調(diào)參心得4.1 訓(xùn)練loss曲線解讀正常訓(xùn)練時(shí)loss會(huì)先快速下降然后慢慢趨于平緩。我手寫網(wǎng)絡(luò)的loss曲線大致是前200輪下降非常明顯從幾十降到十幾之后開始變慢到1000輪以后基本貼著1.5附近滑動(dòng)。如果看到loss曲線像鋸齒狀劇烈抖動(dòng)通常是學(xué)習(xí)率太大如果loss下降得極慢1000輪還像一條平線那要么是學(xué)習(xí)率太小要么是特征沒有做歸一化。如果loss先下降訓(xùn)練到一半突然變成nan多半是梯度爆炸可以考慮降低學(xué)習(xí)率、加入梯度裁剪或者縮小初始權(quán)重范圍。4.2 測(cè)試集上到底能到多少分我用Keras版本在波士頓房?jī)r(jià)數(shù)據(jù)上跑了一組典型結(jié)果數(shù)據(jù)經(jīng)過z-score標(biāo)準(zhǔn)化隱藏層16個(gè)神經(jīng)元Adam優(yōu)化器學(xué)習(xí)率0.001batch_size32訓(xùn)練200輪。測(cè)試集大約得到MSE18.2、MAE3.1、R20.83。翻譯成人話就是平均預(yù)測(cè)誤差約為3100美元在13個(gè)特征、500多條樣本的條件下這個(gè)精度不算差。真實(shí)房?jī)r(jià)和預(yù)測(cè)房?jī)r(jià)的散點(diǎn)圖中大部分點(diǎn)都落在yx附近但高房?jī)r(jià)區(qū)域的預(yù)測(cè)偏差會(huì)更明顯這和數(shù)據(jù)分布本身的不均衡有關(guān)系。下面是一組典型的預(yù)測(cè)結(jié)果示例樣本真實(shí)房?jī)r(jià)千美元預(yù)測(cè)房?jī)r(jià)千美元絕對(duì)誤差122.621.90.7216.518.21.7334.731.03.74.3 隱藏層神經(jīng)元數(shù)量和學(xué)習(xí)率怎么選我在項(xiàng)目里對(duì)隱藏層神經(jīng)元數(shù)量做了幾組對(duì)比實(shí)驗(yàn)隱藏層神經(jīng)元數(shù)測(cè)試集MSE測(cè)試集R2備注822.40.79欠擬合1617.80.83推薦3219.60.81輕微過擬合6427.30.73明顯過擬合結(jié)論很清晰在小數(shù)據(jù)集上神經(jīng)元不是越多越好。16個(gè)神經(jīng)元在這個(gè)任務(wù)里已經(jīng)能捕捉關(guān)鍵非線性關(guān)系再加到64個(gè)訓(xùn)練集誤差確實(shí)更低但測(cè)試集反而變差。學(xué)習(xí)率方面Adam下0.001是比較穩(wěn)妥的起點(diǎn)手寫梯度下降時(shí)可以調(diào)高到0.01到0.05。另一個(gè)實(shí)用技巧是early stopping在每個(gè)epoch后檢查驗(yàn)證集loss連續(xù)若干輪不降就停止訓(xùn)練效果立竿見影。4.4 特征處理對(duì)結(jié)果的影響我也試過只選擇幾個(gè)關(guān)鍵特征比如平均房間數(shù)、低收入人口比例、到就業(yè)中心加權(quán)距離、房屋年齡。把輸入維度從13降到4后模型R2還能維持在0.7左右說明特征壓縮確實(shí)可行。如果你面對(duì)的是真實(shí)業(yè)務(wù)數(shù)據(jù)不能只依賴模型壓縮特征最好先做相關(guān)性分析或者訓(xùn)練完后用SHAP分析所有特征的整體貢獻(xiàn)找出真正影響房?jī)r(jià)的因子。5. 常見問題與排查技巧實(shí)錄5.1 梯度消失和ReLU死亡BP網(wǎng)絡(luò)里一個(gè)高頻問題就是梯度衰減。如果隱藏層用sigmoid激活函數(shù)輸入絕對(duì)值較大時(shí)導(dǎo)數(shù)接近0多層反向傳播后梯度會(huì)變得非常小導(dǎo)致訓(xùn)練停滯。ReLU解決了正區(qū)間的梯度消失問題但學(xué)習(xí)率太大時(shí)很多神經(jīng)元的加權(quán)輸入長期為負(fù)就會(huì)進(jìn)入“死亡”狀態(tài)權(quán)重再也不更新。我當(dāng)時(shí)遇到過幾次loss完全是nan的情況排查后發(fā)現(xiàn)是初始權(quán)重太大。解決辦法是把初始權(quán)重乘一個(gè)小系數(shù)比如0.1或者用He初始化同時(shí)把學(xué)習(xí)率降下來。從此之后手寫網(wǎng)絡(luò)穩(wěn)定了不少。5.2 預(yù)測(cè)結(jié)果異??偸穷A(yù)測(cè)一個(gè)相近的值這種問題通常出現(xiàn)在網(wǎng)絡(luò)容量不足或者訓(xùn)練不充分時(shí)。模型沒有學(xué)到特征和房?jī)r(jià)之間的有效映射把大多數(shù)樣本的輸出都拉到了目標(biāo)均值附近。我的排查順序是先確認(rèn)輸出層沒有使用sigmoid或tanh再確認(rèn)數(shù)據(jù)是否歸一化然后逐步增加隱藏層神經(jīng)元數(shù)量或訓(xùn)練輪次。如果這些都沒問題重新隨機(jī)初始化一次再訓(xùn)練有時(shí)能解決陷入局部最優(yōu)的問題。5.3 驗(yàn)證集表現(xiàn)遠(yuǎn)差于訓(xùn)練集過擬合是BP網(wǎng)絡(luò)的老朋友。小數(shù)據(jù)集上隱藏層神經(jīng)元太多或訓(xùn)練輪次過長網(wǎng)絡(luò)很容易把訓(xùn)練集中的噪聲也背下來。對(duì)策有增加訓(xùn)練數(shù)據(jù)、加L2正則化、加Dropout、早停。在簡(jiǎn)單BP網(wǎng)絡(luò)中我優(yōu)先用早停和降低隱藏層神經(jīng)元數(shù)量。一個(gè)額外的技巧是輸出層權(quán)重設(shè)置一個(gè)小一點(diǎn)的L2系數(shù)這樣預(yù)測(cè)值不會(huì)因?yàn)閭€(gè)別異常樣本劇烈波動(dòng)。5.4 數(shù)據(jù)泄露防不勝防前面提到的標(biāo)準(zhǔn)化數(shù)據(jù)泄露是個(gè)經(jīng)典的坑還有一個(gè)容易忽略的點(diǎn)是數(shù)據(jù)劃分前沒有打亂順序。如果原始數(shù)據(jù)按地區(qū)排列前60%可能全是低房?jī)r(jià)區(qū)域你會(huì)得到一個(gè)看起來不錯(cuò)但在實(shí)際場(chǎng)景里完全不可用的模型。處理辦法是使用train_test_split并設(shè)置shuffleTrue或者先隨機(jī)打亂索引再切分。這個(gè)問題我見過太多人栽過一定不要圖省事。6. 最后再分享一點(diǎn)實(shí)操體會(huì)做這個(gè)房?jī)r(jià)預(yù)測(cè)項(xiàng)目我最大的感受是BP神經(jīng)網(wǎng)絡(luò)本身并不難落地真正影響效果的往往是數(shù)據(jù)質(zhì)量和預(yù)處理細(xì)節(jié)。我在實(shí)際調(diào)試時(shí)曾經(jīng)把標(biāo)準(zhǔn)化的scaler用全量數(shù)據(jù)擬合測(cè)試集指標(biāo)一度看起來很漂亮后來改成只用訓(xùn)練集擬合之后R2掉了將近0.1。這才意識(shí)到數(shù)據(jù)泄露才是暗處最大的坑。如果你也想動(dòng)手做類似項(xiàng)目建議從手寫numpy版本開始跑通之后再換成Keras或PyTorch并且把每次實(shí)驗(yàn)的loss曲線截圖存檔。你會(huì)逐漸發(fā)現(xiàn)調(diào)參不是玄學(xué)而是一種建立在觀察之上的直覺。這個(gè)項(xiàng)目后續(xù)還可以擴(kuò)展把波士頓房?jī)r(jià)換成自己城市的二手房交易數(shù)據(jù)加入更多文本和地理特征用交叉驗(yàn)證挑選網(wǎng)絡(luò)結(jié)構(gòu)甚至結(jié)合SHAP對(duì)模型做可解釋性分析。這些方向都很有延伸空間希望這篇記錄能讓你少走一些彎路。本文還有配套的精品資源點(diǎn)擊獲取