計算機視覺基礎系列(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%,有時不是的,所以並不是每次都可以成功的。當然可以調整這些數據來進行其他的調整。

 

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