Learning Objectives
- 用 numpy 寫出羅吉斯迴歸:sigmoid、交叉熵、梯度下降
- 說明為什麼分類用交叉熵,以及決策邊界在哪裡
- 對照 scikit-learn 的 LogisticRegression,說明它預設有 L2 正則化,C 越小越強
- 讀懂預測機率與係數,並把準確率和基準比較
要預測的是「會不會」
前幾課預測的是分數,答案可以是任何數字。很多問題的答案只有兩種:及格或不及格、是垃圾訊息或不是。這叫二元分類(binary classification),答案寫成 1 與 0。
直接用線性迴歸行不行?線性模型的輸出 w·x + b 可以是任何數字,可能算出 1.7 或 −0.3,這兩個數字當作「及格的機率」都沒有意義。
羅吉斯迴歸(logistic regression)的做法是:線性模型照算,再多一步,把結果壓到 0 到 1 之間,當作機率。名字裡有「迴歸」,但它做的是分類。
z = w·x + b (和線性迴歸一樣)
p = 1 / (1 + e⁻ᶻ) (sigmoid 函數,把 z 壓到 0 到 1)
這一課和第 3、4 課的路線相同:先用 numpy 從頭寫,再對照 scikit-learn。
逐步看:sigmoid 與交叉熵
前半段看 sigmoid 把 z 壓到哪裡;後半段看「答案是 1,模型卻給了各種機率」時,兩種損失各是多少。
分類為什麼用交叉熵
訓練模型要有一個損失函數。分類常用交叉熵,整份資料取平均:
損失 = −平均( y × log(p) + (1 − y) × log(1 − p) )
式子看起來長,其實只有一半會作用:y = 1 時只剩 −log(p),y = 0 時只剩 −log(1 − p)。兩種情況都是「給正確答案的機率越低,損失越大」,而且越接近 0 增加得越快。
配上 sigmoid 時,交叉熵還有一個好處:它的梯度形式很簡單,和線性迴歸幾乎一樣(第 4 課是「誤差 × 特徵」):
對 w 的梯度 = 平均( (p − y) × x )
對 b 的梯度 = 平均( p − y )
p − y 就是這筆資料猜錯多少,也決定往哪個方向修正。下面就用這兩行做梯度下降。
逐步看:用 numpy 訓練
八筆資料、兩個特徵(已標準化),走 8 輪梯度下降。注意每一輪的 loss 怎麼下降,以及 p 怎麼往 y 靠近。
決策邊界在哪裡
什麼時候機率剛好是 0.5?sigmoid 在 z = 0 時是 0.5,所以就是
w1 × x1 + w2 × x2 + b = 0
這條線的一邊預測 1、另一邊預測 0,叫決策邊界(decision boundary)。把它改寫成畫圖用的形式:
x2 = −(w1 × x1 + b) / w2
羅吉斯迴歸的決策邊界是一條直線(特徵更多時是平面)。雖然 sigmoid 是曲線,但分界的位置是由線性的 z 決定的。資料如果是一個類別被另一個類別包圍,直線分不開,要加特徵(第 6 課的多項式特徵)或換模型(第 9、11 課)。
和 scikit-learn 有什麼不同
接下來用 scikit-learn 對照。LogisticRegression 和我們寫的不完全一樣:它預設有 L2 正則化。
| 我們寫的 | LogisticRegression() |
|
|---|---|---|
| 正則化 | 沒有 | 預設 penalty='l2',和第 6 課的 Ridge 同一種懲罰 |
| 強度 | C=1.0,C 是強度的倒數:C 越小,懲罰越強(和 alpha 方向相反) |
|
| 解法 | 梯度下降 | 預設 solver='lbfgs',比單純的梯度下降快得多,max_iter=100 |
因為有懲罰,它和第 6 課一樣受特徵尺度影響,通常要先標準化。
逐步看:和 scikit-learn 對照
30 筆模擬資料(亂數產生)。先用 numpy 走 20 輪,再用三種 C 訓練 LogisticRegression。C = 1000000 時懲罰幾乎等於沒有,應該要和 numpy 的結果接近。
逐步看:預測是否及格
用 students.csv(程式產生的模擬資料,不是任何學校的真實資料),四個特徵預測是否及格。流程和第 5 課相同,看準確率時別忘了和基準比。
機率與係數怎麼讀
係數不是「機率增加多少」。 上面小考的係數是 0.955(已標準化),正確的讀法是:
在這份資料裡,其他特徵固定時,小考多一個標準差,z 增加 0.955。
z 又叫對數勝算(log-odds),它和機率的關係不是直線:同樣增加 0.955,從 z = 0 開始,機率從 0.5 升到約 0.72;從 z = 4 開始,機率只從 0.98 升到約 0.99。正負號可以看方向,大小要換算才知道機率差多少。 和第 5 課一樣,這是關聯,不是因果,而且會隨放進去的特徵改變。
準確率不夠用的時候。 這份資料約 77% 及格,所以全猜及格就有 0.768。如果只有 5% 的同學不及格,全猜及格的準確率高達 0.95,卻一個需要幫助的同學都找不到。第 13 課的混淆矩陣、精確率與召回率就是要解決這個問題。
多於兩個類別。 LogisticRegression 也可以直接處理三個以上的類別,用法相同,predict_proba 每列會有對應數量的機率。
實作:寫出一步梯度下降
sigmoid 已經寫好了。完成 cross_entropy(y, p)(平均交叉熵)與 train_step(X, y, w, b, lr)(走一步,回傳新的 w 與 b)。完成後,損失應該一輪比一輪小。
小測驗:機率與損失
下面程式會印出什麼?
import numpy as np
z = np.array([-1.0, 0.0, 1.0])
print((1 / (1 + np.exp(-z))).round(2))
一筆資料的答案是 0,模型算出 p = 0.99。和答案是 1、p = 0.99 比起來,交叉熵會?
一個訓練好的模型 w = [1, 2]、b = −3,點 (1, 1) 在哪裡?
小測驗:使用 LogisticRegression
關於 LogisticRegression() 的預設值,下面哪一個正確?
測試準確率 0.828,測試資料裡有 76.8% 的同學及格。最恰當的解讀是?
標準化後,小考的係數是 0.955。正確的讀法是?
重點整理
- 羅吉斯迴歸:線性組合 z = w·x + b,再經過 sigmoid 變成機率。名字有迴歸,做的是分類。
- 損失用交叉熵:給正確答案的機率越低,損失越大,很有信心卻答錯時懲罰很重。梯度是平均( (p − y) × x )。
- 機率 0.5 的地方就是決策邊界,也就是 w·x + b = 0,是一條直線(或平面)。
LogisticRegression預設有 L2 正則化(C=1.0,C 越小越強),所以要先標準化;C 很大時才接近我們用 numpy 寫的沒有正則化版本。- 準確率要和全猜最多的類別比;係數是對數勝算,看得出方向,大小要換算才知道機率差多少。
- 下一課的 k 近鄰完全不學係數:直接找最像的幾筆資料來投票。