線性迴歸:從最小平方法到可上線實務

標籤: machinelearning, datascience, python, tutorial

線性迴歸是大多數人接觸的第一個演算法,也是許多人從未深入研究的模型。它也是在其他更複雜演算法失效後,仍能在實際環境中找到的模型,因為它快速、穩定且具可解釋性。

本文不是一份「呼叫 .fit() 再讀取分數」的教學。我們將涵蓋數學、統計假設、診斷、正則化、評估、上線考量,以及區分初學者與工程師的面試題目。

為什麼線性迴歸值得再看一眼

線性迴歸是理解幾乎所有其他監督式模型的基礎:

  • 邏輯迴歸就是在線性迴歸上疊加一個 sigmoid 函數。
  • Ridge 與 Lasso 就是在權重受限制下的線性迴歸。
  • 神經網路就是一堆帶有非線性啟動函數的線性轉換堆疊。
  • 樹模型通常會拿來跟同樣的基準比較:「我能打敗線性模型嗎?」

更重要的是,線性迴歸在許多商業問題中仍然是正確答案。當你需要向監管機構、客戶或財務團隊解釋預測結果時,一個乾淨且係數可解釋的線性模型,勝過黑盒子。

數學:最小平方法與正規方程式

給定特徵 X 與目標 y,線性模型假設:

y = X * beta + epsilon

Enter fullscreen mode Exit fullscreen mode

目標是最小化殘差平方和:

L(beta) = ||y - X*beta||^2

Enter fullscreen mode Exit fullscreen mode

beta 取導數並設為零,即可得到正規方程式

beta = (X^T * X)^(-1) * X^T * y

Enter fullscreen mode Exit fullscreen mode

實務上,請使用偽逆矩陣 (pinv) 而非反矩陣,因為當特徵共線時 X^T X 可能為奇異矩陣或數值不穩定。

import numpy as np

def normal_equation(X, y):
    Xb = np.c_[np.ones(X.shape[0]), X]  # add intercept
    beta = np.linalg.pinv(Xb.T @ Xb) @ Xb.T @ y
    return beta

Enter fullscreen mode Exit fullscreen mode

最小平方法的三種等價觀點

1. 幾何觀點

預測值 X * betayX 行空間中的投影。最小平方法找出該子空間中最接近的點。這也是為什麼殘差會與擬合值正交。

2. 最大似然觀點

假設:

epsilon ~ N(0, sigma^2)

Enter fullscreen mode Exit fullscreen mode

則最大化對數似然等同於最小化平方誤差和。此關聯解釋了為什麼殘差的常態性對信賴區間很重要,儘管在較弱的假設下 OLS 係數仍具一致性。

3. 最佳化觀點

對於大型資料集,計算 (X^T X)^(-1) 會變得很昂貴。梯度下降以迭代方式求解相同的目標:

def gradient_descent(X, y, lr=0.01, epochs=500):
    Xb = np.c_[np.ones(X.shape[0]), X]
    n, d = Xb.shape
    beta = np.zeros(d)
    for _ in range(epochs):
        grad = (2 / n) * Xb.T @ (Xb @ beta - y)
        beta -= lr * grad
    return beta

Enter fullscreen mode Exit fullscreen mode

正規方程式:精確,適合中小型資料集。梯度下降:可擴展,但需要特徵縮放與學習率調整。

你必須檢查的假設

一個 R-squared 很高的模型並不自動值得信賴。讓係數可解釋的,正是這些假設:

假設 意義 檢查方式
線性 特徵與目標之間呈線性關係 residual vs fitted 圖
獨立性 誤差不相關 Durbin-Watson 統計量
同質變異數 誤差變異數恆定 Breusch-Pagan 檢定、殘差圖
常態性 誤差呈常態分布 Q-Q 圖、Jarque-Bera 檢定
無多重共線性 特徵之間不高度相關 VIF、相關矩陣

完整診斷步驟

import statsmodels.api as sm
from statsmodels.stats.stattools import durbin_watson
from statsmodels.stats.diagnostic import het_breuschpagan
from statsmodels.stats.outliers_influence import variance_inflation_factor
from scipy import stats
import pandas as pd

X_const = sm.add_constant(X_train)
model = sm.OLS(y_train, X_const).fit()
resid = model.resid

print("Durbin-Watson:", durbin_watson(resid))
# Values near 2 mean no autocorrelation; near 0 or 4 is a warning.

lm, lm_pvalue, fvalue, f_pvalue = het_breuschpagan(resid, X_const)
print("Breusch-Pagan p-value:", f_pvalue)
# p > 0.05: no strong evidence of heteroscedasticity.

print("Jarque-Bera p-value:", stats.jarque_bera(resid).pvalue)
# p > 0.05: no strong evidence against normality.

vif = pd.DataFrame({
    "feature": X_train.columns,
    "VIF": [variance_inflation_factor(X_train.values, i)
            for i in range(X_train.shape[1])],
})
print(vif)
# VIF > 10 is commonly treated as a multicollinearity warning.

Enter fullscreen mode Exit fullscreen mode

正則化:偏差-變異數權衡

普通最小平方法會最小化訓練誤差,當特徵眾多或共線時容易過擬合。正則化加入懲罰:

Ridge:       L = MSE + alpha * sum(beta_i^2)
Lasso:       L = MSE + alpha * sum(|beta_i|)
ElasticNet:  L = MSE + alpha * (r * L1 + (1 - r) * L2)

Enter fullscreen mode Exit fullscreen mode

