卷積神經網絡的前世今生

2021-02-23 CSDN
深度學習在技術與應用上的突破引發了第三次人工智慧浪潮,獲得了空前成功。在前述章節的基礎上,本章將主要介紹訓練卷積神經網絡和深度神經網絡的重要方法與技巧,深度神經網絡的遷移學習策略,以及如何訓練深度神經網絡以解決實際問題等內容。作為人工智慧的核心研究內容,以卷積神經網絡(ConvolutionalNeural Networks, CNNs)為代表的深度學習技術已在計算機視覺應用,如智能監控、智慧醫療及機器人自動駕駛等領域取得突破性進展,而這些應用的成功落地很大程度上依賴於視覺識別模塊。結合前文內容,本章將詳細介紹如何構建並利用CNNs 這一功能強大的深度學習模型解決實際的圖像識別問題。受20世紀中期興起的神經科學及腦科學研究的啟發,通過模擬生物神經元接收和處理信息的基本特性,研究人員提出並設計了人工神經元。作為計算機科學、生物學和數學的交叉融合,卷積神經網絡已經發展成為計算機視覺領域中最具影響力和有效的基礎技術。
早在20 世紀60 年代,生物學家Hubel 和Wiesel 通過研究貓的視覺皮層,發現每個視覺神經元都只對一個小區域範圍內的視覺圖像產生響應,即感受野(Receptive Field)。初級視覺皮層中的神經元能夠響應視覺環境中特定的簡單特徵,除此之外,Hubel 和Wiesel 通過研究發現了簡單和複雜兩種不同類型的細胞,其中簡單細胞只在特定的空間位置對它們偏好的方向產生最強烈響應,而複雜細胞具有更大的空間不變性。根據這些實驗和分析,他們得出結論:複雜細胞通過在來自多個簡單細胞(每個都有一個不同的偏好位置)的輸入進行池化而實現這種不變性,這兩個特性,即對特定特徵的選擇性和通過前饋連接增大空間不變性,構成了CNN人工視覺系統的生物及神經學基礎。
發展至80年代,日本科學家Kunihiko Fukushima 通過研究並融合有關生物視覺的相關領域知識,提出了Neocognitron 神經認知機的概念,該神經認知機由S 細胞和C 細胞構成,可通過無監督的方式學習識別簡單的圖像。20 世紀90 年代,Yann LeCun 等人發表論文,確立了CNN 影響至今的經典網絡結構,後來經過對網絡結構的不斷完善與改進,得到一種多層的人工神經網絡,命名為LeNet-5,在手寫數字識別任務上取得良好效果。和其他神經網絡一樣,LeNet-5能夠使用反向傳播算法(Back Propagation)訓練。LeNet-5 網絡雖然較小,但它含有諸多神經網絡學習的關鍵模塊,具體包括卷積層、池化層及全連接層,這些基本模塊構成當前深度神經網絡模型的基礎,下文將對LeNet-5 的結構及工作原理進行深入分析。同時,藉助實例加深讀者對卷積神經網絡各個模塊功能的理解。


LeNet-5 出自Yann LeCun 教授於1998 發表的論文Gradient-Based Learning Applied to DocumentRecognition 中,LeNet-5 模型共有7 層,如圖11-1 所示為LeNet-5 的基本網絡架構。該模型除了輸入層之外,每層都包含可訓練參數,每個網絡層產生多個特徵圖,每個特徵圖可通過一種卷積濾波器提取輸入數據一種類型的特徵。各個網絡層的功能與參數情況介紹如下。1. 輸入層
首先是輸入數據網絡層,上例中輸入圖像尺寸統一歸一化為32×32×1,其中1 表示輸入圖像為單通道的灰度圖,一般不將該層作為LeNet-5 網絡的基本構成,即不將輸入層視為網絡層次結構之一。2. C1 層
C 取自Convolutional 的首字母,指卷積。讀者可能對卷積的概念並不陌生,對數字圖像做卷積運算,本質上是通過卷積核(卷積模板)在圖像上滑動,將圖像上的像素灰度值與對應卷積核上的數值相乘,然後將所有相乘後的值相加作為卷積核中間像素對應像素的灰度值,以此方式遍歷完成對整張圖像像素的卷積計算。如圖11-2顯示了圖像卷積計算過程中一次相乘後相加的運算過程,該卷積核大小為3×3,卷積核內共有9 個數值,數值個數即為圖像像素值與卷積核上數值相乘次數,運算結果-4 代替了原圖像中對應位置處的值。按此方式,沿著圖片以步長為1 滑動,每次滑動1個像素都進行一次相乘再相加的操作,即可得到最終的輸出結果。圖像卷積計算中,卷積核的設計十分重要,一般需遵循如下基本規則。

卷積核大小一般是奇數,奇數大小的卷積核使得卷積核關於中間像素點中心對稱,因此卷積核尺寸一般是3×3、5×5或7×7。卷積核有中心,相應地就有半徑的概念,如7×7 大小的卷積核,其半徑為3。

