《PaddlePaddle從入門到煉丹》四——卷積神經網路
文章目錄
前言
上一章我們通過學習線性迴歸例子入門了深度學習,同時也熟悉了PaddlePaddle的使用方式,那麼我們在本章學習更有趣的知識點卷積神經網路。深度學習之所以那麼流行,很大程度上是得益於它在計算機視覺上得到非常好的效果,而在深度學習上幾乎是使用卷積神經網路來提取影象的特徵的。在PaddlePaddle上如何定義一個卷積神經網路,並使用它來完成一個影象識別的任務呢。在本章我們通過學習MNIST影象資料集的分類例子,來掌握卷積神經網路的使用。
訓練模型
首先匯入所需得包,這次使用到了MNIST資料集介面,也使用了處理影象得工具包。
import numpy as np
import paddle as paddle
import paddle.dataset.mnist as mnist
import paddle.fluid as fluid
from PIL import Image
import matplotlib.pyplot as plt
在影象識別上,使用得演算法也經過了多次的迭代更新,比如多層感知器,在卷積神經網路廣泛使用之前,多層感知器在影象識別上是非常流行的,從這方面來看,多層感知器在當時也是有一定的優勢的。那麼如下使用PaddlePaddle來定義一個多層感知器呢,我們可以來學習一下。以下的程式碼判斷就是定義一個簡單的多層感知器,一共有三層,兩個大小為100的隱層和一個大小為10的輸出層,因為MNIST資料集是手寫0到9的灰度影象,類別有10個,所以最後的輸出大小是10。最後輸出層的啟用函式是Softmax,所以最後的輸出層相當於一個分類器。加上一個輸入層的話,多層感知器的結構是:輸入層-->>隱層-->>隱層-->>輸出層
# 定義多層感知器
def multilayer_perceptron(input):
# 第一個全連線層,啟用函式為ReLU
hidden1 = fluid.layers.fc(input=input, size=100, act='relu')
# 第二個全連線層,啟用函式為ReLU
hidden2 = fluid.layers.fc(input=hidden1, size=100, act='relu')
# 以softmax為啟用函式的全連線輸出層,大小為label大小
fc = fluid.layers.fc(input=hidden2, size= 10, act='softmax')
return fc
卷積神經網路普遍用在影象特徵提取上,一些影象分類、目標檢測、文字識別幾乎都回使用到卷積神經網路作為影象的特徵提取方式。卷積神經網路通常由卷積層、池化層和全連線層,有時還有Batch Normalization層和Dropout層。下面我們就建立一個簡單卷積神經網路,一共定義了5層,加上輸入層的話,它的結構是:輸入層-->>卷積層-->>池化層-->>卷積層-->>池化層-->>輸出層
。我們可以通過呼叫PaddlePaddle的介面fluid.layers.conv2d()
來做一次卷積操作,我們可以通過num_filters
引數設定卷積核的數量,通過filter_size
設定卷積核的大小,還有通過stride
來設定卷積操作時移動的步長。使用fluid.layers.pool2d()
介面做一次池化操作,通過引數pool_size
可以設定池化的大小,通過引數pool_stride
設定池化滑動的步長,通過引數pool_type
設定池化的型別,目前有最大池化和平均池化,下面使用的時最大池化,當值為avg
時是平均池化。
# 卷積神經網路
def convolutional_neural_network(input):
# 第一個卷積層,卷積核大小為3*3,一共有32個卷積核
conv1 = fluid.layers.conv2d(input=input,
num_filters=32,
filter_size=3,
stride=1)
# 第一個池化層,池化大小為2*2,步長為1,最大池化
pool1 = fluid.layers.pool2d(input=conv1,
pool_size=2,
pool_stride=1,
pool_type='max')
# 第二個卷積層,卷積核大小為3*3,一共有64個卷積核
conv2 = fluid.layers.conv2d(input=pool1,
num_filters=64,
filter_size=3,
stride=1)
# 第二個池化層,池化大小為2*2,步長為1,最大池化
pool2 = fluid.layers.pool2d(input=conv2,
pool_size=2,
pool_stride=1,
pool_type='max')
# 以softmax為啟用函式的全連線輸出層,大小為label大小
fc = fluid.layers.fc(input=pool2, size=10, act='softmax')
return fc
定義輸入層,輸入的是影象資料。影象是28*28
的灰度圖,所以輸入的形狀是[1, 28, 28]
,如果影象是32*32
的彩色圖,那麼輸入的形狀是[3. 32, 32]
,因為灰度圖只有一個通道,而彩色圖有RGB三個通道。理論上它還有一個維度是Batch的,不過這個是PaddlePaddle幫我們預設設定的,我們可以不用理會。
# 定義輸入層
image = fluid.layers.data(name='image', shape=[1, 28, 28], dtype='float32')
label = fluid.layers.data(name='label', shape=[1], dtype='int64')
上面定義了多層感機器和卷積神經網路,我們可以在這裡呼叫定義好的網路來獲取分類器,讀者可以嘗試這兩種不同的網路進行訓練,觀察一下他們的準確率如何。
# 獲取分類器
# model = multilayer_perceptron(image)
model = convolutional_neural_network(image)
接著是定義損失函式,這次使用的是交叉熵損失函式,該函式在分類任務上比較常用。定義了一個損失函式之後,還有對它求平均值,因為定義的是一個Batch的損失值。同時我們還可以定義一個準確率函式,這個可以在我們訓練的時候輸出分類的準確率。
# 獲取損失函式和準確率函式
cost = fluid.layers.cross_entropy(input=model, label=label)
avg_cost = fluid.layers.mean(cost)
acc = fluid.layers.accuracy(input=model, label=label)
然後我們從主程式中克隆一個程式作為預測程式,之後可以使用這個預測程式預測測試的準確率和預測自己的影象。
# 獲取測試程式
test_program = fluid.default_main_program().clone(for_test=True)
接著是定義優化方法,這次我們使用的是Adam優化方法,同時指定學習率為0.001。
# 定義優化方法
optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.001)
opts = optimizer.minimize(avg_cost)
定義讀取MNIST資料集的reader,指定一個Batch的大小為128,也就是一次訓練128張影象。
# 獲取MNIST資料
train_reader = paddle.batch(mnist.train(), batch_size=128)
test_reader = paddle.batch(mnist.test(), batch_size=128)
接著也是定義一個解析器和初始化引數,Fluid版本使用的流程都差不多。
# 定義一個使用CPU的解析器
place = fluid.CPUPlace()
exe = fluid.Executor(place)
# 進行引數初始化
exe.run(fluid.default_startup_program())
輸入的資料維度是影象資料和影象對應的標籤,每個類別的影象都要對應一個標籤,這個標籤是從0遞增的整型數值。
# 定義輸入資料維度
feeder = fluid.DataFeeder(place=place, feed_list=[image, label])
最後就可以開始訓練了,我們這次訓練5個Pass,讀者可以根據自己的情況自由設定。在上面我們已經定義了一個求準確率的函式,所以我們在訓練的時候讓它輸出當前的準確率,計算準確率的原理很簡單,就是把訓練是預測的結果和真實的值比較,求出準確率。每一個Pass訓練結束之後,再進行一次測試,使用測試集進行測試,並求出當前的Cost和準確率的平均值。
# 開始訓練和測試
for pass_id in range(5):
# 進行訓練
for batch_id, data in enumerate(train_reader()):
train_cost, train_acc = exe.run(program=fluid.default_main_program(),
feed=feeder.feed(data),
fetch_list=[avg_cost, acc])
# 每100個batch列印一次資訊
if batch_id % 100 == 0:
print('Pass:%d, Batch:%d, Cost:%0.5f, Accuracy:%0.5f' %
(pass_id, batch_id, train_cost[0], train_acc[0]))
# 進行測試
test_accs = []
test_costs = []
for batch_id, data in enumerate(test_reader()):
test_cost, test_acc = exe.run(program=test_program,
feed=feeder.feed(data),
fetch_list=[avg_cost, acc])
test_accs.append(test_acc[0])
test_costs.append(test_cost[0])
# 求測試結果的平均值
test_cost = (sum(test_costs) / len(test_costs))
test_acc = (sum(test_accs) / len(test_accs))
print('Test:%d, Cost:%0.5f, Accuracy:%0.5f' % (pass_id, test_cost, test_acc))
預測影象
訓練完成之後,我們可以使用從主程式中克隆的test_program
來預測我們自己的影象。再預測之前,要對影象進行預處理,處理方式要跟訓練的時候一樣。首先進行灰度化,然後壓縮影象大小為28*28
,接著將影象轉換成一維向量,最後再對一維向量進行歸一化處理。
# 對圖片進行預處理
def load_image(file):
im = Image.open(file).convert('L')
im = im.resize((28, 28), Image.ANTIALIAS)
im = np.array(im).reshape(1, 1, 28, 28).astype(np.float32)
im = im / 255.0 * 2.0 - 1.0
return im
我們從網上下載一張影象,並將它命名為infer_3.png
。
!wget https://github.com/yeyupiaoling/LearnPaddle2/blob/master/note4/infer_3.png?raw=true -O 'infer_3.png'
我們可以使用Matplotlib工具顯示這張影象。
img = Image.open('infer_3.png')
plt.imshow(img)
plt.show()
最後把影象轉換成一維向量並進行預測,資料從feed
中的image
傳入,label
設定一個假的label值傳進去。fetch_list
的值是網路模型的最後一層分類器,所以輸出的結果是10個標籤的概率值,這些概率值的總和為1。
# 載入資料並開始預測
img = load_image('./infer_3.png')
results = exe.run(program=test_program,
feed={'image': img, "label": np.array([[1]]).astype("int64")},
fetch_list=[model])
拿到每個標籤的概率值之後,我們要獲取概率最大的標籤,並打印出來。
# 獲取概率最大的label
lab = np.argsort(results)
print("該圖片的預測結果的label為: %d" % lab[0][0][-1])
到處為止,本章就結束了。經過學完這一章節,是不是覺得PaddlePaddle非常好用呢,藉助PaddlePaddle我們很容易就定義了一個卷積神經網路,並完成了影象分類的訓練和預測。卷積神經網路在影象識別上發揮著巨大的作用,而在自然語言處理上,迴圈神經網路同樣起著巨大的作用,我們下一章就學習一下迴圈神經網路。