机器学习实战☛k-近邻算法(k-nearestneighbor,knn)(代码片段)

白鳯 白鳯     2023-01-12     417

关键词:

机器学习实战☛k-近邻算法(K-Nearest Neighbor, KNN)

k-近邻算法概述

原理简介

k-近邻算法采用测量不同特征值之间的距离方法进行分类,它的简单工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集合中每个数据都存在标签,即我们知道样本集中每一数据所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,k通常是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

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

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

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

k-近邻算法一般流程

  1. 收集数据:可以使用任何方法。
  2. 准备数据:距离计算所需要的数值,最好是结构化的数据格式。
  3. 分析数据:可以使用任何方法。
  4. 训练算法:此步骤不适用于k-近邻算法
  5. 测试算法:计算错误率。
  6. 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后序的处理。

伪代码与实现

使用k-近邻算法将每组数据划分到某个类中,伪代码如下。

对未知类别属性的数据集中的每个点依次执行以下操作:

(1)计算已知类别数据集中点与当前点之间的距离;

(2)按照距离递增次序排序;

(3)选择与当前点距离最小的k个点;

(4)确定前k个点所在类别的出现频率;

(5)返回前k个点出现频率最高的类别作为当前点的预测分类。

对应的代码如下:

def classify0(intX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    #计算距离,tile得到(dataSetSize行重复inxX, 列重复1次intX)
    diffMat = tile(intX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat ** 2
    #axis = 1 表示对行求和
    sqDistances = sqDiffMat.sum(axis = 1)
    distances = sqDistances ** 0.5
    #argsort返回数值从小到大的索引值
    sortedDistIndicies = distances.argsort()

    #选择距离最小的k个点, classCount字典保存标签出现频率
    classCount = 
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1

    #排序,返回k邻近中出现频次最大的标签
    sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True)
    return sortedClassCount[0][0]

其中,inX是用于分类的输入向量,dataSet是输入的训练样本集,labels为标签向量,参数k表示用于选择最近邻居的数目。代码中求的是欧氏距离。

如求两个向量点xA和xB之间的欧式距离有如下公式
d = ( x A 0 − x B 0 ) 2 + ( x A 1 − x B 1 ) 2 d=\\sqrt(xA_0 - xB_0)^2+(xA_1-xB_1)^2 d=(xA0xB0)2+(xA1xB1)2

from numpy import *
import operator

def classify0(intX, dataSet, labels, k):
    #···

if __name__ == '__main__':
    group = array([[1.0, 1.1], [1.0, 1.0], [0.5, 0.3], [0.3, 0.1]])
    labels = ['A', 'A', 'B', 'B']
    res = classify0([0, 0], group, labels, 3)
    print(res)

调用上述代码,可知输出结果,与点**[0, 0]**最相近的标签为B。


示例:使用kNN改进约会网站的配对效果

数据集见https://github.com/pbharrin/machinelearninginaction中第二章节Ch02

海伦在约会网站上与交往过的人通过总结,她发现曾经交往过三种类型的人:

  • 不喜欢的人
  • 魅力一般的人
  • 极具魅力的人

此外海伦还收集了网站未曾记录的数据信息,她认为这些数据更有助于匹配对象的归类。海伦收集的约会样本主要包含以下3种特征(datingTestSet.txt)

  • 每年获得的飞行常客里程数
  • 玩视频游戏所耗时间百分比
  • 每周消费的冰淇淋公升数

程序>将文本数据转换到NumPy的解析程序

def file2matrix(filename):
    """
    约会网站数据导入矩阵、标签向量中
    :param filename:
    :return:
    """
    love_dictionary = 'largeDoses':3, 'smallDoses':2, 'didntLike':1
    fr = open(filename)
    arrayOLines = fr.readlines()
    #得到文件行数,即数据条数
    numberOfLines = len(arrayOLines)
    returnMat = zeros((numberOfLines, 3))
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()
        listFromLine = line.split('\\t')
        returnMat[index, :] = listFromLine[0:3]
        if(listFromLine[-1].isdigit()):
            classLabelVector.append(int(listFromLine[-1]))
        else:
            classLabelVector.append(love_dictionary.get(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector

使用matplotlib绘图,导入包并在主程序中进行数据处理的调用

import matplotlib.pyplot as plt

if __name__ == '__main__':
    datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
    fig = plt.figure()
    ax = fig.add_subplot(111)
    #ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2])
    ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0 * array(datingLabels), 15.0 * array(datingLabels))
    plt.xlabel('Percentage of Time Spent Playing Video Games')
    plt.ylabel('Liters of Ice Cream Consumed Per Week')
    plt.show()

根据==”玩游戏所耗时间百分比““每周消费的冰淇淋公升数”==这两个维度,以及向量所对应的标签绘制出的散点图如下所示。虽然能够比较容易地区分数据点从属类别,但依然很难根据这张图得出结论性信息。

根据==”每年获得的飞行常客里程数““玩视频游戏所耗时间百分比”==这两个维度,以及向量所对应的标签绘制出的散点图如下所示。这两个特征更容易区分数据点从属的类别。

ax.scatter(datingDataMat[:, 0], datingDataMat[:, 1], 15.0 * array(datingLabels), 15.0 * array(datingLabels))
    plt.xlabel('Number of flight mileage obtained per year')
    plt.ylabel('Percentage of Time Spent Playing Video Games')

准备数据:归一化数值

数值归一化使得每个特征享有相同的权重,若不归一化,则里程数的大小会直接影响欧式距离的大小,数据小的影响就特别微小了。

def autoNorm(dataSet):
    #min、max中参数0使得函数可以从列中选取最大或最小值(三个维度,取每一列的最大最小值)
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals, (m, 1))
    normDataSet = normDataSet / tile(ranges, (m, 1))
    return normDataSet, ranges, minVals

测试算法:作为完整程序验证分类器

分类器针对约会网站的测试代码,选择10%的测试数据

def datingClassTest():
    #选择10%的测试数据
    hoRatio = 0.10
    datingDataMat, datingLabels = file2matrix('datingTestSet.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)))

调用此函数,得到的输出结果如下所示:

the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
·····
the classifier came back with: 3, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the total error rate is: 0.050000

分类器处理约会数据集的错误率是5%。可以改变函数datingClassTest内变量hoRatio和变量k的值,检测错误率是否随着变量值的变化而增加。依赖于分类算法、数据集合程序设置,分类器的输出结果可能有很大的不同。

使用算法:构建完整可用系统

通过海伦在约会网站上找到的某个人并输入他的信息。程序会给出她对对方喜欢程度的预测值。

约会网站测试函数

def classifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']
    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?"))
    datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = array([ffMiles, percentTats, iceCream])
    classifierResult = classify0((inArr - minVals) / ranges, normMat, datingLabels, 3)
    print("You will probably like this person: ", resultList[classifierResult - 1])

调用并测试结果如下


示例:手写识别系统

手写数字数据集的例子如下图

为了简单起见,这里构造的系统只能识别数字0到9。数据集是采用二进制的文本数据来存储图像,这样便于理解和处理。

目录trainingDigits中包含了大约2000个例子,每个数字大约有200个样本;目录testDigits中包含了大约900个测试数据。拿出其中一个为例,其二进制文本表示如下(数字4):

00000000000000011110000000000000
00000000000000011111000000000000
00000000000000011111000000000000
00000000000000111110000000000000
00000000000000111110000000000000
00000000000001111100000001100000
00000000000011111000000111100000
00000000000011111000000111100000
00000000000011111000001111110000
00000000000111100000011111000000
00000000001111100000011111000000
00000000001111100000011111000000
00000000011111000000111110000000
00000000111110000000111110000000
00000000111110000000111110000000
00000011111000000001111101000000
00000011111000000001111111100000
00000011111000001111111111100000
00001111111111111111111111000000
00001111111111111111111111000000
00001111111111111111111111000000
00001111111111111111111100000000
00001111111111111111111000000000
00000111111111111111100000000000
00000111111000011111000000000000
00000000000000011111000000000000
00000000000000011111000000000000
00000000000000111110000000000000
00000000000000011111000000000000
00000000000000011111000000000000
00000000000000111110000000000000
00000000000000111100000000000000

准备数据:将图像转换为测试向量

为了使用前面两个例子的分类器,必须将图像格式化处理为一个向量。我们将把一个32x32的二进制图像矩阵转换成1x1024的向量,这样前两节使用的分类器就可以处理数字图像信息了。

def img2vector(filename):
    returnVect = 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

测试算法:使用kNN算法识别手写数字

导入模块添加代码:from os import listdir,函数listdir可以列出给定目录的文件名

def handwritingClassTest():
    hwLabels = []
    trainingFileList = listdir('trainingDigits')
    m = len(trainingFileList)
    trainingMat = zeros((m, 1024))
    #从每一个文件中解析数字
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]
        #获取数字标签
        classNumStr = int(fileStr.split('_')[0])
        hwLabels.append(classNumStr)
        trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)
    testFileList = listdir('testDigits')
    errCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr))
        if (classifierResult != classNumStr):
            errCount += 1.0
    print("the total number of errors is: %d" % errCount)
    print("the total error rate os: %f" % (errCount / float(mTest)))

