K近邻算法(KNN)(包含手写体识别、约会类型识别的代码)

简介: 是有监督学习、属于判别模型 、支持多分类以及回归、非线性、有预测函数、无优化目标、无优化求解算法。(算法地图)对应每个训练数据xi有对应的标签yi--监督学习;

K近邻:KNN-(k-nearest neighbor classification)

有监督学习、属于判别模型 、支持多分类以及回归非线性、有预测函数、无优化目标、无优化求解算法。(算法地图)

对应每个训练数据xi有对应的标签yi--监督学习;

优缺点:

优点:精度高、对异常值不敏感、无数据输入假定。

缺点:计算复杂度高、空间复杂度高。

适用数据范围:数值型和标称型。


简单描述预测过程:

假定其中的K值已经选定好,也即是模型已经确定

对于给定的训练数据点输入数据点,确定输入点的k个最近邻训练点,利用k个训练点的类的多数预测输入点的类

1.算法的抽象解释如下:

86f73909737ac50a5588f11c38a8be20_70.png

以上描述的三个要素

距离度量、K值选择、分类决策规则

1、距离度量

距离度量方式有:欧氏距离、曼哈顿距离等

2、K值选择

K值的选择一般采取交叉验证方法,也即是需要调参的地方【4】

k值过小(近似误差小,估计误差大)容易发生过拟合,k值增大(近似误差大,估计误差小)则整体模型变简单

3、分类决策规则

多数表决

2.明确的概念:

最近邻 、 K -最近邻 、 近似误差和估计误差 、交叉验证

(最近邻)相当于k=1,在所有的训练数据集中找出距离目标B最近距离的一个值A,并将它B归类为这个最近距离的值A的类别。

(K-近邻)是找到距离目标B最近的某一个范围内,例如k的范围内;判断k范围内的类别数,选择个数最多的某一个类别A作为目标B的类。

近似误差:理解为对现有训练集的训练误差; 估计误差:理解为对测试集的测试误差

交叉验证:简单交叉验证、k折交叉验证、留一交叉验证

3.算法实现:

1)创建简单的数据集:

import numpy as np
from collections import Counter
from draw import draw
class KNN:
    def __init__(self,X_train,y_train,k=3):   
        # 所需参数初始化   
        self.k=k             #所取k值  
        self.X_train=X_train  
        self.y_train=y_train  
    def predict(self,X_new):  
        # 计算欧氏距离  
        dist_list=[(np.linalg.norm(X_new-self.X_train[i],ord=2),self.y_train[i])
                   for i in range(self.X_train.shape[0])]
        #[(d0,-1),(d1,1)...]
        # 对所有距离进行排序
        dist_list.sort(key=lambda x: x[0])#以第0个元素升序排序 
        # 取前k个最小距离对应的类别(也就是y值)
        y_list=[dist_list[i][-1] for i in range(self.k)]
        # [-1,1,1,-1...]
        # 对上述k个点的分类进行统计
        y_count=Counter(y_list).most_common()#提取每个元素出现的次数
        # [(-1, 3), (1, 2)]
        return y_count[0][0]#返回最大的次数
def main():
    # 训练数据
    X_train=np.array([[5,4],  
                      [9,6],  
                      [4,7],  
                      [2,3],  
                      [8,1],  
                      [7,2]])  
    y_train=np.array([1,1,1,-1,-1,-1])    
    # 测试数据     
    X_new = np.array([[5, 3]]) #目标值  
    # 绘图     
    draw(X_train, y_train, X_new)   
    # 不同的k(取奇数)对分类结果的影响  
    for k in range(1,6,2):#步长为2   也即是:1,3,5
        #构建KNN实例
        clf=KNN(X_train,y_train,k=k)
        #对测试数据进行分类预测
        y_predict=clf.predict(X_new)
        print("k={},被分类为:{}".format(k,y_predict))
if __name__=="__main__":
    main()

SKlearn实现:

import numpy as np
from sklearn.neighbors import KNeighborsClassifier
def main():
    # 训练数据
    X_train=np.array([[5,4],
                      [9,6],
                      [4,7],
                      [2,3],
                      [8,1],
                      [7,2]])
    y_train=np.array([1,1,1,-1,-1,-1])
    # 待预测数据
    X_new = np.array([[5, 3]])
    # 不同k值对结果的影响 
    for k in range(1,6,2):
        # 构建实例
        clf = KNeighborsClassifier(n_neighbors=k,n_jobs=-1)
        # 选择合适算法
        clf.fit(X_train, y_train)
        # print(clf.kneighbors(X_new))
        # 预测
        y_predict=clf.predict(X_new)
        #print(clf.predict_proba(X_new))
        print("预测正确率:{:.0%}".format(clf.score([[5,3]],[[1]])))
        print("k={},被分类为:{}".format(k,y_predict))
