k 近邻法 (k-NN) 是一种基于实例的学习方法,无法转化为对参数空间的搜索问题(参数最优化 问题)。它的特点是对特征空间进行搜索。除了k近邻法,本章还对以下几个问题进行较深入的讨
论:
输入:训练集 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,…,N
输出:实例 x x x 所属的类 y y y
设在给定距离度量下,涵盖最近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 为假
寻找使得函数 ∑ 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 的距离有多种度量方式:
(1) 不同距离度量
一般形式是闵可夫斯基距离( 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
可见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)
参考自:统计学习方法习题解答