机器学习(七):k邻近算法初级浅析案例

注:基于现有小案例攥写

K 近邻算法采用测量不同特征值之间的距离方法进行分类:

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

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

K 近邻算法适用数据范围为:数值型和标称型

标称型:标称型目标变量的结果只在有限目标集中取值,如真与假

数值型:数值型目标变量则可以从无限的数值集合中取值,如0.100,42.001等 

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

k邻近算法的一般流程:

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

准备数据集:

http://labfile.oss.aliyuncs.com/courses/777/digits.zip

trainingDigits:训练数据,1934 个文件,每个数字大约 200 个文件。

testDigits:测试数据,946 个文件,每个数字大约 100 个文件。

全部代码如下:

import numpy as np
import operator
from os import listdir
def  handwritingclassTest():
    hwLabels =[]
    trainingFileList = listdir('digits/trainingDigits')
    m = len(trainingFileList)
    trainingMat = np.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,:]=im2vector('digits/trainingDigits/%s' % fileNameStr)
    testFileList = listdir('digits/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 = im2vector('digits/testDigits/%s' % fileNameStr)
        classifierResult = classify0(vectorUnderTest,trainingMat,hwLabels,3)
        print("测试样本 %d,分类器预测: %d,真实类别:%d" % (i+1,classifierResult,classNumStr))
        if(classifierResult != classNumStr):
            errorCount += 1.0
    print("\n错误分类计数:%d" % errorCount)
    print("\n错误分类 比例:%f" % (errorCount/float(mTest)))


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

def im2vector(filename):
    #创建向量
    returnVect = np.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
#im2vector('digits/testDigits/0_1.txt')
def classify0(inX,dataSet,labels,k):
    #获取dataSet维度值
    dataSetSize = dataSet.shape[0]
    #矩阵运算,计算测试数据与每个 样本数据对应数据项的差值
    diffMat = np.tile(inX,(dataSetSize,1)) - dataSet
    #进行平方运算
    sqDiffMat = diffMat**2
    #平方运算后以行 求和
    sqDistances = sqDiffMat.sum(axis=1)
    #取平方根,得到距离向量
    distances = sqDistances**0.5
    #按照距离进行排序,取出索引值
    sortedDistIndicies = distances.argsort()
    print(sortedDistIndicies)
    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)
    #返回前 k 个点出现频率最高的类别作为当前点的预测分类。
    return sortedClassCount[0][0]
#group,labels = createDataSet()
#print(classify0([0,0],group,labels,3))
handwritingclassTest()

最后运行情况:

解释:

函数 img2vector,将图像转换为向量:该函数创建 1x1024 的 NumPy 数组,然后打开给定的文件,循环读出文件的前 32行,并将每行的头 32 个字符值存储在 NumPy 数组中,把一个 32x32 的二进制图像矩阵转换为 1x1024 的向量,最后返回数组。

函数classify0,通过欧式距离公式,计算临近点,选取与当前点距离最小的 k 个点,确定前 k 个点所在类别的出现频率,返回前 k 个点出现频率最高的类别作为当前点的预测分类。

函数handwritingClassTest(),打开 两个数据集,带入上面两个函数进行计算,进行分类,并与真实分类结果进行对比,得到最后 的错误分类比例。

上面的代码中的错误分类比例是0.0105,可以通过修改k值,改变训练样本进行测试,进一步调进错误分类 比例。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章