卷積核所有的元素之和一般應等於1,這是為了保持圖像卷積計算過程中像素能量(亮度)的守恆。若濾波器矩陣所有元素之和大於1,那麼濾波後的圖像就會比原圖像更亮;反之,若小於1,那麼得到的圖像將會變暗。

濾波後可能會出現負數或大於255 的數值。對這種情況,通常將它們直接截斷到0~255之間即可。而對於負數,也可以取絕對值。

經卷積計算所得輸出通常被稱為「響應」,如果是邊緣檢測算子,那麼響應為圖像邊緣,能夠檢測到特定的圖像邊緣。在LeNet-5網絡中得到的響應是特徵圖(Feature Map),計算結果為輸入圖像的特徵表達,卷積核的參數權重可以通過優化算法在監督信息的指導下自適應地學習得到。LeNet-5 網絡中C1 層輸入圖像尺寸為32×32×1,卷積核大小為5×5,一共包括6 種大小為5×5 的卷積核,卷積核滑動一行之後,得到的結果的邊長變為32-5+1,提取的特徵映射大小是28×28,即(32-5+1)=28。6種不同的卷積核,可以從不同的角度提取圖像不同特性的特徵。神經元數量為28×28×6,則可訓練參數為(5×5+1)×6,即每個濾波器含5×5=25個單元權值參數和1個偏置參數,一共6 個濾波器,因此總的連接數為(5×5+1)×6×28×28=122 304。針對122 304 個連接,通過權值共享策略,只需學習156 個參數。3. S2 層
S 指的是Subsamples,該網絡層完成下採樣操作,得到對應的特徵圖。下採樣的原則是在減少數據量的同時儘可能保留有用的信息。與普通插值下採樣的方式不同,該層實際採用的是一種被稱為池化(Pooling)的方法。具體是將一幅圖像分割成若干塊,每個圖像塊的輸出是該圖像塊原有像素的統計結果。圖像下採樣池化方法有很多,如Mean-pooling( 均值採樣)、Max-pooling( 最大值採樣)、Overlapping ( 重疊採樣)、L2-pooling( 均方採樣)、Local Contrast Normalization( 局部對比歸一化)、Stochastic-pooling( 隨機採樣) 和Def-pooling( 形變約束採樣) 等,其中最經典的是最大池化,也是
最常用的,下面簡要介紹最大池化的實現原理。為直觀起見,假設有如圖11-3(a)中大小為4×4 的圖像,圖像中每個像素點的值是上面各個格子中的數值。現在對這張4×4 大小的圖像進行池化操作,池化的大小為(2,2),步長為2。採用最大池化操作,首先對圖像進行分塊,每個圖像塊大小為2×2,然後按照圖11-3(b)中方式統計每個圖像塊的最大值,作為下採樣後圖像的像素值,得到圖11-3(c)中結果,該過程即為最大池化。除此之外,還有其他池化方法,如均值池化,具體是對每個塊求取平均值作為下採樣的新像素值。上述例子未涉及重疊採樣,即每個圖像塊之間沒有相互重疊的部分,而步長為2 時,圖像分塊不重疊。
LeNet-5 網絡中的S2 層的輸入是上一層的輸出,共有6 個特徵映射,每個特徵映射的尺寸為28×28,使用2×2 大小的核進行池化操作,得到S2,即6 個14×14 大小的特徵映射(28/2=14)。換言之,S2 中的池化層是對C1 中的2×2 區域內的像素求和乘以一個權值係數再加上一個偏置,然後將這個結果再做一次映射。與卷積層連接數的計算方法一樣,連接數=參數個數×特徵映射大小,即(2×2+1)×6×14×14=5880。

4. C3 層
C3 層同樣是卷積層,輸入為S2 中所有6 個或若干個特徵圖的組合。具體地,該層卷積核大小為5×5,一共有6種卷積核,輸出特徵圖大小為10×10,即(14-5+1)=10。需要注意的是,C3 中每個特徵圖是連接到S2 中的所有6 個或若干個特徵圖的,即該層的特徵圖是上一層提取到的特徵圖的不同組合。如圖11-4 所示,LeCun 在原論文中給出的一種組合連接方式。圖11-4 中共有6 行16 列,橫軸代表C3 特徵映射索引,縱軸代表S2 特徵圖索引。每列的X表示C3 中的每個特徵映射與S2 中的特徵圖的連接情況,可以看到C3 的前6 個特徵圖,對應上圖第1 個紅框的6 列,以S2 中3 個相鄰的特徵圖子集為輸入,緊接著6 個特徵圖(對應上圖第2 個紅框的6 列)以S2 中4 個相鄰特徵圖子集為輸入。然後,接下來的3 個特徵圖(對應上圖第3 個紅框的3 列)以S2 中不相鄰的4 個特徵圖子集為輸入,C3 中的最後一個特徵圖對應上圖第4 個紅框的1 列將S2 中所有特徵圖為輸入。這裡得到的每一個特徵圖為多核多通道卷積,將每一列稱為一個卷積核,它由若干個卷積模板構成,因為是多個卷積核模板卷積的結果得到一個特徵圖,仍然認為是一個卷積核,所以每列只有一個偏置參數。之所以採取這種組合方式,LeCun 主要是基於以下兩點考慮:減少參數;採用不對稱的組合連接方式有利於提取多種組合特徵。5. S4 層
S4 層為下採樣層,即池化層,窗口大小為2×2,包括16 個特徵圖,C3 層的16 個10×10 的特徵圖分別進行以2×2 為單位的池化得到16 個5×5 的特徵圖,步長為2,即本網絡層的輸出張量大小為5×5×16,一共有5×5×5×16=2000 個連接,連接的方式與S2 層類似。

