《Python机器学习实战》第一章读书笔记:k-近邻算法

来源:互联网 发布:数控编程员岗位职责 编辑:程序博客网 时间:2024/05/05 15:45

第一章:k-近邻算法

k-近邻算法的优缺点和使用数据范围:
优点:精度高,对异常值不敏感,无数据输入假定
缺点:计算复杂度高、空间复杂度高
使用数据范围:数值型和标称型

下面介绍k-近邻算法准备工作、代码实现及实战应用

1. 使用python导入数据

导入数据函数代码:

from numpy import *import operatordef CreateDataSet():    group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])    labels = ['A','A','B','B']    return group,labels

这里导入了两个模块:

(1)科学计算包Numpy模块(2)运算符模块,用于提供排序操作的函数

进入终端,输入python进入Python开发环境
输入以下命令以导入程序模块:

import KNN

输入以下命令来创建变量group和labels:

group,labels = KNN.CreateDataSet()

在python命令提示符下,输入变量的名字来检验是否正确的定义变量。
这里写图片描述

2. 从文本文件中解析数据

k-近邻算法的伪代码:
对未知类别属性的DataSet的每个点依次执行以下操作:
(1)计算inX(当前点集)中的点到已知类别点集中的距离
(2)以递增序将距离排序
(3)选取与inX距离最小的k个点
(4)确定前k个点所在类别的出现频率
(5)返回前k个点出现频率最高的类别作为当前点的预测分类

k-近邻算法代码:

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 = { }    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)    return sortedClassCount[0][0]

算法中出现的python知识点简单介绍一下:
(1) shape[0]返回矩阵dataSet的行数
(2) tile函数:格式tile(A,reps) ,A是输入的数组,reps是A沿各个维度重复的个数
比如A=[1,2],tile(A,(2,2,3))
结果
[[[1,2,1,2,1,2], [1,2,1,2,1,2]],
[[1,2,1,2,1,2], [1,2,1,2,1,2]]]
沿第一个维度重复三遍,沿第二个维度重复两遍,沿第三个维度重复两遍
这里diffMat相当于将inX这个向量重复dataSize行构成矩阵,并与dataSet矩阵相减且得到结果矩阵。
(3) sqDiffMat.sum(axis = 1),将sqDiffMat的每一个行向量相加,返回一个nx1的向量后转置
Eg c = np.array([[0, 2, 1], [3, 5, 6], [0, 1, 1]])
print c.sum()
print c.sum(axis=0)
print c.sum(axis=1)
结果分别是:19, [3 8 8], [ 3 14 2]
(4) argsort()函数:argsort函数返回的是数组值从小到大的索引值
x = np.array([3, 1, 2])
np.argsort(x)
输出结果:array([1, 2, 0])
这个怎么解释呢……
3最大,其索引值为0,所以argsort数组的第三项(最大项)就是其索引值0
2其次大,其索引值为2,所以argsort数组的第二项(第二大项)就是其索引值2
1最小,其索引值为1,所以argsort数组的第一项(最小项)就是其索引值1
这里最后返回一个1xn的向量用来进行下面的操作
(5) classCount = {}:新建一个字典。字典就是PHP的关联数组,键值对罢了。
用的时候再学习它的内置函数。
.get()方法:dict.get(key,default = None)
返回指定键的值,比如这里返回voteIlabel,若voteIlabel不存在,则返回0(其实就是返回get的第二个参数).
(6) sorted函数:
sorted(…):sorted(iterable, cmp=None, key=None, reverse=False) –> new sorted list
第一个参数iterable:是一个可迭代类型
第二个参数cmp:是一个比较函数,比较什么由key来决定
第三个参数key: 用列表元素的某个属性或函数进行作为关键字,有默认值,迭代集合中的一项;
第四个参数reverse:排序规则。reverse = True降序或者reverse = False升序,有默认值。

 sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)

