1. 程式人生 > >《PaddlePaddle從入門到煉丹》四——卷積神經網路

《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我們很容易就定義了一個卷積神經網路,並完成了影象分類的訓練和預測。卷積神經網路在影象識別上發揮著巨大的作用,而在自然語言處理上,迴圈神經網路同樣起著巨大的作用,我們下一章就學習一下迴圈神經網路。

參考資料