)
簡介面向機器學習初學者與課程實驗者的多項式擬合正弦曲線完整實現(xiàn)基于Python編寫覆蓋最小二乘法、正則化2范數(shù)懲罰項、梯度下降與共軛梯度優(yōu)化并通過加入噪聲的數(shù)據(jù)展示過擬合現(xiàn)象及改進方法。資源包共4個文件包含3個帶詳細注釋的.py腳本分別負責數(shù)據(jù)生成與加噪、梯度下降迭代求解、最小二乘解析求解和1份Word實驗報告整體壓縮包僅581KB結構精簡。報告內完整記錄了實驗目的、環(huán)境配置Windows10/Python3.7/PyCharm、實驗步驟、結果分析并針對不同數(shù)據(jù)量、超參數(shù)與多項式階數(shù)進行過擬合對比便于讀者對照代碼理解從損失函數(shù)構建到參數(shù)求解的全過程。已有182人學習適合用作機器學習課程設計、實驗報告參考以及想從零實現(xiàn)優(yōu)化算法、深入理解正則項作用的學習者。1. 多項式擬合正弦曲線為什么這個實驗是“機器學習入門”繞不開的一道坎很多人在入門機器學習時第一個困惑是算法原理看了好幾遍卻不知道模型在訓練時到底“學”了什么。多項式擬合正弦曲線這個實驗恰好把“學習”這個詞拉下神壇——它用一套不到兩百行的 Python 代碼告訴你所謂訓練本質上是在一組候選函數(shù)里找一條離數(shù)據(jù)點最近的曲線所謂泛化是這條曲線在沒見過的位置也能貼近真實規(guī)律。用正弦曲線作為擬合對象是刻意為之它光滑、非單調、形狀鮮明低階多項式擬合出來的是一條“大致走勢”高階多項式擬合出來的是一條“精確穿過每個點卻劇烈抖動”的曲線——欠擬合和過擬合這兩個機器學習里最重要的概念可以在這一張圖上看穿。這套實驗適合正在做課程作業(yè)的學生也適合想驗證自己對最小二乘、正則化和模型選擇理解程度的從業(yè)者。它需要的只是 numpy、matplotlib 和一點點線性代數(shù)基礎。2. 先看懂要擬合什么多項式、正弦曲線與最小二乘的數(shù)學關系2.1 多項式的“次數(shù)”到底意味著什么多項式擬合的目標很簡單給定一組采樣點 (xi, yi)找一個次數(shù)為 d 的多項式讓它在這些點上的取值盡量接近 yi。這個多項式一般寫成f(x) w0 w1·x w2·x2 ... wd·x?這里的 w 是待求的系數(shù)也就是“模型參數(shù)”。次數(shù) d 決定了這個函數(shù)家族的表達能力d1 是一條直線只能表達單調趨勢d2 是一條拋物線能表達一個彎d越大函數(shù)能拐的彎越多形狀越復雜。多項式擬合的核心工作是解出系數(shù) w而對正弦曲線做擬合時d 這個數(shù)字會直接決定結果是“太簡單”還是“太復雜”。這里要特別提醒一個初學者容易混淆的點多項式函數(shù)是關于變量 x 的非線性函數(shù)但它關于參數(shù) w 是線性的——也就是說把 x 的各次冪看成特征擬合問題就變成了一個標準的線性回歸問題。這個認知非常關鍵因為后面的最小二乘解法完全建立在“關于參數(shù)線性”這個性質上。2.2 最小二乘與正規(guī)方程擬合背后的閉式解“離數(shù)據(jù)點最近”需要一個定量標準。最常見的定義是均方誤差把所有擬合值與真實值的差的平方加起來取平均最小化這個目標函數(shù)。寫成數(shù)學形式就是J(w) (1/N) · Σ(yi - f(xi))2最小化這個函數(shù)不需要梯度下降迭代因為它的導數(shù)是關于 w 的線性方程。求導并令其為 0可以得到一組線性方程組寫成矩陣形式就是著名的正規(guī)方程w (X?X)?1X?y這里的 X 是設計矩陣第 i 行是 [1, xi, xi2, ..., xi?]y 是觀測值向量。numpy 里一行代碼就能解出 w。不過在實現(xiàn)時我一般不用 (X?X)?1 直接求逆而是用偽逆函數(shù) pinv它能處理矩陣奇異或近奇異的情況數(shù)值上更穩(wěn)定后面在避坑部分會展開講。2.3 為什么選正弦曲線實驗設計里的“刻意安排”用正弦曲線做擬合對象不是隨便挑的。sin(x) 的性質讓這個實驗天然具備教學價值第一它是非線性的任何低次多項式都無法完美逼近所以你能看到欠擬合的真實形態(tài)第二它在一個周期內先升后降再升形狀不是單調的這迫使高階多項式用大幅度擺動去擬合過擬合現(xiàn)象會非常明顯第三正弦函數(shù)有一個真實的低維結構你知道“正確答案”是什么這樣就能區(qū)分誤差來自噪聲還是來自模型本身的偏差。數(shù)據(jù)生成這一步通常是自己做采樣然后人為加高斯噪聲。加噪聲是必要的——如果數(shù)據(jù)完美落在正弦曲線上低階擬合的效果看上去也不錯過擬合的反差就不夠強烈。噪聲幅度一般取 0.05 到 0.1 之間能觀察到清晰的效果太小看不出區(qū)別太大會把所有擬合曲線都壓得不像話。3. 用 Python 實現(xiàn)多項式擬合從零手寫最小二乘到 polyfit 一行出結果3.1 環(huán)境與數(shù)據(jù)準備環(huán)境方面只需要 numpy 和 matplotlib這兩個是機器學習實驗最基礎的依賴。如果你在本機還沒裝好用 pip install numpy matplotlib 或者在 Anaconda 里直接創(chuàng)建環(huán)境就行至于具體的 Python 安裝教程和環(huán)境配置vscode 里裝好 Python 插件、在設置里選中解釋器路徑就可以跑起來不用額外折騰。首先生成實驗數(shù)據(jù)在一段區(qū)間內采樣加上高斯噪聲。import numpy as np np.random.seed(42) # 固定隨機種子保證實驗可復現(xiàn) n_samples 12 x np.linspace(0, 2 * np.pi, n_samples) # 在 0 到 2π 之間均勻取 12 個點 y_true np.sin(x) # 真實的正弦曲線 y y_true np.random.normal(0, 0.1, n_samples) # 添加高斯噪聲標準差 0.1這段代碼里np.linspace 負責在指定區(qū)間內均勻采樣第三個參數(shù)是樣本數(shù)量。固定隨機種子 np.random.seed(42) 這一步很重要——做實驗必須可復現(xiàn)不然每次跑出來的圖和數(shù)值都不一樣實驗報告也沒法寫。觀測值 y 是真實正弦值加噪聲噪聲用 np.random.normal 生成0 是均值0.1 是標準差。3.2 手寫最小二乘不把一切交給黑匣子雖然 numpy 提供了現(xiàn)成的擬合函數(shù)我仍然建議你至少手寫一遍正規(guī)方程。這個代碼能讓你真正看到“訓練”是在算什么def polyfit_manual(x, y, degree): # 構建設計矩陣 X每一列是 x 的 0 次冪、1 次冪……degree 次冪 X np.vstack([x**i for i in range(degree 1)]).T # 用偽逆求解正規(guī)方程 w (X^T X)^{-1} X^T y w np.linalg.pinv(X.T X) X.T y return w # 用 4 次多項式擬合帶噪聲數(shù)據(jù) w_manual polyfit_manual(x, y, degree4) print(擬合系數(shù), w_manual)邏輯說明np.vstack 把 x 的 0 次方到 4 次方按行堆疊轉置后得到一個形狀為 (12, 5) 的矩陣——12 個樣本5 個特征從常數(shù)項到 x?。np.linalg.pinv 是偽逆比直接算逆矩陣更穩(wěn)。 是矩陣乘法運算符X.T X 得到的是 5×5 的格拉姆矩陣再與 X.T y 相乘就得到了系數(shù)向量。參數(shù)說明degree 是多項式次數(shù)也是這個函數(shù)里唯一需要關心的超參數(shù)。degree 太小擬合不夠靈活太大數(shù)值容易出問題。12 個樣本點degree 一般不超過 8超過之后矩陣接近奇異偽逆雖然能出結果但數(shù)值已經(jīng)不可靠了。3.3 用 np.polyfit 快速實現(xiàn)省力但要懂參數(shù)含義手寫版驗證了原理之后實際實驗里直接用 numpy 封裝好的函數(shù)即可# degree4 的多項式擬合返回系數(shù)從高次到低次排列 w_polyfit np.polyfit(x, y, deg4) # 生成密集采樣點用于繪制光滑的擬合曲線 x_smooth np.linspace(0, 2 * np.pi, 200) y_smooth np.polyval(w_polyfit, x_smooth)邏輯說明np.polyfit 返回的系數(shù)順序與前面手寫版相反——最高次項系數(shù)在前常數(shù)項在最后。np.polyval 是多項式求值函數(shù)給定系數(shù)和自變量一次算出所有對應函數(shù)值。用 x_smooth 這個密集點集畫圖曲線才會光滑直接用原始 12 個 x 畫出來的折線會顯得很生硬。參數(shù)說明np.polyfit 的第三個參數(shù) deg 就是多項式次數(shù)如果只填前兩個參數(shù)會默認擬合 1 次多項式。它內部用的算法是基于 SVD 的最小二乘解數(shù)值穩(wěn)定性比我手寫的那版更好所以日常實驗優(yōu)先用 polyfit 是沒問題的。但這個函數(shù)的輸出是一個系數(shù)數(shù)組不像 sklearn 里的模型有現(xiàn)成的 predict 方法初學者容易忘了用它配套的 np.polyval 去求值。3.4 可視化欠擬合與過擬合在一張圖上顯形數(shù)據(jù)、擬合曲線都準備好了接下來的關鍵步驟是把它們畫在同一張坐標系里import matplotlib.pyplot as plt plt.figure(figsize(8, 5)) plt.scatter(x, y, label帶噪聲的觀測點, colorblack, zorder3) plt.plot(x_smooth, np.sin(x_smooth), label真實正弦曲線, linestyle--, colorgreen) plt.plot(x_smooth, y_smooth, label4 次多項式擬合, colorred) plt.xlabel(x) plt.ylabel(y) plt.legend() plt.title(多項式擬合正弦曲線degree4) plt.savefig(polyfit_deg4.png, dpi150) plt.show()這段代碼用 plt.scatter 畫原始采樣點用兩條曲線分別表示真實正弦和擬合結果。black 點和綠色虛線是“基準答案”紅色實線是你模型給出的預測三者對比一眼就能看出擬合質量。zorder3 的作用是讓散點顯示在最上層不被曲線遮住。dpi150 保證導出圖片在實驗報告里足夠清晰。到這一步你已經(jīng)跑通了最小實驗閉環(huán)。但只畫一張圖沒有說服力因為你看不出多項式次數(shù)的選擇對結果的影響。下一章會把這個實驗往深度推——對比不同階數(shù)、量化誤差、引入驗證集和正則化。這些才是“實驗報告”里真正能拿分的內容。4. 階數(shù)、噪聲與驗證集把擬合做到能解釋、能比較、能選參4.1 一組對比實驗把 1 到 12 次多項式全部跑一遍擬合質量不能靠肉眼判斷要靠數(shù)值和對比。最常見的做法是在同一個數(shù)據(jù)集上分別用 1 次到 12 次多項式擬合計算每個模型在訓練集上的均方誤差然后畫一條“誤差隨階數(shù)變化”的曲線。代碼可以這樣寫train_errors [] degrees range(1, 13) for d in degrees: w np.polyfit(x, y, degd) y_pred np.polyval(w, x) # 在原始訓練點上做預測 mse np.mean((y - y_pred) ** 2) # 均方誤差 train_errors.append(mse) plt.figure(figsize(8, 5)) plt.plot(list(degrees), train_errors, markero, colorblue) plt.xlabel(多項式次數(shù) degree) plt.ylabel(訓練集均方誤差 (MSE)) plt.title(訓練誤差隨多項式次數(shù)的變化) plt.savefig(train_error_vs_degree.png, dpi150) plt.show()這段代碼的要點是對每個 degree 都做一次完整的“訓練-預測-評估”循環(huán)。mse 是回歸任務最常用的誤差指標對誤差取平方既避免了正負相消又放大了大誤差的懲罰。你會看到訓練誤差隨 degree 增大而單調下降degree 越大多項式越靈活它在訓練點上的近似能力越強到 degree11 時12 個樣本點被一個 11 次多項式完全穿過訓練誤差趨近于 0。這就是過擬合的數(shù)學表現(xiàn)。4.2 驗證集與測試集誤差的“U 形曲線”才是最關鍵的圖訓練誤差單調下降不代表模型就好。一個真正有價值的實驗必須引入驗證集看模型在沒見過的數(shù)據(jù)上表現(xiàn)如何。常見做法是把數(shù)據(jù)集劃分為訓練集和驗證集在訓練集上擬合在驗證集上評估誤差。更嚴謹一點可以用 K 折交叉驗證但對這個實驗來說簡單隨機劃分或留出法就夠用。from sklearn.model_selection import train_test_split x_train, x_val, y_train, y_val train_test_split( x, y, test_size0.4, random_state0 ) val_errors [] for d in degrees: w np.polyfit(x_train, y_train, degd) y_val_pred np.polyval(w, x_val) val_mse np.mean((y_val - y_val_pred) ** 2) val_errors.append(val_mse)train_test_split 把 12 個樣本分成 7 個訓練和 5 個驗證test_size0.4 表示驗證集占 40%。固定 random_state0 保證每次運行劃分方式一致。此時如果把訓練誤差和驗證誤差畫在同一個圖上你會看到一條經(jīng)典的“U 形”曲線訓練誤差一路下降驗證誤差先下降、在某個階數(shù)附近到達最低點然后掉頭向上猛增。驗證誤差的最低點對應的階數(shù)就是在這個數(shù)據(jù)規(guī)模和數(shù)據(jù)噪聲下比較合適的選擇。這個現(xiàn)象是整個實驗的“靈魂”。它是機器學習入門中被引用最多的一張圖也是面試里常被追問的問題——“為什么訓練誤差低但模型不好”。親眼見過 U 形曲線比背十遍過擬合的定義都有用。4.3 噪聲幅度一個被忽視但影響極大的參數(shù)數(shù)據(jù)生成時的噪聲標準差決定了一個擬合問題本身的難度。噪聲越大數(shù)據(jù)點離真實曲線的離散程度越高越容易被高階多項式“強行穿過”過擬合的 U 形曲線會越明顯反之噪聲接近 0 時即使高階多項式也能在樣本點之間合理插值過擬合現(xiàn)象會變得難以察覺。你在做這個實驗時可以用以下代碼對比三檔噪聲for noise in [0.01, 0.1, 0.5]: y_noisy y_true np.random.normal(0, noise, n_samples) errors [] for d in degrees: w np.polyfit(x, y_noisy, degd) y_pred np.polyval(w, x) errors.append(np.mean((y_noisy - y_pred) ** 2)) plt.plot(list(degrees), errors, labelfnoise{noise}) plt.legend() plt.show()注意這里每個噪聲檔位都要重新生成一組 y而不是在同一組數(shù)據(jù)上改標簽。np.random.normal(0, noise, n_samples) 的第二個參數(shù)是標準差不是方差——如果想用方差表示要取平方。經(jīng)驗上noise0.01 的結果幾乎貼著正弦曲線noise0.5 的驗證誤差曲線會在更小的 degree 處就開始反彈說明高噪聲場景下模型更容易過擬合也更需要偏低的模型復雜度。4.4 正則化兜底當高階數(shù)不可避免時該怎么辦有些實驗要求強制對比高次多項式比如固定 degree9的表現(xiàn)這時候過擬合幾乎不可避免。一個標準解法是引入正則化項把優(yōu)化目標從“只減小誤差”改成“誤差 懲罰項”懲罰的是系數(shù)的大小。嶺回歸是最常見的實現(xiàn)它的目標函數(shù)是J(w) (1/N)Σ(yi - f(xi))2 λ·Σw?2sklearn 里可以直接調用from sklearn.linear_model import Ridge from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline model make_pipeline( PolynomialFeatures(degree9, include_biasFalse), Ridge(alpha0.01) ) model.fit(x.reshape(-1, 1), y) x_smooth_2d x_smooth.reshape(-1, 1) y_ridge model.predict(x_smooth_2d)PolynomialFeatures(degree9, include_biasFalse) 把單個 x 特征擴展成 9 個多項式特征Ridge(alpha0.01) 是帶 L2 懲罰的線性回歸。alpha 是正則化強度——alpha 越大系數(shù)被壓得越小擬合曲線越平滑但逼近能力也越弱。正則化適合“必須用高次多項式但不想讓它劇烈擺動”的場景實驗報告里把它作為過擬合補救措施來寫是很大的加分項。5. 擬合實驗避坑手冊五個我真實踩過的坑與排查路徑5.1 擬合出來的曲線瘋狂震蕩甚至出現(xiàn)巨大的正負尖峰現(xiàn)象是degree 設為 9 或更高時擬合曲線在采樣點之間像發(fā)了瘋一樣上下亂穿數(shù)值可以達到幾十甚至上百完全不像正弦曲線。原因是典型的數(shù)值不穩(wěn)定。多項式次數(shù)升高后設計矩陣 X 的各列之間高度相關X?X 的行列式趨近于 0條件數(shù)急劇增大直接求逆會放大微小的浮點誤差。另一個原因是過擬合本身高階多項式為了穿過每個樣本點被迫在區(qū)間邊緣做出大擺動來滿足邊界條件。解決辦法有三條路一是降階這是最直接的二是改用 np.polyfit它內部基于 SVD數(shù)值上比手寫正規(guī)方程穩(wěn)定三是引入正則化用 Ridge 壓制系數(shù)大小。排查的時候可以先打印系數(shù)你會看到高階項的系數(shù)動輒是 103 量級這就是病灶所在。5.2 訓練誤差接近 0但驗證誤差大得離譜現(xiàn)象是把 polyfit 的 deg 設為樣本數(shù)減 1比如 12 個樣本設 11訓練誤差幾乎等于 0但換一組新數(shù)據(jù)預測誤差爆炸式上升。原因是模型把訓練數(shù)據(jù)里的噪聲也當成了規(guī)律記了下來這正是過擬合的定義。要確認這一點把訓練誤差和驗證誤差畫在同一張圖上如果兩條曲線在某個節(jié)點之后明顯分離、誤差方向相反就是過擬合的鐵證。解決方法是用驗證誤差曲線的谷底選擇階數(shù)如果還需要高次數(shù)就加正則化同時增加樣本量也能緩解。這個現(xiàn)象是這次實驗最核心的知識點也是最值得在實驗報告里展開討論的內容。5.3 噪聲標準差設成 0.1但結果有時候好有時候差現(xiàn)象是相同代碼、相同參數(shù)多次運行后誤差數(shù)值和曲線形狀差異很大尤其是 degree 較高時“好的時候很好壞的時候離譜”。原因是隨機種子沒有固定。np.random.normal 每次調用都會生成不同的噪聲數(shù)據(jù)變了模型自然就變了。解決方法是在生成數(shù)據(jù)之前調用 np.random.seed(42)之后所有隨機操作包括 train_test_split 里的 random_state都會按確定序列運行。這也是實驗報告寫作的基本要求——可復現(xiàn)性。實驗報告的“環(huán)境與參數(shù)說明”部分應當寫清楚隨機種子否則評審老師復現(xiàn)不出來會直接質疑結果。5.4 用 matplotlib 在中文系統(tǒng)上畫圖出現(xiàn)方框亂碼現(xiàn)象是圖例和標題里的中文全部變成空心方塊導出到實驗報告里非常難看嚴重影響分數(shù)。原因是 matplotlib 默認字體不含中文字符。解決方法是在畫圖腳本頂部顯式指定支持中文的字體比如 Windows 上用 SimHeimacOS 上用 Arial Unicode MS或者干脆在畫圖時不寫中文改用英文標簽然后在實驗報告里用文字說明含義。我的習慣是實驗代碼里用英文標簽報告里用中文描述這樣既避免了亂碼問題又保持了代碼的通用性。5.5 驗證集劃分不當導致誤差曲線劇烈跳動現(xiàn)象是把 12 個樣本按順序劃分成前 7 后 5驗證誤差曲線出現(xiàn)很多毛刺谷底位置不清晰甚至多次運行結果不一致。原因有兩個一是樣本量太小任何劃分方式都會帶來方差二是按順序切分導致訓練集和驗證集的 x 分布不均勻——比如訓練集集中在區(qū)間左半邊驗證集集中在右半邊模型看不到完整分布驗證誤差自然不穩(wěn)。解決方法是用 train_test_split 而不是手工切片并且設置 stratify 參數(shù)雖然回歸任務里 stratify 不常用但可以按 x 是否大于中位數(shù)分層如果樣本量允許做 K 折交叉驗證取平均值曲線會平滑很多。對 12 個樣本來說更推薦把實驗擴充到 4060 個樣本再談劃分。6. 把源代碼整理成能交的實驗報告結構與畫圖的落地經(jīng)驗代碼跑通了、圖也畫出來了最后一步是把這些整理成一份像樣的實驗報告。很多學生的通病是把代碼整段粘貼進 Word 文檔再配一兩張圖就算交差——這恰恰丟掉了實驗報告的核心價值。實驗報告是用來證明你理解了這個實驗不是用來展示代碼長度的。我的建議是章節(jié)按這樣的順序組織實驗目的一兩句話說明要觀察什么現(xiàn)象、實驗原理多項式擬合的數(shù)學形式 最小二乘目標函數(shù) 正規(guī)方程推導、實驗環(huán)境Python 版本、numpy/sklearn/matplotlib 版本、實驗內容與結果數(shù)據(jù)生成方式、階數(shù)對比實驗、誤差曲線、可視化圖、討論與分析欠擬合過擬合的觀察結論、噪聲影響、正則化效果、附錄完整代碼和運行說明。畫圖時注意三個細節(jié)第一所有圖的橫縱坐標必須有標簽圖例必須和曲線一一對應圖片分辨率不低于 150 dpi第二對比實驗的圖要放在一起比如 degree1、4、11 三條擬合曲線放在同一個圖里這樣欠擬合和過擬合能直接對照第三誤差變化曲線要標注谷底位置這是整篇報告的“論點”沒有標注的圖等于讓讀者自己找重點。我印象最深的是自己做這個實驗時花了一個多小時糾結為什么 degree11 時曲線會沖到 2000 多后來打印出系數(shù)才發(fā)現(xiàn)高階項數(shù)值已經(jīng)失真了而正則化那一節(jié)當時沒寫進報告——現(xiàn)在回頭看那才是整篇報告里最有價值的討論內容。希望這篇筆記能幫你把邊界、參數(shù)和坑一次看懂少走這幾步彎路。本文還有配套的精品資源點擊獲取