以程序中这段代码为例,这里用operator函数来加快排序:
dict.iteritems():获得字典的一个迭代器
key=operator.itemgetter(1):去获取对象的第一个域的值
reverse = True :倒序排序获得第一个值,从而使数组的[0][0]成为最大值。

代码实现的功能:
在前面计算完所有点的距离后,可以对数据按照从小到大的顺序排序。然后确定前k个距离最小元素所在的分类,将classCount字典分解为元组列表,按照第二个元素(第一个域)对元组逆序排序。最后返回频率最高的元素标签。

输入KNN.classify0([0,0],group,labels,3),返回发生频率最高的元素标签。
这里写图片描述

3. 从文本文件中解析数据代码:

def file2matrix(filename):    fr = open(filename)    arrayOLines = fr.readlines()    numberOfLines = len(arrayOLines)    returnMat = zeros((numberOfLines,3))    classLabelVector = []    fr = open(filename)    index = 0    for line in fr.readlines():        line = line.strip()        listFromLine = line.split('\t')        returnMat[index,:] = listFromLine[0:3]        classLabelVector.append(int(listFromLine[-1]))        index += 1    return returnMat,classLabelVector

算法中出现的python知识点简单介绍一下:
(1) 有关readlines请看
http://www.cnblogs.com/qi09/archive/2012/02/10/2344964.html
主要是后面代码有用到readline但是那个时候是需要读一个文件包的文件
readlines用于读取文档内全部内容
(2) Python strip()方法用于移除字符串头尾指定的字符(默认为空格)。
(3) append()在列表末尾添加对象

代码实现的功能:
首先获得文件的行数,并且建立以零填充的矩阵。我们对文件进行处理后,将其前三列存到特征矩阵中,并且将最后一列存到向量classLabelVector,从而将文件中的数据储存到向量和矩阵中。

输入以下命令来检查数据内容:

reload(KNN)datingDataMat,datingLabels = KNN.file2matrix('datingTestSet2.txt')

4. 使用Matplotlib创建散点图
在终端中输入如下命令:

import matplotlibimport matplotlib.pyplot as pltfig = plt.figure()ax = fig.add_subplot(111)ax.scatter(datingDataMat[:,1],datingDataMat[:,2])plt.show()

得到结果如下:

这里写图片描述

这里写图片描述

5. 使用Matplotlib创建彩色的散点图

在终端中输入如下命令:

from numpy import arrayimport matplotlibimport matplotlib.pyplot as pltfig = plt.figure()ax = fig.add_subplot(111)ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))plt.show()

得到结果如下:

这里写图片描述

这里写图片描述

可视化操作……没啥可说的。
scatter函数的应用不妨看官方文档。
http://matplotlib.org/api/pyplot_api.html?highlight=scatter#matplotlib.pyplot.scatter

6. 归一化特征值

def autoNorm(dataSet):    minVals = dataSet.min(0)    maxVals = dataSet.max(0)    ranges = maxVals - minVals    normDataSet = zeros(shape(dataSet))    m = dataSet.shape[0]    normDataSet = dataSet - tile(minVals,(m,1))    normDataSet = normDataSet/tile(ranges,(m,1))    return normDataSet,ranges,minVals

代码实现的功能:
用归一化方法处理不同取值范围的特殊值。其中,normDataSet是归一化后的矩阵,ranges是每一列的数据跨度,minVals是每一列的最小值。

这里写图片描述

7. 验证分类器的测试算法:

def datingClassTest():    hoRatio = 0.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))

验证分类器的时候使用已提供的90%的数据作为训练样本来训练分类器,而使用另外10%去测试分类器,检测分类器的正确率。
它首先使用了函数file2matrix()和autoNorm()从文件中读取数据并将其转换为归一化特征值。接着计算测试向量的数量,此步决定了normMat向量中哪些数据用于测试,哪些数据用于分类器的训练样本;然后将这两部分数据输人到原始KNN分类器函数 classify0( )。最后,函数计算错误率并输出结果注意此处我们使用原始分类器,主要重点是将数据改造为分类器可以使用的特征值。

