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
    评论
KNN(K-Nearest Neighbors)算法是一种常用的机器学习算法,用于分类和回归问题。它的基本思想是通过计算待分类样本与训练集中各个样本之间的距离,选取距离最近的K个样本作为邻居,然后根据邻居的标签进行分类或回归预测。 关于KNN算法的Verilog实现,需要明确一点:Verilog是一种硬件描述语言,主要用于数字电路设计和硬件描述。而KNN算法是一种软件算法,通常使用编程语言(如Python)来实现。 如果你想在FPGA或ASIC等硬件平台上实现KNN算法,可以使用Verilog来描述硬件电路,但需要先将KNN算法转化为硬件电路的形式。这个过程需要对KNN算法进行适当的优化和硬件化设计。 以下是一个简单的KNN算法的Verilog实现示例: ```verilog module knn ( input [N-1:0] test_sample, input [M-1:0] train_samples [K-1:0], input [M-1:0] train_labels [K-1:0], output reg [L-1:0] predicted_label ); reg [N-1:0] distances [K-1:0]; reg [L-1:0] labels_count [L-1:0]; always @(*) begin // 计算测试样本与训练样本之间的距离 for (int i = 0; i < K; i = i + 1) begin distances[i] = calculate_distance(test_sample, train_samples[i]); end // 统计最近的K个邻居中各个标签的数量 for (int i = 0; i < L; i = i + 1) begin labels_count[i] = 0; end for (int i = 0; i < K; i = i + 1) begin labels_count[train_labels[i]] = labels_count[train_labels[i]] + 1; end // 根据邻居的标签数量选择预测标签 predicted_label = select_label(labels_count); end function [N-1:0] calculate_distance; // 计算距离的逻辑实现 endfunction function [L-1:0] select_label; // 选择预测标签的逻辑实现 endfunction endmodule ``` 请注意,上述Verilog代码只是一个简化的示例,实际的KNN算法实现可能更加复杂。在实际应用中,还需要考虑硬件资源、时序约束等因素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值