專項練習 · 9 分鐘閱讀

Python k-NN 練習:距離、投票與縮放

k-NN 是很好的第一個分類器,因為每個決定都看得見:距離、鄰居、投票和評估都能檢查。這些練習讓每一步都可以測試。

只寫一次距離函式

保留一個距離函式,在排序和評估中復用。小函式比散落在迴圈裡的距離邏輯更容易測試。比較點之前要檢查維度是否一致。

def distance(a, b):
    return sum((x - y) ** 2 for x, y in zip(a, b)) ** 0.5

print(distance((0, 0), (3, 4)))

可復用的度量

帶著標籤排序

按距離排序 (點, 標籤) 對,而不是先排點、之後再查標籤。這樣預測始終和證據綁定,也避免排序後索引錯位。

def distance(a, b):
    return sum((x - y) ** 2 for x, y in zip(a, b)) ** 0.5

points = [((1, 1), "a"), ((4, 4), "b"), ((1, 2), "a")]
target = (1, 1.5)
ranked = sorted(points, key=lambda item: distance(target, item[0]))
print(ranked[:2])

標籤跟著點走

用確定的平票規則投票

取前 k 個標籤並計數。如果兩個標籤平票,使用明確規則,例如選擇距離最近的平票點。隨機或依賴雜湊順序的平票規則會讓相同輸入產生不同預測。

from collections import Counter

labels = ["a", "b", "a", "c"]
winner = Counter(labels).most_common(1)[0][0]
print(winner)

可復現的投票

度量前先縮放

距離把每個單位視為同等重要。收入以千計、年齡以年計時,收入會佔主導。用訓練統計量標準化特徵,再把同一變換應用到驗證集和測試集。

def distance(a, b):
    return sum((x - y) ** 2 for x, y in zip(a, b)) ** 0.5

raw = distance((30, 30000), (30, 31000))
scaled = distance((0.3, 0.3), (0.3, 1.0))
print(round(raw, 1), round(scaled, 1))

單位改變答案

用留出點評估

在訓練集上預測不是評估。使用訓練/測試劃分,資料很小時可以用留一法。還要報告基線:多數類預測往往出人意料地強。

data = [((0, 0), "a"), ((1, 0), "a"), ((5, 5), "b")]
correct = 0
for i, (point, label) in enumerate(data):
    train = data[:i] + data[i + 1:]
    nearest = min(train, key=lambda item: (item[0][0] - point[0]) ** 2 + (item[0][1] - point[1]) ** 2)
    correct += nearest[1] == label
print(correct, len(data))

留一法

你的練習清單

常見問題

k 怎麼選?

二分類可從小奇數開始,再在驗證資料上評估多個值。k 太小雜訊大,k 太大會抹平局部結構。

應該標準化還是歸一化?

兩者都可以。標準化按標準差中心化縮放;最小-最大歸一化映射到固定區間。關鍵是變換隻在訓練資料上配適。

k-NN 計算貴嗎?

預測時要把新點與所有訓練點比較,大資料集需要近似索引或換模型。

練習 k-NN

12 道題,覆蓋距離、鄰居、投票、縮放和留出評估。

打開練習

繼續閱讀: