计算机视觉基础系列(python与opencv的操作与运用/tensorflow的基础介绍)(二十五)---手写数字识别之KNN

手写数字识别有两个经典案例,首先是KNN最近领域法和CNN卷积神经网络来做识别。

我们从下面几个步骤来准备,

1.网络的搭建;2.每一层的输入输出的说明。在这个之前的前提是样本的准备:

minst网上很多,直接百度就可以搜到下载,这里附上下载地址:http://yann.lecun.com/exdb/mnist/

这里有四个文件夹,下载完放在项目文件夹下解压即可。

KNN的核心原理是:一张待检测的图片和对应的训练样本图片进行对比,得到了k个类似的样本图片,我们将这k个样本图片记录下来,在这k个图片中,找到相似性最大的图片,例如我们得到了10个相类似的图片,其中有8个相似性最高,且这八个样本图片的值是1,那么这个检测出来的图片的值为1。

注:第一次运行p3的值是130.451,加上派之后变成了155.098,为什么呢:因为训练图片和测试图片的下标是random出来的,是随机的,所以这里会变化,因为在随机每次生成。所以导致每次的计算结果是不一样的,不是程序问题,也许你运行出来的结果和我有不一样的地方也是正常的。

总结一下knn检测手写数字的整个流程:

1 load data
2 knn test train distance
 3 knn k个最近图片 5张测试 500张训练图片做差 -->500张找出4张与当前最近的照片
4 解析图片中的内容parse centent==>label
5 label转化成具体的数字
 6 检测结果是否正确

代码如下,注释清楚,代码也可以从上面的连接直接获取,也可以拷贝直接运行:

