机器学习实战(一):k-近邻算法

k-近邻算法实例笔记

本文为自学《机器学习实战》的总结笔记,对于文中使用的函数基本都做了说明,适合初学者学习,对于文中的矩阵操作需要了解numpy或matlab中的矩阵的基础操作,需要初学者自行了解。
附书中源码、数据集下载地址
1) 算法简介

  1. k-近邻算法思想:测量不同特征值之间的距离并以此为根据进行分类。
  2. 工作原理:训练样本集格式为:数据+标签,输入没有标签的数据后,通过将数据特征与样本中的特征对比,取特征中最相近的数据的分类标签。一般选样本数据集中k个最相近的数据,选择k个最相近的数据中出现最多的分类,作为新数据的分类。
  3. 优点:精度高,对异常值不敏感,无数据输入假定。
  4. 缺点:计算复杂度高,空间复杂度高。
  5. 适用范围:数值型和标称型。

2) 算法实现
算法伪代码:
对未知类别属性的数据集中的每个点依次执行以下操作:

  1. 计算已知类别数据集中的点与当前点之间的距离;
  2. 按照距离递增次序排序;
  3. 选取与当前点距离最小的k个点;
  4. 确定前k个点所在类别的出现频率;
  5. 返回前k个点出现频率最高的类别作为当前点的预测分类。

3) 示例:约会网站的配对系统和手写识别系统
约会网站配对系统:
引用库文件

from numpy import *
import operator
import os

函数classify0(inX, dataSet, labels, k):
4个输入参数:
用于分类的输入向量是inX;输入的训练样本集为dataSet;标签向量为labels;参数k表示用于选择最近邻居的数目,其中labels向量的元素数目与矩阵dataSet的行数相同。
功能:
使用欧氏距离公式,计算两个向量点之间的距离,选择距离最小的k个点,排序。

def classify0(inX,dataSet,labels,k):
    dataSetSize = dataSet.shape[0]                                             
    #shape函数返回矩阵的维度大小
    diffMat = tile(inX,(dataSetSize,1)) - dataSet                              
    #tlie函数的功能是重复某个数组,将inX重复数据集维数次
    sqDiffMat = diffMat ** 2
    sqDiatances = sqDiffMat.sum(axis=1)                                        
    #参数axis=0表示按列操作,axis=1表示按行操作 
    distances = sqDiatances**0.5
    #选择距离最小的k个点
    sortedDisIndicies = sqDiatances.argsort()
    """
    argsort()函数说明:
    argsort函数返回的是数组值从小到大的索引值
    例:
    One dimensional array:一维数组
    x = np.array([3, 1, 2])
    y = np.argsort(x)
    则y的值为array([1, 2, 0])
    Two-dimensional array:二维数组
    x = np.array([[0, 3], [2, 2]])
    np.argsort(x, axis=0) #按列排序
    array([[0, 1],
    [1, 0]])
    np.argsort(x, axis=1) #按行排序
    array([[0, 1],
    [0, 1]])
    """    
    classCount={}                                                              
    for i in range(k):
      voteIlabel = labels[sortedDisIndicies[i]]
      classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    #排序
    sortedClassCount = sorted(classCount.items(),
                              key = operator.itemgetter(1), 
                              reverse = True)
    #源码中使用的是 “iteritems” ,但python3中已经没有 “iteritems” 这个属性了,现在属性是:“ items ”
    return sortedClassCount[0][0] 

函数file2matrix(filename):
输入参数:文件名
功能:将输入的文本转换为训练样本矩阵和类标签向量

def file2matrix(filename):
    #打开文件,得到文件的行数
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    #创建返回的Numpy矩阵
    returnMat = zeros((numberOfLines,3))
    classLabelVector = []
    #解析文件数据到列表
    index = 0
    for line in arrayOLines:
        line = line.strip()
        listFromLine = line.split('\t')                                        
        #用制表符\t将整行数据分割成元素列表
        returnMat[index,: ]= listFromLine[0:3]                                 
        #将3个元素分别存入返回矩阵
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat,classLabelVector

函数atuoNorm(dataSet):
输入参数:数据集dataSet
功能:处理不同取值范围的特征值,希望其影响相同要将特征值归一化
原理:newValue = (oldValue-min)/(max-min)

