机器学习:k-近邻分类

蜗牛K 蜗牛K     2022-09-30     780

关键词:

   今天的主题为:K-近邻分类算法。主要包括对算法的解释及理解,以及对算法用实例代码展示出来。

  

  K-近邻算法属于分类的一种,由之前机器学习简介中所介绍的,分类是需要一些样本数据来供算法进行学习,因此K-近邻分类算法也需要一些训练数据集。

  首先我说一下K-近邻算法的思路:从字面上上看,“K-近邻”可以理解为“K个最近”,这也就是K-近邻算法的核心。对于一个待分类的样本,使用K-近邻算法将其分类,首先要将这个样本与训练样本集中的每个样本计算距离或相似度(使用欧氏距离),找出与该样本最近的或最相似的K个训练样本,对这K个训练样本的类别进行统计,选择其中类别数最多的作为这个待分类样本的类别。这就是K近邻算法的核心思想。通过代码来展示这个算法:

from numpy import *
import operator

def kNN_classification(inX, dataSet, labels, k):
    '''
    inX: 待分类的样本(以一个一维矩阵的形式表示)
    dataSet: 训练样本集(以一个二维矩阵的形式表示,其中行数为样本个数,每一行代表一个样本)
    labels: 训练样本集中样本所对应的分类
    k: K-近邻分类中的K,要事先给定
    '''
    m = dataSet.shape[0] # 计算dataSet的行数
    diffMat = tile(inX, (m, 1)) - dataSet      # numpy中的tile函数
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5
    
    sortedDistances = distances.argsort() 
    # argsort函数返回一个与distances同样大小的一维数组,其中每个索引上的值为distances在该索引的值从小到大排序的“序号-1“

    classCount = {} # 用来存储距离最近的K个样本的类别计数
    for i in range(len(sortedDistances)):
        if sortedDistances[i] < k:
            label = labels[i]
            classCount[label] = classCount.get(labe, 0) + 1

    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    '''
    python字典中还存在items()方法。两者有些许区别。
    items方法是可以将字典中的所有项,以列表方式返回。
    iteritems方法与items方法相比作用大致相同,只是它的返回值不是列表,而是一个迭代器。
    例如要通过student的第三个域排序,可以这么写:
        sorted(students, key=operator.itemgetter(2)) 
    reverse=True表示从大到排序
    '''
    return sortedClassCount[0][0]
    
     

   其中要说下argsort函数和sorted函数。从两个函数的名称可以看出,两个函数和排序有关。

  其中argsort函数返回的是一个值为"排序序号-1"的数组,比如一个数组a=array([ 1.00498756, 1, 2.23606798, 2.19317122]),则a.argsort()的返回值为[1, 0, 3, 2]。

  而这里面sorted数组是对字典classCount进行排序,其中第一个参数表示返回一个元组列表,列表每个元素是key和value组成的元组,其中第二个参数意思是根据元组中第2个域进行排序。reverse=True表示从小到大排序。

  以上就把K-近邻分类算法讲解完了。接下来以两个例子为例来熟悉该算法以及机器学习算法的处理过程。

  对于机器学习算法,由之前机器学习简介可知,主要分为以下过程:

  1、收集数据:这里主要通过文本文件来保存数据。

  2、准备数据:使用Python解析文本文件。

  3、分析数据:使用Matplotlib画二维扩散图。

  4、训练算法:此步骤不适用于K-近邻算法。因为该算法是直接利用训练集数据的,而不需要从训练集数据获取知识模式。

  5、测试算法:使用部分样本数据作为测试数据来检验算法的错误率。(测试数据也已经分类好了,根据算法预测的结果如果与实际分类不一样,则标记为一个错误。)

  6、应用于实际问题。

  

  首先说一下第一个关于kNN的例子:使用K-近邻算法改进约会网站的配对效果。

  这个例子的题目为:我的朋友海伦一直使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的人选但她没有从中找到喜欢的人。经过一番总结,它发现曾交往过三种类型的人:

  • 不喜欢得人

  • 魅力一般的人

  • 极具魅力的人  

  海伦希望我们的分类软件可以更好地帮助她将匹配对象划分到确切的分类中。此外海伦还收集了一些约会网站未曾记录的数据信息,她认为这些数据更有助于匹配对象的归类。海伦收集约会数据巳经有了一段时间,她把这些数据存放在文本文件datingTestSet.txt中,每个样本数据占据一行,总共有1000行。海伦的样本主要包含以下3种特征:

  • 每年获得的飞行常客里程数

  • 玩视频游戏所耗时间百分比

  • 每周消费的冰淇淋公升数

  以上即为该例子的所有叙述, 简练的来说,其中把每个人看作为具有三个特征,根据某人的这三个特征值以及训练样本集和kNN算法,来对某人进行分类,看这个人属于海伦所分类的哪种人。

  根据自己分析以及机器学习算法的处理过程,

  1、首先需要获取数据,由于数据直接保存在txt文件里,

  2、因此直接进入第二步,对txt文件进行解析,将其解析为算法所需要的数据结构。这里用一个函数处理txt文件,下图是txt文件里的数据内容,前三列为三个特征值,第四列为分类标签,解析txt文件为一个样本集矩阵和样本对应的分类列表

  3、准备好数据之后,要进行分析数据,由txt文件里的特征值可以看出,不同特征值的大小相差很大,本文使用欧氏距离计算相似度(或距离),对于第一个特征的值来说,该特征的值比较大,使得该特征的值对距离计算的结果影响太大,因此这里我们需要对数据进行归一化处理,将取值范围处理为0到1或者-1到1之间。

  4、归一化处理则可以对算法进行测试了,从样本中提取一定比例的(这里取百分之10)数据作为测试集,其他作为训练集,查看错误率的大小,错误率的计算为“被错误分类的样本 / 测试集样本数”。

  5、如果错误率较小,则可以将该算法运用于该实际问题(通过输入某人的这三个特征值,来判断是否是海伦想要约会的人)。

  代码中对每一步都有相应的解释,把以上过程的代码贴到下面:

                                 

