zoukankan      html  css  js  c++  java
  • 《ML in Action》笔记(1) —— kNN分类器

    • 本章有三个案例,分别是:
      (1) 根据打斗镜头和接吻镜头数量评估电影类型;
      (2) 根据飞行常客里程数等3个参数预判约会网站的配对效果。
      (3) 利用训练数据集的手写数字识别

    • kNN(k-近邻)算法的实现相对简单:

      1. 计算当前点与训练数据集中每个点的距离
      2. 选取与当前点最近的k个点;
      3. 统计前k个点的类别频率(选举)
      4. 取统计频率最高的那个类别,作为对当前点的分类判别。
    • 以下是代码笔记,原书代码为PYTHON2,经学习消化,改为PYTHON3.5可运行版本。

    【kNN.py: part1 —— 引入库文件】

    from numpy import *
    import operator
    from os import listdir
    
    • 从这第一个最简单的案例起,就严重依赖numpy数组的特性,盖因其便捷的矢量化运算(操作符默认对数组内每一个元素进行批量运算)。

    【kNN.py: part2 —— 分类器核心函数classify0()

    • classify0()函数是实现kNN算法的核心代码。输入参数inX是待分类的当前值,dataSet是训练数据集,labels是训练集的分类标签,k是k近邻的取值。函数输出对inX的分类判定结果。
    def classify0(inX, dataSet, labels, k):
    	# 训练集记录数
        dataSetSize = dataSet.shape[0]  
        # 计算数据与训练集的差值
        diffMat = tile(inX, (dataSetSize,1)) - dataSet 
        # 计算差值的平方
        sqDiffMat = diffMat**2
        # 按记录行求和,得到平方差
        sqDistances = sqDiffMat.sum(axis=1)
        # 开根求得欧式距离 
        distances = sqDistances**0.5  
        # 按距离升序排列
        sortedDistIndicies = distances.argsort() 
        classCount={}       
        # 选取TOP K个近邻样本   
        for i in range(k):  
    	    # TOP K对应样本的分类标签
            voteIlabel = labels[sortedDistIndicies[i]]  
            # 针对某个样本分类标签的投票计数
            classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 
        # 原书代码此处用iteritems(),PYTHON3的DICT已不支持,改用items()和lambda
        #sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
        sortedClassCount = sorted(classCount.items(), key=lambda item:item[1], reverse=True) 
        return sortedClassCount[0][0]
    
    • items()表示取出DICT的键值对(keys()values())。lambda表示取数模式:lambda item:item[1] 按数值排序,lambda item:item[0] 按键值排序,常用于sorted()函数。

    【kNN.py: part3 —— 构建案例一的简单训练集】

    def createDataSet():
        group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
        labels = ['A','A','B','B']
        return group, labels
    

    【IPYTHON —— 运行案例一】

    # 生成训练集数据
    group,labels = kNN.createDataSet()
    # 运行分类器,对样本点[0.3,0.4]进行判定
    rst = kNN.classify0([0.3,0.4],group,labels,3)
    

    【kNN.py: part4 —— 案例二用到的其他函数】

    # 加载包含训练集数据的文本文件
    def file2matrix(filename):
    	# 打开文件
        fr = open(filename)
        # 读取文件内容(一次读所有行)
        arrayOLines = fr.readlines()
        # 获取记录行数
        numberOfLines = len(arrayOLines)
        # 初始化用于存储训练集数据的数组
        returnMat = zeros((numberOfLines,3))
        classLabelVector = []
        index = 0
        # 遍历行
        for line in arrayOLines:
            # 去除行末回车符
            line = line.strip()
            # 按tab分列
            listFromLine = line.split('	')
            # 存入该行记录的3个参数。
            returnMat[index,:] = listFromLine[0:3]
            # 将该行记录对应的标签结果存入标签数组
            classLabelVector.append(int(listFromLine[-1]))
            index += 1
        # 返回训练集参数数组和标签数组
        return returnMat,classLabelVector
        
    # 数据归一化 (x-min)/(max-min)
    def autoNorm(dataSet):
        # 按列取最小值
        minVals = dataSet.min(0)    
        # 按列取最大值
        maxVals = dataSet.max(0)
        ranges = maxVals - minVals  
        normDataSet = zeros(shape(dataSet)) 
        # 取记录数
        m = dataSet.shape[0]    
        # 分子 (x-min)
        normDataSet = dataSet - tile(minVals, (m,1))
        # 计算归一化数值
        normDataSet = normDataSet/tile(ranges, (m,1))
        return normDataSet, ranges, minVals
    
    # 算法准确率测试
    def datingClassTest():
    	# 训练样本比例
        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)))
        print (errorCount)
    
    • 第一个函数file2matrix主要演示了如何加载文本数据到Numpy数组。其中倒数4行,原代码使用了listFromLine[0:3],Numpy数组的下表是“左方右圆”括号,实际只取出3个数。
    • 第二个函数autoNorm对数据进行了归一化。参数点的“尺度”对距离的计算结果影响很大,从而影响分类器的结果。本案例采用了最简单的一种归一化方法。在实际运用中,归一化的方法应该是与样本数据本身的分布密切相关的。
    • 第三个函数datingClassTest()演示了用训练数据测试分类器准确性的过程。

    【kNN.py: part5—— 案例二的实际运用】

    # 根据输入的样本参数,输出分类器结果
    def classifyPerson():
        resultList = ['not at all','in small doses','in large doses']
        # 以此读取用户输入的三个参数。
        # 原代码此处使用raw_input(),Python3 不再支持,改作input()
        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])
    

    【kNN.py: part6—— 案例三的函数代码】

    # 将二进制图形向量转为一维向量
    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
    
    # 批量运行手写数字识别的程序函数
    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') 
        errorCount = 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): errorCount += 1.0
        print ("
    the total number of errors is: %d" % errorCount)
        print ("
    the total error rate is: %f" % (errorCount/float(mTest)))
    
    • 本案例省略了将数字图形转化为二进制向量的过程,算法的模型输入是1024位长的2进制数组。随后可直接运用classify0()函数进行分类。

    算法小结

    • 这两个案例调用了同一个kNN函数classify0()。kNN算法不存在一个经训练生成后可以直接“调用”的参数模型,而是每次运行时都要计算当前点与所有点的距离,并在k-近邻中进行分类选举。
    • 距离的定义和数据的归一化方法是与业务和参数本身高度相关的,不同的选择肯定会影响分类器的结果和准确率,后续可以亲自测试一下。

    PYTHON小结

    • numPy数组的基本用法
    • 读取文本文件到numPy的方法
    • sorted排序函数的用法。sorted(classCount.items(), key=lambda item:item[1], reverse=True)
  • 相关阅读:
    01MySQL内核分析-The Skeleton of the Server Code
    debug PostgreSQL 9.6.18 using Eclipse IDE on CentOS7
    开启PG的归档模式
    11G 新特性之 密码延迟认证
    Oracle细粒度审计
    MySQL数据库回表与索引
    zabbix_api的调用(curl测试)和SQL一些解答
    【强化学习】Markov Decision processes【二】
    【强化学习】强化学习的一些基础理念【一】
    Mysql 索引优化分析
  • 原文地址:https://www.cnblogs.com/herzog/p/6361887.html
Copyright © 2011-2022 走看看