KNN(k近邻算法)最最最全面总结

KNN做回归和分类的主要区别在于最后做预测时候的决策方式不同。KNN做分类预测时,一般是选择多数表决法,即训练集里和预测的样本特征最近的K个样本,预测为里面有最多类别数的类别。而KNN做回归时,一般是选择平均法,即最近的K个样本的样本输出的平均值作为回归预测值。

一 KNN算法概述
KNN(K-Nearest Neighbor)工作原理:存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类对应的关系。输入没有标签的数据后,将新数据中的每个特征与样本集中数据对应的特征进行比较,提取出样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k近邻算法中k的出处,通常k是不大于20的整数。最后选择k个最相似数据中出现次数最多的分类作为新数据的分类。
KNN算法的一般流程:
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。

二 KNN算法
1 不使用库函数实现手写数字识别

**import  numpy as np
from os import listdir
import operator
# k-近邻算法,采用欧氏距离
def classify0(inX,dataSet,labels,k):
    # 计算距离
    dataSetSize=dataSet.shape[0]
    diffMat=np.tile(inX,(dataSetSize,1))-dataSet
    sqDiffmat=diffMat**2
    sqDistances=sqDiffmat.sum(axis=1)
    distances=sqDistances**0.5
    # 排序,并选择k个最小的距离
    sortedDistance=distances.argsort()
    disCount={}
    for i in range(k):
        votelLabel=labels[sortedDistance[i]]
        disCount[votelLabel]=disCount.get(votelLabel,0)+1
    sortedClassCount=sorted(disCount.items(), key=operator.itemgetter(1), reverse=True)
    # 返回频率最高的标签
    return sortedClassCount[0][0]
# 将32*32的二进制图片转化为1*1024的向量
def img2vex(filename):
    returnVect = np.zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect
# 手写数字识别系统的测试代码
def handwritingClassTest():
    hwLabels=[]
    trainListDir=listdir(r"D:\python\机器学习实战代码\machinelearninginaction\Ch02\digits\trainingDigits")
    m=len(trainListDir)
    trainingMax=np.zeros((m,1024))
    for i in range(m):
        filenameStr=trainListDir[i]
        # 用.分隔开,取第一个
        fileStr=filenameStr.split('.')[0]
        classNumStr=int(fileStr.split('_')[0])
        hwLabels.append(classNumStr)
        trainingMax[i,:]=img2vex(r"D:\python\机器学习实战代码\machinelearninginaction\Ch02\digits\trainingDigits\%s.txt"%fileStr)
#     测试
    testListDir=listdir(r"D:\python\机器学习实战代码\machinelearninginaction\Ch02\digits\testDigits")
    mTest=len(testListDir)
    errorCount=0
    for i in range(mTest):
        filenameStr=testListDir[i]
        fileStr = filenameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        vectorUnderTest=img2vex(r"D:\python\机器学习实战代码\machinelearninginaction\Ch02\digits\testDigits\%s.txt"%fileStr)
        classifyResult=classify0(trainingMax,vectorUnderTest,hwLabels,3)
        print("The classifier came back with ;%d,the real result is:%d"%(classifyResult,classNumStr))
        if(classifyResult!=classNumStr):
            errorCount+=1.0
    print("The total error count is:%d"%errorCount)
    print("The total error rate is:%f"%(errorCount/float(mTest)))
def main():
    handwritingClassTest()
if __name__ == '__main__':
    main()**

输出结果: The total error count is:10 The total error rate is:0.010571

2 利用sklearn中KNN算法实现鸢尾花分类

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
def get_iris_data():
    iris = load_iris()
    iris_data = iris.data
    iris_target = iris.target
    return iris_data,iris_target
def run():
    iris_data, iris_target = get_iris_data()
    # 分割验证集和测试集
    x_train,x_test,y_train,y_test=train_test_split(iris_data,iris_target,test_size=0.25)
    # 归一化处理
    std=StandardScaler()
    x_train=std.fit_transform(x_train)
    x_test=std.transform(x_test)
    knn=KNeighborsClassifier(n_neighbors=5)
    knn.fit(x_train,y_train)
    y_predict=knn.predict(x_test)
    print(y_predict)
    labels = ["山鸢尾", "虹膜锦葵", "变色鸢尾"]
    for i in range(len(y_predict)):
        print("第%d次测试:真实值:%s\t预测值:%s" % ((i + 1), labels[y_predict[i]], labels[y_test[i]]))
    print("准确率:", knn.score(x_test, y_test))