'''
在约会网站上使用K-近邻算法:
    1、首先要获取数据,将数据处理为算法要求的格式,这里创建一个从文件中解析数据的函数file2matrix,
    将数据解析为算法所要求的格式(一个样本数据集——矩阵形式;一个样本数据所对应的分类——列表形式)。
    2、数据解析出来之后,需要对数据进行一些处理,要使得每一个属性的值对相似度计算的影响是一样,则要
    进行数据归一化处理。
    所谓的“使每一个属性值对相似度计算的影响是一样的”,意思是:举个例子,使用欧氏
    距离计算相似度时,如果某个属性值非常大,大到使得其他属性值的计算可以忽略,但是这三个属性对于分类
    来说是同等重要的。因此要进行数据归一化处理。创建一个autoNorm函数进行归一化
    3、开始测试分类器的错误率。随机挑选样本中10%的数据作为测试集来测试分类器的错误率,使用一个函数
    datingClassTest来完成。
    4、测试成功之后,则可以使用该分类器为某人确定某一对象的可交往程序:通过输入那三个特征的数值来判断。
    这里使用classifyPerson函数来完成。
'''
def file2matrix(filepath):
    '''
    :param filepath: 数据保存在文本文件里,filepath为该文件的路径
    :return: 一个矩阵形式的样本数据集,一个样本数据所对应的分类标签列表。
    '''
    fr = open(filepath)
    lines = fr.readlines()
    numberOfLines = len(lines)
    # 首先构建第一个样本数据集(矩阵形式)
    returnMat = zeros((numberOfLines, 3))
    # laberVector用来存放数据样本所对应的分类
    labelVector = []
    # index用来控制returnMat的行数,returnMat共有numberOfLines行,也就是共有numberOfLines个样本数据
    index = 0
    for line in lines:
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0: 3]
        # 下面这个判断语句将三种分类类型”极大魅力largeDoses“、”较小魅力smallDoses“、”不喜欢didntLike“用数字来表示
        if listFromLine[-1] == 'largeDoses':
            labelVector.append(3)
        elif listFromLine[-1] == 'smallDoses':
            labelVector.append(2)
        else:
            labelVector.append(1)
        # labelVector.append(listFromLine[-1])
        index += 1

    return returnMat, labelVector

def autoNorm(datas):

    # 使用numpy中的min和max
    # min或max中无参,返回的是数组中所有数字的最小值或最大值(一个值);
    # min或max中参数为0,返回的是数组中每列的最小值或最大值(为一个一维数组);
    # min或max中参数为1,返回的是数组中每行的最小值或最大值(为一个一维数组)。
    minVals = datas.min(0)
    maxVals = datas.max(0)
    ranges = maxVals - minVals
    normDataSet = zeros(shape(datas))
    m = datas.shape[0]
    # numpy中两个数组进行运算时,是数组中对应的数值进行相应运算。
    # 这里运用了tile函数,tile(minVals, (m, 1))表示的是每一行都是minVals,共m行
    normDataSet = datas - tile(minVals, (m, 1))
    normDataSet = normDataSet / tile(ranges, (m, 1))

    return normDataSet, ranges, minVals

