k近邻算法程序实现



算法原理

模型

k近邻算法是一个多分类的机器学习算法。它的实现过程很容易理解,简单来说,就是通过计算待分类样本与所有训练样本的“距离”,然后取出离待分类样本最近的k个训练样本,最后将这k个训练样本中类别最多的一种作为该待分类样本的预测类别。

图解

如图所示,假设有若干个训练样本,不同颜色代表不同的类别。

现在放入一个待分类样本。

定义k值(假设为3),定义距离(假设是欧式距离),计算该待分类样本与所有训练样本的距离。

选取距离最小的k个训练样本。

将这k个训练样本中出现最多的那个类别作为该待分类样本的预测类别。此时图上待分类样本的预测类别是红色。

具体步骤

1、已知训练集 { x i , y i } i = 1 n \{x_i,y_i\}_ {i=1}^n {xi,yi}i=1n,训练集的每个样本包含特征 x i x_i xi和类别 y i y_i yi,设定k值,定义样本间的距离计算公式(这里的距离需要自己定义,可以是曼哈顿距离,欧式距离等等)。

2、对于每个特征为 a a a的待分类样本 A A A,计算其与所有训练样本的距离
d = d ( a , x i ) , i = 1 , 2 , . . . , n d=d(a,x_i),i=1,2,...,n d=d(a,xi),i=1,2,...,n

3、取出距离 d d d最近的k个训练样本
( x i 1 , y i 1 ) , ( x i 2 , y i 2 ) , . . . , ( x i k , y i k ) (x_{i1},y_{i1}),(x_{i2},y_{i2}),...,(x_{ik},y_{ik}) (xi1,yi1),(xi2,yi2),...,(xik,yik)
{ y i 1 , y i 2 , . . . , y i k } \{y_{i1},y_{i2},...,y_{ik}\} {yi1,yi2,...,yik}中出现次数最多的类别作为待分类样本 A A A的预测类别。

4、重复执行2、3步直至所有待分类样本预测完毕。

程序实现

初始化函数

def __init__(self, k=10):
    """
    k         最近邻个数
    """
    self.k = k

训练函数

def fit(self, X, y):
    self.X = X
    self.y = y

训练函数只需存储样本的特征和类别,不需要进行计算,这是knn的一个优点。

预测函数

def predict(self, X):
    res = []
    for x in X:
        # 计算平方和
        sum = np.sum((x-self.X)**2, axis=1)
        num = np.argsort(sum)[:self.k]
        pred_y = np.argmax(np.bincount(self.y[num]))
        res.append(pred_y)
    return np.array(res)

因为需要计算每个待分类样本与每个训练样本的距离,所以虽然实现过程简单,但计算比较耗时。

整合全部代码

class KNeighborsClassifier():
    def __init__(self, k=10):
        """
        k         最近邻个数
        """
        self.k = k
        
    def fit(self, X, y):
        self.X = X
        self.y = y
    
    def predict(self, X):
        res = []
        for x in X:
            # 计算平方和
            sum = np.sum((x-self.X)**2, axis=1)
            num = np.argsort(sum)[:self.k]
            pred_y = np.argmax(np.bincount(self.y[num]))
            res.append(pred_y)
        return np.array(res)

实例化演示

导入相关库并查看数据分布。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

iris = load_iris()
X = iris.data[:100]
y = iris.target[:100]

train_x,test_x,train_y,test_y = train_test_split(X, y, test_size=0.3, random_state=1)

color = ['red','yellow']
plt.rcParams['font.sans-serif'] = ['SimHei'] 
for i in range(2):
    plt.scatter(train_x[train_y==i,0], train_x[train_y==i,1], color=color[i])

plt.scatter(test_x[:,0], test_x[:,1],color='silver')
plt.legend([1,2,'待分类样本'])


实例化函数并预测样本类别。

clf = KNeighborsClassifier(k=20)
clf.fit(train_x, train_y)
pred_y = clf.predict(test_x)
accuracy_score(test_y, pred_y)

进一步

k值是一个超参数,k值的大小决定了模型的好坏,可以通过k折交叉验证来选取适合的k值,注意k折交叉的k值和k近邻的k值不是同一个意思,只是刚好都这么叫而已。这里采用5折交叉验证,主要步骤是将训练集均分为5份小训练集,取出其中的4份用于knn分类器的训练,剩余的1份用来验证模型的效果,可以得到模型的准确率。不同的取法会得到不一样的结果,对于每个k值都将计算得到5个模型的准确率,我们将在重新生成的二分类样本集上进行操作。

from sklearn.model_selection import KFold
from sklearn.datasets import make_classification
X,y = make_classification(n_samples=300, n_features=5, shuffle=True, random_state=6, n_classes=2) 

color = ['red','yellow']
for i in range(2):
    plt.scatter(X[y==i,0], X[y==i,1], color=color[i])


进行5折交叉验证,选出最优的k值。

kf = KFold(n_splits=5)

accuracy = []
for i in range(1,40):
    acc = []
    for train_index, test_index in kf.split(X):
        train_x,train_y = X[train_index],y[train_index]
        test_x,test_y = X[test_index],y[test_index]
        clf = KNeighborsClassifier(k=i)
        clf.fit(train_x,train_y)
        pred_y = clf.predict(test_x)
        acc.append(sum(pred_y==test_y)/len(pred_y))
    accuracy.append(acc)
        
accuracy = np.array(accuracy)
m,n = accuracy.shape
for i in range(n):
    plt.scatter(range(1,m+1),accuracy[:,i])
plt.errorbar(range(1,m+1),accuracy.mean(axis=1),accuracy.std(axis=1))


从图上可以看出,当参数k取5的时候,模型的效果最好。

在数据量还不是很大的情况下,可以使用上述的k折交叉验证或网格搜索来寻找最优的k值;但当数据量很大时,采用这些方法,虽然可以实现,但是同时计算时间也会很长,要慎重使用。

以上便是k近邻算法的代码实现,如有不对之处,欢迎交流。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值