if __name__=="__main__":
    main()

9dc7e3066474c51e0cbff43a16e8d11c_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hlZGEz,size_16,color_FFFFFF,t_70.png

afd7e6e4ead1362ddef241a593c3da84_20190914195942183.png

8668f994d07b77f2195b4a70eceb823f_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hlZGEz,size_16,color_FFFFFF,t_70.png

2).使用 k­近邻算法改进网站的配对效果-代码理解

见github:https://github.com/codehgq/ML-note/blob/master/Date_KNN.ipynb

分为以下步骤:

1)文件到矩阵转换(准备数据)

#导入矩阵运算模块
import numpy as np
import operator
#数据集文件datingTestSet2.txt保存在当前目录下,也即是把txt拷贝到本地的用户文件夹下(我的) 输入:文件 输出:数据集的特征矩阵returnMat和标签向量classLabelVector
def file2matrix(filename):
    love_dictionary = {'largeDoses':3, 'smallDoses':2, 'didntLike':1}    # 三个类别
    fr = open(filename)    # 打开文件
    arrayOLines = fr.readlines()    # 逐行打开
    numberOfLines = len(arrayOLines)            #得到文件的行数
    returnMat = np.zeros((numberOfLines, 3))        #初始化特征矩阵
    classLabelVector = []                       #初始化输出标签向量
    index = 0
    for line in arrayOLines:#循环处理文件中的每行数据
        line = line.strip()    # 删去字符串首部尾部空字符
        listFromLine = line.split('\t')    # 按'\t'对字符串进行分割,得到列表
        returnMat[index, :] = listFromLine[0:3]    # listFromLine的0,1,2元素是特征,赋值给returnMat的当前行
        if(listFromLine[-1].isdigit()):    # 如果listFromLine最后一个元素是数字
            classLabelVector.append(int(listFromLine[-1]))    # append 的作用是在列表的末尾添加元素,直接赋值给classLabelVector
        else:    # 如果listFromLine最后一个元素不是数字,而是字符串
            classLabelVector.append(love_dictionary.get(listFromLine[-1]))    # 根据字典love_dictionary转化为数字
        index += 1
    return returnMat, classLabelVector    # 返回的类别标签classLabelVector是1,2,3

 2)数据归一化

#输入:数据矩阵 输出:归一化的数据矩阵normDataSet 范围 ranges 有3列 最小值minVals 3列
def autoNorm(dataSet):
    minVals = dataSet.min(0)#从列中选择最小值
    maxVals = dataSet.max(0)#从列中选择最大值
    ranges = maxVals - minVals
    normDataSet = np.zeros(np.shape(dataSet))#shape返回矩阵中维度 此句相当于创建一个同等规模的0矩阵
    m = dataSet.shape[0]#取矩阵的行数
    normDataSet = dataSet - np.tile(minVals, (m, 1))#函数title用于扩充minVals成m行1列
    normDataSet = normDataSet/np.tile(ranges, (m, 1))   # normDataSet值被限定在[0,1]之间
    return normDataSet, ranges, minVals

3)对数据分析

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import operator
import logging
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
fig=plt.figure()
ax=fig.add_subplot(131)#1*3的第一个
ax.scatter(datingDataMat[:,1],datingDataMat[:,2])#第二列和第三列数据
#plt.show()
ax=fig.add_subplot(132)
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],10.0*np.array(datingLabels),10.0*np.array(datingLabels))
ax=fig.add_subplot(133)
#datingLabels的值为1、2、3 每个数据点的大小不一样
ax.scatter(datingDataMat[:,0],datingDataMat[:,1],10.0*np.array(datingLabels),10.0*np.array(datingLabels))
fig=plt.figure()
ax=fig.add_subplot(111)
#datingLabels=array(datingLabels)
idx_1=np.where(np.array(datingLabels)==1)
p1=ax.scatter(datingDataMat[idx_1,0],datingDataMat[idx_1,1],marker = '*',color ='xkcd:purple',label='1',s=20)
idx_2=np.where(np.array(datingLabels)==2)
p2=ax.scatter(datingDataMat[idx_2,0],datingDataMat[idx_2,1],marker = 'o',color ='xkcd:teal',label='2',s=10)
idx_3=np.where(np.array(datingLabels)==3)
p3=ax.scatter(datingDataMat[idx_3,0],datingDataMat[idx_3,1],marker = '+',color ='y',label='3',s=30)
plt.legend(loc = 'upper right')
plt.show()#最后显示即可
#scatter函数参考 https://www.cnblogs.com/shanlizi/p/6850318.html
#matplot 官方教程 https://matplotlib.org/tutorials/colors/colors.html#sphx-glr-tutorials-colors-colors-py
#最后一张图参考 http://blog.sina.com.cn/s/blog_8d249b140102wf3m.html 并修改其中的错误

