一元線性回歸

這份教材把「線性回歸」用教科書式結構整理:
概念 → 定義 → 推導 → 實作(互動訓練)→ 練習題
同時提供:封閉解(最小平方法)梯度下降訓練,建立「公式」與「訓練」的對照感。

模型 y = b0 + b1·x
Loss MSE
方法 Closed-form / GD
評估
學習目標(Learning Objectives)
會解釋+會跑+會改
1) 會說清楚 MSE 與 R² 的意義
2) 會用封閉解算 b0,b1
3) 會用梯度下降訓練並觀察收斂
4) 會知道「標準化」對 GD 的幫助
先備知識(Prerequisites)
代數+基本微積分概念
會看懂平均值、求和符號 Σ;理解「斜率/截距」;
梯度下降部分只要接受「偏微分=斜率」直覺即可。
使用方式
照章節走
建議順序:教材導讀 → 原理 → 推導 → Demo → Python → 練習題。
互動 Demo 可匯入 CSV、切換標準化、下載結果。

教材導讀:本章內容地圖

本教材以「一元線性回歸」作為機器學習入門的第一個模型,因為它具備三個優點:
(1) 模型形式直覺:就是一條線。(2) 有封閉解:公式一次算出最佳解。(3) 也能用訓練法:梯度下降讓你理解「訓練」在幹嘛。

本章你會遇到的關鍵名詞

  • 模型(Model)y = b0 + b1*x
  • 參數(Parameters)b0b1
  • 損失函數(Loss)MSE 衡量預測誤差
  • 封閉解(Closed-form):直接用公式求最佳參數
  • 梯度下降(Gradient Descent):反覆更新參數,讓 Loss 下降
  • 標準化(Standardization):把 x 轉成均值 0、標準差 1,有助 GD 收斂
  • R²(決定係數):模型解釋資料變異程度的指標
本章學習成果(你做完應該能說出來):
「線性回歸就是找 b0,b1 讓 MSE 最小;封閉解一次算到最小值;梯度下降是用梯度一步步走到最小值;R² 用來衡量擬合好不好;標準化可以讓 GD 更穩定。」

原理與定義:模型、MSE、R²

1) 模型(Hypothesis / Model)

我們假設資料大致呈現線性趨勢,因此使用一條直線描述: y_hat = b0 + b1*x。其中 y_hat 是模型預測值(prediction)。

2) 損失函數(Loss):MSE

最常用的損失是 MSE(Mean Squared Error)

MSE = (1/n) * Σ (y - y_hat)^2
其中 y_hat = b0 + b1*x

使用平方可以避免正負抵消,並放大大誤差,使模型更重視「差很多」的點。

3) 目標(Objective)

訓練目標:找 b0,b1 讓 MSE 最小: min_{b0,b1} MSE(b0,b1)

4) 評估:R²(決定係數)

R² 表示模型能解釋資料變異的程度(越接近 1 越好):

SS_res = Σ (y - y_hat)^2
SS_tot = Σ (y - y_bar)^2
R^2 = 1 - SS_res / SS_tot
注意:R² 高不代表因果,只代表「線性擬合效果」。

推導:封閉解 vs 梯度下降

你可以先跑 Demo,再回來看推導會更好懂。

推導 A:封閉解(最小平方法)公式
b1 = Σ((x - x̄)(y - ȳ)) / Σ((x - x̄)^2)
b0 = ȳ - b1*x̄
  • 分子:x 與 y 同步變動程度(共變)
  • 分母:x 自己的變動程度(變異)
重點:封閉解是直接算到 Loss 的最小點,不需要 epochs、lr。
推導 B:梯度下降的梯度(結論)
grad_b0 = (-2/n) * Σ (y - (b0 + b1*x))
grad_b1 = (-2/n) * Σ (x * (y - (b0 + b1*x)))
重點:梯度下降是「走向答案」,會受 lr、資料尺度、epochs 影響。
推導 C:為什麼標準化能幫助 GD?(參數轉回原尺度)
x' = (x - μ)/σ

在 x' 空間訓練:y = b0' + b1' * x'
轉回原尺度:b1 = b1'/σ,b0 = b0' - b1'*μ/σ

