用Python构建一个K近邻分类器,识别手写数字,其中书中给出的训练集数据1934个,测试集数据946个,
在没有交叉验证的情况下,最终的识别率为98.2%
手写数字经过图像处理之后,变成只由0、1构成32*32矩阵,存放在.txt文件中,如下图:
好了,详细剖析如下:
"""
用 k-nearest neighbors实现手写数字识别
"""
import os
import numpy as np
project_path = '/Users/lan2720/Documents/lan/machinelearninginaction/Ch02/digits/'
def img2vector(filename): #dateSet_path type: string--path
#list_dir = os.listdir(dir)
returnVector = np.zeros((1,1024)) #代表1行1024列
"""
创建一个(1,1024)全零向量,每读一行就将读到的内容赋值到这个向量中去,每一行都是纯数字,0或1,而不是string类型的
"""
"""
numpy中二维:a = np.array([[x,x,x],[x,x,x],(x,x,x)]) 一定要有外部的中括号---代表一个矩阵 每一行也是用[]包含
一维: a = np.array([x,x,x])
下标从0开始
numpy默认是形成float64型0.0 64位机
"""
fn = open(filename)
for row in range(32):
line = fn.readline()
for column in range(32):
returnVector[0][row*32+column] = int(line[column])
fn.close()
return returnVector
#print line
def classify0(inX , dataSet , labels , k): #此时dateSet给的是一个矩阵
"""
np.tile() : a = [1,2,3]
则 a1 = np.tile(a , 2) --> [1,2,3,1,2,3] 此时是一个向量
a2 = np.tile(a, (1,2)) --> [[1,2,3,1,2,3]] 与上一中的区别在于,这是个矩阵了,只有一行的矩阵
a3 = np.tile(a , (2,1,2)) --> 最前面那个2代表现在有2个矩阵了
"""
""".sort() : 把当前序列变得有序
sorted() : 原序列不变,返回一个排好序的序列,可以另赋值
"""
"""
.shape 可以求出一个矩阵的维,多少行,多少列(row , column)
.shape[0] 得到矩阵的行数
.shape[1] 得到矩阵的列数
"""
"""思路:"""
"""1、求出dataset是有多少行,才好将这个inX和每一行进行处理"""
dataSetRow = dataSet.shape[0]
"""2、将inX向量扩展成这么多行的,以便每行对应处理,用到np.tile(瓦片)"""
diffMat = np.tile(inX , (dataSetRow , 1)) - dataSet #dataSetRow行,1整个列,1个矩阵
sqDiffMat = diffMat**2 #差的每一项都要平方
"""3、将sqDiffMat按行求和,每一行代表着inX和数据集中每一个数据比较的结果"""
sqDistance = sqDiffMat.sum(axis = 1) #axis = 0 代表按列求和, 这个可以按行或列求和的sum,是numpy中的,所以需要.sum(axis = ...)
# 按行求和之后的sqDistance是一个向量
distance = sqDistance**0.5 #向量
"""4、既然得到了距离,接下来就是排序了,找到距离最小的K个,
这时希望有一个函数,能排序,且返回的是元素在原序列中的位置信息--这样就能用位置信息快速找到他们的label,方便接下来统计他们的label
np.argsort()就可以返回位置信息
"""
sortedDistIndice = distance.argsort()
"""5、得到位置信息之后,取前K个,统计前K个中哪个label最多"""
countLabels = {}
for i in range(k):
voteLabel = labels[sortedDistIndice[i]]
countLabels[voteLabel] = countLabels.get(voteLabel , 0) + 1
"""6、将得到的字典{(label , count)}按照count值排序,取count最大的,代表inX就最可能是这个label"""
sortedLabels = sorted(countLabels.iteritems() , key = lambda aa: aa[1] , reverse = True) # .iteritems()返回的(key ,value),则aa[1]代表的是按第1域value排序
"""返回,注:sortedLabels还是一个dict"""
return sortedLabels[0][0]
def createDateSet():
group = np.array([[1.0,1.1],
[1.0,1.0],
[ 0 , 0 ],
[ 0 ,0.1]])
labels = [ 'A' , 'A' , 'B' , 'B']
return (group , labels)
"""但是如何让.txt文件中存储的数据变成一个matirx?"""
def handWritingClassTest():
""" /Users/lan2720/Documents/lan/machinelearninginaction/Ch02/digits/testDigits """
hwTrainLabels = []
trainFileList = os.listdir(project_path + 'trainingDigits')
"""1、提取每个样本的label---究竟是哪个数字,存到labels列表中"""
trainDataNum = len(trainFileList)
trainMat = np.zeros((trainDataNum , 1024))
for i in range(trainDataNum): #每处理一个txt文件,就将他的label取出,特征向量取出
processed_filename = trainFileList[i].strip('.txt')
label = int(processed_filename.split('_')[0])
hwTrainLabels.append(label)
trainMat[i,:] = img2vector(project_path + 'trainingDigits/' + trainFileList[i]) #第i行整体赋值
"""接下来,处理测试集"""
hwTestLabels = []
testFileList = os.listdir( project_path + 'testDigits')
testDataNum = len(testFileList)
correctCount = 0
#testMat = np.zeros((m,1024))
for i in range(testDataNum):
processed_filename = testFileList[i].strip('.txt')
correctLabel = int(processed_filename.split('_')[0])
feaVector = img2vector(project_path + 'testDigits/' + testFileList[i])
resultLabel = classify0(feaVector , trainMat , hwTrainLabels , k = 5)
if resultLabel == correctLabel:
correctCount += 1
"""计算正确率"""
correctRate = correctCount/float(testDataNum)
return (trainDataNum ,testDataNum , correctRate)
def main():
(traindata, testdata, correctrate) = handWritingClassTest()
print 'data for training: %s' % traindata
print 'data for testing : %s' % testdata
print 'Corerect Rate : %s%%' % (correctrate*100)