KNN算法及其实现(学习记录)

KNN算法的基本概念        

        KNN(K-NearestNeighbor)即K近邻算法,是一种基本的分类和回归算法,它基于实例之间的距离来进行分类或回归预测。其基本思想是:对于一个未知样本,通过计算其与训练样本集中的每个样本之间的距离,选取距离最近的K个样本,根据这K个样本的标签进行投票或加权投票,将得票最多的标签作为未知样本的预测标签。 KNN算法的输入为实例的特征向量,对应与特征空间的点;输出为实例的类别,可以取多类。

算法实现过程

        KNN算法的实现过程如下:

  1. 准备数据集:收集带有标签的训练数据集和待分类或回归的数据点。
  2. 计算距离:对于待分类或回归的数据点,计算它与训练数据集中每个点的距离,常用的距离度量方法有欧氏距离、曼哈顿距离等。
  3. 选择K个最近邻:根据距离选择离待分类或回归数据点最近的K个训练数据点。
  4. 进行投票或平均:对于分类问题,K个最近邻中的样本投票决定待分类数据点的类别;对于回归问题,K个最近邻中的样本取平均值作为待回归数据点的预测值。
  5. 输出结果:将待分类数据点的类别或待回归数据点的预测值作为输出结果。

三个基本要素:k 值的选择、距离度量及分类决策规则。

        常用的距离度量是欧氏距离及更一般的pL距离。(除了欧式距离,还有切比雪夫距离,闵科夫斯基距离,曼哈顿距离,余弦距离等算法用于计算距离度量。)

        k值小时,k近邻模型更复杂,容易发生过拟合;即模型过于拟合数据,导致泛用性降低的现象。k值大时,k近邻模型更简单,又容易欠拟合。即模型对于数据特征的描述不够详细。因此k值得选择会对分类结果产生重大影响。k值的选择反映了对近似误差与估计误差之间的权衡,通常由交叉验证选择最优的k。

KNN算法的优点

KNN算法的优点是简单、易于理解和实现,适用于多分类和回归问题。且思想简单、效果强大。
,天然可解决多分类问题。重新训练的代价较低。

KNN算法的不足

KNN算法作为一种较简单的算法,存在不足之处:

        时间复杂度较高,如果训练集有m个样本,n个特征,则预测每一个新的数据,计算复杂度O(m*n)

        没有明显的训练过程,它是 "懒惰学习"的典型代表,它在训练阶段所做的仅仅是将样本保存起来,如果训练集很大,必须使用大量的存储空间,训练时间开销为零

        KNN必须对每一个测试点来计算到每一个训练数据点的距离, 并且这些距离点涉及到所有的特征,当数据的维度很大,数据量也很大的时候,KNN的计算会成为诅咒,形成维度灾难

具体实现

实现一个KNN算法

from sklearn import datasets
from collections import Counter  # 为了做投票
from sklearn.model_selection import train_test_split
import numpy as np

# 导入iris数据
iris = datasets.load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=2003)


def euc_dis(instance1, instance2):
    """
    计算两个样本instance1和instance2之间的欧式距离
    instance1: 第一个样本, array型
    instance2: 第二个样本, array型
    """
    # TODO
    dist = np.sqrt(np.sum((instance1-instance2)**2))
    return dist
    
    
def knn_classify(X, y, testInstance, k):
    """
    给定一个测试数据testInstance, 通过KNN算法来预测它的标签。 
    X: 训练数据的特征
    y: 训练数据的标签
    testInstance: 测试数据,这里假定一个测试数据 array型
    k: 选择多少个neighbors? 
    """
    # TODO  返回testInstance的预测标签 = {0,1,2}

    # 计算 testInstance 与 X的距离
    dists=[euc_dis(x,testInstance) for x in X]
   
    # 找出最近的K个元素的idx
    idxknn= np.argsort(dists)[:k] #将dists从小到大排序,返回排序后的元素indices

    # 找出KNN对应的n个y值
    yknn=y[idxknn]

    # 返回投票结果
    return Counter(yknn).most_common(1)[0][0]