调用输出结果如下:

the classifier came back with: 0, the real answer is: 0
the classifier came back with: 0, the real answer is: 0
···
the classifier came back with: 5, the real answer is: 5
the classifier came back with: 3, the real answer is: 5
the classifier came back with: 6, the real answer is: 5
···
the classifier came back with: 9, the real answer is: 9
the classifier came back with: 9, the real answer is: 9
the total number of errors is: 10
the total error rate os: 0.010571

k-近邻法识别手写数字数据集,错误率为1.056%,改变遍历k的值,修改函数handwritingClassTest随机选取训练样本、改变训练样本的数目,都会对k-近邻算法的错误率产生影响,感兴趣的话可以改变这些变量值,观察错误率的变化。

实际使用这个算法时,算法的执行效率并不高。因为算法要为每个测试向量做2000次距离计算,每个距离计算包括了1024个维度浮点运算,总计要执行900次,此外,我们还需要为向量准备2MB的存储空间。k决策树是k-近邻算法的优化版,可以节省大量的计算开销。


本章小结

k-近邻算法是分类数据最有效的算法。k-近邻算法是基于实例的学习,使用算法时我们必须有接近实际数据的训练样本数据。k-近邻算法必须保存全部数据集,如果训练的数据集很大,必须使用大量的存储空间。此外,由于必须对数据集中的每个数据计算距离值,实际使用时可能非常耗时。

