您的位置:首页 > 编程语言 > Python开发

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

2017-09-09 00:32 344 查看

第一章:k-近邻算法

k-近邻算法的优缺点和使用数据范围:

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

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

使用数据范围:数值型和标称型

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

1. 使用python导入数据

导入数据函数代码:

from numpy import *
import operator

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


这里导入了两个模块:

(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)
1015f
–> 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 matplotlib
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
plt.show()


得到结果如下:





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

在终端中输入如下命令:

from numpy import array
import matplotlib
import matplotlib.pyplot as plt
fig = 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 listdir
import operator

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]

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

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

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))

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 returnVect

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))

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: