Python:3.7.0
Anconda:3-5.3.1 64位
操作系统:win10
开发工具:sublime text(非必要)
本次实验中的重点为采用kNN算法进行手写数字识别 其中kNN算法是机器学习中入门的分类算法。其核心思想是将需要进行分类的目标放入已有充足样本的向量集中 求得与其距离最近的前k(自定超参数)个点 并返回这k个点中出现频率最高的类别 并将此类别作为模型的预测结果。
kNN算法为了实现手写数字的分类 最重要的是取得分类算法
#kNN算法
def kNNclassify()
#下示kNN算法来自人民邮电出版社的《机器学习实战》 注释为笔者所作
#输入参数依次为分类向量 样本集 标签向量和最近邻居数量
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 {} #创建一个用于存储前k个点和对应类别的字典
for i in range(k):
voteIlabel labels[sortedDistIndicies[i]] #依次取得前k个点对应的分类
classCount[voteIlabel] classCount.get(voteIlabel,0) 1 #使用get函数取得上一步中得到分类的已有得分数 若无则取0 并将结果 1保存至classCount字典内
sortedClassCount sorted(classCount.items(), key operator.itemgetter(1), reverse True) #对结果进行排序
return sortedClassCount[0][0] #返回结果
简要来说 上述算法实现了将待分类的inx向量与样本集中每一个样本进行距离求值 并对所求的距离进行排序 选取前K小的点并依次读取其所属类别 将出现次数最多的类别作为分类结果进行输出。
其中每一步的具体含义都在上示代码中写有注释 所有出现的函数操作在下面都有大致介绍。
以下所列的算法中采用函数为简介 实际应用可以自行查询
shape函数
numpy中的shape函数用于返回指定变量的某一维度的数量值 在例子中为返回dataset的第一维度值 即样本个数
tile函数
numpy中的tile函数为平铺函数 在上述kNN算法中的用法是将inX在第一维度上复制datasesize倍 在第二维度上复制一倍 即不复制
sum函数
numpy中的sum函数为指定维度求和函数 可以通过axis参数指定进行求和的维度 在本算法中将按照第二维度的值进行相加 即求距离的平方的和
argsort函数
numpy中的argsort函数为将上例中distances的元素从小到大排列 提取其对应的index(索引) 然后输出
get函数
字典自带函数get的操作为取得key对应的value 若无则取第二个参数 可不设置第二个参数
sorted函数
python中自带的sorted函数有着复杂的用法 在本算法中的作用是从大到小输出不同分类的排序
items函数
字典自带函数iteritems用于返回一个指定字典的迭代器
itemgetter函数
operator模块中的itemgetter()函数 是获取对象指定域中的值
本实验采用的样本集和测试集均为人民邮电出版社的《机器学习实战》所提供的源代码中的数据 其已将图片文件转换为文本文件 包含约2000个样本集和约900和测试集。其中具体图片如下 都取自样本集 左为数字3 右为数字7
此时可知 我们的样本是类似32✖32的文本数据 但为了分类器的处理 我们需要对其进行矩阵转换为1✖1024的向量以期可以使用前面完成的kNN算法进行分类。
#下示函数来自人民邮电出版社的《机器学习实战》 注释为笔者所作 def img2vector(filename): returnVect zeros((1,1024)) #新建并初始化一个初值赋0的矩阵 fr open(filename) #打开传入文件 for i in range(32): lineStr fr.readline() #读取文本文件 for j in range(32): returnVect[0,32*i j] int(lineStr[j]) #将32*32的矩阵依次赋值给1*1024的矩阵 return returnVect #返回结果
上示函数中调用的值得说明的numpy函数只有zeros 新建并初始化一个初值赋0的矩阵。该函数可以完成32✖32的原文件转换并存储为1✖1024的numpy数组。下面进行一个小小的测试 将上图中左边的3进行转换。可以看到前两行的结果完全正确。
接下来准备工作都已经做完 只要将数据喂入准备好的识别系统代码即可 详细代码如下
#下示函数来自人民邮电出版社的《机器学习实战》 注释为笔者所作 def handwritingClassTest(): hwLabels [] #设定用于存储的列表 trainingFileList listdir( trainingDigits ) #读取样本集 m len(trainingFileList) #读取样本集长度 trainingMat zeros((m,1024)) #新建并初始化一个初值赋0的矩阵 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) #将32*32转换为1*1024 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) #将32*32转换为1*1024 classifierResult classify0(vectorUnderTest, trainingMat, hwLabels, 3) #将转化后的结果丢入kNN分类器 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))) #输入错误率
上述的函数用于完成手写数字识别系统 简单来说 上述的函数所作的操作是先将样本集依次读入 并依其所属的数字分类 并将他们进行格式的转换。完成后对测试数据进行依次读入的操作 丢入kNN分类算法后得出预测值 并与真实值进行比较 并将不符合的错误结果进行记录 从而得出最终整个系统的错误率。下面附上完整代码截图和测试截图。
如果同样是采用本书的源代码 需要注意由python版本带来的两点注意事项
源代码中的print采用的是python2的格式 本次实验采用python3 需要依照本博客提供的格式进行修改.源代码中采用了iteritems函数 在python3.5中需要如本博客中一样修改为items


