k近邻法
1. k k k近邻法是基本且简单的分类与回归方法。 k k k近邻法的基本做法是:对给定的训练实例点和输入实例点,首先确定输入实例点的 k k k个最近邻训练实例点,然后利用这 k k k个训练实例点的类的多数来预测输入实例点的类。
2. k k k近邻模型对应于基于训练数据集对特征空间的一个划分。 k k k近邻法中,当训练集、距离度量、 k k k值及分类决策规则确定后,其结果唯一确定。
3. k k k近邻法三要素:距离度量、 k k k值的选择和分类决策规则。常用的距离度量是欧氏距离及更一般的pL距离。 k k k值小时, k k k近邻模型更复杂; k k k值大时, k k k近邻模型更简单。 k k k值的选择反映了对近似误差与估计误差之间的权衡,通常由交叉验证选择最优的 k k k。
常用的分类决策规则是多数表决,对应于经验风险最小化。
4. k k k近邻法的实现需要考虑如何快速搜索k个最近邻点。kd树是一种便于对k维空间中的数据进行快速检索的数据结构。kd树是二叉树,表示对 k k k维空间的一个划分,其每个结点对应于 k k k维空间划分中的一个超矩形区域。利用kd树可以省去对大部分数据点的搜索, 从而减少搜索的计算量。
距离度量
设特征空间 x x x是 n n n维实数向量空间 , x i , x j ∈ X x_{i}, x_{j} \in \mathcal{X} xi,xj∈X, x i = ( x i ( 1 ) , x i ( 2 ) , ⋯   , x i ( n ) ) T x_{i}=\left(x_{i}^{(1)}, x_{i}^{(2)}, \cdots, x_{i}^{(n)}\right)^{\mathrm{T}} xi=(xi(1),xi(2),⋯,xi(n))T, x j = ( x j ( 1 ) , x j ( 2 ) , ⋯   , x j ( n ) ) T x_{j}=\left(x_{j}^{(1)}, x_{j}^{(2)}, \cdots, x_{j}^{(n)}\right)^{\mathrm{T}} xj=(xj(1),xj(2),⋯,xj(n))T ,则: x i x_i xi, x j x_j xj的 L p L_p Lp距离定义为:
L p ( x i , x j ) = ( ∑ i = 1 n ∣ x i ( i ) − x j ( l ) ∣ p ) 1 p L_{p}\left(x_{i}, x_{j}\right)=\left(\sum_{i=1}^{n}\left|x_{i}^{(i)}-x_{j}^{(l)}\right|^{p}\right)^{\frac{1}{p}} Lp(xi,xj)=(∑i=1n∣∣∣xi(i)−xj(l)∣∣∣p)p1
p
=
1
p= 1
p=1 曼哈顿距离
p
=
2
p= 2
p=2 欧氏距离
p
=
i
n
f
p= inf
p=inf 闵式距离minkowski_distance
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from collections import Counter
# 例 3.1 ,计算各个范式下的距离
def lp(x1,x2,p=2):
if len(x1) == len(x2) and len(x1)>1:
sum=0
for i in range(len(x1)):
sum+=(abs(x1[i]-x2[i]))**p
return sum**(1/p)
else:
return 0
x1 = [1, 1]
x2 = [5, 1]
x3 = [4, 4]
for i in range(1,5):
print('L'+str(i)+'(x1,x3) = '+str(lp(x1,x3,p=i)))
# out:
# L1(x1,x3) = 6.0
# L2(x1,x3) = 4.242640687119285
# L3(x1,x3) = 3.7797631496846193
# L4(x1,x3) = 3.5676213450081633
# data
iris=load_iris()
df=pd.DataFrame(data=iris.data,columns=iris.feature_names)
df['label']=iris.target
df.head()
# 划分训练集 测试集
data=np.array(df.iloc[:100,[0,1,-1]])
x,y=data[: , :-1],data[: , -1]
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2)
class KNN:
def __init__(self,x_train,y_train,n_neighbors=3,p=2):
"""
n_neighbors 选择的临近点的个数
p 距离度量时选择用什么范数
"""
self.n_neighbors=n_neighbors
self.p=p
self.x_train=x_train
self.y_train=y_train
def predict(self,x):
"""
knn 是一种惰性算法,没有显式的学习过程。也就是代码中没有 fit 方法
"""
# 找出距离最小的n个点
knn_list=[]
for i in range(self.n_neighbors):
distance=np.linalg.norm(x-self.x_train[i],ord=self.p) # 求范数
knn_list.append((distance,self.y_train[i]))
for i in range(self.n_neighbors,len(self.x_train)):
max_index=knn_list.index(max(knn_list,key=lambda x:x[0])) # 类似于sorted() 函数中key的用法
distance=np.linalg.norm(x-self.x_train[i],ord=self.p)
if distance < knn_list[max_index][0]:
knn_list[max_index]=(distance,y_train[i])
# 统计类别数
knn_labels=[cell[-1] for cell in knn_list]
count_labels=Counter(knn_labels)
max_count_label=sorted(count_labels.items(),key=lambda x:x[1])[-1][0]
return max_count_label
def score(self,x_test,y_test,n=10):
right_count=0
for x,y in zip(x_test,y_test):
label=self.predict(x)
if label == y:
right_count+=1
return right_count/len(x_test)
# 进行预测
clf=KNN(x_train,y_train,n_neighbors=3)
clf.score(x_test,y_test) # 准确率
sklearn中代码实现
from sklearn.neighbors import KNeighborsClassifier
clf_sk = KNeighborsClassifier()
clf_sk.fit(x_train, y_train)
clf_sk.score(x_test, y_test)
为了加快搜寻,可以使用KD树,环树搜寻法,环树搜寻法优于KD树