def datingClassTest():
    hoRatio = 0.10 # 要取样本中10%作为测试集
    datingDataMat, datingLabels = file2matrix('D:/machinelearninginaction/Ch02/datingTestSet.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = normMat.shape[0]   # 样本数据总量
    numTestVecs = int(m * hoRatio)  # 求出10%样本数据的数量
    # print(normMat[1])   # 这两行结果一样
    # print(normMat[1,:])

    errorCount = 0.0
    for i in range(numTestVecs):
        classifyResult = classify0(normMat[i, :], normMat[numTestVecs: m, :], datingLabels[numTestVecs: m], 5)
        print('the classifier came back with: %d, the real answer is: %d' % (classifyResult, datingLabels[i]))
        if classifyResult != datingLabels[i]:
            errorCount += 1
    print('the total error rate is: %f' % (errorCount / float(numTestVecs)))


# datingClassTest() 结果为0.04,错误率很低,因此可以使用

def classifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']
    percentTats = float(raw_input('percentage of time spent playing video games?'))
    ffMiles = float(raw_input('frequent flier miles earned per year?'))
    iceCream = float(raw_input('ilters of ice cream consumed per year?'))
    datingDataMat, datingLabels = file2matrix('D:/machinelearninginaction/Ch02/datingTestSet.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = array([ffMiles, percentTats, iceCream])
    classifierResult = classify0((inArr - minVals) / ranges, normMat, datingLabels, 5)
    print('You will probably like this person: ', resultList[classifierResult - 1])


# classifyPerson()


# 使用一个界面来可视化这个过程
window = Tk()


def classify():
    resultList = ['不喜欢', '较小魅力', '极具魅力']
    percentTats = entry1.get()
    ffMiles = entry2.get()
    iceCream = entry3.get()
    datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = array([float(ffMiles), float(percentTats), float(iceCream)])
    classifierResult = classify0((inArr - minVals) / ranges, normMat, datingLabels, 5)
    theStringResult = '通过你所输入的某个人的特征得出,该人应该属于:' + resultList[ classifierResult - 1]
    text.insert(0.0, theStringResult)

var1 = StringVar()
var2 = StringVar()
var3 = StringVar()
window.geometry('400x320') # 其中的乘号是x不是*
window.title('约会网站人员分类')
label = Label(window, text='请输入某人的以下三个特征值:', font='Helvetica -16 bold')
label1 = Label(window, text='玩视频游戏所耗时间百分比:')
label2 = Label(window, text='每年获得的飞行常客里程数:')
label3 = Label(window, text='每周消费的冰淇淋公升数:')
entry1 = Entry(window, textvariable=var1)
entry2 = Entry(window, textvariable=var2)
entry3 = Entry(window, textvariable=var3)
label.pack()
label1.place(x=20, y=50)
label2.place(x=20, y=100)
label3.place(x=20, y=150)
entry1.place(x=180, y=50)
entry2.place(x=180, y=100)
entry3.place(x=180, y=150)

button = Button(window, text='查看该人类别', width=15, command=classify)
button.place(x=150, y=200)

text = Text(window, height=3)
text.place(x=0, y=250)


window.mainloop()

    将该界面利用python中的pyinstaller生成一个exe文件,海伦可以通过exe文件来查看某人是否是自己心仪的约会对象界面如下,不太好看,大家将就得看下。。

    

  以上所有代码即为kNN算法的第一个例子。

 

  接下来是kNN算法的第二个例子,识别手写数字0——9。

  利用kNN(K-近邻分类器)的手写识别系统,这里构造的系统只识别0-9的数字。
  在使用kNN分类器之前,
  1、首先收集到数据(这里主要是两个文件夹,一个训练集数据trainingDigits(数字0-9),一个测试集数据testDigits(数字0-9))。
  2、然后要处理数据(准备数据),将图像格式转换为分类器使用的list格式。
  3、最后是分析数据,在Python中检查数据,确保它符合要求。
  处理好数据格式之后,就可以开始测试kNN分类算法了,
  4、使用kNN分类器来识别手写数字(利用测试集,来查看错误率)。
  图像以txt文件存储,文件格式如下图所示,是一个32x32的矩阵,文件名为:
 

  直接把该例子代码贴到下面:
# -*- coding: utf-8 -*-
'''
利用kNN(K-近邻分类器)的手写识别系统,这里构造的系统只识别0-9的数字。
在使用kNN分类器之前,
    首先收集到数据(这里主要是两个文件夹,一个训练集数据,一个测试集数据)。
    然后要处理数据(准备数据),将图像格式转换为分类器使用的list格式。
    最后是分析数据,在Python中检查数据,确保它符合要求。
处理好数据格式之后,就可以开始测试kNN分类算法了,
    使用kNN分类器是识别手写数字,这里构建一个函数handwritingClssTest
'''

from numpy import *
from os import listdir # 从os模块导入listdir函数,该函数可以列出给定目录的文件名


'''
图像格式为一个32x32的矩阵,由于在kNN分类器中,每个样本在样本矩阵中都是以一行的形式存在,
因此要将它转换为1x1024的矩阵形式。
'''
def img2vector(filepath):
    '''
    :param filepath: 一个图像32x32矩阵所保存的文本文件路径
    :return: 将该图像32x32矩阵转换为1x1024矩阵形式。
    '''
    returnVec = zeros((1, 1024))
    file = open(filepath)
    for i in range(32):
        lineStr = file.readline()
        for j in range(32):
            returnVec[0, i * 32 + j] = int(lineStr[j])
    return returnVec

# 测试数据是否符合要求
# testdata = img2vector('testDigits/0_13.txt')
# print(testdata[0, 0: 31])
# print(testdata[0, 32: 63])

def handwritingClassTest():
    # 首先提取训练集数据,得到一个样本矩阵trainingMat以及样本对应类别标签列表hwLabel
    hwLabels = []
    trainingFileList = listdir('trainingDigits')
    m = len(trainingFileList)
    trainingMat = zeros((m, 1024))
    for i in range(m):
        fileNameStr = trainingFileList[i]
        # 首先获取该文件的所表示的数字是哪个(即所属类别),由文件名称的第一个字符可知。文件名通常为:数字_第几个.txt
        digitStr = fileNameStr.split('.')[0]
        firstDigitStr = digitStr.split('_')[0]

        hwLabels.append(firstDigitStr)
        trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)

    # 然后从文本文件中提取每个测试集数据进行测试,计算错误率
    testFileList = listdir('testDigits')
    errorCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):
        testFileNameStr = testFileList[i]
        # 获取该文件类别标签
        testDigitStr = testFileNameStr.split('.')[0]
        classNumStr = testDigitStr.split('_')[0]

        fileVec = img2vector('testDigits/%s' % testFileNameStr)
        classifierResult = classify0(fileVec, trainingMat, hwLabels, 3)
        print('the classifier came back with: %d, the real answer is: %d' %(int(classifierResult), int(classNumStr)))
        if classifierResult != classNumStr:
            errorCount += 1
    print('the total number of errors is: %d' % errorCount)
    print('the total error rate is: %f' % (errorCount / mTest))