这里写图片描述

这里写图片描述

由此可见,错误率5%左右。

8. 约会网站预测函数:

def classifyPerson():    resultList = ['not at all','in small doses','in large doses']    percentTats =  float(raw_input("percentage of time spend in playing video games?"))    ffMiles = float(raw_input("frequent flier miles earned per year?"))    iceCream = float(raw_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]

算法中出现的python知识点简单介绍一下:
(1)raw_input读取输入的内容,并且以字符串的形式返回
这里写图片描述

这里就可以交互并进行预测了……

9. 将32x32的矩阵转换成1x1024的向量

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

这里写图片描述
这里主要就是矩阵的转换,将32x32的矩阵转换成1x1024的向量,否则没有办法使用classify0()函数。

10. 手写识别系统:

使用k-近邻算法的手写识别系统

(1) 收集数据:提供文本文件。
(2) 准备数据:编写函数classify0(), 将图像格式转换为分类器使用的list格式。
(3) 分析数据:在Python命令提示符中检查数据,确保它符合要求。
(4) 训练算法:此步骤不适用于各近邻算法。
(5) 测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
(6) 使用算法:本例没有完成此步驟,若你感兴趣可以构建完整的应用程序,从图像中提取数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统。(未实现)

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 "\nthe total number of errors is: %d" % errorCount    print "\nthe total error rate is: %f" % (errorCount / float(mTest))

算法中出现的python知识点简单介绍一下:
(1) listdir : os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。这个列表以字母顺序。 它不包括 ‘.’ 和’..’ 即使它在文件夹中。

代码实现的功能:
(1) 将trainingDigits目录中的文件内容储存在列表trainingFileList,然后可以得到目录中有多少文件,并将其储存在变量m中。
(2) 创建一个m行1024列的训练矩阵,该矩阵每行数据储存一个图像。
(3) 将类代码储存在hwLabels向量中,使用img2vector函数加载图像。
(4) 使用classify0()函数测试testDigits目录下的每个文件。
(5) 在Python命令提示符中输入KNN.handwritingClassTest(),测试该函数的输出结果。

这里写图片描述

这里写图片描述

这里写图片描述

(1)错误率为1.16%。改变变量 k 的值、修改函数handwritingClassTest()随机选取训练样本、改变训练样本的数目,都会对k-近邻算法的错误率产生影响。
(2)使用k决策树相当于k-近邻算法的优化版,可解决大量的计算开销。

代码

from numpy import *from os import listdirimport operatordef 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 = { }    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)    return sortedClassCount[0][0]def autoNorm(dataSet):    minVals = dataSet.min(0)    maxVals = dataSet.max(0)    ranges = maxVals - minVals    normDataSet = zeros(shape(dataSet))    m = dataSet.shape[0]    normDataSet = dataSet - tile(minVals,(m,1))    normDataSet = normDataSet/tile(ranges,(m,1))    return normDataSet,ranges,minValsdef file2matrix(filename):    fr = open(filename)    arrayOLines = fr.readlines()    numberOfLines = len(arrayOLines)    returnMat = zeros((numberOfLines,3))    classLabelVector = []    fr = open(filename)    index = 0    for line in fr.readlines():        line = line.strip()        listFromLine = line.split('\t')        returnMat[index,:] = listFromLine[0:3]        classLabelVector.append(int(listFromLine[-1]))        index += 1    return returnMat,classLabelVectordef datingClassTest():    hoRatio = 0.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))def classifyPerson():    resultList = ['not at all','in small doses','in large doses']    percentTats =  float(raw_input("percentage of time spend in playing video games?"))    ffMiles = float(raw_input("frequent flier miles earned per year?"))    iceCream = float(raw_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]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 returnVectdef 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 "\nthe total number of errors is: %d" % errorCount    print "\nthe total error rate is: %f" % (errorCount / float(mTest))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
原创粉丝点击