《机器学习实战》学习笔记(2、k-近邻算法)

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

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

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

工作原理:

存在样本训练集和对应的标签。输入没有标签的新数据后,将新数据的每个特征与,样本数据的对应特征进行比较,然后提取样本集中,特征最近邻的分类标签。一般选样本集中,前k个最相似的数据。

k-近邻算法的一般流程:

收集数据、准备数据(距离计算,最好是结构化数据格式)、分析数据、测试算法(计算错误率)、使用算法

导入数据,编写创建数据集和标签模块:

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):
    # inX为用于分类的输入向量
    # 计算距离
    dataSetSize = dataSet.shape[0]  # 样本集的行数
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet  # 把inX二维数组化,dataSetSize表示生成数组后的行数,1表示列的倍数。
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)  # 参数等于1的时候,表示矩阵中行之间的数的求和
    distances = sqDistances ** 0.5

    sortedDistIndicies = distances.argsort()  # 对一个数组进行非降序排序
    classCount = {}
    # 选择距离最小的k个点
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    # 按照键值的值,从大到小排序
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    # 返回最大频率的类标签,sortedClassCount是个元组构成的列表
    return sortedClassCount[0][0]

使用欧式距离公式计算所有点的距离,对距离按照从小到大的次序排序,然后确定前k个距离最小元素所在的主要分类。

最后,将classCount字典分解为元组列表,通过运算符模块的itemgetter方法,按照第二元素逆序排序,最后返回,发生频率最高的元素标签。

 

如何测试分类器:

可以使用多种方法检测分类器的正确率。不同的算法在不同数据集上的表现可能完全不同。

分类器的错误率,分类器给出错误结果的次数,除以测试执行的总数。

错误率,是常用的评估方法,主要用于评估分类器在某个数据集上的执行效果。

 

示例:使用k-近邻算法改进约会网站的配对效果

分析数据时,可使用Matplotlib画二维扩散图

解析文本文件,导入数据:

def file2matrix(filename):
    fr = open(filename)
    numberOfLines = len(fr.readlines())  # 得到文件行数
    returnMat = zeros((numberOfLines, 3))  # 创建返回的numPy矩阵
    classLabelVector = []
    fr = open(filename)
    index = 0
    # 解析文件数据到列表
    for line in fr.readlines():
        line = line.strip()  # 截取掉所有的回车字符
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]  # 选取前3个元素
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector

Numpy库提供的数组操作,并不支持python自带的数组类型。

 

分析数据:使用Matplotlib创建散点图:

Matplotlib库提供的scatter函数,支持个性化标记散点图上的点。

import matplotlib.pyplot as plt

datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
fig = plt.figure()
ax = fig.add_subplot(111)
# 后面两个为标签矩阵,第一个参数标记x轴,第二个标记y轴
ax.scatter(datingDataMat[:, 0], datingDataMat[:, 1], 15.0 * array(datingLabels), 15.0 * array(datingLabels))
plt.show()

 

准备数据:归一化数值:

因为某一特征值有可能会远大于其他特征值,因此需要数值归一化。将取值范围,处理为0到1,或者-1到1之间。

公式为:

def autoNorm(dataSet):
    minVals = dataSet.min(0)  # 将每列的最小值,放在变量minVals中,0表示求列的最小值
    maxVals = dataSet.max(0)  # 将每列的最大值,放在变量maxVals中
    ranges = maxVals - minVals
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals, (m, 1))  # dataSet矩阵有1000x3个值
    normDataSet = normDataSet / tile(ranges, (m, 1))  # minVals和range的值都为1x3
    return normDataSet, ranges, minVals

在NumPy库中,矩阵除法需要使用函数,linalg.solve(matA, matB)

 

测试算法:作为完整程序验证分类器:

机器学习算法,一个很重要得工作就是,评估算法的正确率。

通常,使用已有数据的90%作为训练样本来训练分类器,使用其余的10%数据,通过随机选取,去测试分类器。

def datingClassTest():
    hoRatio = 0.50  # hold out 10%
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = normMat.shape[0]  # 1000行
    numTestVecs = int(m * hoRatio)  # 从1000行中抽取测试集
    errorCount = 0.0
    for i in range(numTestVecs):
        # 参数分别为测试集的一个元素、样本集、标签集、k值
        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)

numTestVecs为测试向量的数量,这个决定了normMat向量中,那些数据可用于测试。

检测错误率,依赖于分类算法、数据集和程序设置,如k值设置。

 

使用算法:构建完整可用系统:

写一段程序,输入一个男生的个人信息,程序会给出,某个女生对对方的喜欢程度的预测值。

def classifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']
    percentTats = 10
    ffMiles = 10000
    iceCream = 0.5
    datingDataMat, datingLabels = file2matrix('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])

输出是:in small doses

 

示例:手写识别系统

将把一个32x32的二进制图像矩阵,转换为1x1024的向量。

def img2vector(filename):
    returnVect = zeros((1, 1024))  # 创建1x1024的NumPy数组
    fr = open(filename)
    for i in range(32):
        # 循环读出文件的前32行
        lineStr = fr.readline()
        for j in range(32):
            # 将每行的头32个字符值存储在NumPy数组中
            returnVect[0, 32 * i + j] = int(lineStr[j])
    return returnVect

手写数字识别系统的测试代码: 

def handwritingClassTest():
    hwLabels = []
    trainingFileList = listdir('trainingDigits')  # 列举该目录下的所有文件名
    m = len(trainingFileList)
    trainingMat = zeros((m, 1024))  # 创建m行1024列的训练矩阵
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]  # take off .txt
        classNumStr = int(fileStr.split('_')[0])
        hwLabels.append(classNumStr)
        trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)
    testFileList = listdir('testDigits')  # iterate through the test set
    errorCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]  # take off .txt
        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("\nthe total number of errors is: %d" % errorCount)
    print("\nthe total error rate is: %f" % (errorCount / float(mTest)))

改变k值、随机选取训练样本、改变训练样本数目,都会对错误率产生影响。

上述算法执行效率并不高,因为每个测试向量要做2000次距离计算,每个距离计算包括,1024个维度浮点运算。

因此,可用k决策树来优化。

小结:

缺点:1、算法必须保存全部数据集,若训练数据集很大,必须使用大量的存储空间。2、也必须对数据集中每个数据计算距离值。3、无法给出任何数据的基础结构信息,也就无法知晓,平均实例样本和典型实例样本具有什么特征。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值