進階觀念:L1 / L2 正則化 (Regularization)

當模型過於複雜(或數據過少/雜訊多)時,模型可能會為了「硬記」雜訊而導致過擬合 (Overfitting)。 正則化就是在 Loss 函數中加入一個「懲罰項」,限制參數(權重)不要太大。

1) L2 Ridge Regression (嶺回歸)

在 MSE 後面加上權重的平⽅和作為懲罰:

Loss = MSE + λ * (b1)^2
  • 效果:迫使 b1 變小(接近 0 但不等於 0)。
  • 幾何意義:像是一條橡皮筋把參數往原點拉,避免參數數值過大。
  • 用途:防止過擬合,處理共線性 (Multicollinearity) 問題。

2) L1 Lasso Regression

在 MSE 後面加上權重的絕對值作為懲罰:

Loss = MSE + λ * |b1|
  • 效果:可以把 b1 壓到變為 0。
  • 用途:具有「特徵選擇」能力(把不重要的特徵權重砍成 0),留下真正重要的變數。
實驗觀察重點:
在下方 Demo 中,試著選用 L2 (Ridge) 並調大 λ (例如 50 或 100)。
你會發現:雖然 MSE 會變大(回歸線稍微偏離資料點,沒那麼準),但 b1 (斜率) 會變得比較小(線變平緩)。
這就是「犧牲一點準確度,換取模型更簡單/保守(抗雜訊)」的權衡。

互動訓練 Demo:封閉解 + 梯度下降 + 標準化切換

可改資料、改 lr/epochs,並切換「GD 標準化」觀察收斂差異;也支援匯入 CSV(前兩欄為 x,y)。

慢(細看過程)
正在訓練...

Closed-form(最小平方法)

b0
-
b1
-
-
模型
-
x_new
-
y~
-

Gradient Descent(梯度下降)

b0
-
b1
-
-
模型
-
最後 MSE
-
GD 標準化
-

訓練 Log(每 400 epoch)

(按「開始訓練」後會出現)

📂 訓練資料下載(可匯入上方)

圖表

左:散點 + 回歸線(Closed-form / GD)| 右:GD Loss(MSE)隨 epochs 變化

建議你做兩個對照實驗:
1) 把 x 改成很大的數(例如 1000,2000,3000...),比較 GD「標準化開/關」。
2) 把 lr 調大(例如 0.2),觀察 Loss 是否震盪或發散。
教材版 Demo:CSV 匯入 / 標準化切換 / 結果下載

Python 完整程式(超詳細註解 + 程式碼上色)

這段是可直接執行的 Python 程式:封閉解+梯度下降+R²+預測+畫圖(可選)。

LINER.py(完整程式)

# -*- coding: utf-8 -*-
"""
一元線性回歸(Linear Regression)正式教材用範例(繁中超詳細註解)
====================================================================

本程式示範兩種求解方式:
(1) 封閉解(Closed-form / 最小平方法):一次算出最佳 b0, b1
(2) 梯度下降(Gradient Descent):模擬「訓練」過程,逐步更新 b0, b1

核心模型:
    y_hat = b0 + b1*x

核心 Loss(MSE,均方誤差):
    MSE = (1/n) * Σ (y - y_hat)^2

核心評估(R^2,決定係數):
    R^2 = 1 - SS_res/SS_tot

------------------------------------------------------------
Windows 輸出編碼的保險
------------------------------------------------------------
- Windows 主控台可能使用 cp950
- 某些特殊符號可能無法輸出,造成 UnicodeEncodeError
- 本程式採取兩個策略:
  (1) 嘗試把 stdout 改成 UTF-8(errors="replace" 避免炸裂)
  (2) 輸出避免使用高風險符號(例如改用 ~ 表示約略)
"""

from __future__ import annotations

import sys
import numpy as np

# -----------------------------
# A) 輸出編碼保險:盡量用 UTF-8,避免主控台編碼炸裂
# -----------------------------
try:
    if hasattr(sys.stdout, "reconfigure"):
        sys.stdout.reconfigure(encoding="utf-8", errors="replace")
except Exception:
    pass