import tensorflow as tf
import numpy as np
import random
from tensorflow.examples.tutorials.mnist import input_data      # 将input_data用来读取图片
# 1.数据装载
minst = input_data.read_data_sets('minst_data/', one_hot = True)
# 两个参数,第一个是minst文件夹的路径,第二个参数是one-hot,它是布尔类型,对于一个数组来说,这个数组定义为one-hot,那么这个数组中的元素,有一个元素为1的话,那么其他元素全为0
# 属性设置,1,训练的样本数量;2.测试集的数量,3.每次训练喂入的图片数量.4,测试图片的每次喂入的数据
trainNum = 55000
testNum = 10000
trainSize = 500
testSize = 5
# 接下来就是将minst中的数据进行分解,首先是训练数据和训练标签,接下来是测试数据和测试标签,所以进一步证明了这个也是监督学习的一种
# 这些数据也都是以数组的形式存储在数组中的,所以必须要注意文件的下标,首先也是需要获取训练数据的下标
trainIndex = np.random.choice(trainNum, trainSize, replace=False)           # 这里的参数设置:1,训练的样本数量2.训练喂入多少个
# 这个choice函数生成了trainSize个随机数,所以它的个数也是trainSize,它的范围是0-trainNum之间,在0-trainNum之间随机选取trainSize这么多个数字,replace为False代表不可以重复
# 其中根本核心是生成一系列的随机数
testIndex = np.random.choice(testNum, testSize, replace=False)
# 下标已经全部生成,那么接下来就是获取训练和测试数据了
trainData = minst.train.images[trainIndex]          # 通过这个方法,将之前生成的下标填写进去,获取训练图片,
trainLabel = minst.train.labels[trainIndex]         # 通过这个方法,将之前生成的下标填写进去,获取训练的对应标签,
testData = minst.test.images[testIndex]
testLabel = minst.test.labels[testIndex]
# 还需要定义k的参数,knn的本质:获取k个和测试数据一样的样本数据,记为k,这里的k就是这个值
k = 10
# 接下来打印一下训练和测试数据的维度
print('trainData.shape=', trainData.shape)        # 500*786 这里面500代表图片的个数,图片的大小为28*28=784,则代表每个图片上的像素点
print('trainLabel.shape=', trainLabel.shape)      # 500*10  这里面的500和上面的一样
print('testData.shape=', testData.shape)          # 5*784
print('testLabel.shape=', testLabel.shape)        # 5*10
print('testLabel=', testLabel)        # 5*10   下面输出的testlabel中,4:testData[0],3:testData[1],6:testData[2]
'''
这里的输出为:
trainData.shape= (500, 784)
trainLabel.shape= (500, 10)
testData.shape= (5, 784)
testLabel.shape= (5, 10)
testLabel= [[ 1.  0.  0.  0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  1.  0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  1.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.  0.  1.  0.]
 [ 0.  0.  0.  0.  0.  1.  0.  0.  0.  0.]]
'''
# 接下来定义输入和输出
trainDataInput = tf.placeholder(shape=[None, 784], dtype=tf.float32)        # 不管是输入数据还是输出数据,784表示一个完整的图片,因为图片的大小就是24*24
trainLabelInput = tf.placeholder(shape=[None, 10], dtype=tf.float32)        # 标签数据是0-9,是一个十维的数据,所以这里是10
testDataInput = tf.placeholder(shape=[None, 784], dtype=tf.float32)
testLabelInput = tf.placeholder(shape=[None, 10], dtype=tf.float32)
# 完成了所有的数据加载,那么接下来就是进行knn的距离计算了,knn的距离计算是有一个公式的:两张对应图片进行相减,然后求和进行计算
f1 = tf.expand_dims(testDataInput, 1)                   # 维度的扩展
# 可以完成数据维度的转换,为什么要完成维度的转换呢:testData本来是一个5行乘以784列的数组,转换成5行*1列*784
# 这样转换的目的是:因为需要计算数据,两张图片的距离之差,测试图片有5张,训练图片有500张,每一个维度有784维
# 所以它进行做差之后是一个三维数据,这个三维数据,行数表示我们的测试数据,列数表示我们的训练数据,第三个维度表示这二者之差,所以5张测试图片和500张训练图片
# 计算出来之后至少有5*500=2500种组合,再乘以784维,因为每张图片有784个维度,或者说有784个像素点,所以这就是为什么我们要把testDataInput扩展为1维的原因
f2 = tf.subtract(trainDataInput, f1)                    # 完成对应元素的相减,得到我们需要的结果,subtract做差
# 这样得到的实际上是一个三维的数据,这个三维的数据描述的是测试图片和训练图片二者之差,经过做差之后,全部放到这个784维中,接下来就是将差异累加计算了
f3 = tf.reduce_sum(tf.abs(f2), reduction_indices=2)
# reduce_sum做和,取绝对值,绝对值取完之后,我们要设置累加的到底是哪个维度,我们设置再第二个维度上,累加的是784个像素点之间的差值,要注意是取的绝对值abs
# 所有的距离都放在法f3中,f3是一个5*500的,某一个点,表示某一个测试图片和某一个训练图片它们二者之间的距离之差
f4 = tf.negative(f3)      # 取反
f5, f6 = tf.nn.top_k(f4, k=10)    # 选取f4中,最大的10值,即也是f3中最小的10个值
f7 = tf.gather(trainLabelInput, f6)      # 通过这个方法,我们获取所有的标签,我们知道f6放的是与测试图片距离最近的十个点的下标,通过下标,我们可以索引到标签
# f8 将最近的10张图片 累加 维度为1,将label转化成具体的数字,reduce_sum累加的是在竖直方向上的累加
f8 = tf.reduce_sum(f7, reduction_indices=1)
# f9 根据训练数据 推测的值,tf.argmax选取在某一个最大的值,然后记录其下标index,f9中则装载着所有的检测图片的数字,预测值
f9 = tf.argmax(f8, dimension=1)
with tf.Session() as sess:
    # 运行f1,给它一个参数,这个参数是testData,这里面有5张图片,这5张是我们需要检测的图片
    p1 = sess.run(f1, feed_dict={testDataInput: testData[0: 5]})
    print('p1.shape=', p1.shape)    # p1= (5, 1, 784)
    # 这里计算的是实现做差传参,对应像素做差
    p2 = sess.run(f2, feed_dict={trainDataInput: trainData, testDataInput: testData[0: 5]})
    print('p2.shape=', p2.shape)   # p2= (5, 500, 784)
    p3 = sess.run(f3, feed_dict={trainDataInput: trainData, testDataInput: testData[0: 5]})
    print('p3.shape=', p3.shape)  # p3= (5, 500)
    print('p3[0,0]=', p3[0, 0])       # 这里代表第一个训练图片和第一张测试图片的距离之差,所以我们可以通过这个计算得到任意一张训练图片和任意一张测试图片它们之间的距离差值
    # 第一次运行的是130.451,加上派之后变成了155.098,为什么呢:因为训练图片和测试图片的下标是random出来的,是随机的,所以这里会变化,因为在随机每次生成。导致每次的计算结果是不一样的
    # 如何根据knn的距离,如何找到k个最近的图片,我们使用5张测试图片和500张训练图片进行做差,那么每一张测试图片对应着500张训练图片,我们需要在这500张训练图片中。找到10张与我们的测试图片最接近的图片,把这10张图片找出来
    # 接下来就实现这些功能,距离算出来之后,是一个5*500的矩阵,行列信息分别表面输入的测试图片和训练图片的下标,而它们的内容表明的是二者之间的距离差值,接下来我们需要找到对于每一个测试图片来说,在500张训练图片中找到10张与它最接近的图片
    p4 = sess.run(f4, feed_dict={trainDataInput: trainData, testDataInput: testData[0: 5]})
    print('p4.shape=', p4.shape)
    print('p4[0,0]=', p4[0, 0])
    p5, p6 = sess.run((f5, f6), feed_dict={trainDataInput: trainData, testDataInput: testData[0:5]})
    print('p5.shape=', p5.shape)
    print('p6.shape=', p6.shape)
    # p5=(5,10) 每一张测试图片(5张)分别对应当前10张最近的图片
    # p6=(5,10) 每一张测试图片(5张)分别对应当前10张最近的图片下标
    print('p5[0,0]=', p5[0])
    print('p6[0,0]=', p6[0])
    # 距离和下标,我们都知道了,可是我们还是不知道到底描述的是那些点,所以我们需要解析图片中的内容
    p7 = sess.run(f7, feed_dict={trainDataInput: trainData, testDataInput: testData[0:5], trainLabelInput: trainLabel})
    # f7 = tf.gather(trainLabelInput, f6)      # 通过这个方法,我们获取所有的标签,我们知道f6放的是与测试图片距离最近的十个点的下标,通过下标,我们可以索引到标签,
    # 所以输入还需要加上训练的标签值
    print('p7.shape=', p7.shape)  # (5,10,10)
    print('p7=', p7)
    '''
    p7.shape= (5, 10, 10)
    p7= [[[ 0.  0.  0.  0.  0.  0.  0.  0.  1.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  0.  0.  1.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]]

    [[ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]
    [ 0.  1.  0.  0.  0.  0.  0.  0.  0.  0.]]

    [[ 0.  0.  0.  0.  0.  0.  1.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  1.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  1.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  1.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  1.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  1.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  1.  0.  0.  0.]
    [ 0.  0.  0.  1.  0.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  1.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  1.  0.  0.  0.]]

    [[ 0.  0.  0.  0.  1.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  1.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  0.  0.  0.  1.]
    [ 0.  0.  0.  0.  1.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  1.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  1.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  1.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  0.  1.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  0.  1.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  0.  0.  0.  1.]]

    [[ 0.  0.  0.  0.  0.  0.  0.  0.  0.  1.]
    [ 0.  0.  0.  0.  0.  0.  0.  0.  0.  1.]
    [ 0.  0.  0.  0.  1.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  0.  0.  0.  1.]
    [ 0.  0.  0.  0.  0.  0.  0.  1.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  0.  1.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  0.  0.  0.  1.]
    [ 0.  0.  0.  0.  0.  0.  0.  0.  0.  1.]
    [ 0.  0.  0.  0.  1.  0.  0.  0.  0.  0.]
    [ 0.  0.  0.  0.  0.  0.  0.  0.  0.  1.]]]
    '''

    # f8 将最近的10张图片 累加 维度为1,将label转化成具体的数字,num的获取,是p7里面竖直方向上的累加
    p8 = sess.run(f8, feed_dict={trainDataInput: trainData, testDataInput: testData[0:5], trainLabelInput: trainLabel})
    print('p8.shape=', p8.shape)  # (5,10)
    # 直接打印p8的内容
    print('p8=', p8)
    '''
    p8.shape= (5, 10)
    p8= [[  0.   8.   0.   0.   0.   0.   0.   0.   2.   0.]
    [  0.  10.   0.   0.   0.   0.   0.   0.   0.   0.]
    [  0.   0.   0.   1.   0.   0.   9.   0.   0.   0.]
    [  0.   0.   0.   0.   6.   0.   0.   2.   0.   2.]
    [  0.   0.   0.   0.   2.   0.   0.   2.   0.   6.]]
    '''
    p9 = sess.run(f9, feed_dict={trainDataInput: trainData, testDataInput: testData[0:5], trainLabelInput: trainLabel})
    print('p9.shape=', p9.shape)
    print('p9=', p9)          # 原来p8是一个5行10列的,我们可以找到第一行最大的元素是8,这个8对应的下标是1,第二行最大的元素是10,对应的下标是1,第三行最大的元素是1,对应的下标是6
    # 所以,也就是把p8中第二个维度中所有的元素遍历一次,找到其最大值,然后将其下标记录下来,存在p9中
    '''
    p9.shape= (5,)
    p9= [1 1 6 4 9]
    p10= [8 2 6 4 9]
    ac= 60.0
    '''
    # p10 根据测试数据 得到的值,这个值是测试标签的值,就是正确的。
    p10 = np.argmax(testLabel[0:5], axis=1)
    print('p10=', p10)

# 比较p9和p10,计算准确率
j = 0
for i in range(0, 5):
	if p10[i] == p9[i]:
		j=j+1
print('ac=',j*100/5)

得到的数据的准确率有时是100%,有时不是的,所以并不是每次都可以成功的。当然可以调整这些数据来进行其他的调整。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章