K近邻算法本身是个很简单的算法,某些时候甚至不能称之为机器学习算法,因为它没有“学习”的过程,仅仅通过判定和要预测的 x x x相近的点都呈现的表征来确定 x x x的对应类标 y y y就可以了,但是《统计学习方法》这本书使用了KD-tree的方法来寻找最近邻,其主要目的是在数据量比较大的时候能够优化查找效率,从原理来讲,树结构的查找的时间复杂度 O ( l o g N ) O\left( logN \right) O(logN)比线性查找的复杂度 O ( N ) O\left( N \right) O(N)低,因此效率比较高。
3.1k近邻算法(KNN)
输入:训练数据集
T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x N , y N ) } T = \{ (x_1,y_1),(x_2,y_2),...,(x_N,y_N) \} T={(x1,y1),(x2,y2),...,(xN,yN)}
其中, x i ∈ X ⊆ R n x_i \in \mathcal{X} \subseteq R^n xi∈X⊆Rn为实例的特征向量, y i ∈ Y = { c 1 , c 2 , . . . , c K } y_i \in \mathcal{Y} =\{ c_1,c_2,...,c_K\} yi∈Y={c1,c2,...,cK}为实例的类别, i = 1 , 2 , . . . N i = 1,2,...N i=1,2,...N; 实例特征向量 x x x.
输出:实例 x x x所属类别 y y y
- 根据距离度量,在训练集 T T T中找到与 x x x最近邻的 k k k个点,涵盖这 k k k个点的邻域记作 N k ( x ) N_k(x) Nk(x)
- 在
N
k
(
x
)
N_k(x)
Nk(x)中根据分类决策规则(如多数表决)决定
x
x
x的类别
y
y
y
y = arg max c j ∑ x i ∈ N k ( x ) I ( y i = c j ) , i = 1 , 2 , . . . , N ; j = 1 , 2 , . . . , k y = \arg\underset{c_j}{\max} \sum_{x_i \in N_k(x)} I(y_i = c_j),i=1,2,...,N;j=1,2,...,k y=argcjmaxxi∈Nk(x)∑I(yi=cj),i=1,2,...,N;j=1,2,...,k
上式中, I I I为指示函数,当 y i = c j y_i = c_j yi=cj的时候, I I I为1,否则置0
3.2距离度量方法以及定义
- L p L_p Lp distance: 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(x_i,x_j) = (\sum^n_{l=1} {\left| x^{(l)}_i - x^{(l)}_j \right|}^p)^\frac{1}{p} Lp(xi,xj)=(l=1∑n∣∣∣xi(l)−xj(l)∣∣∣p)p1
- Euclidean distance: 欧式距离
L 2 ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 L_2(x_i,x_j) = (\sum^n_{l=1} {\left| x^{(l)}_i - x^{(l)}_j \right|}^2)^\frac{1}{2} L2(xi,xj)=(l=1∑n∣∣∣xi(l)−xj(l)∣∣∣2)21 - Manhattan distance: 曼哈顿距离
L 1 ( x i , x j ) = ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ L_1(x_i,x_j) = \sum^n_{l=1} {\left| x^{(l)}_i - x^{(l)}_j \right|} L1(xi,xj)=l=1∑n∣∣∣xi(l)−xj(l)∣∣∣
def calDis(x_1,x_2,dis_type = None):
"""
距离度量公式,包含2种,分别记为'E','M',E为欧式距离,M是曼哈顿距离
x_1,x_2为两个向量S
"""
distance = 0
if (dis_type == None)|(dis_type == 'E'):
distance = math.sqrt(sum((x_1-x_2)**2))
elif (dis_type == 'M'):
distance = sum(abs(x_1-x_2))
else:
print('WARNING: 函数calDis(self,x_1,x_2,dis_type)没有dis_type='+ str(dis_type) +' 这种距离度量方法,请重新定义dis_type=E或者M')
return distance
测试距离计算
import numpy as np
import math
a = np.array([1,2])
b = np.array([2,1])
print('欧式距离为:' ,calDis(a,b,'E'))
print('曼哈顿距离为:' , calDis(a,b,'M'))
欧式距离为: 1.4142135623730951
曼哈顿距离为: 2
3.3 常规遍历算法,并设计自己的KNN算法框架
这里使用遍历计算的方法直接及计算距离并找到最近的k个元素
data = np.array([[2,3],[5,4],[9,6],[4,7],[8,1],[7,2]])
data_label = np.array([1,1,0,1,0,0])
这里首先定义训练部分“train”,但是实质不是训练过程,这里用来保存模型的参数,即x,y,k,distance_type
class myKNN():
def train(self, x, y, k = None, dis_type = None):
model = []
self.x = x
self.y = y
if k == None:
self.k = 1
else:
self.k = k
if dis_type == None:
self.dis_type = 'E'
else:
self.dis_type = dis_type
model.append(self.x)
model.append(self.y)
model.append(self.k)
model.append(self.dis_type)
return model
def predict(self,x,model,dis_type = None):
if x.ndim == 1: # 如果是一个样本
x = x.reshape(1,len(x)) # 转化为1×n的张量样本
predicted_y = [] # 用于保存预测类别
for i in range(len(x)): # 预测x的每个元素
distance = [] # 保存x[j]与原有数据所有样本之间的距离
for j in range(len(model[0])): # 计算x[j]与原有数据所有样本之间的距离
distance.append(calDis(model[0][j],x[i],model[3]))
predicted_y_distance = np.column_stack((model[1],distance)) # 合并得到(y,distance)两列数据
# 排序,以下步骤实现argmax的那个步骤
sorted_y_distance = predicted_y_distance[
predicted_y_distance.argsort(0)[:,1]] # 对得到的(y,distance)按照第二列排序
knn_data = sorted_y_distance[:model[2]]
nearestky_list = knn_data[:,0].tolist()
count = {c:nearestky_list.count(c) for c in set(nearestky_list)}
y = sorted(count.keys())[0]
predicted_y.append(y)
return predicted_y
def accuracy(self,precited_y,real_y):
self.y = precited_y
self.realy = real_y
return 1-sum(np.sign(np.abs(self.y-self.realy)))/len(self.y)
测试:
mk = myKNN()
model1 = mk.train(data,data_label,k=3,dis_type='M') # 曼哈顿距离,k=3
predicted_y = mk.predict(data,model1)
print('使用曼哈顿距离得到的正确率:',mk.accuracy(predicted_y,data_label))
model2 = mk.train(data,data_label) # 欧式距离,k为默认值k=1
predicted_y = mk.predict(data,model2)
print('使用欧式距离得到的正确率:',mk.accuracy(predicted_y,data_label))
使用曼哈顿距离得到的正确率: 1.0
使用欧式距离得到的正确率: 1.0