统计学习方法中算法实现(基于Python)--- KNN

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,xjX, 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=1nxi(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树

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Systemd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值