k 近鄰法 (k-NN) 是一種基於實例的學習方法,沒法轉化爲對參數空間的搜索問題(參數最優化 問題)。它的特色是對特徵空間進行搜索。除了k近鄰法,本章還對如下幾個問題進行較深刻的討
論:html
輸入:訓練集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) } , x i ∈ X ⊆ R n T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \ldots,\left(x_{N}, y_{N}\right)\right\}, \quad x_{i} \in X \subseteq R^{n} T={ (x1,y1),(x2,y2),…,(xN,yN)},xi∈X⊆Rn 爲實例特徵向 y i ∈ Y = { c 1 , c 2 , … , c K } y_{i} \in Y=\left\{c_{1}, c_{2}, \ldots, c_{K}\right\} yi∈Y={ c1,c2,…,cK} 爲實例的類別, i = 1 , 2 , … , N \quad i=1,2, \ldots, N i=1,2,…,Npython
輸出:實例 x x x 所屬的類 y y yweb
設在給定距離度量下,涵蓋最近k個點的鄰域爲 N K ( x ) N_{K}(x) NK(x)
y = arg max c j ∑ x i ∈ N K ( x ) I ( y i = c j ) , i = 1 , 2 , … , K y=\arg \max _{c_{j}} \sum_{x_{i} \in N_{K}(x)} I\left(y_{i}=c_{j}\right), \quad i=1,2, \ldots, K y=argcjmaxxi∈NK(x)∑I(yi=cj),i=1,2,…,K算法
其中示性函數 I ( x ) = { 1 , x 爲真 0 , x 爲假 I(x)=\left\{\begin{array}{l}1, x \text { 爲真 }\\ 0, x \text { 爲假}\end{array}\right. I(x)={ 1,x 爲真 0,x 爲假svg
尋找使得函數 ∑ x i ∈ N K ( x ) I ( y i = c j ) \sum_{x_{i} \in N_{K}(x)} I\left(y_{i}=c_{j}\right) ∑xi∈NK(x)I(yi=cj) 取得最大值的變量 c j , c_{j}, cj, 也就是說, 看看距離 x i x_{i} xi 最近的k個點裏面哪一類別最多,以此做爲輸出。函數
根據模型的分類, k-NN模型屬於非機率模型。學習
觀察 y = arg max c j ∑ x i ∈ N K ( x ) I ( y i = c j ) y=\arg \max _{c_{j}} \sum_{x_{i} \in N_{K}(x)} I\left(y_{i}=c_{j}\right) y=argmaxcj∑xi∈NK(x)I(yi=cj) 可發現它與感知機不一樣的之處, 做爲決策函數, 它並不須要任何未知參數(感知機須要肯定W和b),直接從訓練集的數據獲得輸出。優化
k-NN的基本思想是,特徵空間中的距離反映了兩個點的類似程度, 所以 「距離" 是做出分類判斷 的基本依據。向量空間 R n R^{n} Rn 的距離有多種度量方式:ui
(1) 不一樣距離度量spa
通常形式是閔可夫斯基距離( L p L_{p} Lp 範數):
L p ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) 1 / p L_{p}\left(x_{i}, x_{j}\right)=\left(\sum_{l=1}^{n}\left|x_{i}^{(l)}-x_{j}^{(l)}\right|^{p}\right)^{1 / p} Lp(xi,xj)=(∑l=1n∣∣∣xi(l)−xj(l)∣∣∣p)1/p
當p=1時, 稱爲曼哈頓距離( L 1 L_{1} L1 範數):
L 1 ( x i , x j ) = ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ L_{1}\left(x_{i}, x_{j}\right)=\sum_{l=1}^{n}\left|x_{i}^{(l)}-x_{j}^{(l)}\right| L1(xi,xj)=∑l=1n∣∣∣xi(l)−xj(l)∣∣∣
當p=2時,稱爲歐幾里得距離( L 2 L_{2} L2 範數),也就是最經常使用的距離::
L 2 ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 L_{2}\left(x_{i}, x_{j}\right)=\left(\sum_{l=1}^{n}\left|x_{i}^{(l)}-x_{j}^{(l)}\right|^{2}\right)^{\frac{1}{2}} L2(xi,xj)=(∑l=1n∣∣∣xi(l)−xj(l)∣∣∣2)21
import math from itertools import combinations def L(x, y, p=2): # x1 = [1, 1], x2 = [5,1] if len(x) == len(y) and len(x) > 1: sum = 0 for i in range(len(x)): sum += math.pow(abs(x[i] - y[i]), p) return math.pow(sum, 1 / p) else: return 0
下圖表示平面上A、B兩點之間不一樣的距離:
可見p取值越大,兩點之間的距離越短。
(2) 問題:爲何切比雪夫距離 L ∞ ( x i , x j ) = max l ∣ x i ( l ) − x j ( l ) ∣ ? L_{\infty}\left(x_{i}, x_{j}\right)=\max _{l}\left|x_{i}^{(l)}-x_{j}^{(l)}\right| ? L∞(xi,xj)=maxl∣∣∣xi(l)−xj(l)∣∣∣?
其實這個問題等價於:爲何 ∥ x ∥ ∞ = max ∣ x ( l ) ∣ , \|x\|_{\infty}=\max \left|x^{(l)}\right|, ∥x∥∞=max∣∣x(l)∣∣, 即 R n R^{n} Rn 空間中的向量 x , x, x, 它的切比雪 夫長度等於它的最大份量方向上的長度。
證實: 設 x = ( x ( 1 ) , x ( 2 ) , … x ( n ) ) T ∈ X ⊆ R n x=\left(x^{(1)}, x^{(2)}, \ldots x^{(n)}\right)^{T} \in X \subseteq R^{n} x=(x(1),x(2),…x(n))T∈X⊆Rn
不妨設 ∣ x ( 1 ) ∣ = max { ∣ x ( 1 ) ∣ , ∣ x ( 2 ) , … , ∣ x ( n ) ∣ ∣ } , \left|x^{(1)}\right|=\max \left\{\left|x^{(1)}\right|,\left|x^{(2)}, \ldots,\right| x^{(n)}||\right\}, ∣∣x(1)∣∣=max{ ∣∣x(1)∣∣,∣∣x(2),…,∣∣x(n)∣∣}, 即 ∣ x ( l ) ∣ ≤ ∣ x ( 1 ) ∣ \left|x^{(l)}\right| \leq\left|x^{(1)}\right| ∣∣x(l)∣∣≤∣∣x(1)∣∣
注意:最大份量的長度惟一, 但最大份量可能並不惟 一,$設有 x ( 1 ) , x ( 2 ) , … x ( k ) x^{(1)}, x^{(2)}, \ldots x^{(k)} x(1),x(2),…x(k) 等K個份量的 長度都等於 ∣ x ( 1 ) ∣ \left|x^{(1)}\right| ∣∣x(1)∣∣
當 ∣ x ( l ) ∣ = ∣ x ( 1 ) ∣ , lim p → ∞ ( ∣ x ( l ) ∣ ∣ x ( 1 ) ∣ ) p = 1 , \left|x^{(l)}\right|=\left|x^{(1)}\right|, \quad \lim _{p \rightarrow \infty}\left(\frac{\left|x^{(l)}\right|}{\left|x^{(1)}\right|}\right)^{p}=1, ∣∣x(l)∣∣=∣∣x(1)∣∣,limp→∞(∣x(1)∣∣x(l)∣)p=1, 即 x ( l ) x^{(l)} x(l) 爲 x ( 1 ) , x ( 2 ) , … x ( k ) x^{(1)}, x^{(2)}, \ldots x^{(k)} x(1),x(2),…x(k) 時
當 ∣ x ( l ) ∣ < ∣ x ( 1 ) ∣ , lim p → ∞ ( ∣ x ( l ) ∣ ∣ x ( 1 ) ∣ ) p = 0 , \left|x^{(l)}\right|<\left|x^{(1)}\right|, \quad \lim _{p \rightarrow \infty}\left(\frac{\left|x^{(l)}\right|}{\left|x^{(1)}\right|}\right)^{p}=0, ∣∣x(l)∣∣<∣∣x(1)∣∣,limp→∞(∣x(1)∣∣x(l)∣)p=0, 即 x ( l ) x^{(l)} x(l) 爲非最大長度份量時
計算 x x x 的切比雪夫長度:
∥ x ∥ ∞ = lim p → ∞ ( ∑ l = 1 n ∣ x ( l ) ∣ p ) 1 p = lim p → ∞ ( ∣ x ( 1 ) ∣ p ∑ l = 1 n ∣ x ( l ) ∣ p ∣ x ( 1 ) ∣ p ) 1 p \|x\|_{\infty}=\lim _{p \rightarrow \infty}\left(\sum_{l=1}^{n}\left|x^{(l)}\right|^{p}\right)^{\frac{1}{p}}=\lim _{p \rightarrow \infty}\left(\left|x^{(1)}\right|^{p} \sum_{l=1}^{n} \frac{\left|x^{(l)}\right|^{p}}{\left|x^{(1)}\right|^{p}}\right)^{\frac{1}{p}} ∥x∥∞=limp→∞(∑l=1n∣∣x(l)∣∣p)p1=limp→∞(∣∣x(1)∣∣p∑l=1n∣x(1)∣p∣x(l)∣p)p1
因爲已知 lim p → ∞ ( ∣ x ( l ) ∣ ∣ x ( 1 ) ∣ ) p \lim _{p \rightarrow \infty}\left(\frac{\left|x^{(l)}\right|}{\left|x^{(1)}\right|}\right)^{p} limp→∞(∣x(1)∣∣x(l)∣)p 等於0或1,且有k個份量結果爲1, 因此
lim p → ∞ ∑ l = 1 n ( ∣ x ( l ) ∣ ∣ x ( 1 ) ∣ ) p = ( 1 + 1 + … + 1 + 0 + 0 + … . 0 ) = k \lim _{p \rightarrow \infty} \sum_{l=1}^{n}\left(\frac{\left|x^{(l)}\right|}{\left|x^{(1)}\right|}\right)^{p}=(1+1+\ldots+1+0+0+\ldots .0)=k limp→∞∑l=1n(∣x(1)∣∣x(l)∣)p=(1+1+…+1+0+0+….0)=k
所以
∥ x ∥ ∞ = lim p → ∞ ( ∣ x ( 1 ) ∣ p ∑ l = 1 n ∣ x ( l ) ∣ p ∣ x ( 1 ) ∣ p ) 1 p = lim p → ∞ ( ∣ x ( 1 ) ∣ p k ) 1 p = lim p → ∞ ∣ x ( 1 ) ∣ k 1 p = ∣ x ( 1 ) ∣ \|x\|_{\infty}=\lim _{p \rightarrow \infty}\left(\left|x^{(1)}\right|^{p} \sum_{l=1}^{n} \frac{\left|x^{(l)}\right|^{p}}{\left|x^{(1)}\right|^{p}}\right)^{\frac{1}{p}}=\lim _{p \rightarrow \infty}\left(\left|x^{(1)}\right|^{p} k\right)^{\frac{1}{p}}=\lim _{p \rightarrow \infty}\left|x^{(1)}\right| k^{\frac{1}{p}}=\left|x^{(1)}\right| ∥x∥∞=p→∞lim(∣∣∣x(1)∣∣∣pl=1∑n∣∣x(1)∣∣p∣∣x(l)∣∣p)p1=p→∞lim(∣∣∣x(1)∣∣∣pk)p1=p→∞lim∣∣∣x(1)∣∣∣kp1=∣∣∣x(1)∣∣∣
即 ∥ x ∥ ∞ = max ∣ x ( l ) ∣ , \|x\|_{\infty}=\max \left|x^{(l)}\right|, ∥x∥∞=max∣∣x(l)∣∣, 得證。
以上證實參考
(3) 平面上的圓
L p = 1 L_{p}=1 Lp=1 在平面上的圖像:
若是圓形的定義是 「到某一點距離相等的曲線圍成的圖形" ,那麼在不一樣的距離度量下,圓形的形 狀能夠徹底不一樣。爲什麼 L 1 L_{1} L1 正則化在平面上的等高線爲同心正方形, 不就很容易理解嗎?
李航老師書中引入了「近似偏差」和「估計偏差」兩個概念,但沒有給出具體定義。
這裏簡單總結一下:
I [ f ^ H , l ] − I [ f 0 ] = ( I [ f ^ H , l ] − I [ f H ] ) + ( I [ f H ] − I [ f 0 ] ) I\left[\hat{f}_{H, l}\right]-I\left[f_{0}\right]=\left(I\left[\hat{f}_{H, l}\right]-I\left[f_{H}\right]\right)+\left(I\left[f_{H}\right]-I\left[f_{0}\right]\right) I[f^H,l]−I[f0]=(I[f^H,l]−I[fH])+(I[fH]−I[f0])
右側兩項分別是 「估計偏差」 和 「近似偏差」
通常經過交叉驗證來肯定最優的 k 值。
k 近鄰的決策規則就是 「多數表決" ,即少數服從多數, 用數學表達式表示爲
1 k ∑ x i ∈ N k ( x ) I ( y i ≠ c j ) = 1 − 1 k ∑ x i ∈ N k ( x ) I ( y i = c j ) \frac{1}{k} \sum_{x_{i} \in N_{k}(x)} I\left(y_{i} \neq c_{j}\right)=1-\frac{1}{k} \sum_{x_{i} \in N_{k}(x)} I\left(y_{i}=c_{j}\right) k1xi∈Nk(x)∑I(yi=cj)=1−k1xi∈Nk(x)∑I(yi=cj)
等號左側爲誤分類率,要是誤分類率最小,就要使得 ∑ x i ∈ N k ( x ) I ( y i = c j ) \sum_{x_{i} \in N_{k}(x)} I\left(y_{i}=c_{j}\right) ∑xi∈Nk(x)I(yi=cj) 最大, 即選擇集合 N k ( x ) N_{k}(x) Nk(x) 中最多的一類。
參見本篇文章:(必定看)
kd樹介紹(KNN算法引出)
3.1
參照圖3.1,在二維空間中給出實例點,畫出kk爲1和2時的kk近鄰法構成的空間劃分,並對其進行比較,體會kk值選擇與模型複雜度及預測準確率的關係。
%matplotlib inline import numpy as np from sklearn.neighbors import KNeighborsClassifier import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap data = np.array([[5, 12, 1], [6, 21, 0], [14, 5, 0], [16, 10, 0], [13, 19, 0], [13, 32, 1], [17, 27, 1], [18, 24, 1], [20, 20, 0], [23, 14, 1], [23, 25, 1], [23, 31, 1], [26, 8, 0], [30, 17, 1], [30, 26, 1], [34, 8, 0], [34, 19, 1], [37, 28, 1]]) X_train = data[:, 0:2] y_train = data[:, 2] models = (KNeighborsClassifier(n_neighbors=1, n_jobs=-1), KNeighborsClassifier(n_neighbors=2, n_jobs=-1)) models = (clf.fit(X_train, y_train) for clf in models) titles = ('K Neighbors with k=1', 'K Neighbors with k=2') fig = plt.figure(figsize=(15, 5)) plt.subplots_adjust(wspace=0.4, hspace=0.4) X0, X1 = X_train[:, 0], X_train[:, 1] x_min, x_max = X0.min() - 1, X0.max() + 1 y_min, y_max = X1.min() - 1, X1.max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.2), np.arange(y_min, y_max, 0.2)) for clf, title, ax in zip(models, titles, fig.subplots(1, 2).flatten()): Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) colors = ('red', 'green', 'lightgreen', 'gray', 'cyan') cmap = ListedColormap(colors[:len(np.unique(Z))]) ax.contourf(xx, yy, Z, cmap=cmap, alpha=0.5) ax.scatter(X0, X1, c=y_train, s=50, edgecolors='k', cmap=cmap, alpha=0.5) ax.set_title(title) plt.show()
3.2
利用例題3.2構造的kd樹求點x = ( 3 , 4.5 ) T =(3,4.5)^{\mathrm{T}} =(3,4.5)T 的最近鄰點。
import numpy as np from sklearn.neighbors import KDTree train_data = np.array([(2, 3), (5, 4), (9, 6), (4, 7), (8, 1), (7, 2)]) tree = KDTree(train_data, leaf_size=2) dist, ind = tree.query(np.array([(3, 4.5)]), k=1) x1 = train_data[ind[0]][0][0] x2 = train_data[ind[0]][0][1] print("x點的最近鄰點是({0}, {1})".format(x1, x2)) #x點的最近鄰點是(2, 3)
3.3
解答:
算法:用kd樹的kk近鄰搜索
輸入:已構造的kd樹;目標點xx;
輸出:xx的最近鄰
# 構建kd樹,搜索待預測點所屬區域 from collections import namedtuple import numpy as np # 創建節點類 class Node(namedtuple("Node", "location left_child right_child")): def __repr__(self): return str(tuple(self)) # kd tree類 class KdTree(): def __init__(self, k=1): self.k = k self.kdtree = None # 構建kd tree def _fit(self, X, depth=0): try: k = self.k except IndexError as e: return None # 這裏能夠展開,經過方差選擇axis axis = depth % k X = X[X[:, axis].argsort()] median = X.shape[0] // 2 try: X[median] except IndexError: return None return Node( location=X[median], left_child=self._fit(X[:median], depth + 1), right_child=self._fit(X[median + 1:], depth + 1) ) def _search(self, point, tree=None, depth=0, best=None): if tree is None: return best k = self.k # 更新 branch if point[0][depth % k] < tree.location[depth % k]: next_branch = tree.left_child else: next_branch = tree.right_child if not next_branch is None: best = next_branch.location return self._search(point, tree=next_branch, depth=depth + 1, best=best) def fit(self, X): self.kdtree = self._fit(X) return self.kdtree def predict(self, X): res = self._search(X, self.kdtree) return res
KNN = KdTree() X_train = np.array([[2, 3], [5, 4], [9, 6], [4, 7], [8, 1], [7, 2]]) KNN.fit(X_train) X_new = np.array([[3, 4.5]]) res = KNN.predict(X_new) x1 = res[0] x2 = res[1] print("x點的最近鄰點是({0}, {1})".format(x1, x2)) #x點的最近鄰點是(2, 3)