6. C5 層
C5 層是一個卷積層,輸入為S4 層的全部16 個特徵圖,該層卷積原理與普通卷積層一致,只是因為恰巧卷積核大小與輸入特徵圖尺寸一樣,因此得到一維,即1×1(5-5+1)的輸出,卷積核種類為120,得到120 維的卷積結果,每個都與上一層的16 個特徵圖相連,因此一共有(5×5×16+1)×120=48 120 個可訓練參數。

7. F6 層
F6 層是全連接層,採用全連接的方式與C5 層連接,由對C5 層的輸入乘以權重加上偏置,結果通過激活Sigmoid 函數輸出。F6 層有84 個節點,對應於一個7×12 的比特圖,-1 表示白色,1 表示黑色,這樣每個符號的比特圖的黑白色對應一個編碼,F6 層的訓練參數/ 連接數為(120+1)×84=10 164。8. Output 輸出層
Output 輸出層同樣是全連接層,共有10 個節點,分別代表數字0~9,且如果節點i 的值為0,則網絡識別的結果是數字i。採用的是徑向基函數(RBF)的網絡連接方式。假設x 是上一層的輸入,y 是RBF 的輸出,則RBF 輸出的計算方式如式(11-1)所示。
式中wij 的值由i 的比特圖編碼確定,i 取值為0~9,j 取值為0~(7*12-1)。RBF 輸出的值越接近於0,表示當前網絡的輸入越接近於i,即越接近於i 的ASCII 編碼,該層一共包含84×10=840 個可學習參數。卷積神經網絡在本質上是在不需要獲取輸入和輸出之間精確的數學表達的情況下,學習從輸入數據到目標輸出的複雜映射,卷積神經網絡的優勢在於能夠很好地利用圖像的二維結構信息,LeNet-5 在銀行支票手寫體字符識別問題上得到成功應用。
不考慮輸入層,LeNet-5 是一個7 層的網絡,卷積層的參數較少,這得益於卷積層的若干重要特性,即局部連接和共享權重。現在常用的LeNet-5 結構和Yann LeCun 教授在1998 年論文中提出的結構在某些細節上存在一定的區別,如激活函數的使用,現在一般使用ReLU 作為激活函數,而輸出層一般選用Softmax。CNN 能夠提取原始圖像的有效表徵,這賦予CNN 經過較少的預處理,即可從原始像素中學習和識別視覺規律的能力。然而,由於LeNet-5 提出伊始,缺乏大規模的訓練數據,計算機的計算能力也難以滿足要求,CNN 的網絡架構在不同文獻中的描述略有差異。不過,CNN 的基本組成單元和模塊相對一致,可以像搭積木一樣將不同功能的網絡層組合起來,從而實現規模更大、深度更深的網絡。因此,從某種意義上說,CNN 或深度學習中的網絡層本質上是能夠進行信息處理的積木單元。LeNet-5 對於更複雜問題的處理效果並不理想,但通過對LeNet-5 的網絡結構的分析與研究,可以直觀地了解卷積神經網絡的構建方法,能夠為分析和構建更複雜、更深層的卷積神經網絡打下堅實的基礎。總結卷積神經網絡的成功經驗,主要在於局部連接(LocalConnection)、權值共享(Weight Sharing)和池化層(Pooling)中的降採樣(Down-Sampling)。(1)卷積層(Convolutions Layer)。卷積層由很多的卷積核(Convolutional Kernel)組成,卷積核用來計算不同的特徵圖,卷積層是卷積神經網絡的核心。在圖像識別裡用到的卷積是二維卷積,具體是二維濾波器滑動到二維圖像上所有位置,並在每個位置上與該像素點及其領域像素點做內積。卷積操作被廣泛應用於圖像處理領域,不同類型的卷積核可以提取圖像不同類型的特徵,例如,邊緣、角點等特徵。在深層卷積神經網絡中,通過卷積操作可以提取出圖像低級簡單到抽象複雜的特徵,學習輸入數據具有較強普適性的特徵表達。除此之外,激活函數能夠為CNN 卷積神經網絡引入非線性,增強網絡的複雜建模能力,常用的非線性激活函數有Sigmoid、Tanh 和ReLu 等,前兩者常見於全連接層,後者ReLu 則多用於卷積層。
(2)池化層(Pooling Layer)。池化是非線性下採樣的一種形式,主要作用是通過減少網絡的參數來減小計算量,同時池化層能降低卷積層輸出的特徵向量,通常在卷積層的後面會加上一個池化層,通過卷積層與池化層交替使用可以獲得更複雜的高層抽象特徵,並且能夠在一定程度上避免和緩解過擬合現象。常用的池化操作包括最大池化、平均池化等,其中最大池化是用不重疊的矩形框將輸入層分成不同的區域,對於每個矩形框內的數值取最大值作為統計輸出。
(3)全連接層(Full Connected Layer)。如果說卷積層、池化層和激活函數映射等操作是將原始數據映射到隱層特徵空間的話,那麼全連接層則起到將學到的「分布式特徵表示」映射到樣本標記空間的作用,將多層的特徵表達拉直成一個一維的向量,實現神經網絡的高層抽象推理能力,在整個卷積神經網絡中起到「分類器」的作用。
(4)局部連接(Local Connection)。局部連接指的是每個神經元僅與輸入神經元的一塊區域相連,該局部區域也被稱為感受野(Receptive Field)。局部連接的思想可追溯至生物學裡面的視覺系統結構,即視覺皮層的神經元實質上是局部接收信息的。在圖像卷積操作中,神經元在空間維度上是局部連接的,但在深度上是全部連接的。對於二維圖像本身而言,局部像素關聯較強,這種局部連接保證了學習後的過濾器能夠對於局部的輸入特徵有最強的響應。
(5)權重共享(Weight Sharing)。實際中,圖像的底層邊緣特徵與特徵在圖中的具體位置無關,即特徵可能出現在圖像的任意位置,權重共享正是利用這一特點,具體是指卷積核內權重參數被整張圖共享,而不會因圖像內位置的不同而改變,可在圖像中的不同位置學習到同樣的特徵,權重共享可以在很大程度上減少參數數量。