# 预测结果。    
predictions = [knn_classify(X_train, y_train, data, 3) for data in X_test]
correct = np.count_nonzero((predictions==y_test)==True)
print ("Accuracy is: %.3f" %(correct/len(X_test)))

K近邻的决策边界以及决策边界的python可视化实现 

        决策边界寻找:把平面均匀的分为很多像素点,按KNN算法依次标记每一个像素点的类别,标记完之后,决策边界就出来了。

        决策边界的可视化实现

        导入numpy以及pyplot模块,还有KNN模块。这里有个模块叫做product,这主要是用在可视化模块。

import matplotlib.pyplot as plt    
import numpy as np    
from itertools import product    
from sklearn.neighbors import KNeighborsClassifier 
          生成随机样本 
# 生成一些随机样本    
n_points = 100    
X1 = np.random.multivariate_normal([1,50], [[1,0],[0,10]], n_points)    
X2 = np.random.multivariate_normal([2,50], [[1,0],[0,10]], n_points)    
X = np.concatenate([X1,X2])    
y = np.array([0]*n_points + [1]*n_points)    
print (X.shape, y.shape)  
训练多个KNN模型 
# KNN模型的训练过程    
clfs = []    
neighbors = [1,3,5,9,11,13]    
for i in range(len(neighbors)):    
    clfs.append(KNeighborsClassifier(n_neighbors=neighbors[i]).fit(X,y))   
# 可视化结果    
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1    
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1    
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1), 
                     np.arange(y_min, y_max, 0.1)) 
f, axarr = plt.subplots(2,3, sharex='col', sharey='row', figsize=(15, 12))   
for idx, clf, tt in zip(product([0, 1, 2], [0, 1, 2]), clfs,['KNN (k=%d)'%k for k in neighbors]):   
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) 
    Z = Z.reshape(xx.shape)    
    axarr[idx[0], idx[1]].contourf(xx, yy, Z, alpha=0.4)    
    axarr[idx[0], idx[1]].scatter(X[:, 0], X[:, 1], c=y, s=20, edgecolor='k') 
    axarr[idx[0], idx[1]].set_title(tt)    
plt.show()    
          结果展示

         从结果我们很容易看出K值的增加会导致决策边界越来越平。但是决策边界越平并不意味着决策越好,所以我们得通过另外一种方式找到最佳k值。

用交叉验证选择超参数K

        交叉验证的第一步是把训练数据进一步分成训练集和验证集。常用的交叉验证技术叫做K折交叉验证(K-fold Cross Validation)。 我们先把训练数据再分成训练集和验证集,之后使用训练集来训练模型,然后再验证集上评估模型的准确率。举个例子,比如一个模型有个参数叫α,我们一开始不清楚要选择0.1还是1,所以这时候我们进行了交叉验证:把所有训练集分成K块,依次选其中一块作为验证集,然后求在K个验证集上的性能平均值作为评估α值的性能。

 实现KNN的交叉验证代码

        一般情况下数据量较少的时候我们取的K值会更大,因为数据量较少的时候如果每次留出比较多的验证数据,对于训练模型本身来说是比较吃亏的,所以这时候我们尽可能使用更多的数据来训练模型。由于每次选择的验证数据量较少,这时候K折中的K值也会随之而增大,但到最后可以发现,无论K值如何选择,用来验证的样本个数都是等于总样本个数。

        最极端的情况下,我们可以采用leave_one_out交叉验证,也就是每次只把一个样本当做验证数据,剩下的其他数据都当做是训练样本。(留一法)

import numpy as np
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import KFold  #  主要用于K折交叉验证

# 以下是导入iris数据集
iris = datasets.load_iris()
X = iris.data
y = iris.target
print (X.shape, y.shape)