handwritingClassTest()

  其中img2vector主要是为了解析txt文件,handwritingClassTest是为了kNN算法在识别手写数字时的错误率。

  这就是今天要说的,kNN算法以及两个例子和它们的代码。

  K-近邻算法是分类数据时最简单最有效的算法了,它是基于对实例的学习,因为使用时必须有接近实际数据的训练样本数据。因此K-近
邻算法必须保存所有数据集,如果训练数据集很大,则必定会使用大量的存储空间。此外,在kNN算法执行,要计算待分类样本与每个训练集
样本的距离,这也导致算法很耗时。K-近邻算法还有一个缺陷就是它无法给出任何数据的基本结构信息,因此我们无法知晓平均实例样本和典
型实例样本具有什么特征。(后续会讲使用概率测量方法处理分类,解决了这个问题)

 

 


  

 

 

 

 

 

 

机器学习100天(二十六):026k近邻分类算法-理论

机器学习100天,今天讲的是:K近邻分类算法-理论。《机器学习100天》完整目录:目录一、什么是K近邻算法K近邻算法也叫KNN(k-NearestNeighbor)算法,它是一个比较成熟也是最简单的机器学习算法之一。K近邻分类算法的思路是:如果... 查看详情

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

目录K-近邻算法k-近邻算法概述解析和导入数据使用Python导入数据实施kNN分类算法测试分类器使用k-近邻算法改进约会网站的配对效果收集数据准备数据:使用Python解析文本文件分析数据:使用Matplotlib画二维散点图准备数据:归... 查看详情

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

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

