下面给出用KNN算法识别Minist数字集的源码讲解:
背景介绍:
KNN算法也叫k临近算法。Minist数字集是比较常用的数字识别的数字集。数字集中包含0到9这九种数字,每一种数字都有很多不同的样本。分为训练集和测试集两个部分。实例中训练集中一共有1934个样本,测试集中一共有946个样本。
本程序要实现的就是用KNN算法,以训练集为依托,对测试集的样本进行预测,最后观察KNN算法识别准确率。
Minist样本数据集展示:
以数字0为例,文件名字0_0第一个0表示数字零,第二个零表示数字零的第零个样本,以此类推第一个第二个...,下图为样本0部分截图。
打开其中一个文件后是这样的:
可以看出是用数字1表示出来的0。
程序思路:
首先在将训练集中的每个文件读取进来,由于每一个文件是32行32列的,将它变为一行1024列(32*32)。变换的办法是将第二行数据接到第一行尾部,第三行、第四行依次接下去,形成一个1行1024列的数组。这样训练集中的每一份文件就用一个1行1024列的数组表示了。然后对于测试集也这样做,每一个要测试的样本都用一个数组表示。最后比较测试集合中要测试数字的一维数组和每个训练集的一维数组之间的欧式距离,取前k个距离最小的一维数组。这k个一维数组表征的是数字就是将要测试的数字判别为该数字。如果这k个数字表证的数字不一样,按多数来判断。
程序框架:
将二维图像数据转换为一维数字数组
加载Minist数据集
用KNN算法识别Minist数据集
主程序执行
导入库函数:
import numpy as np #数组相关的数据包
import os #操作文件系统的数据包
首先需要导入程序所需要的代码库,numpy代码库包含了一些对数组操作的函数,科学计算,运算起来很方便。os库提供了操作系统相关的函数,利用它们加载文件库。
图像数据转换为一维数组:
def img2vector(filename): #将二维数据转换为一维数组,filename为文件路径名
rows = 32 #原数据是32行
cols = 32 #原数据是32列
imgVector = np.zeros((1, rows * cols)) #创建一个初始化是零的1行1024列的数组
fileIn = open(filename) #打开文件
for row in range(rows): #按行遍历这里是32,执行32次
lineStr = fileIn.readline() #读取当前数据中的每一行
for col in range(cols): # 遍历当前行中的每一列数据
imgVector[0, row * 32 + col] = int(lineStr[col]) #将当前行的每一个数据一次放到imgVector数组中,即将二维数据变成一维数组
return imgVector #函数返回数组
样例中每个文件是32行32列,这里将二维的图像数据变为一维的数组。方法是将第2行到第32行数据依次拼接到第一行。形成一个1行1024列的数组。即将每一个类别文件用一个1行1024列的数组表征。
加载Minist数据集:
def DataSetLoad(): #加载数据函数
print ("---Getting training set...")
dataSetDir = './Mnist_Dataset/' #数据集的根目录,这种表示是相对路径
trainingFileList = os.listdir(dataSetDir + 'trainingDigits') #将数据集目录中训练集的文件名放到列表中,列表中每一行元素
numSamples = len(trainingFileList) #列表的长度,也是列表的行数,由于列表中每行存储的是一个文件名,所以也是文件的数量
train_x = np.zeros((numSamples, 1024)) #创建一个列表,行数是列表的行数(文件的数量),列数是1024,它的每一行表征的是一个文件数据
train_y = [] #标签0~9这九个数字
for i in range(numSamples): #将训练集目录中每个文件的类别标签提取到train_y这个列表中
filename = trainingFileList[i]
train_x[i, :] = img2vector(dataSetDir + 'trainingDigits/%s' % filename) #训练集列表中每一行存储一个训练集的文件
label = int(filename.split('_')[0]) #提取训练集文件名的第一个字符作为类别标签,由于文件名是"0_0"或者"0_1"这样的格式下划线前面表示类别
train_y.append(label)
print ("---Getting testing set...") #测试集同训练集一样的步骤
testingFileList = os.listdir(dataSetDir + 'testDigits')
numSamples = len(testingFileList)
test_x = np.zeros((numSamples, 1024))
test_y = []
for i in range(numSamples):
filename = testingFileList[i]
test_x[i, :] = img2vector(dataSetDir + 'testDigits/%s' % filename)
label = int(filename.split('_')[0]) # return 1
test_y.append(label)
return train_x, train_y, test_x, test_y #返回训练集列表,训练集列表标签,测试集列表,测试集列表标签
加载数据集函数,分别从训练集和测试集目录中提取出训练集列表、训练集标签列表,测试集列表、测试集标签列表。
KNN算法分类测试集:
def kNNClassify(newInput, dataSet, labels, k): #knn分类算法 newInput:待测试的测试集数字。dataSet:整个训练集列表。labels:训练集标签列表。k: 取k个相近的
numSamples = dataSet.shape[0] #训练集列表的行数
diff = np.tile(newInput, (numSamples, 1)) - dataSet #输入的数据列数不变,行数变成numSamples,即将原数组复制numSamples份
squaredDiff = diff ** 2 #计算欧式距离
squaredDist = np.sum(squaredDiff, axis = 1) #计算欧式距离
distance = squaredDist ** 0.5 #计算欧式距离
sortedDistIndices = np.argsort(distance)#将计算后的距离由小到大排序,提取排序后的索引值列表放到sortedDistIndices
classCount = {} #创建字典
for i in range(k):
voteLabel = labels[sortedDistIndices[i]]#取索引列表前k个值遍历,即距离最小的前k个点,记录其类别
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1 #如果字典中没有值填0,有的话加1,即投票
maxCount = 0
for key, value in classCount.items(): #找到投票数量最多的标签类别
if value > maxCount:
maxCount = value
maxIndex = key
return maxIndex
对于每个测试集文件样本数组分别计算和训练集样本数组的欧式距离,取前k个距离最小的数字,看它们的类别标签,用投票的方法到所属类别最多的那个,这个测试样本就预测为此类别。
主程序执行:
if __name__ == '__main__':
print ("step 1: load data...")
train_x, train_y, test_x, test_y = DataSetLoad() #加载并生成训练集、训练集标签,测试集、测试集标签
numTestSamples = test_x.shape[0] #测试集行数
matchCount = 0
for i in range(numTestSamples): #遍历测试集中每一个数组
predict = kNNClassify(test_x[i], train_x, train_y, 3)#进行KNN算法分类
if predict == test_y[i]:
#print(predict)
matchCount += 1
accuracy = float(matchCount) / numTestSamples #计算准确率
print ("step 2: show the result...")
print ('The classify accuracy is: %.2f%%' % (accuracy * 100))
生成测试集列表以及训练集列表,及其它们的标签列表,对测试集中每样本用KNN算法预测其类别,如果预测的类别和测试集中其相应的类别一致,正确数量加1。遍历整个测试集结束后,用最终的正确数量除以测试集总数,得到准确率。
整个工程及源码请关注微信平台:IT职场说
或者扫描下方二维码
关注后回复:KNN算法
扫描下方二维码
关注我们