4)KNN分类器

#输入:测试集inX,训练集dataSet,训练样本标签lebels,取的最近邻个数k 输出:返回K近邻中所属类别最多的一类 参考:https://www.cnblogs.com/vrfighters/articles/4715527.html 写的不错
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]#得到行数
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet#title 相当于将inX扩充为dataSeetSize行目的是为了和后面的训练集求距离
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)#求和axis=1表示对所在行的全部列求和
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()#从小到大排序并找到索引值可参考:https://docs.scipy.org/doc/numpy/reference/generated/numpy.argsort.html和https://www.cnblogs.com/yyxf1413/p/6253995.html
    classCount = {}#创建字典,用于存储各标签出现的次数
    for i in range(k):#从上述的k个排序好的点,统计类别次数
        voteIlabel = labels[sortedDistIndicies[i]]#解释 距离最小的数据样本的标签参考
                                                   #:https://blog.csdn.net/zengxyuyu/article/details/54382182
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1#获取键值voteIlabel对应的值(次数),若不存在则为0
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)#将字典分解为元组列表,并按照第二个元素(次数)排序 true 为降序排列 默认升序 
    #参考:http://www.runoob.com/python/python-func-sorted.html
    return sortedClassCount[0][0]#得到k近邻中所属类别最多的类

5)测试算法

输入:样本,输出:分类结果和实际类别 ,错误率,错误个数
def datingClassTest():
    hoRatio = 0.10      #整个数据集的10%用来测试
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')       #导入数据集
    normMat, ranges, minVals = autoNorm(datingDataMat)    # 所有特征归一化
    m = normMat.shape[0]    # 样本个数(行)
    numTestVecs = int(m*hoRatio)    # 测试样本个数
    errorCount = 0.0
    #对测试数据遍历
    for i in range(numTestVecs):
        #对每一条数据进行分类
        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)
       #输出分类结果和实际的类别
        print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]))
      #如果分类结果和实际不符
    if (classifierResult != datingLabels[i]): errorCount += 1.0
    print("the total error rate is: %f" % (errorCount / float(numTestVecs)))    # 打印错误率
    print(errorCount)    # 打印错误个数

6)使用算法(构建完整可用系统)

#依据用户输入3个参数,输出:判断结果类别 详细注释;参考:https://blog.csdn.net/quincuntial/article/details/50471423
def classifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']#定义分类结果类别
   #读取输入的数据  percentTats  ffMiles  iceCream
    percentTats = float(input(\
                                  "percentage of time spent playing video games?"))
    ffMiles = float(input("frequent flier miles earned per year?"))
    iceCream = float(input("liters of ice cream consumed per year?"))
    #从文件datingTestSet2.txt中读取已有数据
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)#归一化
    #将单个数据定义为一条数据
    inArr = np.array([ffMiles, percentTats, iceCream, ])
    #对输入数据分类
    classifierResult = classify0((inArr - \
                                  minVals)/ranges, normMat, datingLabels, 3)
    #输出预测的分类结果
    print("You will probably like this person: %s" % resultList[classifierResult - 1])

3)手写识别系统 -代码的理解

见github:https://github.com/codehgq/ML-note/tree/master/KNN

欢迎

1)加载数据  2)图像转换为向量  3)分类

#图像转化为向量
def img2vector(filename):
    returnVect = np.zeros((1, 1024))    # 存储图片像素的向量维度是1x1024
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0, 32*i+j] = int(lineStr[j])    # 图片尺寸是32x32,将其依次放入向量returnVect中
    return returnVect

4、如何对训练数据进行快速K近邻搜索

分为两过程:构造KD树(二叉树)和搜索KD树

KD树表示为对K维空间的划分,其每个结点对应于k维空间划分中的一个超矩形区域

优势:省去大部分数据点搜索,减少搜索计算量

其中的KD树适用于训练实例数远大于空间维数的K近邻搜索

参考资料

【1】机器学习之深入理解K-means、与KNN算法区别及其代码实现https://blog.csdn.net/sinat_35512245/article/details/55051306

【2】https://blog.csdn.net/gzj_1101/article/details/77774524

【3】近似误差和估计误差 https://blog.csdn.net/qq_35664774/article/details/79448076