# 定义我们想要搜索的K值(候选集),这里定义8不同的值
ks = [1,3,5,7,9,11,13,15]

# 进行5折交叉验证, KFold返回的是每一折中训练数据和验证数据的index
# 假设数据样本为: [1,3,5,6,11,12,43,12,44,2], 总共10个样本
# 则返回的kf的格式为(前面的是训练数据,后面的是验证集):
# [0,1,3,5,6,7,8,9], [2,4]
# [0,1,2,4,6,7,8,9], [3,5]
# [1,2,3,4,5,6,7,8], [0,9]
# [0,1,2,3,4,5,7,9], [6,8]
# [0,2,3,4,5,6,8,9], [1,7]
kf = KFold(n_splits = 5, random_state=2001, shuffle=True)

# 保存当前最好的K值和对应的准确率值
best_k = ks[0]
best_score = 0

# 循环每一个K值

for k in ks:
    curr_score = 0
    for train_index, valid_index in kf.split(X):    
        # 每一折的训练以及计算准确率
        clf = KNeighborsClassifier(n_neighbors=k)
        clf.fit(X[train_index], y[train_index])
        curr_score = curr_score + clf.score(X[valid_index], y[valid_index])
    # 求一下5折的平均准确率
    avg_score = curr_score/5 
    if avg_score > best_score:
        best_k = k
        best_score = avg_score
    print ("current best score is: %.2f"%best_score, "best k: %d"%best_k)
print ("after cross validation, the final best k is: %d"%best_k)

实现K折交叉验证 

from sklearn.model_selection import GridSearchCV # 通过网格方式来搜索参数
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier

# 导入iris是数据
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 设置需要搜索的K值, 'n_neighbors'是sklearn中KNN的参数
parameters = {'n_neighbors':[1,3,5,7,9,11,13,15]}
knn = KNeighborsClassifier()  # 注意:在这里不用指定参数

# 通过GridSearchCV来搜索最好的K值。 这个模块的内部其实
# 就是对于每一个K值做了评估
clf = GridSearchCV(knn, parameters, cv=5)   
clf.fit(X, y)

# 输出最好的参数以及对应的准确率
print ("best score is: %.2f"%clf.best_score_, "  best param: ",clf.best_params_)

得到结论:最佳k值为7 

