昨天介紹了線性迴歸 (Linear Regression),它適合用來處理特徵與目標之間為線性關係的情境。然而,真實世界的資料往往並非純粹線性,而是呈現複雜的非線性關係,例如曲線、拋物線、甚至更複雜的波動趨勢。
就有了多項式特徵 (Polynomial Feature) 的出現,而線性迴歸搭配多項式特徵,就是所謂的多項式迴歸 (Polynomial Regression),便是為了解決線性模型難以處理的非線性問題。它的核心概念非常簡單就是透過對特徵進行多項式轉換,使模型能夠捕捉非線性趨勢。
這塊幾乎與昨天介紹的線性迴歸一樣,重複的部分就不多做介紹。因為多項式迴歸本質上仍是線性迴歸,但特徵空間經過非線性轉換,讓模型能擬合更複雜的曲線。以下為多項式迴歸的公式:
$$
\hat{y} = \beta_0 + \beta_1 x + \beta_2 x^2 + \dots + \beta_d x^d
$$
將使用經典的 Boston Housing Dataset 為例。由於 scikit-learn 已移除該資料集,我們改採自 Carnegie Mellon University 所提供的公開版本。樣本內容如下:
欄位說明:
import requests
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
def get_boston_housing_data() -> pd.DataFrame:
url = "http://lib.stat.cmu.edu/datasets/boston" # Boston Housing Dataset from Carnegie Mellon University
raw_data = requests.get(url).text.splitlines()[22:] # 從第 23 行開始
# 每筆資料分為 2 行 → 共 506 筆 → 總共 1012 行
data = []
for i in range(0, len(raw_data), 2):
line_1 = list(map(float, raw_data[i].strip().split()))
line_2 = list(map(float, raw_data[i + 1].strip().split()))
data.append(line_1 + line_2)
column_names = [
"CRIM", # 每人平均犯罪率
"ZN", # 區域住宅用地比例
"INDUS", # 區域非零售商業用地比例
"CHAS", # 查爾斯河虛擬變數 (1 = 河流旁, 0 = 其他)
"NOX", # 一氧化氮濃度 (parts per 10 million)
"RM", # 每個住宅的平均房間數
"AGE", # 1940 年之前建造的自用住宅比例
"DIS", # 到波士頓五個中心區域的加權距離
"RAD", # 公路接近指數 (1 = 最接近, 24 = 最遠)
"TAX", # 每 $10,000 的財產稅率
"PTRATIO", # 學生與教師比例
"B", # 1000(Bk - 0.63)^2, Bk = 區域黑人比例
"LSTAT", # 區域人口中低收入者的比例
"MEDV", # 自用住宅的中位數價格 (單位: $1000s)
]
df = pd.DataFrame(data, columns=column_names)
return df
def main():
# ----- 讀取資料 -----
original_data = get_boston_housing_data() # 讀取資料
# ----- 資料前處理 -----
cleaned_data = original_data.copy()
## 因透過 print(cleaned_data.isnull().sum()) 檢查缺失值 (0 筆)
## 且 Pearson 相關係數檢查,相關係數 >= 0.8 (0 筆)
### correlation_matrix = cleaned_data.corr()
### target_corr = correlation_matrix['MEDV']
### high_corr_features = target_corr[target_corr > 0.8].drop('MEDV')
### print(f"與 target 高度正相關的欄位:", high_corr_features)
## 故不做資料前處理
# ----- 模型訓練 -----
## 資料分割
X = cleaned_data.drop(columns=["MEDV"])
y = cleaned_data["MEDV"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
## 建立模型
model = Pipeline([
("poly", PolynomialFeatures(degree=2, include_bias=False)),
("scaler", StandardScaler()),
("lr", LinearRegression())
])
model.fit(X_train, y_train)
## 預測
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)
## 評估
train_mse = mean_squared_error(y_train, y_train_pred)
train_r2 = r2_score(y_train, y_train_pred)
print(f"train data 均方誤差 (MSE): {train_mse:.4f}")
print(f"train data 決定係數 R²: {train_r2:.4f}")
test_mse = mean_squared_error(y_test, y_test_pred)
test_r2 = r2_score(y_test, y_test_pred)
print(f"test data 均方誤差 (MSE): {test_mse:.4f}")
print(f"test data 決定係數 R²: {test_r2:.4f}")
if __name__ == '__main__':
main()
本次模型使用二次多項式特徵 (PolynomialFeatures(degree=2)) 結合標準化處理與線性回歸進行建模。
從評估指標來看:
然而,我們也觀察到訓練與測試間的 R² 存在明顯落差 (0.94 → 0.81),這可能表示模型在高階特徵下已出現一定程度的過擬合 (overfitting) 現象,應持續關注其在新資料上的穩定性。
為進一步強化模型的泛化能力與解釋力,採取以下方向優化:
總體而言,本次結果已具備相當良好的建模基礎,後續重點將轉向「控制模型複雜度」與「提升泛化能力」,以邁向穩定、可解釋、可部署的預測系統。
多項式迴歸提供了一個簡單卻強大的手段,讓我們能在不改變演算法本身的情況下,用非線性特徵轉換擴展模型的表達能力。只需對原始特徵做一次轉換,就能讓線性模型擬合出曲線趨勢,這正是它的魅力所在。
但能力越強,風險越高。多項式階數的提升固然能增加擬合度,但也極易導致過擬合 (overfitting),特別是在資料量有限或特徵多樣時。此時就需要引入新的工具來協助我們控制模型的複雜度──這就是「正則化迴歸」。
在邁向正則化模型之前,請記得: 多項式擬合本質上不是解決非線性的萬靈丹,它是一種「特徵空間重構」的技巧,適用於低維資料、可視化分析與 baseline 建立,但不適合直接套用在高維或多雜訊的真實問題中。