--- title: '簡單線性迴歸(Simple Linear Regression)以及目標函數' disqus: hackmd --- 簡單線性迴歸(Simple Linear Regression)以及目標函數 === ## Table of Contents [TOC] ## 簡單線性迴歸(Simple Linear Regression) 假設**只有一個x特徵**,即**簡單線性迴歸**,我們會希望**預測值**和**實際樣本點**差距越小越好,兩者差距稱為**誤差(Error)** 或 **殘差(Residual)**,想要評估模型的能力,就會需要用到迴歸的目標函數(Objective function)(又叫做損失函數(Loss function)或成本函數(Cost function))。 :::info 在訓練集:你算的是「殘差」,因為模型看過這些資料,模型是針對這些資料調整的。 在測試集:你算的是「誤差」,因為這些資料是模型從沒看過的,用來估計模型未來表現。 ::: ## 目標函數及衡量指標 :::info 接下來程式碼主要會以SSE、MSE、OLS為主 ::: ### SSE(Sum of Squared Errors,誤差平方和) **SSE**(Sum of Squared Errors 或 Residual Sum of Squares)不僅用於訓練,也常被用於整個模型評估流程中,是預測時「**真實值 – 預測值**」的平方(避免正的誤差跟負的誤差抵銷)再相加: $$ \mathrm{SSE} = \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 $$ <!-- <details> <summary>完整公式</summary> 套入簡單線性迴歸 $$ \mathrm{SSE} = \sum_{i=1}^{n} (y_i - w \cdot \text{x} - b)^2 $$ </details> --> 目標就是希望SSE越小越好,也就是預測值跟真實質(答案)差距越小越好。 ### MSE(Mean Squared Error,平均平方誤差) MSE 是 SSE 的平均版本,用來衡量資料集上的平均預測誤差,**平均會讓誤差對不同樣本大小**(n)更清楚比較,也減少隨著n增加導致 SSE 過大的問題 $$ \mathrm{MSE} = \frac{1}{n}\mathrm{SSE} $$ <!-- <details> <summary>完整公式</summary> $$ \mathrm{MSE} = \frac{1}{n} \sum_{i=1}^n \left(y_i - \hat{y}_i\right)^2 $$ $$ \mathrm{MSE} = \frac{1}{n} \sum_{i=1}^n \left(y_i - w \cdot \text{x} - b)\right)^2 $$ </details> --> ### MAE(Mean Absolute Error,平均絕對誤差) MAE 衡量預測值與真實值間的**平均「絕對」誤差,比 MSE 更不容易受極端值影響**,**適合關注穩定誤差表現**。 $$ \mathrm{MAE} = \frac{1}{n} \sum_{i=1}^n \left| y_i - \hat{y}_i \right| $$ <!-- <details> <summary>完整公式</summary> * MAE:對每一筆誤差 \$e\_i\$,直接取絕對值,**線性懲罰** * MSE/SSE:誤差平方,**誇大懲罰大誤差(離群點)** $$ \mathrm{MAE} = \frac{1}{n} \sum_{i=1}^n \left| y_i - w \cdot \text{x} - b \right| $$ </details> --> | 指標 | 優點 | 缺點 | 常見用途 | | ------- | -------------- | -------- | ---------- | | **MSE** | 平滑可導、適合優化、理論完備 | 對離群點過於敏感 | 機器學習、OLS推導 | | **MAE** | 抗離群點干擾、直覺 | 不可導、無封閉解 | 醫療、金融、穩定預測 | ### RMSE(Root Mean Squared Error,均方根誤差) RMSE 是 MSE 的平方根,**與原始資料單位尺度相同**,更容易解釋。 $$ \mathrm{RMSE} = \sqrt{ \frac{1}{n} \sum_{i=1}^n \left( y_i - \hat{y}_i \right)^2 } = \sqrt{\mathrm{MSE}} $$ RMSE 與 MSE 本質相同,但更直觀易讀。 <!-- <details> <summary>完整公式</summary> $$ \mathrm{RMSE} =\sqrt{\mathrm{MSE}} \mathrm{RMSE} = \sqrt{ \frac{1}{n} \sum_{i=1}^n \left( y_i - w \cdot \text{x} -b\right)^2 } $$ </details> --> #### SST(Total Sum of Squares,總平方和) **SST** 衡量的是「真實值相對於其平均值」的總變異量。可以理解為: > **資料本身有多少變化?如果我們都不知道 X,只用平均值 $\bar{y}$ 來預測 Y,會錯多少?** 公式如下: $$ \mathrm{SST} = \sum_{i=1}^{n} (y_i - \bar{y})^2 $$ ### R2(決定係數,R-squared) $R^{2}$ 衡量模型解釋資料變異的能力,值介於 0 到 1,越接近 1 表示模型擬合越好: $$ R^2 = 1 - \frac{\mathrm{SSE}}{\mathrm{SST}} = 1 - \frac{\sum_{i=1}^n (y_i - \hat{y}_i)^2}{\sum_{i=1}^n (y_i - \bar{y})^2} $$ * SST:總平方和,代表總變異量 * SSE:模型未解釋的變異量 直觀解釋 * $R^{2} = 0$:模型跟用平均值猜一樣爛 * $R^{2} = 1$:完美預測 #### COV(Covariance,協方差) 衡量兩變數 **同時變化的方向與程度**,常見於迴歸中的斜率計算: $$ \mathrm{Cov}(X, Y) = \frac{1}{n} \sum_{i=1}^n (x_i - \bar{x})(y_i - \bar{y}) $$ * Cov > 0:同方向(X↑ ⇒ Y↑) * Cov < 0:反方向(X↑ ⇒ Y↓) * Cov ≈ 0:不相關(或更複雜的關係) #### VAR(Variance,變異數) 衡量一組數值的**發散程度**,即離均值有多遠: $$ \mathrm{Var}(X) = \frac{1}{n} \sum_{i=1}^n (x_i - \bar{x})^2 $$ * 單一變數的波動性 * 也是回歸中計算斜率的分母 :::success ### Covariance 與 Regression 的連結: 在簡單線性回歸中,斜率 $w$ 其實是: $$ w = \frac{\mathrm{Cov}(X, Y)}{\mathrm{Var}(X)} $$ 這也顯示了線性回歸本質上是衡量「x 與 y 的共變動關係」(x 與 y 的共變動程度(Cov)➗x 自己的變化程度(Var))。 ::: ## OLS(Ordinary Least Square,最小平方法) OLS 是一種在**線性迴歸模型**中用來估計參數(w、b)好壞的方法,SSE 是 OLS 優化的核心目標,來求取最優参数。 :::info 根據**凸函數的性質(可以把它想像成一個「碗形」的圖像──中間最低,兩邊往上翹)**, 對 OLS 的目標函數(SSE)求**一階導函數=0(斜率=0)** 保證是全域的最小點,不會出現「局部最小但非全域最小」(local minimum)的情況。 ::: SSE分別對的斜率(w,slope)跟截距(b,bias/intercept)做**偏微分**,並且令一階導函數為0,就可得到SSE最小值(預測跟真實差距最小)。 <details> <summary>微分與偏微分</summary> #### 微分 給一個函數 $y = f(x)$,它的微分(導數)定義為: $$ \frac{dy}{dx} = f'(x) = \lim_{\Delta x \to 0} \frac{f(x+\Delta x) - f(x)}{\Delta x} $$ 微分就是問:「當 $x$ 變動一點點($\Delta x$),$y$ 會怎麼跟著變?」 想像一條彎彎的曲線上的某一點,我們想知道那一點的「斜率」有多陡,這就是微分。 在OLS的情況,**我想知道當w、b只變動一點點時,我的SSE會如何變化?** #### 偏微分 給一個多變數函數 $z = f(x, y)$,針對 $x$ 的偏微分記為: $$ \frac{\partial z}{\partial x} = \lim_{\Delta x \to 0} \frac{f(x+\Delta x, y) - f(x, y)}{\Delta x} $$ 類似地,針對 $y$: $$ \frac{\partial z}{\partial y} = \lim_{\Delta y \to 0} \frac{f(x, y+\Delta y) - f(x, y)}{\Delta y} $$ 偏微分就像在多個按鈕中,只轉動其中一個,看看輸出會怎麼改變。 在OLS的情況,**我想一次只變動w、b其中之一時,我的SSE會如何變化?** </details> 給定模型: $$ \hat{y} = wx + b $$ 定義 SSE(殘差平方和)為: $$ SSE = \sum_{i=1}^{n} (y_i - wx_i - b)^2 $$ ### 對 b 偏微分 因為: $f(x)=g(x)g(x)$ $f'(x)=g'(x)g(x)+g(x)g'(x)=2g'(x)g(x)$ 所以: $$ \frac{d}{db}SSE =\sum_{i=1}^{n} 2(y_i - wx_i - b)(-1)= -2 \sum_{i=1}^{n} (y_i - wx_i - b) = 0 $$ 兩邊同除 $-2$: $$ \sum_{i=1}^{n} (y_i - wx_i - b) = 0 $$ 展開: $$ \sum_{i=1}^{n} y_i - \sum_{i=1}^{n} wx_i - \sum_{i=1}^{n} b = 0 $$ 移項$b$: $$ \sum_{i=1}^{n} y_i - w \sum_{i=1}^{n} x_i = \sum_{i=1}^{n} b $$ 同除$n$,整理後解得: $$ \frac{1}{n} \sum_{i=1}^{n} y_i - w \cdot \frac{1}{n} \sum_{i=1}^{n} x_i=\frac{1}{n} \sum_{i=1}^{n} b \quad \Rightarrow \quad \bar{y} - w \bar{x}=b $$ * $\bar{x}$: x的平均數 * $\bar{y}$: y的平均數 ### 對 w 偏微分: 代入 $b = \bar{y} - w \bar{x}$: $$ SSE = \sum_{i=1}^{n} (y_i - wx_i - \bar{y} + w \bar{x})^2 $$ $$ \frac{d}{dw}SSE = -2 \sum_{i=1}^{n} (y_i - wx_i - \bar{y} + w \bar{x})(x_i -\bar{x}) = 0 $$ 消去 $-2$: $$ \sum_{i=1}^{n} (y_i - wx_i - \bar{y} + w \bar{x})(x_i -\bar{x}) = 0 $$ 整理括號: $$ \sum_{i=1}^{n} (y_i - \bar{y} - w(x_i - \bar{x}))(x_i -\bar{x}) = 0 $$ $$ \sum_{i=1}^{n} (y_i - \bar{y})(x_i -\bar{x}) - w \sum_{i=1}^{n} (x_i - \bar{x})(x_i -\bar{x}) = 0 $$ 兩邊移項: $$ w \sum_{i=1}^{n} (x_i - \bar{x})(x_i -\bar{x}) = \sum_{i=1}^{n} (y_i - \bar{y})(x_i -\bar{x}) $$ $$ \quad \Rightarrow \quad w = \sum_{i=1}^{n} (y_i - \bar{y})(x_i -\bar{x})/\sum_{i=1}^{n} (x_i - \bar{x})(x_i -\bar{x}) $$ 得到: $$ w = \frac{\sum_{i=1}^{n} (y_i - \bar{y})(x_i - \bar{x})}{\sum_{i=1}^{n} (x_i - \bar{x})^2} $$ ### 最後整理得到 OLS 解 \begin{aligned} w &= \frac{\sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^{n} (x_i - \bar{x})^2} \\ b &= \bar{y} - w \bar{x} \end{aligned} ## numpy程式碼範例 :::info 以下範例建議在colab裡使用 ::: 引入模組庫 ```python= import numpy as np import matplotlib.pyplot as plt ``` 產生隨機數據 ```python= # 設定隨機種子(讓每次執行結果一致) np.random.seed(42) # 產生自變數 x(例如 100 筆均勻分布的數據) x = np.random.uniform(0, 10, 100) # 定義線性關係:y = 2.5 * x + 3,加上一點隨機雜訊 noise = np.random.normal(0, 1, size=x.shape) # 標準差為 1 的正態分布雜訊 y = 2.5 * x + 3 + noise ``` 繪圖觀察 ```python= # 繪製圖形觀察線性關係 plt.scatter(x, y) plt.title("Linear Relationship with Noise") plt.xlabel("x") plt.ylabel("y") plt.show() ``` 範例圖 ![線性關係圖](https://hackmd.io/_uploads/rkhbyKkSex.png) 計算$w$、$b$ ```python= w=np.sum((x-x.mean())*(y-y.mean()))/np.sum((x-x.mean())*(x-x.mean())) b=y.mean()-w*x.mean() ``` 繪圖觀察預測結果 ```python= x_sorted = np.sort(x) plt.plot(x_sorted,x_sorted*w+b, color='red', linestyle='--', linewidth=2, label=f'y={w:.2f}*x+{b:.2f}') plt.scatter(x, y) plt.title("Linear Relationship with Noise") plt.xlabel("x") plt.ylabel("y", rotation=0, labelpad=20) plt.legend() plt.show() ``` :::info matplotlib.pyplot.plot() **是「按順序」畫線,不是按大小**,因為x隨機生成沒按照大小排序,因此先排序產生x_sorted,才不會導致線亂折 ::: 範例圖 ![線性關係圖(迴歸線)](https://hackmd.io/_uploads/BymfkF1Sgl.png) 得到結果應該非常接近原來的斜率(2.5)和截距(3) 觀察MSE和SSE ```python= SSE=np.sum((y-x*w-b)**2) MSE=SSE/len(y) print(f'SSE:{SSE},MSE:{MSE}') ``` 統整程式碼 ```python= import numpy as np import matplotlib.pyplot as plt # 產生資料 np.random.seed(0) x = np.random.uniform(0, 10, 100) noise = np.random.normal(0, 1, size=x.shape) y = 2.5 * x + 3 + noise # 線性回歸(手動) w = np.sum((x - x.mean()) * (y - y.mean())) / np.sum((x - x.mean()) ** 2) b = y.mean() - w * x.mean() y_pred = w * x + b # SSE 與 MSE SSE = np.sum((y - y_pred) ** 2) MSE = SSE / len(y) # 建立子圖(1 列 2 欄) fig, axs = plt.subplots(1, 2, figsize=(14, 6)) # 子圖1:線性回歸圖 axs[0].scatter(x, y, label='Data Points') axs[0].plot(np.sort(x), y_pred[np.argsort(x)], color='red', linestyle='--', label=f'y = {w:.2f}x + {b:.2f}') axs[0].set_title("Linear Regression") axs[0].set_xlabel("x") axs[0].set_ylabel("y", rotation=0, labelpad=20) axs[0].legend() axs[0].grid(True) # 子圖2:顯示公式與誤差 formula_text = ( r"$\mathbf{Linear\ Regression\ Formula}$" "\n\n" r"$y = wx + b$" "\n" rf"$w = \frac{{\sum (x_i - \bar{{x}})(y_i - \bar{{y}})}}{{\sum (x_i - \bar{{x}})^2}} = {w:.2f}$" "\n" rf"$b = \bar{{y}} - w \bar{{x}} = {b:.2f}$" "\n\n" r"$\mathbf{Error\ Metrics}$" "\n" rf"$SSE = \sum (y_i - \hat{{y}}_i)^2 = {SSE:.2f}$" "\n" rf"$MSE = \frac{{SSE}}{{n}} = {MSE:.2f}$" ) axs[1].axis('off') axs[1].text(0.05, 0.95, formula_text, fontsize=18, va='top', linespacing=1.5) plt.tight_layout() plt.show() ``` ![線性關係圖(迴歸線和損失函數)](https://hackmd.io/_uploads/SkkkxFyHxl.png) :::info matplotlib 支援的格式,它內建的是簡化版的 LaTeX 渲染器(稱為 mathtext) | 調整 | 原因 | | --------------------- | ---------------------- | | `r"..."` | 使用 raw 字串避免跳脫錯誤 | | `\mathbf{}` | 支援、取代 `\textbf{}` | | `\hat{y}` `\bar{x}` 等 | 可以正確渲染 | | 換行用 `"\n"`,非 `\\` | `text()` 不能直接用 `\\` 換行 | ::: ## scikit-Learn LinearRegression範例 ```python= import numpy as np from sklearn.linear_model import LinearRegression # 設定隨機種子(讓每次執行結果一致) np.random.seed(42) # 產生自變數 x(例如 100 筆均勻分布的數據) x = np.random.uniform(0, 10, 100) # 定義線性關係:y = 2.5 * x + 3,加上一點隨機雜訊 noise = np.random.normal(0, 1, size=x.shape) # 標準差為 1 的正態分布雜訊 y = 2.5 * x + 3 + noise x=x.reshape(-1,1) lr=LinearRegression() lr.fit(x,y) print(lr.coef_,lr.intercept_) ```