总结 

  1. KNN 算法原理较为简单
  2. 适合低维空间问题(a.高维特征中不相干的特征会降低重要特征对距离的影响;b.维度越高,算法时间复杂度越高。)
  3. KNN算法本质上没有训练过程
  4. KNN预测过程时间复杂度与样本个数线性相关。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: knn算法(k-近邻算法)是一种分类和回归的算法,被广泛应用于模式识别、数据挖掘、图形识别等领域。其原理是根据数据本身的特征,将测试集中的每一个样本按照特征相似度进行分类。其中,k的值表示选择多少个最相似的邻居作为判断依据,通常采用欧氏距离来计算相似度。 在knn算法实现过程中,需要先将数据集分为训练集和测试集。接着,通过计算测试集中每一个样本与训练集中每一个样本的欧氏距离(或曼哈顿距离等),选择距离最近的k个邻居。最后,采用“多数表决”的方式选择样本类别,并将该类别赋给测试集中的样本。 在Python中,可以使用scikit-learn库实现knn算法。以下是一个简单的代码示例: from sklearn.neighbors import KNeighborsClassifier # 创建训练集和测试集 train_x = [[0], [1], [2], [3]] train_y = [0, 0, 1, 1] test_x = [[1.5]] # 创建knn分类器(k=2) knn = KNeighborsClassifier(n_neighbors=2) # 拟合模型 knn.fit(train_x, train_y) # 进行预测 print(knn.predict(test_x)) 以上代码中,第一行引用了scikit-learn库下的KNeighborsClassifier类,用于创建一个knn分类器。接着,分别创建了训练集和测试集,并针对训练集中的两类样本对应标签进行了标记。接下来,创建k值为2的knn分类器,并使用fit()方法对训练集进行拟合。最后,通过predict()方法进行实际的预测,并输出测试样本的分类结果。 总体来说,knn算法是一种简单易用的分类和回归算法,具有可解释性强、不受算法实现形式的特点,同时能够适应各种数据类型和特征。在Python中,采用scikit-learn库实现knn算法也非常方便。 ### 回答2: KNN算法是一种基于实例的学习方法,通过计算样本之间的距离来确定新样本的类别。KNN算法是一种简单而有效的分类方法,尤其适用于小数据集。算法原理是基于这样一种思想:样本空间中的每个样本都可以用它最近的K个邻居来代表。其中K是一个正整数,是预定的参数。当K=1时,为最近邻分类算法,即只考虑最近的一个邻居。 具体实现步骤: 1.读入数据集,并将其分为训练集和测试集。 2.对数据集进行归一化处理。 3.对每个测试实例,计算其与训练集中所有实例之间的距离。 4.按照距离的大小降序排列。 5.选取前K个距离最小的实例,得到它们所属的类别中出现次数最多的那个类别作为该测试实例的预测类别。 6.计算预测结果与实际结果的差异。 在Python中实现KNN算法需要用到一些基本的库:Numpy和Scikit-learn。具体步骤如下: 1.导入Numpy库。 2.导入数据集并将其分为训练集和测试集。 3.使用Scikit-learn库中的MinMaxScaler函数进行数据归一化处理。 4.使用Scikit-learn库中的KNeighborsClassifier函数进行训练,设定参数k和metric。 5.使用Scikit-learn库中的predict函数进行预测,得到预测结果。 6.计算预测结果与实际结果的差异,得到预测准确率。 KNN算法的优点是简单易懂,精度高;缺点是计算复杂度高,对数据的大小敏感。当数据维度较高时,其计算复杂度会变得极高,而且KNN算法对数据的距离非常敏感,如果数据特征选取不当,会导致预测精度大幅下降。因此,在使用KNN算法的时候需要注意数据特征的选取和K值的选择。 ### 回答3: K近邻(k-NN)算法是最简单的基于实例的学习算法之一,它的主要思想是使用距离度量来对特征空间中的样本进行分类。KNN算法中的K代表选择邻居的数量,邻居是指在训练集中与测试数据距离最近的样本点。KNN算法的基本步骤如下: 1. 计算测试数据与所有训练数据之间的距离。 2. 根据距离度量,对距离最近的K个样本进行投票。 3. 根据投票结果,决定测试数据属于哪一类别。 KNN算法的优点是简单易用,能够处理多分类和回归问题;缺点是计算量大,对训练数据敏感,需要进行归一化处理,并需要选择合适的距离度量和K值。 Python实现KNN算法需要使用Scikit-learn或Numpy等相关库。下面给出一个简单的Python代码实现,该代码实现了一个基于欧氏距离的KNN分类器: ``` import numpy as np from sklearn.neighbors import KNeighborsClassifier # 生成训练数据 X_train = np.array([[1, 2], [3, 4], [5, 6], [7, 8]]) y_train = np.array([0, 0, 1, 1]) # 创建KNN分类器,选择K=3 clf = KNeighborsClassifier(n_neighbors=3) # 训练分类器 clf.fit(X_train, y_train) # 测试数据,预测其所属类别 X_test = np.array([[2, 3], [4, 5], [6, 7]]) y_test = clf.predict(X_test) print(y_test) ``` 该代码中,通过Numpy库生成了一个4个样本点的训练数据集,其中前两个样本属于类别0,后两个样本属于类别1。同时,也生成了3个测试数据点。然后使用Scikit-learn库中的KNN分类器,在训练数据上训练模型,选择K=3。最后,对测试数据进行分类,并输出分类结果。 以上就是KNN算法的基本原理和Python实现,希望对读者有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值