k-近邻算法的另一个缺陷是它无法给出任何数据的基础结构信息,因此我们也无法知晓平均示例样本和典型实例样本具有什么特征。


参考资料

数据集见—路径指南>Ch02

《机器学习实战》[美]Peter Harrington,李锐、李鹏等译

numpy.argsort()函数详解

NumPy教程

Python numpy函数:zeros()、ones()、empty()

机器学习实战☛k-近邻算法(k-nearestneighbor,knn)(代码片段)

机器学习实战☛k-近邻算法(K-NearestNeighbor,KNN)文章目录机器学习实战☛k-近邻算法(K-NearestNeighbor,KNN)k-近邻算法概述原理简介k-近邻算法一般流程伪代码与实现示例:使用kNN改进约会网站的配对效... 查看详情

《机器学习实战》学习笔记——k近邻算法

1.numpy中一些函数的用法学习shape()用法:shape :tupleofintsTheelementsoftheshapetuplegivethelengthsofthecorrespondingarraydimensions.。  shape返回一个元组,依次为各维度的长度。shape[0]:第一维长度,shape[1]:第二维长度。  tile()用法:numpy.tile 查看详情

机器学习实战笔记--k近邻算法

1#encoding:utf-82fromnumpyimport*3importoperator4importmatplotlib5importmatplotlib.pyplotasplt67fromosimportlistdir89defmakePhoto(returnMat,classLabelVector):#创建散点图10fig=plt.figure()11ax=fig.add_subpl 查看详情

《机器学习实战》读书笔记2:k-近邻(knn)算法

声明:文章是读书笔记,所以必然有大部分内容出自《机器学习实战》。外加个人的理解,另外修改了部分代码,并添加了注释1、什么是K-近邻算法?简单地说,k-近邻算法采用测量不同特征值之间距离的方法进行分类。不恰当... 查看详情

机器学习实战精读--------k-近邻算法

对机器学习实战的课本和代码进行精读,帮助自己进步。#coding:utf-8from numpy import *import operator #运算符模块from os import listdir  #os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字... 查看详情

机器学习实战之第二章k-近邻算法

