K-邻近算法(KNN)详解+Python实现

1.什么是KNN算法?

KNN(K-Nearest Neighbor)算法是机器学习算法中最基础,最简单的算法之一。它既能用于分类,也能用于回归。KNN通过测量不同特征值的距离来进行分类。

k近邻算法简单,直观:对于一个需要预测的输入向量x,我们只需要在训练数据集中寻找k个与向量x最近的向量的集合,然后把x的类别预测为这k个样本中类别数最多的那一类。

1.1基于上述思想给出KNN算法过程

输入:训练数据集

输出:实例x所属的类y.

(1)根据给定的距离度量,在训练集T中找出与x最临近的k个点,涵盖这k个点的x的领域记作Nk(x)

(2)在Nk(x)中根据分类决策规则(如多数表决)决定x的类别y:

I为指示函数,即当yi=ci时I为1,否则I为0

k近邻法的特殊情况是k=1的情形,称为最近邻算法,对于输入的实例点(特征向量)x,最近邻法将训练数据集中与x最邻近点的类作为x的类。

2. k近邻模型

k近邻法使用的模型实际上对应于对特征空间的划分。模型主要由三个基本要素----距离度量,k值的选择和分类决策规则决定。

2.1 距离度量

特征空间中两个实例点的距离是两个实例点相似程度的反应的反应。使用的是欧式距离(即坐标轴中两点的距离),但也可以是其他距离,如更一般的Lp距离(Lpdistance)或Minkowski (Minkowski distance)

这里给出numpy的实现方法,便于下文代码理解

import numpy as np
# 求范数
dist1=np.lianlg.norm(x,ord=None,axis=None,keepdims=False)

①x: 表示矩阵(也可以是一维)

②ord:范数类型

矩阵的范数:

ord=1:列和的最大值

ord=2:计算A*A的转置矩阵的最大特征值的开平方 ord=∞:行和的最大值

ord=None:默认情况下,是求整体的矩阵元素平方和,再开根号。

③axis:处理类型

axis=1表示按行向量处理,求多个行向量的范数

axis=0表示按列向量处理,求多个列向量的范数

axis=None表示矩阵范数。

④keepding:是否保持矩阵的二维特性

True表示保持矩阵的二维特性,False相反

2.2 k值的选择

k值的选择会对k近邻法的产生重大影响.

如果选择较小的k值,就相当于用较小的领域中的训练实例进行预测,“学习”的近似误差会减小,只有与输入实例较近的(相似的)训练实例才会对预测结果起作用.但缺点是“学习”的估计误差会增大。预测结果会对近邻的实例点非常敏感.如果邻近点恰巧是噪声,预测就会出错.换句话说,k值的减小就意味着整体模型变得复杂,容易发生过拟合.

如果选择较大的k值,就相当于用较大领域中的训练实例进行预测,其优点是可以减小学习的估计误差,但缺点是学习的近似误差会增大,这时与输入实例较远的(不相似的)训练实例也会对预测起作用,使预测发生错误,k值的增大就意味着整体模型变得简单.

若果k=N,那么无论输入实例是什么,都将简单地预测它属于在训练实例中最多的类,这时,模型过于简单,完全忽略训练实例中的大量有用信息,是不可取的.

在应用中,k值一般取一个较小的数值,通常采用交叉验证法来选取最优的k的值.

注: 近似误差:可以理解为对现有训练集的训练误差。 估计误差:可以理解为对测试集的测试误差。 近似误差关注训练集,如果近似误差小了会出现过拟合的现象,对现有的训练集能有很好的预测,但是对未知的测试样本将会出现较大偏差的预测。模型本身不是最接近最佳模型。 估计误差关注测试集,估计误差小了说明对未知数据的预测能力好。模型本身最接近最佳模型。

2.3 分类决策规则

k近邻法中的分类决策规则往往是多数表决,既由输入实例的k个邻近的训练实例中的多数决定输入实例的类.

3. Python实现KNN

看了上面描述,是不是觉得手很痒,那就来手撸代码吧!

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 本例子训练数据采用sklearn提供的自带的数据集-莺尾花数据集
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from collections import Counter

class KNN:
    def __init__(self,X_train,y_train,n_neighbors=3,p=2):
        """
                parameter: n_neighbors 临近点个数
                parameter: p 距离度量
        "
""
        self.n = n_neighbors
        self.p = p
        self.X_train = X_train
        self.y_train = y_train
    def predict(self,X):
        # 取出n个点
        knn_list=[]
        for i in range(self.n):
            # 求范数,ord=2相当于欧式距离
            dist1=np.linalg.norm(X-self.X_train[i],ord=self.p,keepdims=False)
            knn_list.append((dist1,self.y_train[i]))
        # 扫描剩余数据,如果有距离小于所选k个测试数据则进行替换
        for i in range(self.n,len(self.X_train)):
            # 寻找k个测试数据中距离最大的数据索引
            max_index=knn_list.index(max(knn_list,key=lambda x:x[0]))
            dist1=np.linalg.norm(X-self.X_train[i],ord=self.p)
            # 判断是否需要进行替换
            if knn_list[max_index][0]>dist1:
                knn_list[max_index] = (dist1,self.y_train[i])
        # 统计
        knn1 = [k[-1] for k in knn_list]
        # 以字典的方式统计目标值出现的次数,即y_train:次数
        count_pairs = Counter(knn1)
        # 将字典转换为元祖,并根据值进行排序,得到的是一个列表包含n个有序元祖,然后取排序后最后一个元祖首元素
        # 即出现次数最多的y_train
        max_count=sorted(count_pairs.items(),key=lambda x:x[1])[-1][0]
        return max_count
    # 利用测试数据进行测试并统计预测正确的次数
    def score(self,X_test,y_test):
        # 对预测正确的数进行统计
        right_count=0
        for x,y in zip(X_test,y_test):
            result=self.predict(x)
            if result==y:
                right_count+=1
        return right_count/len(X_test)

if __name__=='__main__':
    # 对数据进行简单的处理,主要是运用pandas一些特性,可以参考我的另一篇博文
    iris = load_iris()
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['label'] = iris.target
    df.columns = ['sepal length''sepal width''petal length''petal width''label']
    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)
    knn=KNN(X_train,y_train)
    # 打印测试数据的正确率
    print("测试数据的正确率为:"+knn.score(X_test,y_test))
    
    test_point=[5.1,3.5]
    # 画出输入实例所在的位置
    plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
    plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
    plt.plot(test_point[0], test_point[1], 'bo',color='r', label='test_point')
    plt.xlabel('sepal length')
    plt.ylabel('sepal width')
    plt.legend()
    plt.show()

4. 总结

我们可以用一句成语来总结k近邻算法的思想: “近朱者赤,近墨者黑”,由你的邻居来推断出你的类别。最后提一点KNN算法没有显示的学习过程,即不用训练数据.

行业应用: 客户流失预测、欺诈侦测等(更适合于稀有事件的分类问题)

如果你觉得这篇文章有收获就给我点个赞吧!

下期预告:朴素贝叶斯算法

猜你喜欢

转载自blog.csdn.net/justorderman/article/details/107862186
今日推荐