def autoNorm(dataSet):
    #计算数据的取值范围
    minVals = dataSet.min(0)                                                   
    #参数0使得函数可以从列中选取最小值,而不是选取当前行的最小值
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    #创建返回矩阵
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    """
    shape函数:numpy的库函数,用法如下:
    1、建立一个矩阵a,  那么a.shape表示矩阵a的维度大小
    2、建立一个数组b,  那么a.shape表示数组b的大小  
    3、建立一个4*3矩阵a,  那么a.shape[0]表示矩阵的行数,a.shape[1]表示矩阵的列数
       0表示对行操作,1表示对列操作
    """
    #特征值矩阵归一化
    normDataSet = dataSet - tile(minVals,(m,1))
    normDataSet = normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges, minVals

函数datingClassTest():
功能:测试分类算法,通过比较对训练集中数据种类的预测结果与实际结果,计算错误率

def datingClassTest():
    hoRatio = 0.10
    #采用留出法,将数据集分为训练集和测试集,此处选10%为测试集的比例
    datingDataMat, datingLabels = file2matrix('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 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)))
    #输出对应结果

函数classifyPerson():
功能:统合上述函数,对输入数据进行分类

def classifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']
    #定义三种分类结果
    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('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])
    #输出结果

手写识别系统:
系统中会用到前述的函数在此不在重复
函数img2vector(filename):
输入参数:文件名
功能:将32×32的二进制图像矩阵转换为1×1024的向量

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

函数handwritingClassTest():
功能:
a) 将训练集目录中的文件内容存储在列表中,得到训练集中文件个数,创建对应的行数的训练矩阵,从文件名中提取分类数字,将类代码存储在标签向量中,使用img2vector函数载入图像。
b) 对测试集中的文件执行类似操作,但不将文件载入矩阵,而是通过classify0函数测试该目录下的文件,得到分类结果,对比标签向量,可以得到错误率。

def handwritingClassTest():
    hwLabels = []
    trainingFileList = os.listdir('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('MLiA_SourceCode\\machinelearninginaction\\Ch02\\digits\\trainingDigits/%s' % fileNameStr)
    testFileList = os.listdir('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('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)))

测试用代码可用于检测前面所写的代码是否正确执行,将预测试的代码取消注释即可

import Knn
from numpy import *
"""
#matplotlib绘图测试

import matplotlib.pyplot as plt

datingDataMat,datingLabels = Knn.file2matrix('F:\学习任务\学习进度\MLiA_SourceCode\machinelearninginaction\Ch02\datingTestSet2.txt')

fig = plt.figure()
ax = fig.add_subplot(111)                                                      
#一行一列一个
a = array(datingLabels)
#array方法是numpy的库函数需要调用才能使用
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],
           15.0*a,15.0*a)
#scatter画散点图,使用标签属性绘制不同颜色不同大小的点
#datingDataMat为数据集,datingLabels为标签集
#ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
plt.show()
"""


"""
argsort函数测试
x = array([[0, 3,2], 
           [2, 2,1]])
h=argsort(x, axis=0) #按列排序
l=argsort(x, axis=1)
print(x)
print(h)
print(l)
"""

"""
#测试归一化矩阵
datingDataMat,datingLabels = Knn.file2matrix('MLiA_SourceCode\machinelearninginaction\Ch02\datingTestSet2.txt')
normMat, ranges, minVals = Knn.autoNorm(datingDataMat)
print(normMat)
print(ranges)
print(minVals)
"""

#Knn.datingClassTest()
#错误分类后得到的结果只有2,推测为分类函数出现问题,根据检查是分类函数的参数错误

#Knn.classifyPerson()
"""
#测试函数img2vector:图像转换为向量
testVector = Knn.img2vector('F:\\学习任务\\学习进度\\MLiA_SourceCode\\machinelearninginaction\\Ch02\\digits\\testDigits\\0_13.txt')
v1 = testVector[0,0:31]
v2 = testVector[0,32:63]
print(v1)
print(v2)
#报错ValueError: embedded null character,解决方法:地址的\需要转义符,写成\\
"""

#Knn.handwritingClassTest()

4) 总结

  1. 在以上示例中对于错误率检测,使用方法是留出法,是一种最简单的测试方法,还可以采取其他高级方法进行测试,以获得更为合理的错误率。
  2. k-近邻算法虽然简单有效,但必须保存全部的数据集,且要对数据集中每个数据计算距离,时间,空间的开销非常大,仍需对其进行优化。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值