前文介紹了LeNet-5 的基本網絡結構,以及各個網絡功能層的特點與作用,本節將利用TensorFlow 具體實現這一網絡。首先需要說明以下幾點。(1)LeNet-5 主要採用Tanh 和Sigmoid 作為非線性激活函數,但相對這兩者採用ReLu 激活函數的卷積神經網絡更加有效。
(2)LeNet-5 採用平均池化作為下採樣操作,但是目前最大池化操作應用更為廣泛。
(3)LeNet-5 網絡最後一層採用Gaussian 連接層,用於輸出0~9 這10 個類別中的一類,但是目前分類器操作已經被Softmax 層取代。
第1 步:建立config.py 文件,可以將超參數設置在config.py 中,方便後期對模型進行調整。代碼實現與說明如程序清單11-1 所示。程序清單11-1 config.py 文件建立及超參數設置

1. """
2. 設置模型的超參數
3.
4.
5. KEEP_PROB: 網絡隨機失活的概率
6. LEARNING_RATE: 學習的速率,即梯度下降的速率
7. BATCH_SIZE: 一次訓練所選取的樣本數
8. PARAMETER_FILE: 模型參數保存的路徑
9. MAX_ITER: 最大迭代次數
10. """
11.
12. KEEP_PROB = 0.5
13. LEARNING_RATE = 1e-5
14. BATCH_SIZE =50
15. PARAMETER_FILE = "checkpoint/variable.ckpt"
16. MAX_ITER = 50000

第2 步:構建LeNet 模型的LeNet.py 文件,建立一個名為Lenet 的類,類中實現模型的初始化與構建,代碼實現與說明如程序清單11-2 所示。
程序清單11-2 構建LeNet 模型與LeNet.py 文件

1. import tensorflow as tf
2. import tensorflow.contrib.slim as slim
3. import config as cfg
4.
5. class Lenet:
6. def __init__(self):
7. """
8. 初始化LeNet 網絡
9. """
10. # 設置網絡輸入的圖片為二維張量,數據的類型為float32,行數不固定,列固定為784
11. self.raw_input_image = tf.placeholder(tf.float32, [None, 784])
12.
13. # 改變網絡輸入張量的形狀為四維,-1 表示數值不固定
14. self.input_images = tf.reshape(self.raw_input_image, [-1, 28, 28, 1])
15.
16. # 設置網絡輸入標籤為二維張量,數據類型為float,行數不固定,列固定為10
17. self.raw_input_label = tf.placeholder("float", [None, 10])
18.
19. # 改變標籤的數據類型為int32
20. self.input_labels = tf.cast(self.raw_input_label,tf.int32)
21.
22. # 設置網絡的隨機失活概率
23. self.dropout = cfg.KEEP_PROB
24.
25. # 構建兩個網絡
26. # train_digits 為訓練網絡,開啟dropout
27. # pred_digits 為預測網絡,關閉dropout
28. with tf.variable_scope("Lenet") as scope:
29. self.train_digits = self.construct_net(True)
30. scope.reuse_variables()
31. self.pred_digits = self.construct_net(False)