模型 懲罰 效果 最佳使用情境
Ridge L2 縮減權重,保留所有特徵 許多相關特徵
Lasso L1 將部分權重縮減至正好為零 需要特徵選擇
ElasticNet L1 + L2 在相關群組中稀疏且穩定 同時需要兩者

重要細節:在正則化之前務必先將特徵標準化,否則懲罰會不公平地縮減量值較大的特徵。

from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Ridge, Lasso, ElasticNet

models = {
    "OLS": LinearRegression(),
    "Ridge(alpha=1)": Ridge(alpha=1.0),
    "Lasso(alpha=0.01)": Lasso(alpha=0.01),
    "ElasticNet(alpha=0.01)": ElasticNet(alpha=0.01, l1_ratio=0.5),
}

for name, model in models.items():
    pipe = make_pipeline(StandardScaler(), model)
    pipe.fit(X_train, y_train)
    y_pred = pipe.predict(X_test)
    print(f"{name:20s} RMSE={mean_squared_error(y_test, y_pred, squared=False):.4f} "
          f"R2={r2_score(y_test, y_pred):.4f}")

Enter fullscreen mode Exit fullscreen mode

評估指標

不要只報告 R-squared。每個指標都告訴你不同的故事:

指標 公式意義 使用時機
MAE 平均絕對誤差 誤差不應平方、異常值影響較小
RMSE 均方根誤差 大誤差特別糟糕
MAPE 平均絕對百分比誤差 商業需要百分比解釋
解釋變異比例 比較模型品質
Adjusted R² 依特徵數量懲罰後的 R² 比較不同特徵集合的模型

永遠與平均值基準比較。如果你的模型只比「預測平均值」好一點,那特徵並沒有帶來太多幫助。

baseline_pred = np.full_like(y_test, y_train.mean())
print("Baseline RMSE:", mean_squared_error(y_test, baseline_pred, squared=False))

Enter fullscreen mode Exit fullscreen mode

完整端到端範例

import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

data = fetch_california_housing(as_frame=True)
X = data.frame.drop(columns=["MedHouseVal"])
y = data.frame["MedHouseVal"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 1. Handcrafted normal equation
beta = normal_equation(X_train.values, y_train.values)
X_test_b = np.c_[np.ones(X_test.shape[0]), X_test.values]
y_pred_manual = X_test_b @ beta

# 2. sklearn baseline
from sklearn.linear_model import LinearRegression
lr = LinearRegression().fit(X_train, y_train)
y_pred_sklearn = lr.predict(X_test)

for name, y_pred in [("manual", y_pred_manual), ("sklearn", y_pred_sklearn)]:
    print(f"{name}: RMSE={mean_squared_error(y_test, y_pred, squared=False):.4f} "
          f"MAE={mean_absolute_error(y_test, y_pred):.4f} "
          f"R2={r2_score(y_test, y_pred):.4f}")

Enter fullscreen mode Exit fullscreen mode

手動解與 sklearn 解應該產生幾乎相同的數字。如果不同,代表你的梯度下降尚未收斂,或特徵尚未縮放。

上線檢查清單

  1. 特徵縮放:梯度下降與正則化模型都需要。
  2. 交叉驗證:永遠不要在測試集上調整 alpha。
  3. 資料洩漏:在縮放、編碼或插補之前先分割。
  4. 監控:部署後追蹤特徵分布與預測漂移。
  5. 可解釋性:記錄係數、特徵重要性與 SHAP 值以供稽核。
  6. 從簡單開始:在嘗試 XGBoost 之前,先用線性迴歸。

常見錯誤

1. 相關不等於因果

高係數不代表改變特徵就會改變目標。遺漏變數與反向因果始終存在。

2. 外插

線性模型在訓練範圍之外很危險。在房屋面積最多 300 平方公尺上訓練的模型,無法告訴你 3000 平方公尺的房屋價格。

3. 高 R-squared 不代表預測好

R-squared 衡量樣本內擬合度。一個模型可能在訓練資料上有高 R-squared,但在未見資料上卻有很差的 RMSE。

4. 忽略殘差

如果殘差有模式,代表你的模型缺少結構。不要試圖盲目加入更多線性特徵來「修正」非線性模式。

5. 在正則化時使用原始特徵

沒有標準化,Ridge 與 Lasso 會對不同單位的特徵施加不平等的懲罰。

面試必備問題

  1. 正規方程式與梯度下降有什麼差別?
  2. 為什麼在常態誤差假設下 OLS 等同於最大似然?
  3. 如果 X^T X 是奇異矩陣會發生什麼事?Ridge 怎麼幫助?
  4. 如何偵測並修正多重共線性?
  5. 為什麼要用 adjusted R-squared 而非 R-squared?
  6. residual vs fitted 圖告訴你什麼?
  7. 什麼時候會偏好 Lasso 而非 Ridge?
  8. 為什麼在 L1 或 L2 正則化之前必須先標準化?
  9. 異質變異數的後果是什麼?
  10. 如何在迴歸管線中偵測資料洩漏?

結論

線性迴歸看似簡單,卻包含了機器學習的核心概念:目標函數、最佳化、統計假設、正則化、評估與部署。如果你深入理解這個模型,之後的每個演算法都會更容易學習。

從數學開始,驗證假設,並將上線考量放在心上。那個「太簡單」的模型,往往是在實際環境中存活最久的模型。


覺得有用嗎?歡迎追蹤我,獲取更多實務 AI 與資料工程文章。
沒 ;。l