【4】 一文搞懂k近邻(k-NN)算法

https://mp.weixin.qq.com/s?srcid=0325JZ3uAQG1JwQJobFUIx37&scene=23&mid=2247483771&sn=e8cc00032205a73584867e85aa509a64&idx=1&__biz=MzI4MDYzNzg4Mw%3D%3D&chksm=ebb439afdcc3b0b9bb4446bc874def2962886b6db112171c2c8fd73d1dc5d51ad27fb49d5ffd&mpshare=1#rd

【5】最近邻法和k-近邻法 KD树https://blog.csdn.net/u012422446/article/details/56486342

【6】李航《统计学习方法》k近邻法

【7】KNN算法理解 https://blog.csdn.net/jmydream/article/details/8644004

【8】https://blog.csdn.net/qq_35082030/article/details/60965320 有问题

【9】kmeans和knn的区别 机器学习之深入理解K-means、与KNN算法区别及其代码实现

https://blog.csdn.net/sinat_35512245/article/details/55051306

【10】一文搞懂k近邻(k-NN)算法二 https://mp.weixin.qq.com/s/qfHBn7YydSOOnM43Be8aTg

【11】 机器学习实战


目录
相关文章
|
2天前
|
算法 PHP
【php经典算法】冒泡排序,冒泡排序原理,冒泡排序执行逻辑,执行过程,执行结果 代码
【php经典算法】冒泡排序,冒泡排序原理,冒泡排序执行逻辑,执行过程,执行结果 代码
6 1
|
11天前
|
机器学习/深度学习 算法
基于鲸鱼优化的knn分类特征选择算法matlab仿真
**基于WOA的KNN特征选择算法摘要** 该研究提出了一种融合鲸鱼优化算法(WOA)与K近邻(KNN)分类器的特征选择方法,旨在提升KNN的分类精度。在MATLAB2022a中实现,WOA负责优化特征子集,通过模拟鲸鱼捕食行为的螺旋式和包围策略搜索最佳特征。KNN则用于评估特征子集的性能。算法流程包括WOA参数初始化、特征二进制编码、适应度函数定义(以分类准确率为基准)、WOA迭代搜索及最优解输出。该方法有效地结合了启发式搜索与机器学习,优化特征选择,提高分类性能。
|
12天前
|
机器学习/深度学习 存储 算法
K 近邻算法(二)
K-近邻(KNN)算法是一种监督学习方法,用于分类和回归。关键步骤包括计算新样本与训练样本的距离,选择合适的邻近样本数K,基于K个邻居的多数类别或平均值做出预测。K值的选择影响模型性能:小K易受噪声影响(过拟合),大K可能导致模型过于简单(欠拟合)。评估模型通常使用测试集的预测准确率,如sklearn.metrics.accuracy_score。最优K值可通过交叉验证,如GridSearchCV,来确定,但它可能计算密集。KNN常用于手写数字识别等任务,如MNIST数据集。
|
6天前
|
机器学习/深度学习 分布式计算 算法
在机器学习项目中,选择算法涉及问题类型识别(如回归、分类、聚类、强化学习)
【6月更文挑战第28天】在机器学习项目中,选择算法涉及问题类型识别(如回归、分类、聚类、强化学习)、数据规模与特性(大数据可能适合分布式算法或深度学习)、性能需求(准确性、速度、可解释性)、资源限制(计算与内存)、领域知识应用以及实验验证(交叉验证、模型比较)。迭代过程包括数据探索、模型构建、评估和优化,结合业务需求进行决策。
10 0
|
6天前
|
机器学习/深度学习 算法 数据可视化
基于googlenet深度学习网络的睁眼闭眼识别算法matlab仿真
**算法预览图展示睁眼闭眼识别效果;使用Matlab2022a,基于GoogLeNet的CNN模型,对图像进行分类预测并可视化。核心代码包括图像分类及随机样本显示。理论概述中,GoogLeNet以高效Inception模块实现眼部状态的深度学习识别,确保准确性与计算效率。附带三张相关图像。**
|
6天前
|
移动开发 算法 计算机视觉
技术笔记:openCV特征点识别与findHomography算法过滤
技术笔记:openCV特征点识别与findHomography算法过滤
|
9天前
|
人工智能 算法 Java
java中经典算法代码整理
java中经典算法代码整理
16 0
|
9天前
|
机器学习/深度学习 算法 搜索推荐
KNN算法(k近邻算法)原理及总结
KNN算法(k近邻算法)原理及总结
|
9天前
|
算法 IDE 开发工具
c语言的经典算法代码
c语言进阶11-经典算法代码