K-近邻算法(k-Nearest Neighbor)

简单的说,K-近邻算法采用测量不同特征值之间的距离方法进行分类。{使用欧几里得度量(euclidean metric)}
优点:精度高、对异常值不敏感、无数据输入假定。
缺点:计算复杂度高、空间复杂度高。
适用数据范围:数值型和标程称型。
 
K-近邻算法的一般流程
(1)收集数据:可以使用任何方法。
(2)准备数据:距离计算所需要的数值,最好是结构化的数据格式。
(3)分析数据:可以使用任何方法。
(4)训练算法:此步骤不适用于K-近邻算法。
(5)测试算法:计算错误率。
(6)使用算法:首先需要输入样本数据和结构化的输出结果,然后运行K-近邻算法判断输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。
 
K-近邻算法是分类数据最简单最有效的算法。K-近邻算法是基于实例的学习,使用算法时我们必须有接近实际数据的训练样本数据。K-近邻算法必须保存全部数据集,如果训练数据集的很大,必须使用大量的存储空间。此外,由于必须对数据集中的没个数据计算距离值。实际使用时可能非常耗时。
K-近邻算法的另一个缺陷是无法给出任何数据的基础结构信息,因此我们也无法知晓平均实例样本和典型实例样本具有什么特征。
 
官方给的是python 2的代码,很多东西在python 3中已经无法使用,并且python 2 也快停止维护了。
增加了很多注释,能够更方便的理解 。
 

# -*- coding: utf-8 -*-
# K阶邻算法
from numpy import *
import operator
import matplotlib
import matplotlib.pyplot as plt
from os import listdir
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
# 用kNN计算电影级别
def classify0(inX, dataSet, labels, k):  # 测试  训练  结果  k阶
    # shape 的功能是查看矩阵或者数组的维数。
    # shape()或者a.shape返回的是矩阵的行列数([a,b],其中a是行数,b是列数)
    # shape[0] 只返回行数  shape[1] 只返回列数
    dataSetSize = dataSet.shape[0]
    # tile 重复A的各个维度
    # tile(A,B) 在行方向上重复A次,在列方向上重复B次
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet
    # diffMat的二次方  (a**b == a的b次方)
    sqDiffMat = diffMat ** 2
    # sum  numpy 下的方法 默认axis为None,表示将所有元素的值相加 axis=1表示按行相加 , axis=0表示按列相加
    sqDistances = sqDiffMat.sum(axis=1)
    # 开根号
    distances = sqDistances ** 0.5
    # argsort返回数值从小到大的索引(值数组索引0,1,2,3)
    sortedDistIndecies = distances.argsort()
    # 新建字典
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndecies[i]]
        # 在字典中查找voteIlabel,如果找不到返回0,默认为 None
        # 不断累加计数的过程
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    # python3中:classCount.iteritems()修改为classCount.items()
    # sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list。
    # reverse默认升序
    # key关键字排序itemgetter(1)按照第1位元素的大小(字典序)排序
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    # 返回的sortedClassCount 是对出现次数的排序 返回排序的第一个元素的第一个子元素
    # eg:[('A', 2), ('B', 1)]   返回  ('A', 2)  中的  第0个  'A'
    return sortedClassCount[0][0]
# 从文件里读取数据
def file2Matrix(filename):
    fr = open(filename)
    # readlines() 方法用于读取所有行(直到结束符 EOF)并返回列表
    arrryOlines = fr.readlines()
    # 获取数据行数
    numberOfLines = len(arrryOlines)
    # zeros 创建0矩阵
    returnMat = zeros((numberOfLines, 3))
    classLabelVector = []
    index = 0
    for line in arrryOlines:
        line = line.strip()
        listFromLine = line.split('\t')
        # t[index, :]   ==   t[index: , index: ]  对于二维数组的遍历
        # t[: ,index]   ==   t[: index, : index]  对于二维数组的遍历
        returnMat[index, :] = listFromLine[0:3]
        # -1索引表示最后一列元素,位label信息存储在classLabelVector
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector
# 画特征图
def paintFigure(datingDataMat, datingLabels):
    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.show()
# 归一化数值方法
def autoNorm(dataSet):
    # min(0)中的参数0 使得函数可以从从列中选取最小值,而不是选取当前行的最小值
    minVals = dataSet.min(0)
    # max(0)中的参数0 使得函数可以从从列中选取最大值,而不是选取当前行的最大值
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    # 创建数据长度大小的数组(shape 返回维数[a,b])
    normDataSet = zeros(shape(dataSet))
    # shape[0] 只返回行数  shape[1] 只返回列数
    m = dataSet.shape[0]
    # 这里是对矩阵进行计算,而不是对单一数据进行计算,用tile()对minVals展开到m,(m,1) 沿着各个维度延伸的次数
    normDataSet = dataSet - tile(minVals, (m, 1))
    normDataSet = normDataSet / tile(ranges, (m, 1))
    return normDataSet, ranges, minVals
# 对文件数据进行k阶邻算法
def datingClassTest():
    # 测试数据百分比
    hoRatio = 0.10
    datingDataMat, datingLabels = file2Matrix(
        'G:\MLiA_SourceCode\machinelearninginaction\Ch02\datingTestSet2.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 classifer came back with:%d,the real answer is:%d" % (classifierResult, datingLabels[i]))
        if (classifierResult != datingLabels[i]):
            errorcount += 1
    print("the total errror rate is:%f" % (errorcount / float(numTestVecs)))
def classifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']
    percentTats = float(input("percentage of time spent playing miles earned per year?"))
    ffMiles = float(input("frequent flier miles earned per year?"))
    iceCream = float(input("liters of ice cream consumed per year?"))
    datingDataMat, datingLabels = file2Matrix(
        "G:\MLiA_SourceCode\machinelearninginaction\Ch02\datingTestSet2.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])
# 将二进制图片存到数组中
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('G:/MLiA_SourceCode/machinelearninginaction/Ch02/digits/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(
            'G:/MLiA_SourceCode/machinelearninginaction/Ch02/digits/trainingDigits/%s' % fileNameStr)
    testFileList = listdir('G:/MLiA_SourceCode/machinelearninginaction/Ch02/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 = img2vector(
            'G:/MLiA_SourceCode/machinelearninginaction/Ch02/digits/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("\nthe total number of errors is: %d" % errorCount)
    print("\nthe total error rate is: %f" % (errorCount / float(mTest)))
if __name__ == '__main__':
    # 测试电影分级方法
    # groups, labels = createDataSet()
    # print(classify0([0.5, 1.5], groups, labels, 3))
    # 测试画特征图方法
    # datingDataMat, datingLabels = file2Matrix(
    #     'G:\MLiA_SourceCode\machinelearninginaction\Ch02\datingTestSet2.txt')
    # paintFigure(datingDataMat, datingLabels)
    # 测试autoNorm 归一化数值方法
    # print(datingDataMat[0:5])
    # print("\n\n")
    # datingDataMat, ranges, minVals = autoNorm(datingDataMat)
    # print(ranges)
    # print("\n\n")
    # print(minVals)
    # print("\n\n")
    # print(datingDataMat[0:5])
    # 测试约会网站评级
    # datingClassTest()
    # 输入数据测试约会网站
    # classifyPerson()
    # 测试手写系统
    handwritingClassTest()
    # 矩阵输出测试
    # a = arange(9)
    # b = a.reshape(3, 3)
    # print(b)
    # print("\n\n")
    # print(b[0:1, 0:2])

 


0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注