输出

准确率: 0.9210526315789473

三 K-近邻算法优缺点

优点:
1)简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归;
2)可用于数值型数据和离散型数据;
3)训练时间复杂度为O(n);无数据输入假定;
4)对异常值不敏感。
缺点:
1)计算复杂性高;
2)必须保存全部的数据,空间复杂性高;
3)样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少),此时的预测偏差会比较大

四 K-近邻算法的优化

KNN算法的缺陷(1) 样本不均衡
图
通过KNN算法,我们显然可以得到X应属于红点,但对于样本Y,通过KNN算法我们似乎得到了Y应属于蓝点的结论,而这个结论直观来看并没有说服力。

解决方法
1)距离加权加权
将距离该样本距离小的邻居权值大,距离该样本距离大的邻居权值则相对较小,由此,将距离远近的因素也考虑在内,避免因一个样本数量过多导致误判的情况。
权值加权:为每个点的距离增加一个权重,使得距离近的点可以得到更大的权重,在此描述如何加权。
A.反函数
  该方法最简单的形式是返回距离的倒数,比如距离d,权重1/d。有时候,完全一样或非常接近的商品权重会很大甚至无穷大。基于这样的原因,在距离求倒数时,在距离上加一个常量:
  weight = 1 / (distance + const)
  这种方法的潜在问题是,它为近邻分配很大的权重,稍远一点的会衰减的很快。虽然这种情况是我们希望的,但有时候也会使算法对噪声数据变得更加敏感。

B.高斯函数
高斯函数比较复杂,但克服了前述函数的缺点,其形式:
高斯函数
高斯函数的图形在形状上像一个倒悬着的钟。a是曲线的高度,b是曲线中心线在x轴的偏移,c是半峰宽度(函数峰值一半处相距的宽度)。
图像

def gaussian(dist, a=1, b=0, c=0.3):
return a * math.e ** (-(dist - b) ** 2 / (2 * c ** 2))

上面的高斯函数在距离为0的时候权重为1,随着距离增大,权重减少,但不会变为0。下图是高斯函数和其它几个函数的区别,其它函数在距离增大到一定程度时,权重都跌至0或0以下。
加权kNN首先获得经过排序的距离值,再取距离最近的k个元素。
 1.在处理离散型数据时,将这k个数据用权重区别对待,预测结果与第n个数据的label相同的概率:
   3
 2.在处理数值型数据时,并不是对这k个数据简单的求平均,而是加权平均:通过将每一项的距离值乘以对应权重,让后将结果累加。求出总和后,在对其除以所有权重之和。
   2
 Di代表近邻i与待预测值x的距离,Wi代表其权重,f(x)是预测的数值型结果。每预测一个新样本的所属类别时,都会对整体样本进行遍历,可以看出kNN的效率实际上是十分低下的。

KNN算法的缺陷(2) 计算量太大
第一个是需要存储全部的训练样本,
第二个是计算量较大,因为对每一个待分类的样本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。
解决办法
构造kd树:kd树是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。kd树是是一种二叉树,表示对k维空间的一个划分,构造kd树相当于不断地用垂直于坐标轴的超平面将K维空间切分,构成一系列的K维超矩形区域。kd树的每个结点对应于一个k维超矩形区域。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。
构造kd-tree
输入:多维空间数据集
输出:kd树
(1)开始:构造根结点。选择X为切分坐标轴,以所有实例X坐标的中位数为切分点,将根结点对应的区域切分为左、右两个子区域。左子结点对应坐标小于切分点的子区域,右子结点对应于坐标大于切分点的子区域。将落在切分超平面上的实例点保存在根结点。
(2)重复。对左右子区域,轮换切分坐标轴,继续以坐标轴对应坐标的中位数为切分点。直到所有的点都切分完毕。

四 K-近邻算法的使用场景
文本分类,多分类领悟,模式识别

  • 17
    点赞
  • 120
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值