Learning Objectives
- 用 numpy 寫出 k 近鄰:算距離、排序、取最近的 k 筆、投票
- 說明特徵尺度為什麼會影響 k 近鄰,並用 StandardScaler 標準化
- 說明 scaler 為什麼只能用訓練資料 fit
- 從訓練與測試準確率的變化,判斷 k 太小或太大
看看最像的幾筆資料
想知道一位同學這學期會不會及格,一個很直接的做法是:找過去資料裡和他最像的幾位同學,看那些人大多及格還是不及格。 這就是 k 近鄰(k-nearest neighbors,kNN):
- 算出要預測的這一筆,和每一筆訓練資料的距離;
- 取距離最近的 k 筆;
- 這 k 筆投票,票數最多的類別就是預測。
k 近鄰「訓練」時幾乎什麼都沒做,只是把資料存起來,真正的計算發生在預測的時候。 距離最常用歐氏距離,也就是把每個特徵的差平方、加總、再開根號:
距離 = √( (x1 − y1)² + (x2 − y2)² + … )
先用 numpy 親手寫一次,之後用 scikit-learn 時才知道它在做什麼。
逐步看:用 numpy 寫 k 近鄰
六筆訓練資料、兩個特徵、兩個類別,要預測 query 這一點。注意右邊的陣列:每一行都是對整個陣列一次算完,不需要自己寫迴圈(「資料分析」軌學過的向量化)。
尺度會決定誰說了算
距離是把每個特徵的差加起來,所以數字範圍大的特徵會主導距離。 小考分數相差 10 分,平方是 100;通勤時間如果用秒記,相差 10 分鐘就是 600,平方是 360,000。 這時候「最像的同學」幾乎就是「通勤時間最接近的同學」,小考分數再怎麼有用也發揮不了作用。
解決方法是先把每個特徵轉成相同的尺度。最常用的是標準化(standardization):減掉平均、除以標準差,轉換後每個特徵的平均是 0、標準差是 1。
scaler = StandardScaler().fit(X_train) # 只用訓練資料算平均與標準差
X_train_s = scaler.transform(X_train)
X_test_s = scaler.transform(X_test) # 測試資料用同一組平均與標準差
scaler 只能用訓練資料 fit。測試資料代表「模型還沒看過的資料」,如果平均與標準差是連測試資料一起算的,就等於偷看了答案的一部分,這叫資料洩漏(第 14 課會細談)。
靠距離或梯度下降的模型(k 近鄰、羅吉斯迴歸、支援向量機、神經網路)都要注意尺度;決策樹只比大小,不受尺度影響(第 9 課)。
逐步看:標準化前後差多少
這裡直接用亂數產生 300 筆模擬資料,讓現象清楚:及格與否由小考分數決定,通勤秒數和及格無關,但數字大得多。
k 要選多大
k 控制模型的複雜度:
| k | 模型的樣子 | 問題 |
|---|---|---|
| 很小(例如 1) | 只看最近的一筆,決策邊界彎來彎去 | 把訓練資料裡的雜訊也記住了:訓練準確率很高,測試準確率不好(過擬合) |
| 適中 | 邊界平滑 | |
| 很大(接近全部資料) | 幾乎所有人都投票,預測接近「永遠猜最多的類別」 | 什麼規律都學不到(欠擬合) |
k=1 時,每一筆訓練資料的最近鄰居就是自己,所以訓練準確率一定是 1.0,這個數字沒有意義。判斷模型好不好要看沒看過的資料。
逐步看:k 怎麼影響準確率
這次用 students.csv(程式產生的模擬資料,不是任何學校的真實資料)預測是否及格。看輸出的三欄:k、訓練準確率、測試準確率;最後畫成折線圖。
可以用測試資料挑 k 嗎?
前面的折線圖看起來 k=5 最好,但要小心:如果用測試資料挑 k,測試資料就不再是「沒看過的資料」了,這個 0.809 會偏樂觀。 而且測試資料只有 94 筆,差一兩筆,準確率就差 1~2%,k=5 和 k=41 的差距可能只是運氣。 正確的做法是另外切出驗證資料,或用交叉驗證,第 14 課會教。
實作:寫一個 knn_predict 函數
完成 knn_predict(X, y, query, k):回傳 query 的預測類別。步驟同上面的逐步範例:算距離、argsort 取最近的 k 筆、bincount 加 argmax 投票。
小測驗:k 近鄰
下面程式會印出什麼?
import numpy as np
dist = np.array([4.0, 1.0, 3.0, 2.0])
print(np.argsort(dist)[:2].tolist())
k=1 時訓練準確率是 1.0,最恰當的解讀是?
資料裡有「年收入(元)」和「年齡」兩個特徵,直接用 k 近鄰,最可能發生什麼?
標準化時,下面哪一種寫法才正確?
k 設成和訓練資料一樣多,預測會變成什麼樣子?
重點整理
- k 近鄰:算距離、取最近的 k 筆、投票。訓練只是存資料,計算都在預測時發生,資料多時預測會變慢。
- 距離會被數字範圍大的特徵主導,所以要先標準化;scaler 只用訓練資料 fit,否則是資料洩漏。
- k 太小會過擬合(訓練高、測試低),k 太大會欠擬合(兩者都接近猜最多的類別)。
- 不要用測試資料挑 k,第 14 課用交叉驗證解決。
- 下一課的決策樹走另一條路:不算距離,而是問一連串「這個特徵大於某個值嗎?」,所以不受尺度影響。