# -----------------------------
# B) matplotlib:可選畫圖
# -----------------------------
try:
    import matplotlib.pyplot as plt
    HAS_MATPLOTLIB = True
except ImportError:
    HAS_MATPLOTLIB = False


def fit_closed_form(x: np.ndarray, y: np.ndarray) -> tuple[float, float]:
    """
    封閉解(最小平方法):
        b1 = Σ((x - x̄)(y - ȳ)) / Σ((x - x̄)^2)
        b0 = ȳ - b1*x̄
    """
    x_mean = x.mean()
    y_mean = y.mean()

    numerator = np.sum((x - x_mean) * (y - y_mean))
    denominator = np.sum((x - x_mean) ** 2)

    b1 = numerator / denominator
    b0 = y_mean - b1 * x_mean
    return b0, b1


def fit_gradient_descent(
    x: np.ndarray,
    y: np.ndarray,
    lr: float = 0.02,
    epochs: int = 2000
) -> tuple[float, float, list[float]]:
    """
    梯度下降訓練(Gradient Descent)

    梯度(結論):
        d(MSE)/d(b0) = (-2/n) * Σ( y - (b0 + b1*x) )
        d(MSE)/d(b1) = (-2/n) * Σ( x * (y - (b0 + b1*x)) )

    更新:
        b0 = b0 - lr * grad_b0
        b1 = b1 - lr * grad_b1
    """
    n = len(x)
    b0 = 0.0
    b1 = 0.0
    losses: list[float] = []

    for epoch in range(1, epochs + 1):
        y_pred = b0 + b1 * x
        mse = float(np.mean((y - y_pred) ** 2))
        losses.append(mse)

        error = y - y_pred
        grad_b0 = (-2 / n) * float(np.sum(error))
        grad_b1 = (-2 / n) * float(np.sum(x * error))

        b0 = b0 - lr * grad_b0
        b1 = b1 - lr * grad_b1

        if epoch % 400 == 0:
            print(f"[GD] epoch={epoch:4d}  MSE={mse:.4f}  b0={b0:.4f}  b1={b1:.4f}")

    return b0, b1, losses


