动手学PaddlePaddle(4):MNIST(手写数字识别)
本次練習將使用 PaddlePaddle 來實現三種不同的分類器,用于識別手寫數字。三種分類器所實現的模型分別為 Softmax 回歸、多層感知器、卷積神經網絡。
您將學會
-
實現一個基于Softmax回歸的分類器,用于識別手寫數字
-
實現一個基于多層感知器的分類器,用于識別手寫數字
-
實現一個基于卷積神經網絡的分類器,用于識別手寫數字
-
卷積神經網絡的基本組成和搭建
目錄
1 - 引用庫
2 - 數據獲取
3 - 知識點介紹
3.1 常見激活函數介紹
3.2 - Softmax回歸(Softmax Regression)
3.3 - 多層感知器(Multilayer Perceptron, MLP)
3.4 - 卷積神經網絡(Convolutional Neural Network, CNN)
4 - 構建分類器
4.1 Softmax回歸:
4.2 多層感知器:
4.3 卷積神經網絡:
5 - 訓練過程
5.1 配置網絡結構:
5.2 創建優化器
5.3 設置訓練場所
5.4 讀取數據集
6 - 應用模型
7 - 總結
1 - 引用庫
首先,載入幾個需要用到的庫,它們分別是:
- numpy:一個python的基本庫,用于科學計算
- paddle.fluid:PaddlePaddle深度學習框架
- PIL:Python上的圖像處理標準庫
- os:在本例中用于獲取文件或目錄的路徑
- Image:用于處理圖像數據
- from paddle.utils.plot import Ploter :paddle集成了一個畫圖工具包,可以利用這個包把訓練曲線畫出來
問題描述:
在學習編程的時候,編寫的第一個程序一般是實現打印"Hello World"。而機器學習(或深度學習)的入門教程,一般都是 MNIST 數據庫上的手寫數字識別問題。原因是手寫數字識別屬于典型的圖像分類問題,比較簡單,同時MNIST數據集也很完備。
目標:
構建三種不同的分類器來對手寫數字進行識別
數據集分析:
MNIST 數據集作為一個簡單的計算機視覺數據集,包含一系列如下圖所示的手寫數字圖片和對應的標簽。圖片是 28x28 像素的矩陣,標簽則對應著0~9的10個數字。每張圖片都經過了歸一化和居中處理。
MNIST數據集是從 NIST 的Special Database 3(SD-3)和Special Database 1(SD-1)構建而來。由于SD-3是由美國人口調查局的員工進行標注,SD-1是由美國高中生進行標注,因此SD-3比SD-1更干凈也更容易識別。Yann LeCun等人從SD-1和SD-3中各取一半作為MNIST的訓練集(60000條數據)和測試集(10000條數據),其中訓練集來自250位不同的標注員,此外還保證了訓練集和測試集的標注員是不完全相同的。
Yann LeCun早先在手寫字符識別上做了很多研究,并在研究過程中提出了卷積神經網絡(Convolutional Neural Network),大幅度地提高了手寫字符的識別能力,也因此成為了深度學習領域的奠基人之一。如今的深度學習領域,卷積神經網絡占據了至關重要的地位,從最早Yann LeCun提出的簡單LeNet,到如今ImageNet大賽上的優勝模型VGGNet、GoogLeNet、ResNet等(請參見圖像分類 教程),人們在圖像分類領域,利用卷積神經網絡得到了一系列驚人的結果。
有很多算法在 MNIST 上進行實驗。1998年,LeCun 分別用單層線性分類器、多層感知器(Multilayer Perceptron, MLP)和多層卷積神經網絡LeNet進行實驗,使得測試集上的誤差不斷下降(從12%下降到0.7%)[1]。此后,科學家們又基于K近鄰(K-Nearest Neighbors)算法[2]、支持向量機(SVM)[3]、神經網絡[4-7]和Boosting方法[8]等做了大量實驗,并采用多種預處理方法(如去除歪曲、去噪、模糊等)來提高識別的準確率。
本教程中,我們從簡單的模型 Softmax 回歸開始,帶大家入門手寫數字識別,并逐步進行模型優化。
輸入值:
-
X是輸入:MNIST圖片是28×28 的二維圖像,為了進行計算,將其轉化為 784維向量,即 ?=(?0,?1,…,?783) 。轉化的具體做法:每張圖片是由 28×28 個像素構成的,將其按固定順序(如按行或者按列)展開成為一個行向量,并將每個原始像素值歸一化為[0,1] 之間的數值。
-
LLL是圖片的真實標簽:?=(?0,?1,…,?9)也是10維,但只有一維為1,其他都為0。值為1的維度對應圖片表示的真實數字,例如?=(1,0,…,0)表示圖片對應的數字是1。
輸出值:
- Y是輸出:分類器的輸出是 10 類數字(0-9),即 ?=(?0,?1,…,?9),每一維 yi代表圖片分類為第 ii類數字的概率。
要注意的是,PaddlePaddle將圖像的通道數放在最前面,即為[C H W],區別于[H W C]。
2 - 數據獲取
文件路徑
在該項目中,已準備好數據集,路徑:/home/aistudio/dataset/
數據集信息如下:
# 定義reader,用于獲取數據集數據import struct import gzipTRAIN_IMAGE = "/home/aistudio/data/data65/train-images-idx3-ubyte.gz" TRAIN_LABEL = "/home/aistudio/data/data65/train-labels-idx1-ubyte.gz" TEST_IMAGE = "/home/aistudio/data/data65/t10k-images-idx3-ubyte.gz" TEST_LABEL = "/home/aistudio/data/data65/t10k-labels-idx1-ubyte.gz"def reader_creator(image_filename, label_filename, buffer_size):def reader():with gzip.GzipFile(image_filename, 'rb') as image_file:img_buf = image_file.read()with gzip.GzipFile(label_filename, 'rb') as label_file:lab_buf = label_file.read()step_label = 0offset_img = 0# read from Big-endian# get file info from magic byte# image file : 16Bmagic_byte_img = '>IIII'magic_img, image_num, rows, cols = struct.unpack_from(magic_byte_img, img_buf, offset_img)offset_img += struct.calcsize(magic_byte_img)offset_lab = 0# label file : 8Bmagic_byte_lab = '>II'magic_lab, label_num = struct.unpack_from(magic_byte_lab,lab_buf, offset_lab)offset_lab += struct.calcsize(magic_byte_lab)while True:if step_label >= label_num:breakfmt_label = '>' + str(buffer_size) + 'B'labels = struct.unpack_from(fmt_label, lab_buf, offset_lab)offset_lab += struct.calcsize(fmt_label)step_label += buffer_sizefmt_images = '>' + str(buffer_size * rows * cols) + 'B'images_temp = struct.unpack_from(fmt_images, img_buf,offset_img)images = numpy.reshape(images_temp, (buffer_size, rows * cols)).astype('float32')offset_img += struct.calcsize(fmt_images)images = images / 255.0 * 2.0 - 1.0for i in range(buffer_size):yield images[i, :], int(labels[i])return readerdef train():"""MNIST training set creator.It returns a reader creator, each sample in the reader is image pixels in[0, 1] and label in [0, 9].:return: Training reader creator:rtype: callable"""return reader_creator(TRAIN_IMAGE, TRAIN_LABEL, 100)def test():"""MNIST test set creator.It returns a reader creator, each sample in the reader is image pixels in[0, 1] and label in [0, 9].:return: Test reader creator.:rtype: callable"""return reader_creator(TEST_IMAGE, TEST_LABEL, 100)3 - 知識點介紹
3.1 常見激活函數介紹
?sigmoid激活函數:
?????????????????????????????????????????????????????????
tanh激活函數:
??????????????????????????????????????????????????????????
ReLU激活函數:
????????????????????????????????????????????????????????????????????????
3.2 - Softmax回歸(Softmax Regression)
最簡單的Softmax回歸模型是先將輸入層經過一個全連接層得到的特征,然后直接通過softmax 函數進行多分類[9]。
輸入層的數據X傳到輸出層,在激活操作之前,會乘以相應的權重 W ,并加上偏置變量 b ,具體如下:
?????????????????????????????????????????????????????????
其中
??????????????????????????????????????????????????????????????????
對于有 N 個類別的多分類問題,指定 NN個輸出節點,N 維結果向量經過softmax將歸一化為 N 個[0,1]范圍內的實數值,分別表示該樣本屬于這 N 個類別的概率。此處的 yi? 即對應該圖片為數字 i 的預測概率。
在分類問題中,我們一般采用交叉熵代價損失函數(cross entropy),公式如下:
?????????????????????????????????????????????????????
圖2為softmax的網絡圖,圖中權重用藍線表示、偏置用紅線表示、+1代表偏置參數的系數為1。
3.3 - 多層感知器(Multilayer Perceptron, MLP)
Softmax回歸模型采用了最簡單的兩層神經網絡,即只有輸入層和輸出層,因此其擬合能力有限。為了達到更好的識別效果,我們考慮在輸入層和輸出層中間加上若干個隱藏層[10]。
圖3為多層感知器的網絡結構圖,圖中權重用藍線表示、偏置用紅線表示、+1代表偏置參數的系數為1。
3.4 - 卷積神經網絡(Convolutional Neural Network, CNN)
在多層感知器模型中,將圖像展開成一維向量輸入到網絡中,忽略了圖像的位置和結構信息,而卷積神經網絡能夠更好的利用圖像的結構信息。LeNet-5是一個較簡單的卷積神經網絡。下圖顯示了其結構:輸入的二維圖像,先經過兩次卷積層到池化層(降采樣層),再經過全連接層,最后使用softmax函數作為輸出層激活函數。下面我們主要介紹卷積層和池化層。
卷積層
卷積層是卷積神經網絡的核心基石。在圖像識別里我們提到的卷積是二維卷積,即離散二維濾波器(也稱作卷積核)與二維圖像做卷積操作,簡單的講是二維濾波器滑動到二維圖像上所有位置,并在每個位置上與該像素點及其領域像素點做內積。卷積操作被廣泛應用與圖像處理領域,不同卷積核可以提取不同的特征,例如邊沿、線性、角等特征。在深層卷積神經網絡中,通過卷積操作可以提取出圖像低級到復雜的特征。
上圖5給出一個卷積計算過程的示例圖,輸入圖像大小為H=5,W=5,D=3,即5×5大小的3通道(RGB,也稱作深度)彩色圖像。這個示例圖中包含兩(用K表示)組卷積核,即圖中濾波器W0?和W1。在卷積計算中,通常對不同的輸入通道采用不同的卷積核,如圖示例中每組卷積核包含(D=3)個3×3(用F×F表示)大小的卷積核。另外,這個示例中卷積核在圖像的水平方向(W方向)和垂直方向(H方向)的滑動步長為2(用S表示);對輸入圖像周圍各填充1(用P表示)個0,即圖中輸入層原始數據為藍色部分,灰色部分是進行了大小為1的擴展,用0來進行擴展。經過卷積操作得到輸出為3×3×2(用H?o??×W?o??×K表示)大小的特征圖,即3×3大小的2通道特征圖,其中H?o??計算公式為:Ho=(H?F+2×P)/S+1,W?o??同理。 而輸出特征圖中的每個像素,是每組濾波器與輸入圖像每個特征圖的內積再求和,再加上偏置bo??,偏置通常對于每個輸出特征圖是共享的。輸出特征圖o[:,:,0]中的最后一個?2計算如圖中右下角公式所示。
在卷積操作中卷積核是可學習的參數,經過上面示例介紹,每層卷積的參數大小為D×F×F×K。在多層感知器模型中,神經元通常是全部連接,參數較多。而卷積層的參數較少,這也是由卷積層的主要特性即局部連接和共享權重所決定。
-
局部連接:每個神經元僅與輸入神經元的一塊區域連接,這塊局部區域稱作感受野(receptive field)。在圖像卷積操作中,即神經元在空間維度(spatial dimension,即上圖示例H和W所在的平面)是局部連接,但在深度上是全部連接。對于二維圖像本身而言,也是局部像素關聯較強。這種局部連接保證了學習后的過濾器能夠對于局部的輸入特征有最強的響應。局部連接的思想,也是受啟發于生物學里面的視覺系統結構,視覺皮層的神經元就是局部接受信息的。
-
權重共享:計算同一個深度切片的神經元時采用的濾波器是共享的。例如圖4中計算o[:,:,0]的每個每個神經元的濾波器均相同,都為W0??,這樣可以很大程度上減少參數。共享權重在一定程度上講是有意義的,例如圖片的底層邊緣特征與特征在圖中的具體位置無關。但是在一些場景中是無意的,比如輸入的圖片是人臉,眼睛和頭發位于不同的位置,希望在不同的位置學到不同的特征 (參考斯坦福大學公開課)。請注意權重只是對于同一深度切片的神經元是共享的,在卷積層,通常采用多組卷積核提取不同特征,即對應不同深度切片的特征,不同深度切片的神經元權重是不共享。另外,偏重對同一深度切片的所有神經元都是共享的。
通過介紹卷積計算過程及其特性,可以看出卷積是線性操作,并具有平移不變性(shift-invariant),平移不變性即在圖像每個位置執行相同的操作。卷積層的局部連接和權重共享使得需要學習的參數大大減小,這樣也有利于訓練較大卷積神經網絡。
池化層
一般情況下,在連續的卷積層之間會周期性地插入一個池化層(也稱匯聚層),其處理輸入數據的準則被稱為池化函數。池化函數在計算某一位置的輸出時,會計算該位置相鄰區域的輸出的某種總體統計特征,作為網絡在該位置的輸出。池化層的作用是逐漸降低數據體的空間尺寸,從而減少網絡中參數的數量以及耗費的計算資源,同時也能有效控制過擬合。
池化包括最大池化、平均池化、L-2范數池化等。以最大池化(Max Pooling)為例,池化層使用最大化(Max)操作,即用一定區域內輸入的最大值作為該區域的輸出。最大池化最常用的形式是使用尺寸為2×22\times22×2的濾波器、步長為2來對每個深度切片進行降采樣,每個Max操作是從4個數字中取最大值(也就是在深度切片中某個的區域),這樣可以將其中75%的激活信息都過濾掉,而保持數據體通道數不變,具體示例如圖6所示。
更詳細的關于卷積神經網絡的具體知識可以參考斯坦福大學公開課和圖像分類教程。
4 - 構建分類器
4.1 Softmax回歸:
只通過一層簡單的以Softmax為激活函數的全連接層得到分類結果。具體過程和網絡結構如圖7所示:784維的輸入特征經過節點數目為10的全連接層后,直接通過Softmax函數進行多分類。
按照上述描述實現 Softmax 回歸分類器對應的 softmax_regression() 函數。
API 解釋 全連接 通常寫法為 hidden = fluid.layers.fc(input=x, size=10, act='sigmoid')
def softmax_regression(img):"""定義softmax分類器:一個以softmax為激活函數的全連接層:param img: 輸入層:Return:predict -- 分類的結果"""# 以softmax為激活函數的全連接層,輸出層的大小必須為數字的個數10 predict = fluid.layers.fc(input=img, size=10, act='softmax')return predict4.2 多層感知器:
下面代碼實現了一個含有兩個隱藏層(即全連接層)的多層感知器。其中兩個隱藏層的激活函數均采用ReLU,輸出層的激活函數用 Softmax。Softmax 回歸模型采用了最簡單的兩層神經網絡,即只有輸入層和輸出層,因此其擬合能力有限。為了達到更好的識別效果,我們考慮在輸入層和輸出層中間加上若干個隱藏層,從而得到了多層感知器模型。其對應的網絡結構如圖8所示:784維的輸入特征,先后經過兩個節點數為128和64的全連接層,最后通過 Softmax 函數進行多分類。
按照上述描述定義多層感知器模型對應的函數 multilayer_perceptron()。
API 解釋 全連接 通常寫法為 hidden = fluid.layers.fc(input=x, size=10, act='sigmoid')
def multilayer_perceptron(img):"""定義多層感知機分類器:含有兩個隱藏層(全連接層)的多層感知器其中前兩個隱藏層的激活函數采用 ReLU,輸出層的激活函數用 Softmax:param img: 輸入層Return:predict_image -- 分類的結果"""# 第一個全連接層,本層神經元有128個 激活函數為 ReLU hidden = fluid.layers.fc(input=img, size=128, act='relu') # 第二個全連接層,本層神經元有64個 激活函數為 ReLU hidden = fluid.layers.fc(input=hidden, size=64, act='relu')# 以 softmax 為激活函數的全連接輸出層,輸出層的大小必須為 10,對應 0-9 這 10 個數字prediction = fluid.layers.fc(input=hidden, size=10, act='softmax')return prediction4.3 卷積神經網絡:
卷積網絡的結構示意圖如下所示:
?
比如本次實驗,輸入的二維圖像,可以經過兩次卷積層后接池化層的結構,在通過輸出節點數目為10的以Softmax函數作為激活函數的全連接層后得到多分類輸出。
根據上述描述和圖示,補全下面練習1—3處的代碼,實現卷積神經網絡模型對應的函數 convolutional_neural_network()。
API 解釋
卷積 通常寫法為 hidden = fluid.nets.simple_img_conv_pool(input, num_filters, filter_size, pool_size, pool_stride, pool_padding=0)
要注意的是,PaddlePaddle將圖像的通道數放在最前面,即為[C H W],區別于[H W C]。其中:
nput - 輸入
num_filters - filters 的數量
filter_size - filter 的大小
pool_size - 池化的大小
pool_stride - 池化的步長
pool_padding - 池化的 padding
建議適當調節這些參數,以達到訓練精度要求。
API 參考文檔:http://paddlepaddle.org/documentation/docs/zh/1.4/api_cn/nets_cn.html
### hidden = fluid.nets.simple_img_conv_pool(input=img, num_filters=2, filter_size=3, pool_size=2, pool_stride=2, pool_padding=1)### 練習1代碼結束處 ####練習2:# 使用fluid.nets.simple_img_conv_pool()函數,完成第二個卷積-池化層# 根據需求,自己設定參數### 練習2代碼開始處 ### hidden = fluid.nets.simple_img_conv_pool(input=hidden, num_filters=2, filter_size=3, pool_size=2, pool_stride=2, pool_padding=1)### 練習2代碼結束處 ####練習3:# 使用fluid.layers.fc(----------)函數,完成最后全連接層預測輸出# 根據需求,自己設定參數### 練習3代碼開始處 ### prediction = fluid.layers.fc(input=hidden, size=10, act='softmax')### 練習3代碼結束處 ###return prediction為了在訓練的時候,能實時的觀察到模型訓練效果,可以用一個畫圖函數,把訓練曲線展示出來。這個函數定義為:
event_handler_plot(ploter_title, step, cost)
train_prompt = "Train cost" test_prompt = "Test cost" cost_ploter = Ploter(train_prompt, test_prompt)# 將訓練過程繪圖表示 def event_handler_plot(ploter_title, step, cost):cost_ploter.append(ploter_title, step, cost)cost_ploter.plot()5 - 訓練過程
5.1 配置網絡結構:
接下來需要配置分類網絡的結構:
- 首先通過 layers.data 配置數據輸入層,
- 然后配置分類器(代碼中提供了三個不同的分類器,每次使用選擇其中一個,注釋掉其余兩個即可)。例如下方代碼中選用的是卷積神經網絡分類器。
- 然后設置損失函數,對于分類問題常常選擇交叉熵損失函數。
設置test_program :
我們通過克隆main_program得到test_program
為什要要這樣做呢?因為有些operator在訓練和測試之間的操作是不同的,例如batch_norm,使用參數for_test來區分該程序是用來訓練還是用來測試。
另外:該api不會刪除任何操作符,請在backward和optimization之前使用
5.2 創建優化器
參數創建完成后,我們需要定義一個優化器optimizer,為了改善模型的訓練速度以及效果,學術界先后提出了很多優化算法,包括: Momentum、RMSProp、Adam 等,已經被封裝在fluid內部,讀者可直接調用。比如,Adam就是一種常用的、效果良好的自適應學習率調整優化算法,在 fluid 中可以使用接口 fluid.optimizer.Adam() 來創建 Adam 優化器。這個函數接受 4個參數分別是:learning_rate, beta1, beta2 和 epsilon。相關的API在這里:http://paddlepaddle.org/documentation/docs/zh/1.4/api_cn/optimizer_cn.html
#練習4:補全代碼,完成優化器的配置; #提示:你也可以嘗試使用其他優化器,也可以適當的配置相關參數,尤其是learning_rate,對訓練結果有一定影響 ### 練習代碼開始處 ### (≈2行代碼) optimizer = fluid.optimizer.Adam(learning_rate=0.005) optimizer.minimize(avg_loss)### 練習代碼結束處 ### print("optimizer is ready") #我們還可以定義一個train_test()函數,使用測試數據,來測試訓練效果。 def train_test(train_test_program, train_test_feed, train_test_reader):# 將分類準確率存儲在acc_set中acc_set = []# 將平均損失存儲在avg_loss_set中avg_loss_set = []# 將測試 reader yield 出的每一個數據傳入網絡中進行訓練for test_data in train_test_reader():acc_np, avg_loss_np = exe.run(program=train_test_program,feed=train_test_feed.feed(test_data),fetch_list=[acc, avg_loss])acc_set.append(float(acc_np))avg_loss_set.append(float(avg_loss_np))# get test acc and loss# 獲得測試數據上的準確率和損失值acc_val_mean = numpy.array(acc_set).mean()avg_loss_val_mean = numpy.array(avg_loss_set).mean()# 返回平均損失值,平均準確率return avg_loss_val_mean, acc_val_mean5.3 設置訓練場所
下面設置訓練使用的設備。在復雜量較低的時候使用 CPU 就可以完成任務,但是對于大規模計算就需要使用 GPU 訓練。目前 GPU 訓練都是基于 CUDA 工具之上的。
#練習5: # 補全代碼,完成訓練場所的設置。 #要求:工作場所變量名稱為place ### 練習代碼開始處 ### (≈2行代碼) use_cuda = True place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() print("運行完畢!") ### 練習代碼結束處 ###5.4 讀取數據集
paddle.dataset.mnist.train()和paddle.dataset.mnist.test()分別做訓練和測試數據集。這兩個函數各自返回一個reader——PaddlePaddle中的reader是一個Python函數,每次調用的時候返回一個Python yield generator。
下面shuffle是一個reader decorator,它接受一個reader A,返回另一個reader B。reader B 每次讀入buffer_size條訓練數據到一個buffer里,然后隨機打亂其順序,并且逐條輸出。
batch是一個特殊的decorator,它的輸入是一個reader,輸出是一個batched reader。在PaddlePaddle里,一個reader每次yield一條訓練數據,而一個batched reader每次yield一個minibatch。
# 一個minibatch中有64個數據 BATCH_SIZE = 32 #BATCH_SIZE# 每次讀取訓練集中的500個數據并隨機打亂,傳入batched reader中,batched reader 每次 yield 64個數據 train_reader = paddle.batch(paddle.reader.shuffle(train(), buf_size=500),batch_size=BATCH_SIZE)# 讀取測試集的數據,每次 yield 64個數據 test_reader = paddle.batch(paddle.reader.shuffle(test(), buf_size=500),batch_size=BATCH_SIZE)feeder = fluid.DataFeeder(feed_list=[img, label], place=place) #創建執行器: exe = fluid.Executor(place) exe.run(fluid.default_startup_program())#設置 main_program main_program = fluid.default_main_program()#設置訓練過程的超參: PASS_NUM = 8 #設置訓練樣本訓練的總 n 輪數,這個參數可以根據需要進行改動,以達到我們需要的訓練效果# 將模型參數存儲在名為 save_dirname 的文件中 save_dirname = "saved_model"設置訓練主循環
我們構建一個循環來進行訓練,直到訓練結果足夠好或者循環次數足夠多。 如果訓練迭代次數滿足參數保存的迭代次數,可以把訓練參數保存到params_dirname。
epochs = [epoch_id for epoch_id in range(PASS_NUM)]lists = [] step = 0 for epoch_id in epochs:for step_id, data in enumerate(train_reader()):metrics = exe.run(main_program,feed=feeder.feed(data),fetch_list=[avg_loss, acc])#我們可以把訓練結果打印輸出,也可以用畫圖展示出來if step % 100 == 0: #每訓練100次 打印一次log,或者添加一個繪圖點#print("Pass %d, Batch %d, Cost %f" % (step, epoch_id, metrics[0]))event_handler_plot(train_prompt, step, metrics[0])step += 1# test for epoch# 測試每個epoch的分類效果avg_loss_val, acc_val = train_test(train_test_program=test_program,train_test_reader=test_reader,train_test_feed=feeder)print("Test with Epoch %d, avg_cost: %s, acc: %s" %(epoch_id, avg_loss_val, acc_val))lists.append((epoch_id, avg_loss_val, acc_val))# 保存訓練好的模型參數用于預測if save_dirname is not None:fluid.io.save_inference_model(save_dirname, ["img"], [prediction],exe) Test with Epoch 0, avg_cost: 0.2838544199784724, acc: 0.9069488817891374 Test with Epoch 1, avg_cost: 0.22450397592841292, acc: 0.9254193290734825 Test with Epoch 2, avg_cost: 0.18711660684089357, acc: 0.9409944089456869 Test with Epoch 3, avg_cost: 0.17830089215909284, acc: 0.943091054313099 Test with Epoch 4, avg_cost: 0.1803132943502559, acc: 0.9401956869009584 Test with Epoch 5, avg_cost: 0.1785307344102774, acc: 0.9427915335463258 Test with Epoch 6, avg_cost: 0.17006214236217246, acc: 0.9452875399361023 Test with Epoch 7, avg_cost: 0.17242987517538233, acc: 0.9462859424920128 #我們從訓練好的模型中,找到acc值最好的: # find the best pass # 選擇效果最好的pass best = sorted(lists, key=lambda list: float(list[2]))[-1] print('Best pass is %s, testing Avgcost is %s' % (best[0], best[1])) print('The classification accuracy is %.2f%%' % float((best[2]) * 100))6 - 應用模型
模型訓練好之后,我們可以使用訓練好的模型對手寫體數字圖片進行分類,下面程序展示了如何使用訓練好的模型進行推斷。
首先,定義一個用來處理輸入的圖片的函數 load_image(file)。 它會將輸入的圖片處理成滿足分類器輸入要求的格式。
def load_image(file):im = Image.open(file).convert('L')#因為mnist數據集是28*28的,所以我們最好把預測圖片的也resize成(28、28的)im = im.resize((28, 28), Image.ANTIALIAS)#并且我么也需要把預測圖片reshpe成(1,1,28,28),第一個1表示是一張圖片,第二個1表示是單通道灰度圖,后面兩個28還是圖片的H/W#并且我們需要把數據類型變成float32im = numpy.array(im).reshape(1, 1, 28, 28).astype(numpy.float32)#因為訓練的時候我們的圖片進行了歸一化處理,所以預測時我們也做一下歸一化。im = im / 255.0 * 2.0 - 1.0return im #加載圖片 tensor_img = load_image("/home/aistudio/data/data2304/infer_3.png")然后,開始預測,并輸出結果
nference_scope = fluid.core.Scope() with fluid.scope_guard(inference_scope):# 使用 fluid.io.load_inference_model 獲取 inference program,# feed_target_names 用于指定需要傳入網絡的變量名# fetch_targets 指定希望從網絡中fetch出的變量名[inference_program, feed_target_names,fetch_targets] = fluid.io.load_inference_model(save_dirname, exe)# 開始預測results = exe.run(inference_program,# 將預測數據封裝為字典格式:{feed_target_name: feed_target_data}feed={feed_target_names[0]: tensor_img},fetch_list=fetch_targets)# 獲取lab = numpy.argsort(results)print("Inference result of image/infer_3.png is: %d" % lab[0][0][-1])# 將feed構建成字典 {feed_target_name: feed_target_data}# 結果將包含一個與fetch_targets對應的數據列表# 我們可以實現批量預測,通過一個循環,每次預測一個mini_batchfor mini_batch in test_reader():#test_x = numpy.array([data[0] for data in mini_batch]).astype("float32")test_x = numpy.array([data[0].reshape(1, 28, 28) for data in mini_batch]).astype("float32")test_y = numpy.array([data[1] for data in mini_batch]).astype("int64")# 真實進行預測mini_batch_result = exe.run(inference_program,feed={feed_target_names[0]: test_x},fetch_list=fetch_targets)# 打印預測結果mini_batch_result = numpy.argsort(mini_batch_result) #找出可能性最大的列標,升序排列mini_batch_result = mini_batch_result[0][:, -1] #把這些列標拿出來print('預測結果:%s'%mini_batch_result)# 打印真實結果 label = test_y # 轉化為 labelprint('真實結果:%s'%label)#計數label_len = len(label)right_counter = 0for i in range(label_len):if mini_batch_result[i] == label[i]:right_counter += 1ratio = (right_counter/label_len)print("準確率為:%.2f%%"%(ratio*100))7 - 總結
通過這個練習學習了:
如何利用PaddlePaddle搭建CNN卷及網絡,并調用相關的的API接口對圖片進行特征提取和分類
本次搭建的卷積神經網絡是最基礎的卷積模型,后續章節中復雜的CNN網絡都是從它們衍生出來的,因此這個模型對之后的學習大有裨益。
?
?
總結
以上是生活随笔為你收集整理的动手学PaddlePaddle(4):MNIST(手写数字识别)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 星链恐遭断网关停 SpaceX彻底急了
- 下一篇: mcmnhdlr.exe是什么进程 能删