32.
33. # 獲取網絡的預測數值
34. self.prediction = tf.argmax(self.pred_digits, 1)
35.
36. # 獲取網絡的預測數值與標籤的匹配程度
37. self.correct_prediction = tf.equal(tf.argmax(self.pred_digits, 1), tf.argmax
(self.input_labels, 1))
38.
39. # 將匹配程度轉換為float 類型,表示為精度
40. self.train_accuracy = tf.reduce_mean(tf.cast(self.correct_prediction, "float"))
41.
42. # 計算train_digits 與labels 之間的係數softmax 交叉熵,定義為loss
43. self.loss = slim.losses.softmax_cross_entropy(self.train_digits, self.
input_labels)
44.
45. # 設置學習速率
46. self.lr = cfg.LEARNING_RATE
47. self.train_op = tf.train.AdamOptimizer(self.lr).minimize(self.loss)
48.
49.
50. def construct_net(self,is_trained = True):
51. """
52. 接收is_trained 參數判斷是否開啟dropout
53. 用slim 構建LeNet 模型
54. 第一、三、五層為卷積層、第二、四層為池化層
55. 接下來對第五層扁平化,再接入全連接
56. 接著進行隨機失活防止過擬合,再次接入全連接層
57. 最後返回構建的網絡
58. """
59. with slim.arg_scope([slim.conv2d], padding='VALID',
60. weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
61. weights_regularizer=slim.l2_regularizer(0.0005)):
62. net = slim.conv2d(self.input_images,6,[5,5],1,padding='SAME',scope='conv1')
63. net = slim.max_pool2d(net, [2, 2], scope='pool2')
64. net = slim.conv2d(net,16,[5,5],1,scope='conv3')
65. net = slim.max_pool2d(net, [2, 2], scope='pool4')
66. net = slim.conv2d(net,120,[5,5],1,scope='conv5')
67. net = slim.flatten(net, scope='flat6')
68. net = slim.fully_connected(net, 84, scope='fc7')
69. net = slim.dropout(net, self.dropout,is_training=is_trained, scope=
'dropout8')
70. digits = slim.fully_connected(net, 10, scope='fc9')
71. return digits

本模型是對著名的手寫字體MNIST數據集進行訓練,可以在網站http://yann.lecun.com/exdb/mnist/ 上很方便地直接下載數據,得到如圖11-5 的MNIST 數據集。MNIST 數據集列表第3 步:建立模型訓練文件Train.py,主要實現數據讀取、模型訓練等功能,代碼實現與說明如程序清單11-3 所示。
程序清單11-3 模型訓練文件Train.py 的建立