def r2_score(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    ss_res = float(np.sum((y_true - y_pred) ** 2))
    ss_tot = float(np.sum((y_true - y_true.mean()) ** 2))
    return 1 - (ss_res / ss_tot)


def main() -> None:
    x_list = [1, 2, 3, 4, 5, 6]
    y_list = [52, 55, 61, 66, 72, 75]
    x = np.array(x_list, dtype=float)
    y = np.array(y_list, dtype=float)

    b0_cf, b1_cf = fit_closed_form(x, y)
    y_pred_cf = b0_cf + b1_cf * x
    r2_cf = r2_score(y, y_pred_cf)

    print("\n=== Closed-form (Least Squares) ===")
    print(f"b0={b0_cf:.4f}, b1={b1_cf:.4f}, R^2={r2_cf:.4f}")
    print(f"Model: y = {b0_cf:.4f} + {b1_cf:.4f} * x")

    b0_gd, b1_gd, losses = fit_gradient_descent(x, y, lr=0.02, epochs=2000)
    y_pred_gd = b0_gd + b1_gd * x
    r2_gd = r2_score(y, y_pred_gd)

    print("\n=== Gradient Descent Training ===")
    print(f"b0={b0_gd:.4f}, b1={b1_gd:.4f}, R^2={r2_gd:.4f}")
    print(f"Model: y = {b0_gd:.4f} + {b1_gd:.4f} * x")

    x_new = 7
    y_new_pred = b0_cf + b1_cf * x_new
    print("\nPrediction (using closed-form):")
    print(f"x={x_new} -> y~{y_new_pred:.2f}")

    if HAS_MATPLOTLIB:
        plt.scatter(x, y, label="Data (x,y)")
        plt.plot(x, y_pred_cf, label="Closed-form line")
        plt.plot(x, y_pred_gd, label="GD line")
        plt.title("Simple Linear Regression Demo")
        plt.xlabel("x")
        plt.ylabel("y")
        plt.legend()
        plt.show()

        plt.plot(np.arange(1, len(losses) + 1), losses, label="GD Loss (MSE)")
        plt.title("Gradient Descent Training Loss")
        plt.xlabel("epoch")
        plt.ylabel("MSE")
        plt.legend()
        plt.show()
    else:
        print("\n(matplotlib not installed, skip plotting)  Install: pip install matplotlib")


if __name__ == "__main__":
    main()
              

練習題與自我檢核

點擊題目下方的「查看答案」可展開參考答案說明。

A1(概念)|用一句話說明:線性回歸的訓練目標是什麼?
重點:MSE 最小化
查看答案
參考答案:
找到參數 b0、b1,使所有資料點的預測誤差平方平均(MSE)最小,也就是讓回歸線「最貼近資料」。
A2(理解)|MSE 為什麼要平方?至少寫兩點理由。
重點:正負抵消 / 放大大誤差
查看答案
參考答案:
(1) 讓誤差不會正負抵消(因為平方永遠 ≥ 0)。
(2) 放大較大的誤差,模型會更重視「差很多」的點。
(3) 數學上可微分且凸(對一元線性回歸是凸),方便求解與收斂分析。
A3(觀念)|R² 可以推論因果嗎?為什麼不行?
重點:相關≠因果
查看答案
參考答案:
不行。R² 只衡量「擬合得像不像」,代表相關程度,不代表因果。
可能存在第三變數、反向因果或資料偏誤,即使 R² 很高也不能直接說「x 導致 y」。
B1(實驗)|把 x 全部乘以 1000,比較 GD「標準化開/關」差異。
重點:尺度影響 GD
查看答案
參考答案:
標準化 OFF 時,因為 x 尺度變超大,梯度也會變很大,常見結果是:收斂變慢、震盪甚至發散。
標準化 ON 時,x 變成均值 0、標準差 1,更新步伐更穩,通常更快、更穩收斂。
B2(實驗)|把 lr 調大(例如 0.2),觀察 Loss 是否震盪或發散。
重點:學習率過大
查看答案
參考答案:
lr 過大時每一步跨太遠,會「跳過最小點」來回震盪,甚至越跳越遠造成發散(Loss 上升)。
解法:降低 lr、使用標準化、或採用自適應學習率方法(進階)。
B3(實驗)|對 y 加雜訊,觀察 R² 變化。
重點:雜訊越大 R² 越低
查看答案
參考答案:
雜訊增加會讓資料點更分散,模型用一條線更難解釋 y 的變異,通常 R² 下降。
因為 SS_res(殘差平方和)變大,而 SS_tot 不一定同比例變大。
自我檢核:
若你能清楚說出「封閉解 vs 梯度下降差在哪、為什麼標準化讓 GD 更穩」,就是真的掌握了。

📝 iPAS 考點提醒

多元線性迴歸用多個自變數預測目標,iPAS 中級科目三考點。重點:每個係數是其他變數固定下該變數的邊際影響,用 R² 與調整後 R²、殘差圖評估。易混點:多重共線性(自變數高度相關)會使係數不穩、難解讀,可用 VIF 檢測或正則化;R² 只升不降,要看調整後 R²。情境:用梯度下降或正則化時要先標準化特徵。

想練情境題與詳解 → AI 學習與考證地圖

❓ 常見問題

多元線性迴歸是什麼?

用多個自變數的線性組合預測目標;每個係數代表在其他變數固定下、該變數每增一單位對目標的邊際影響。

什麼是多重共線性?

自變數彼此高度相關,使係數估計不穩、難以解讀;可用 VIF 檢測,並用移除變數或正則化處理。

怎麼判斷模型好壞?

看 R 平方與調整後 R 平方、殘差圖(是否隨機散布)、誤差指標,並用測試集或交叉驗證確認不是過擬合。

為什麼要看調整後 R 平方?

R 平方只會隨變數增加而升高;調整後 R 平方會懲罰無用變數,較能反映真正的解釋力。

特徵要標準化嗎?

用梯度下降或正則化時建議標準化,讓各特徵尺度一致、收斂更快、係數可比;純最小平方法則非必要。

🧭 相關主題

← 返回 AI 學習與考證地圖