机器学习分类算法--k近邻算法knn(代码片段)

...应用数学知识少(近乎为零)效果好(缺点?)可以解释机器学习算法使用过程中很多细节问题更完整的刻画机器学习应用的流程 importnumpyasnpimportmatplotlib.pyplotasplt实现我们自己的kNN创建简单测试用例raw_data_X=[[3.39353 查看详情

机器学习笔记——k近邻法

K-nearestneighbor(KNN)kk近邻法一种基本的分类与回归方法,原理和实现都比较直观。其输入为样本的特征向量,输出为样本的类别,可以进行多类别分类。k近邻法是通过统计与未知样本最近点的训练样本的类别来投票决定... 查看详情

机器学习100天(三十):030k近邻分类算法-k值的选择(代码片段)

机器学习100天,今天讲的是:K近邻分类算法-K值的选择。《机器学习100天》完整目录:目录上一节我们讲了K折交叉验证的理论,下面我们将K折交叉验证算法应用到K近邻分类算法中,用来选择最合适的超参数K值。数据集iris_data.c... 查看详情

机器学习100天(三十):030k近邻分类算法-k值的选择(代码片段)

机器学习100天,今天讲的是:K近邻分类算法-K值的选择。《机器学习100天》完整目录:目录上一节我们讲了K折交叉验证的理论,下面我们将K折交叉验证算法应用到K近邻分类算法中,用来选择最合适的超参数K值。数据集iris_data.c... 查看详情

机器学习机器学习分类算法--k近邻算法knn(下)(代码片段)

六、网格搜索与K邻近算法中更多的超参数七、数据归一化FeatureScaling解决方案:将所有的数据映射到同一尺度      八、scikit-learn中的Scalerpreprocessing.pyimportnumpyasnpclassStandardScaler:def__init__(self):self.mean_=None 查看详情

机器学习100天(三十一):031k近邻回归算法

机器学习100天,今天讲的是:K近邻回归算法!《机器学习100天》完整目录:目录一、理论介绍我们之前讲了K近邻分类算法,用来处理分类问题。其实K近邻也可以用来处理回归问题。如左图所示,K近邻分类算法的思路是选取与... 查看详情

机器学习100天(三十一):031k近邻回归算法

机器学习100天,今天讲的是:K近邻回归算法!《机器学习100天》完整目录:目录一、理论介绍我们之前讲了K近邻分类算法,用来处理分类问题。其实K近邻也可以用来处理回归问题。如左图所示,K近邻分类算法的思路是选取与... 查看详情

机器学习|浅谈k-近邻算法

K-近邻(KNN)算法是解决分类问题的算法。既可以解决二分类,也可以解决多分类问题。其实它也可以解决回归问题。  K-近邻原理:  某个样本的类别,由与之最相近的K个邻居投票所决定。  例子:  现在有一个样本集... 查看详情

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

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

机器学习——基础整理:非参数方法——parzen窗估计k近邻估计;k近邻分类器

   本文简述了以下内容:(一)非参数方法(二)Parzen窗估计(三)k近邻估计(四)k近邻算法(k-nearestneighbor,kNN) (一)非参数方法(Non-parametricmethod)   对于生成模型来说,重要的地方在于类条件... 查看详情

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

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

mooc机器学习第六天-k近邻,决策树,朴素贝叶斯分类器简单尝试

1.下面的代码是上一篇理论中的小例子fromsklearn.neighborsimportKNeighborsClassifier#K近邻分类器fromsklearn.datasetsimportload_iris#鸢尾花数据fromsklearn.treeimportDecisionTreeClassifier#决策树分类器fromsklearn.model_selectionimportcro 查看详情

机器学习实战笔记-k近邻算法1(分类动作片与爱情片)

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

机器学习---算法---k-近邻算法

转自:https://www.cnblogs.com/Rosanna/p/3615507.html K-近邻和最近邻(K=1)是模式识别中常用的分类方法,K-近邻算法思想是找到与当前样本相邻的K个有标签样本,然后通过投票决定此样本的类别。例如下图中如何分类未知的绿色圆圈... 查看详情

机器学习k-近邻算法(代码片段)

目录1K-近邻算法简介2K-近邻算法(KNN)2.1定义2.2距离公式3电影类型分析3.1问题3.2K-近邻算法数据的特征工程处理4K-近邻算法API5案例:预测签到位置5.1分析5.2代码5.3结果分析6K-近邻总结1K-近邻算法简介目标说明K-近邻算法的距离... 查看详情