zoukankan      html  css  js  c++  java
  • 《机器学习实战》-学习笔记02-k近邻算法(KNN)

    1、算法概述

      1.1算法特点

        简单的说:k-近邻算法采用测量不同特征值之间的距离方法进行分类。

        k-近邻算法    

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

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

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

     

    1.2 工作原理

      存在一个训练样本集,并且每个样本都存在标签(有监督学习)。输入没有标签的新样本数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,

      然后算法提取出与样本集中特征最相似的数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,

      而且k通常不大于20。最后选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

     

    1.3 实例解释

        以电影分类为例子,使用k-近邻算法分类爱情片和动作片。有人曾经统计过很多电影的打斗镜头和接吻镜头,下图显示了6部电影的打斗和接吻镜头数。 

        假如有一部未看过的电影,如何确定它是爱情片还是动作片呢?

                        

        ①首先需要统计这个未知电影存在多少个打斗镜头和接吻镜头,下图中问号位置是该未知电影出现的镜头数 

                

        ②之后计算未知电影与样本集中其他电影的距离(相似度),具体算法先忽略,结果如下表所示:

                

        ③将相似度列表排序,选出前k个最相似的样本。此处我们假设k=3,将上表中的相似度进行排序后前3分别是:He’s Not Really into Dudes,Beautiful Woman,

        California Man.

        ④统计最相似样本的分类。此处很容易知道这3个样本均为爱情片。

        ⑤将分类最多的类别作为未知电影的分类。那么我们就得出结论,未知电影属于爱情片。

        

    2 代码实现

      2.1 k-近邻简单分类的应用

        2.1.1k-近邻算法一般流程

        

        2.1.2 Python实现代码及注释

        

      1 #-*- coding:utf-8 -*-
      2 __author__ = 'huststl'
      3 
      4 from numpy import *
      5 import operator
      6 
      7 def createDataSet():
      8     """
      9     函数作用:构建一组训练数据(样本),共四个样本
     10     同时给出了这四个样本的标签,及labels
     11     """
     12     group = array([
     13         [1.0,1.1],[1.0,1.0],[0.,0.],[0.,0.1]
     14     ])
     15     labels = ['A','A','B','B']
     16     return group,labels
     17 
     18 def classify0(inX,dataset,labels,k):
     19     """
     20     :param inX: inX是一个输入样本,是一个[x,y]样式的
     21     :param dataset: 是训练样本集
     22     :param labels: 是训练样本集
     23     :param k: 是top k最相近的
     24     :return:
     25     """
     26     #shape返回矩阵的[行数,列数],
     27     #那么shape[0]获取数据集的行数,
     28     #行数就是样本的数量
     29     dataSetSize = dataset.shape[0]
     30 
     31     """
     32     下面的求距离过程就是按欧式距离的公式计算的,即根号(x^2+y^2)
     33     """
     34     #tile属于numpy模块下的函数
     35     #tile(A,reps)返回一个shape = reps的矩阵,矩阵的每个元素是A
     36     #比如A = [0,1,2]那么,tile(A,2)=[0,1,2,0,1,2]
     37     #tile(A,(2,2)) = [[0,1,2,0,1,2],
     38     #                 [0,1,2,0,1,2]]
     39     #tile(A,(2,1,2)) =[[[0,1,2,0,1,2]],
     40     #                  [[0,1,2,0,1,2]]]
     41     # 上边那个结果的分开理解就是:
     42     # 最外层是2个元素,即最外边的[]中包含2个元素,类似于[C,D],而此处的C=D,因为是复制出来的
     43     # 然后C包含1个元素,即C=[E],同理D=[E]
     44     # 最后E包含2个元素,即E=[F,G],此处F=G,因为是复制出来的
     45     # F就是A了,基础元素
     46     # 综合起来就是(2,1,2)= [C, C] = [[E], [E]] = [[[F, F]], [[F, F]]] = [[[A, A]], [[A, A]]]
     47     # 这个地方就是为了把输入的测试样本扩展为和dataset的shape一样,然后就可以直接做矩阵减法了。
     48     # 比如,dataset有4个样本,就是4*2的矩阵,输入测试样本肯定是一个了,就是1*2,为了计算输入样本与训练样本的距离
     49      # 那么,需要对这个数据进行作差。这是一次比较,因为训练样本有n个,那么就要进行n次比较;
     50     # 为了方便计算,把输入样本复制n次,然后直接与训练样本作矩阵差运算,就可以一次性比较了n个样本。
     51     # 比如inX = [0,1],dataset就用函数返回的结果,那么
     52     # tile(inX, (4,1))= [[ 0.0, 1.0],
     53     #                   [ 0.0, 1.0],
     54     #                   [ 0.0, 1.0],
     55     #                   [ 0.0, 1.0]]
     56     # 作差之后
     57     # diffMat = [[-1.0,-0.1],
     58     #           [-1.0, 0.0],
     59     #           [ 0.0, 1.0],
     60     #           [ 0.0, 0.9]]
     61     diffMat = tile(inX,(dataSetSize,1))-dataset
     62     # diffMat就是输入样本与每个训练样本的差值,然后对其每个x和y的差值进行平方运算。
     63     # diffMat是一个矩阵,矩阵**2表示对矩阵中的每个元素进行**2操作,即平方。
     64     # sqDiffMat = [[1.0, 0.01],
     65     #              [1.0, 0.0 ],
     66     #              [0.0, 1.0 ],
     67     #              [0.0, 0.81]]
     68     sqDiffMat = diffMat **2
     69     # axis=1表示按照横轴,sum表示累加,即按照行进行累加。
     70     # sqDistance = [[1.01],
     71     #               [1.0 ],
     72     #               [1.0 ],
     73     #               [0.81]]
     74     sqDistance = sqDiffMat.sum(axis=1)
     75     #对平方根进行开根号
     76     distance = sqDistance **0.5
     77 
     78     # 按照升序进行快速排序,返回的是原数组的下标。
     79     # 比如,x = [30, 10, 20, 40]
     80     # 升序排序后应该是[10,20,30,40],他们的原下标是[1,2,0,3]
     81     # 那么,numpy.argsort(x) = [1, 2, 0, 3]
     82     sortedDistIndicies = distance.argsort()
     83     #存放最终的分类结果及相应的结果投票集
     84     classCount = {}
     85 
     86     #投票过程,就是统计前k个最近的样本所属类别包含的样本个数
     87     for i in range(k):
     88     # index = sortedDistIndicies[i]是第i个最相近的样本下标
     89     # voteIlabel = labels[index]是样本index对应的分类结果('A' or 'B')
     90         voteIlabel = labels[sortedDistIndicies[i]]
     91     # classCount.get(voteIlabel, 0)返回voteIlabel的值,如果不存在,则返回0
     92     # 然后将票数增1
     93         classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
     94 
     95     # 把分类结果进行排序,然后返回得票数最多的分类结果
     96     sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
     97     return sortedClassCount[0][0]
     98 
     99 if __name__ == "__main__":
    100     #导入数据
    101     dataset,labels = createDataSet()
    102     inX = [0.1,0.1]
    103     #简单分类
    104     className = classify0(inX,dataset,labels,3)
    105     print ('the class of test sample is %s' %className)
    knn01

         其结果如图(python3.6编译环境)

        

      

      2.2 使用K-近邻算法改进约会网站的配对效果

        2.2.1算法一般流程

          

        2.2.2 python3代码实现及注释  

    datingTestSet.txt 文件中有1000行的约会数据,样本主要包括以下3种特征:

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

    上述特征数据输人到分类器之前,必须将待处理数据的格式改变为分类器可以接受的格式 。在kNN.py中创建名为 file2matrix 的函数,以此来处理输人格式问题。该函数

    的输人为文件名字符串输出为训练样本矩阵和类标签向量。autoNorm 为数值归一化函数,将任意取值范围的特征值转化为0到1区间内的值。最后,datingClassTest 函

    数是测试代码。

    将下面的代码增加到 kNN.py 。  

     1 #使用k-近邻算法在约会网站
     2 def file2matris(filename):
     3     """
     4     从文件中读入训练数据,并存储为矩阵
     5     :param filename:
     6     :return:
     7     """
     8     fr = open(filename)
     9     array0lines = fr.readlines()
    10     number0fLines = len(array0lines) #获取 n =样本的行数
    11     returnMat = zeros((number0fLines,3)) #创建一个2维矩阵用于存放训练样本数据,一共有n行,每一行存放3个数据
    12     classLabelVector = []  #创建一个1维数组用于存放训练样本标签。
    13     index =0
    14     for line in array0lines:
    15         line = line.strip() #把回车符号给去掉
    16         listFromLine = line.split('	') #把每一行数据用	分割
    17         returnMat[index,:] = listFromLine[0:3] # 把分割好的数据放至数据集,其中index是该样本数据的下标,就是放到第几行
    18         classLabelVector.append(int(listFromLine[-1])) # 把该样本对应的标签放至标签集,顺序与样本集对应。
    19         index +=1
    20     return returnMat,classLabelVector
    21 
    22 def autoNorm(dataSet):
    23     """
    24     训练数据归一化
    25     :param dataSet:
    26     :return:
    27     """
    28     # 获取数据集中每一列的最小数值
    29     # 以createDataSet()中的数据为例,group.min(0)=[0,0]
    30     minVals = dataSet.min(0)
    31     maxVals = dataSet.max(0) # 获取数据集中每一列的最小数值 group.max(0)=[1, 1.1]
    32     ranges = maxVals - minVals #最大值与最小的差值
    33     normDataSet =zeros(shape(dataSet)) # 创建一个与dataSet同shape的全0矩阵,用于存放归一化后的数据
    34     m = dataSet.shape[0]
    35     # 把最小值扩充为与dataSet同shape,然后作差,具体tile请翻看 第三节 代码中的tile
    36     normDataSet = dataSet -tile(minVals,(m,1))
    37     # 把最大最小差值扩充为dataSet同shape,然后作商,是指对应元素进行除法运算,而不是矩阵除法。
    38     # 矩阵除法在numpy中要用linalg.solve(A,B)
    39     normDataSet = normDataSet/tile(ranges,(m,1))
    40     return normDataSet,ranges,minVals
    41 
    42 def datingClassTest():
    43     #将数据集中10%的数据留作测试用,其余的90%用于训练
    44     hoRatio = 0.10
    45     datingDataMat,datingLabels = file2matris('datingTestSet2.txt')  #load data setfrom file
    46     normMat,ranges,minVals = autoNorm(datingDataMat)
    47     m = normMat.shape[0]
    48     numTestVecs = int(m*hoRatio)
    49     errorCount = 0.0
    50     for i in range(numTestVecs):
    51         classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
    52         print("the classifier came back with: %d, the real answer is: %d, result is :%s"
    53               % (classifierResult,datingLabels[i],classifierResult==datingLabels[i]))
    54         if(classifierResult!=datingLabels[i]):
    55             errorCount +=1.0
    56         print("the total error rate is: %f" %(errorCount/float(numTestVecs)))
    57         print(errorCount)
    knn.py

     

       2.3 手写识别系统实例

        2.3.1算法流程:

        

      ·   

        2.3.2 准备数据:              

        为了简单起见,这里构造的系统只能识别数字09。需要识别的数字已经使用图形处理软件,处理成具有相同的色彩和大小 : 宽髙是32像素x 32像素的黑白图像。

        尽管采用文文本格式存储图像不能有效地利用内存空间,但是为了方便理解,我们还是将图像转换为文本格式。

        trainingDigits是2000个训练样本,testDigits是900个测试样本。

    2.3.3 Python实现代码 

     1 from os import listdir
     2 def img2vector(filename):
     3     """
     4     将图片数据转换为01矩阵,每张图片是32*32像素,也就是一共1024字节,因此转换的时候,每行代表
     5     一个样本,每个样本含1024个字节
     6     :param filename:
     7     :return:
     8     """
     9     #每个样本数据是1024 = 32*32个字节
    10     returnVect = zeros((1,1024));
    11     fr = open(filename)
    12     for i in range(32):#循环读取32行,32列
    13         lineStr = fr.readlines()
    14         for j in range(32):
    15             returnVect[0,32*i+j] = int(lineStr[j])
    16     return returnVect
    17 def handwritingClassTest():
    18     hwLabels = []
    19     trainingFileList = listdir('trainingDigits')
    20     m = len(trainingFileList)
    21     trainingMat = zeros((m,1024))
    22     for i in range(m):
    23         # 从文件名中解析出当前图像的标签,也就是数字是几
    24          # 文件名格式为 0_3.txt 表示图片数字是 0
    25         fileNameStr = trainingFileList[i]
    26         fileStr = fileNameStr.split('.')[0]
    27         classNumStr = int(fileStr.split('_')[0])
    28         hwLabels.append(classNumStr)
    29         trainingMat[i,:] = img2vector('trainingDigits/%s' %fileNameStr)
    30     #加载测试数据
    31     testFileList = listdir('testDigits')
    32     errorCount = 0.0
    33     mTest = len(testFileList)
    34     for i in range(mTest):
    35         fileNameStr = testFileList[i]
    36         fileStr = fileNameStr.split('.')[0]
    37         classNumStr = int(fileStr.split('_')[0])
    38         vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
    39         classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
    40         print("the classifier came back with: %d, the real answer is: %d, The predict result is: %s" % (
    41         classifierResult, classNumStr, classifierResult == classNumStr))
    42         if (classifierResult != classNumStr): errorCount += 1.0
    43         print("
    the total number of errors is: %d / %d" % (errorCount, mTest))
    44         print("
    the total error rate is: %f" % (errorCount / float(mTest)))
    knn03
  • 相关阅读:
    WinForm简单的打包和部署
    C#实现获取枚举的描述
    C#实现序列化对象到XML文档与反序列化
    cookie与session的区别与联系
    windows环境下创建多个Redis实例
    struts2中的constant配置详解
    PowerDesigner使用教程
    配置JAVA环境变量
    CAS实现SSO单点登录原理
    OVER(PARTITION BY)函数用法
  • 原文地址:https://www.cnblogs.com/huststl/p/7905486.html
Copyright © 2011-2022 走看看