一、实验目的: 通过本实验掌握kNN算法的原理,熟悉kNN算法如何应用在真实世界问题中,同时掌握sklearn机器学习库的使用。 |
二、实验内容: 本实验首先使用基于Python实现kNN算法实现手写字识别,然后使用sklearn库的kNN算法实现手写字识别。 |
三、实验环境:
|
四、实验内容 def classify0(inX, dataSet, labels, k): ### Start Code Here ### #具体实现逻辑如KNN算法中所示 num = len(dataSet) #行向量方向上将inX复制m次,然后和dataSet矩阵做相减运算 n=num-1 ans=np.asarray(inX) tmp=np.asarray(inX) while n!=0: ans = np.append(ans,tmp) n -= 1 ans = np.reshape(ans,[num,2]) ans = ans-dataSet #减完后,对每个数做平方 ans = ans*ans #平方后按行求和,axis=0表 示列相加,axis-1表示行相加 dis = np.zeros([num,1],dtype=int) #开方计算出欧式距离 for i in range(num): dis[i] = ans[i][0]+ans[i][1] ans = np.reshape(dis,[1,num]) #对距离从小到大排序,注意argsort函数返回的是数组值从小到大的索引值2 y = np.argsort(ans) #用于类别/次数的字典,key为类别, value为次数 y=list(y[0]) #取出第近的元素对应的类别 dic = dict.fromkeys(list(set(labels)), 0) #对类别次数进行累加 for i in range(k): dic[labels[y[i]]]+=1 #根据字典的值从大到小排序 dic = sorted(dic.items(),key =lambda item:item[1],reverse=True) #返回次数最多的类别,即所要分类的类别 return dic[0][0] ### End Code Here ### dataSet=np.array([[250,100],[270,120],[111,230],[130,260],[200,80],[70,190]]) labels=["理科生","理科生","文科生","文科生","理科生","文科生"] inX=[105,210] print(classify0(inX,dataSet,labels,3)) 加载手写字训练集数据 接下来,我们将加载手写字训练集数据,由于kNN核心算法中,每个点都是用向量表示的。尽管已经转换为文本形式了,不过图片数据依然是32X32的二维数据格式,首先需要将其转换为一维数组,即表示成一个向量。在kNN_hand.py中添加函数img2vector用于将32X32的二维数组转换为一维数组,该函数传入参数文件名,返回转换后的一维数组。函数实现如下所示: def img2vector(filename): ### Start Code Here ### #创建1x1024零向量 returnVect = np.zeros((1, 1024)) #打开文件 fr = open(filename) #按行读取 for i in range(32): #读一行数据 lineStr = fr.readline() #每一行的前32个元素依次添加到returnVect中 for j in range(32): returnVect[0, 32*i+j] = int(lineStr[j]) #返回转换后的1x1024向量 return returnVect
### End Code Here ### 1到2行测试函数img2vector是否正确,传入的文件路径为:kNN_hand_writing/trainingDigits/1_1.txt,由于np打印矩阵时,默认情况下会省略中间内容,需要进行设置,使所有数据都能显示出来,第1行进行了设置。从以上代码可以看到打印结果和预期结果相符合。接下来我们编写loadTrainingData函数,加载所有训练集数据。该函数将返回表示训练集数据的矩阵以及每个点对应的分类标签: np.set_printoptions(threshold=np.inf) print(img2vector("kNN_hand_writing/trainingDigits/1_1.txt")) def loadTrainData(): ### Start Code Here ### #定义测试集的Labels hwLabels = [] #返回trainingDigits目录下的文件名 trainingFileList = listdir('kNN_hand_writing/trainingDigits') #返回文件夹下文件的个数 m = len(trainingFileList) #初始化训练的Mat矩阵,训练集 trainingMat = np.zeros((m, 1024)) #从文件名中解析出训练集的类别 for i in range(m): #获得文件的名字 fileNameStr = trainingFileList[i] #获得分类的数字 classNumber = int(fileNameStr.split('_')[0]) #将获得的类别添加到hwLabels中 hwLabels.append(classNumber) #将每一个文件的1x1024数据存储到trainingMat矩阵中 trainingMat[i,:] = img2vector('kNN_hand_writing/trainingDigits/%s' % (fileNameStr)) #返回标签数组和训练数据集 #构建kNN分类器 neigh =KNN(n_neighbors = 3, algorithm = 'auto') #拟合模型, trainingMat为训练矩阵,hwLabels为对应的标签 neigh.fit(trainingMat, hwLabels) #返回testDigits目录下的文件列表 testFileList = listdir('kNN_hand_writing/testDigits') def handwritingClassTest():
### Start Code Here ### #定义测试集的Labels hwLabels = [] #返回trainingDigits目录下的文件名 trainingFileList = listdir('kNN_hand_writing/trainingDigits') #返回文件夹下文件的个数 m = len(trainingFileList) #初始化训练的Mat矩阵,训练集 trainingMat = np.zeros((m, 1024)) #从文件名中解析出训练集的类别 for i in range(m): #获得文件的名字 fileNameStr = trainingFileList[i] #获得分类的数字 classNumber = int(fileNameStr.split('_')[0]) #将获得的类别添加到hwLabels中 hwLabels.append(classNumber) #将每一个文件的1x1024数据存储到trainingMat矩阵中 trainingMat[i,:] = img2vector('kNN_hand_writing/trainingDigits/%s' % (fileNameStr)) #构建kNN分类器 neigh = kNN(n_neighbors = 3, algorithm = 'auto') #拟合模型, trainingMat为测试矩阵,hwLabels为对应的标签 neigh.fit(trainingMat, hwLabels) #返回testDigits目录下的文件列表 testFileList = listdir('kNN_hand_writing/testDigits') #错误检测计数 errorCount = 0.0 #测试数据的数量 mTest = len(testFileList) #从文件中解析出测试集的类别并进行 分类测试 for i in range(mTest): #获得文件的名字 fileNameStr = testFileList[i] #获得分类的数字 classNumber = int(fileNameStr.split('_')[0]) #获得测试集的1x1024向量,用于训练 vectorUnderTest = img2vector('kNN_hand_writing/testDigits/%s' % (fileNameStr)) #获得预测结果 classifierResult = neigh.predict(vectorUnderTest)
### End Code Here ###
print(fileNameStr+"分类返回结果为%d\t真实结果为%d" % (classifierResult, classNumber)) if(classifierResult != classNumber): errorCount += 1.0 print("总共错了%d个数据\n错误率为%f%%" % (errorCount, errorCount/mTest)) 最后,我们来验证一下我们的kNN手写字识别算法的准确性,测试使用的数据集位于目录testDigits下。测试的流程为:依次读取testDigits目录下的每个文件,根据文件中的数据,使用分类函数classify0确定其分类,并和其真实分类进行对比,如果一致,表示分类正确,如果不一致,表示分类错误。统计错误的数量,计算错误率,错误率为分类出错的数量除以测试集中测试数据总量。 编写kNNHandWrittenTest函数测试基于kNN算法实现的手写字识别系统准确性。 errorCount变量用于记录分类出错的样本数量,并获取标签数据和训练集数据,使用测试集数据进行测试。 依次读取testDigits目录下的每个文件,根据文件名称获取其真实分类数字,然后使用classify0函数基于kNN算法进行分类,对比两个分类结果。如果不一致,就对errorCount自增1。 def handwritingClassTest():
### Start Code Here ### #定义测试集的Labels hwLabels = [] #返回trainingDigits目录下的文件名 trainingFileList = listdir('kNN_hand_writing/trainingDigits') #返回文件夹下文件的个数 m = len(trainingFileList) #初始化训练的Mat矩阵,训练集 trainingMat = np.zeros((m, 1024)) #从文件名中解析出训练集的类别 for i in range(m): #获得文件的名字 fileNameStr = trainingFileList[i] #获得分类的数字 classNumber = int(fileNameStr.split('_')[0]) #将获得的类别添加到hwLabels中 hwLabels.append(classNumber) #将每一个文件的1x1024数据存储到trainingMat矩阵中 trainingMat[i,:] = img2vector('kNN_hand_writing/trainingDigits/%s' % (fileNameStr)) #构建kNN分类器 neigh = kNN(n_neighbors = 3, algorithm = 'auto') #拟合模型, trainingMat为测试矩阵,hwLabels为对应的标签 neigh.fit(trainingMat, hwLabels) #返回testDigits目录下的文件列表 testFileList = listdir('kNN_hand_writing/testDigits') #错误检测计数 errorCount = 0.0 #测试数据的数量 mTest = len(testFileList) #从文件中解析出测试集的类别并进行 分类测试 for i in range(mTest): #获得文件的名字 fileNameStr = testFileList[i] #获得分类的数字 classNumber = int(fileNameStr.split('_')[0]) #获得测试集的1x1024向量,用于训练 vectorUnderTest = img2vector('kNN_hand_writing/testDigits/%s' % (fileNameStr)) #获得预测结果 classifierResult = neigh.predict(vectorUnderTest)
### End Code Here ###
print(fileNameStr+"分类返回结果为%d\t真实结果为%d" % (classifierResult, classNumber)) if(classifierResult != classNumber): errorCount += 1.0 print("总共错了%d个数据\n错误率为%f%%" % (errorCount, errorCount/mTest)) 由于基于sklearn提供的kNN算法,修改kNNHandWrittenTest函数,将调用classify0的部分逻辑修改为sklearn提供的kNN接口。两个版本的其他部分的逻辑是完全一样的。具体修改的部分如下图红色框中的代码所示: from sklearn.neighbors import KNeighborsClassifier as kNN def handwritingClassTest(): #测试集的Labels hwLabels = [] #返回trainingDigits目录下的文件名 trainingFileList = listdir('kNN_hand_writing//trainingDigits') #返回文件夹下文件的个数 m = len(trainingFileList) #初始化训练的Mat矩阵,测试集 trainingMat = np.zeros((m, 1024)) #从文件名中解析出训练集的类别 for i in range(m): #获得文件的名字 fileName_Str = trainingFileList[i] #获得分类的数字 classNumber = int(fileName_Str.split('_')[0]) #将获得的类别添加到hwLabels中 hwLabels.append(classNumber) #将每一个文件的1x1024数据存储到trainingMat矩阵中 trainingMat[i,:] = img2vector('kNN_hand_writing//trainingDigits/%s' % (fileName_Str)) #构建kNN分类器 neigh = kNN(n_neighbors = 3, algorithm = 'auto') #拟合模型, trainingMat为测试矩阵,hwLabels为对应的标签 neigh.fit(trainingMat, hwLabels) #返回testDigits目录下的文件列表 testFileList = listdir('kNN_hand_writing/testDigits') #错误检测计数 errorCount = 0.0 #测试数据的数量 mTest = len(testFileList) ### End Code Here ###
#从文件中解析出测试集的类别并进行分类测试 for i in range(mTest): #获得文件的名字 fileNameStr = testFileList[i] #获得分类的数字 classNumber = int(fileNameStr.split('_')[0]) #获得测试集的1x1024向量,用于训练 vectorUnderTest = img2vector('kNN_hand_writing/testDigits/%s' % (fileNameStr)) #获得预测结果 classifierResult = neigh.predict(vectorUnderTest) print(fileNameStr+"分类返回结果为%d\t真实结果为%d" % (classifierResult, classNumber)) if(classifierResult != classNumber): errorCount += 1.0 print("总共错了%d个数据\n错误率为%f%%" % (errorCount, errorCount/mTest)) |
五、实验结论 1.掌握内容 通过本实验,你应该至少掌握了机器学习的以下几点: kNN算法的数学原理 kNN算法的步骤流程。 基于Python实现kNN算法。 使用kNN算法解决手写字识别问题。 学会使用sklearn提供的kNN算法。 知道怎样查阅sklearn的使用手册。 学会使用sklearn的kNN算法解决手写字识别问题。 2.kNN算法的优缺点 优点: 简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归; 可用于数值型数据和离散型数据; 训练时间复杂度为O(n);无数据输入假定; 对异常值不敏感。 缺点: 计算复杂性高;空间复杂性高; 样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少); 一般数值很大的时候不用这个,计算量太大。但是单个样本又不能太少,否则容易发生误分。 最大的缺点是无法给出数据的内在含义。 |
六、教师评议
成绩或评语: 指导教师签名: 2022 年 3 月 31日 |
kNN_hand_writing(机器学习)(实验报告)
最新推荐文章于 2024-08-05 17:56:51 发布