第2章k-近邻算法KNN概述k-近邻(kNN,k-NearestNeighbor)算法主要是用来进行分类的.KNN场景电影可以按照题材分类,那么如何区分 动作片 和 爱情片 呢?动作片:打斗次数更多爱情片:亲吻次数更多基于电影中的亲吻、... 查看详情

机器学习实战之k近邻算法

k近邻算法概述简单地说,K近邻算法采用测量不同特征值之间的距离方法进行分类。优点:精度高、对异常值不敏感、无数据输入假定。缺点:计算复杂度高、空间复杂度高。适用数据范围:数值型和标称型。它的工作原理是:存在一... 查看详情

机器学习实战k-近邻算法实施knn分类算法

2.预测数据分类时,出现‘dict’objecthasnoattribute‘iteritems‘如: 最常见的解决办法是更改环境变量顺序如 注意:哪个版本在上面,cmd中的python版本即是谁。如又如:  然后预测数据所在分类即可实现:  查看详情

《机器学习实战》之k-近邻算法

看了这本书的第一个算法—k-近邻算法,这个算法总体构造思想是比较简单的,在ACM当中的话就对应了kd树这种结构。首先需要给定训练集,然后给出测试数据,求出训练集中与测试数据最相近的k个数据,根据这k个数据的属... 查看详情

机器学习实战task1(knn)k近邻算法的应用(代码片段)

...算法的简介k-近邻算法是属于一个非常有效且易于掌握的机器学习算法,简单的说就是采用测量不同特征值之间距离的方法对数据进行分类的一个算法。(2)k近邻算法的工作原理给定一个样本的集合,这里称为训... 查看详情

机器学习实战ch02:k-近邻算法

k-近邻算法算是一个非常暴力也非常好理解的算法(抽象来讲,就是和谁长得像就分为哪一类如何划分长得像还是不像的尺度?把特征值当做坐标,把个体当做线性空间中的离散点,取k个离目标最近的训练集点,进行labelvote,少... 查看详情

《机器学习实战》——k近邻算法

原理:(1)输入点A,输入已知分类的数据集data(2)求A与数据集中每个点的距离,归一化,并排序,选择距离最近的前K个点(3)K个点进行投票,票数最多的分类即为所求优点:简单,可用于非线性分类缺点:当样本不均衡时影响投票... 查看详情

机器学习实战第2章k-近邻算法(k-nearestneighbor,knn)

第2章k-近邻算法<scripttype="text/javascript"src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=default"></script>KNN概述k-近邻(kNN,k-NearestNeighbor)算法主要是用来进行分类的.KNN场景电影可以按照题材分类,那么如何区分&nbs 查看详情

机器学习实战笔记-k近邻算法2(改进约会网站的配对效果)

案例二.:使用K-近邻算法改进约会网站的配对效果案例分析:海伦收集的数据集有三类特征,分别是每年获得的飞行常客里程数、玩视频游戏所耗时间百分比、每周消费的冰淇淋公升数。我们需要将新数据的每个新数据的每个特... 查看详情

机器学习实战---k-近邻(代码片段)

一:简单实现K-近邻算法(一)导入数据importnumpyasnpimportmatplotlib.pyplotaspltimportpandasaspddefCreateDataSet():data=np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])labels=np.array([‘A‘,‘A‘,‘B‘,‘B‘])returndata,labelsda 查看详情

《机器学习实战》之k-近邻算法(手写识别系统)

这个玩意和改进约会网站的那个差不多,它是提前把所有数字转换成了32*32像素大小的黑白图,然后转换成字符图(用0,1表示),将所有1024个像素点用一维矩阵保存下来,这样就可以通过knn计算欧几里得距离来得到最接近的答案... 查看详情

机器学习——k-近邻(k-nearestneighbor)

...arestneighbor(个人观点,仅供参考。)k-近邻算法,第一个机器学习算法,非常有效且易掌握,本文将主要探讨k-近邻算法的基本理论和使用距离侧量的算法分类物品;最后通过k-近邻算法改进约会网站和手写数字识别系统。文章... 查看详情

机器学习实战k-近邻算法使用matplotlib创建散点图

问题一:>>>importmatplotlib 出现Nomodulenamed’matplotlib‘  解决过程>pipinstallmatplotlib出现pip版本升级以后再导入matplotlib,仍然出现上图情况 在pycharm中选择2.7.14版本的projectinterpreter,并在其中安装matplotlibp 查看详情