1. import tensorflow.examples.tutorials.mnist.input_data as input_data
2. import tensorflow as tf
3. import config as cfg
4. import os
5. import lenet
6. from lenet import Lenet
7.
8.
9. def main():
10. # 從指定路徑加載訓練數據
11. mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
12.
13. # 開啟TensorFlow 會話
14. sess = tf.Session()
15.
16. # 設置超參數
17. batch_size = cfg.BATCH_SIZE
18. parameter_path = cfg.PARAMETER_FILE
19. lenet = Lenet()
20. max_iter = cfg.MAX_ITER
21.
22. # 加載已保存的模型參數文件,如果不存在則調用初始化函數生成初始網絡
23. saver = tf.train.Saver()
24. if os.path.exists(parameter_path):
25. saver.restore(parameter_path)
26. else:
27. sess.run(tf.initialize_all_variables())
28.
29. # 迭代訓練max_iter 次,每次抽取50 個樣本進行訓練
30. # 每100 次列印出當前數據的精度
31. # 訓練完成後保存模型參數
32. for i in range(max_iter):
33. batch = mnist.train.next_batch(50)
34. if i % 100 == 0:
35. train_accuracy = sess.run(lenet.train_accuracy,feed_dict={
36. lenet.raw_input_image: batch[0],lenet.raw_input_label: batch[1]
37. })
38. print("step %d, training accuracy %g" % (i, train_accuracy))
39. sess.run(lenet.train_op,feed_dict={lenet.raw_input_image: batch[0],lenet.
raw_input_label: batch[1]})
40. save_path = saver.save(sess, parameter_path)
41.
42. if __name__ == '__main__':
43. main(

第4 步:在上述完成步驟的基礎上,運行Train.py。如圖11-6 所示,可以看到隨著不斷迭代優化,模型精度在逐步提高。
模型迭代優化過程第5 步:建立測試文件Inference.py。具體地,建立一個Inference 類完成對圖片的識別,成員函數predict 接收圖片作為參數,返回預測值,代碼實現與說明如程序清單11-4 所示。程序清單11-4 測試文件Inference.py 的建立

1. import tensorflow as tf
2. from PIL import Image,ImageOps
3. import numpy as np
4. from lenet import Lenet
5. import config as cfg
6.
7. class inference:
8. def __init__(self):
9. """
10. 構建Lenet 網絡,設置模型參數文件路徑,開啟TensorFlow 會話
11. """
12. self.lenet = Lenet()
13. self.sess = tf.Session()
14. self.parameter_path = cfg.PARAMETER_FILE
15. self.saver = tf.train.Saver()
16.
17. def predict(self,image):
18. """
19. 接收要測試的圖片作為參數,返回預測值
20. """
21. # 將圖片轉換為合適的大小進行輸入
22. img = image.convert('L')
23. img = img.resize([28, 28], Image.ANTIALIAS)
24. image_input = np.array(img, dtype="float32") / 255
25. image_input = np.reshape(image_input, [-1, 784])
26.
27. # 讀取模型參數並對圖片進行預測,返回預測值
28. self.saver.restore(self.sess,self.parameter_path)
29. predition = self.sess.run(self.lenet.prediction, feed_dict={self.lenet.raw_
input_image: image_input})
30. return predition

第6 步:為了方便地實現對手寫數字的識別,可以使用python 的tkinter方便地繪製一個UI 進行識別,具體實現代碼如程序清單11-5 所示。

1. import tkinter
2. from PIL import Image,ImageDraw
3. from Inference import inference
4.
5. class MyCanvas:
6. """
7. 設置一個256*256 大小的容器進行手寫界面的繪製
8. 背景色設置為黑色,繪製軌跡設置為白色
9. """
10. def __init__(self,root):
11. self.root=root
12. self.canvas=tkinter.Canvas(root,width=256,height=256,bg='black')
13. self.canvas.pack()
14. self.image1 = Image.new("RGB", (256, 256), "black")
15. self.draw = ImageDraw.Draw(self.image1)
16. self.canvas.bind('<B1-Motion>',self.Draw)
17.
18. # 繪製軌跡
19. def Draw(self,event):
20. self.canvas.create_oval(event.x,event.y,event.x,event.y,outline="white",
width = 20)
21. self.draw.ellipse((event.x-10,event.y-10,event.x+10,event.y+10),fill=(255,
255,255))
22.
23.
24. def main():
25. # 建立一個tkinter 對象, 設置大小為380*300
26. root = tkinter.Tk()

27. root.geometry('380x300')
28. # 創建一個256*256 的框架容納手寫的容器,位於tkinter 對象的左邊,填充y 方向
29. frame = tkinter.Frame(root, width=256, height=256)
30. frame.pack_propagate(0)
31. frame.pack(side="left", fill='y')
32. # 將frame 導入canvas 容器
33. canvas1 = MyCanvas(frame)
34. # 創建一個圖像識別的實例
35. infer = inference()
36.
37. # 定義識別按鈕觸發函數
38. # 按下的時候將cavas 導出為圖片,放入infer 中進行圖像識別,並將結果顯示在label2 中
39. def inference_click():
40. img = canvas1.image1
41. result = infer.predict(img)
42. result = int(result)
43. label2["text"] = str(result)
44.
45. # 定義清除按鈕的觸發函數
46. # 按下的時候將canvas 情況並重新繪製背景,並將label 設置為空
47. def clear_click():
48. canvas1.canvas.delete("all")
49. canvas1.image1 = Image.new("RGB", (256, 256), "black")
50. canvas1.draw = ImageDraw.Draw(canvas1.image1)
51. label2["text"] = ""
52.
53. # 定義識別按鈕的樣式
54. botton_Inference = tkinter.Button(root,
55. text=" 檢測",
56. width=14,
57. height=2,
58. command=inference_click
59. )
60. # 定義清除按鈕的樣式
61. botton_Clear = tkinter.Button(root,
62. text=" 清屏",
63. width=14,
64. height=2,
65. command=clear_click
66. )
67. # 綁定識別按鈕到tkinter 中,設置位置為頂層
68. botton_Inference.pack(side="top")
69.
70. # 綁定清除按鈕到tkinter 中
71. botton_Clear.pack(side="top")
72.
73. # 定義label1
74. label1 = tkinter.Label(root, justify="center", text=" 檢測結果為:")
75. label1.pack(side="top")
76.
77. # 定義label2
78. label2 = tkinter.Label(root, justify="center")
79.
80. # 設置字體樣式與大小
81. label2["font"] = ("Arial, 48")
82. label2.pack(side="top")
83. root.mainloop()
84.
85. if __name__ == '__main__':
86. main()

第7 步:運行代碼並進行如下的幾組測試,測試結果如圖11-7 所示。


更多精彩推薦

相關焦點

  • 圖神經網絡的「前世今生」
    GNN的前世今生 Humility簡介GNN的分類GCNGATGAEGGNGSTN附參考文獻代碼實現GNN的前世今生簡介為處理圖數據之上的任務, 圖神經網絡就應運而生了.GNN的分類圖一: GNN的分類網絡Embedding與GNN的異同網絡embedding旨在在保留網絡拓撲結構和節點信息的基礎之上, 在低維向量空間之中對網絡節點進行表示.
  • 阜陽本土紀錄片《奎星樓的前世今生》在網絡走紅
    原標題:紀錄片《奎星樓的前世今生》背後的故事最近一段時間,本土文化紀錄片《奎星樓的前世今生》在網絡上火了。紀錄片用光影,記錄了奎星樓的「前世今生」,得到了不少網友的點讚。紀錄片是誰拍的?是如何拍出來的?
  • 今生的夫妻是前世情人,今生的情人是前世夫妻:善待每一份相遇!
    作者:胡楊映月情人之所以對你柔情似水,之所以是浪漫溫柔的代名詞,之所以讓你感覺愛得百轉柔腸,之所以讓你刻骨銘心,是因為你們是前世的夫妻。今生之所以尋你而來,只因為前世的一份緣還沒有盡,所以今生來續前緣,是來還債的。
  • 前世今生因果輪迴
    世界如此之大無奇不有,我們生活在這美好的世界裡,人生在世是否真的會有前世與今生。每一個人都在猜想,都在找答案。如果真的有前世,就會想到有沒有來世。前世與今生如果真的還有今生,那麼今生無法報答的恩情等到來世再報 。人世間是如此美好,今生修來的福分是前世的因果。好人必有好報。前世的因果,決定了今生的命運。
  • 廖閱鵬:前世今生催眠曲,帶你夢回前世,總結今生!
    最近在最右上,看到了一則消息,許多人聽了廖閱鵬的前世今生催眠曲,都看到了自己的前世,我覺得很神奇,便趁著月黑風高之夜,孤身一人躲在被窩裡,悄悄的打開了喜馬拉雅收音機,點開了前世今生催眠曲,帶上耳機,準備一場穿越之旅。
  • 人人值得一看——談前世 | 贈書《前世今生》
    佛在經上常說「欲知前世因,今生受者是」,你要知道這前世的因果,你看看現在受的就是;「欲知來世果,今生作者是」,想想來世什麼樣的果報,現在造的因就是。 你看看現在這個世界,這果!你就曉得前世造什麼樣的因;再看看現在社會大家造的因,我就曉得將來社會會有什麼果報。歷史是一面鏡子!這決定不是假的。
  • 輪迴的實證:貝滿中學老師的前世今生
    左邊前世照片,女校算術老師;右邊今生照片,某部門主編邊前世照片,女校算術老師;右邊今生,某部門主編左邊是前世照片,還是個名人,康有為的外孫女--羅儀鳳,在女校當英文老師;右邊今生照片,成普通人了,某部門核心員工。這世跑到自己前世的墓前轉了轉,不過貌似也沒得到啥靈感。
  • 袁勇麟教授暢談協和的前世今生
    《協和的前世今生》專題教育講座 楊競雯/攝談及學院的「今生」,新生入學講座《協和的前世今生》同學認真聽講 劉夢卿/攝新生入學講座《協和的前世今生》同學認真聽講 劉夢卿/攝新生入學講座《協和的前世今生》 國際商學系同學認真聽講座  謝寧靜/攝
  • 欲知前世因,今生受者是;欲知後世果,今生作者是!
    今生長壽為何因  前世買物放生靈今生短命為何因  前世宰殺眾生命今生無妻為何因  前世偷奸謀人妻今生守寡為何因  前世輕賤丈夫身今生奴婢為何因  前世忘恩負義人今生眼明為何因  前世舍油點佛燈今生瞎眼為何因  前世指路不分明今生缺口為何因
  • 催眠治療與前世今生
    那麼,說回到我現在的工作上,我是一個催眠治療師,也是一個系統整合療愈師,自然我是相信輪迴的,所以從世間的層面來說,前世今生,在我看來無疑是存在且真實不虛的。同時,我更相信的是「空性」,也就是出世間的角度來說,一切有為法,如夢幻泡影,如露亦如電,應作如是觀。 為什麼這樣說呢?
  • 今生的愛人就是前世埋葬你的人
    佛祖解釋道,其實那具海灘上的女屍,就是你那個未婚妻的前世,而你正是那個路過她身邊,給她穿上一件衣服的路人。她今生和你相戀,只為還你一個情。但是她最終要報答一生一世的人,便是最後把她掩埋的那個人。那人便是她後來所嫁之人……「眾裡尋他千百度,驀然回首,那人卻在燈火闌珊處。」其實,當你與愛人攜手之時,就是前世殘存的記憶在提醒你了。
  • 小兒推拿的前世今生(前世篇下)
    小兒推拿的前世今生(前世篇上) 上一期我們講了小兒推拿的史料積累期,那麼當資料積累到一定程度,那麼就會交叉混合產生出新的學科。今天我們就來了解一下小兒推拿的形成期。這個時期主要從明清時代開始。下一講我們就要開始聊一聊小兒推拿的今生了,共同了解一下近現代小兒推拿的發展。
  • 「天門冬 」的前世今生!
    「天門冬」的前世今生!「天門冬」的前世:古籍中「天門冬」的記載:「天門冬「天門冬」的今生:「天門冬」怎麼吃果實類:熟透的紅色果子可以直接吃現在大家知道「天門冬」的前世今生了吧?感謝您在百忙之中的閱讀,希望本文的知識對您有所幫助!想了解更多的農業知識,請您持續關注我!
  • 今生夫妻的愛情是前世的「因果」,一切都是緣分
    佛陀說:今生萬物皆是前世的因果。前世的因果,今生的命運。佛陀說:婚姻真的是過去註定的嗎?很多都是前夫和前妻,所以這輩子也是夫妻。也有情侶來報答他們的恩情、委屈和債務,具體原因要分析。做夫妻是過去的一種宿命,有些夫妻種了更多的善緣,所以這輩子的恩愛。有些夫妻因為感情不好經常吵架,有些人比其他人更嚴肅,這是因為前世的不良關係。
  • 北大未名湖的前世今生(組圖)
    北大未名湖的前世今生【中國人的聖誕節】探訪中國聖誕村越南更奇葩:年輕人幹50年才能買套房貴州:北盤江畔的「天籟之音」北大未名湖的前世今生【中國人的聖誕節 ");}
  • 佛說:今生種種皆是前世因果
    佛說:今生種種皆是前世因果,出現在你身邊的人一定會讓你經歷一些什麼,有的人帶給你快樂幸福,有的人讓你悲傷不安,一切的經歷是因為一個緣字,前世有緣今生相聚,三世因果,循環不失。愛情是一場輪迴,這輩子的愛,是上輩子的債;一切相遇皆有原因,一切事皆有輪迴,我們能得到到是前世種下的善因,我們得不到的是沒有姻緣,前世因今世果,有因有果,才會有愛的萌芽,你能得到但不能長久的是種下的善因不夠,我們能得到長久但是生活不如意到是因為前世的虧欠,傷害了別人所以今生不能安寧!
  • 精選5本前世今生文,無論前世,還是今生,我想一直在你身邊!
    今天為寶寶們整理了5本前世今生文,快一起來看看吧。如果寶寶們想看什麼類型的小說,可以在留言區告訴九九哦,九九會整理分享給大家。01《洞房前還有遺言嗎》作者:且墨<文案簡介>卿如是:我是你的祖宗,我們之間是不會有好結果的,這樣是會遭天譴的。
  • 多粘菌素的前世今生
    多粘菌素的前世今生多粘菌素(Polymyxins)的前世發現於1947年,是由多粘類芽孢桿菌產生的抗菌多肽HEALTH多粘菌素的今生21世紀後,針對革蘭陰性桿菌治療的限制級抗菌藥物碳青黴烯類耐藥率逐年增加,因此耐碳青黴烯類革蘭陰性桿菌的治療成為了臨床,尤其是重症感染患者救治的難點。多粘菌素由於其破壞細菌外膜完整性的殺菌機制,成為這類耐藥菌的最後一道挽救性治療方案,重新回歸到臨床視野。
  • 盤點動漫裡的前世戀人們:前世相戀,今生也要相愛
    人們常說前世的戀人,今生的緣分,還有就是前世500次的回眸,才換來今生的擦肩而過。這是人們對愛情的無限美好遐想,雖然沒有什麼科學根據,但是有些東西是科學也無法解釋的事,同時這會給人一絲希望和慰藉。如果世界上真的存在前世的戀人,說不定今生還可以互相找到對方,但是這個肯定是機率特別小的一件事情,因為很多人根本記不住前生發生了些什麼。今天我們來說說動漫裡的前世戀人。神無月的巫女《神無月的巫女》作為一部2004年上映的老番,相信很多小夥伴都沒看過,但就其畫風、人設、劇情、內涵,即使放到現在也毫不遜色於一般番劇。
  • 《七月半2:前世今生》預告:怨氣歸來鬼門大開
    《七月半2:前世今生》預告:怨氣歸來鬼門大開 1905電影網訊 由著名製片人劉鴻再度製片,導演李紅建執導,陳美行、翟子陌、羅翔、王良、趙吉、苗青、夏星翎、常奕然、付曼、張力飛、曾漪蓮等青春偶像聯袂主演的恐怖電影《七月半2:前世今生