百度PaddlePaddle >>> 3. 手寫數字識別

在這裏插入圖片描述

一、前言

本文基於百度PaddlePaddle 的教程:
https://aistudio.baidu.com/aistudio/projectdetail/349025

上一篇我們介紹了什麼是深度學習 👉 百度PaddlePaddle >>> 2. 什麼是深度學習?

這次我們來寫個手寫數字識別的深度學習程序!

二、開始

在這裏插入圖片描述

1. 準備數據

MNIST數據集包含60000個訓練集和10000測試數據集。分爲圖片和標籤,圖片是28*28的像素矩陣,標籤爲0~9共10個數字。
定義讀取MNIST數據集的train_readertest_reader,指定一個Batch的大小爲128,也就是一次訓練或驗證128張圖像。
paddle.dataset.mnist.train()test()接口已經爲我們對圖片進行了灰度處理、歸一化、居中處理等處理。

import numpy as np
import paddle as paddle
import paddle.fluid as fluid
from PIL import Image
import matplotlib.pyplot as plt
import os

train_reader = paddle.batch(paddle.reader.shuffle(paddle.dataset.mnist.train(),
                                                    buf_size=512),
                                                    batch_size=128)
test_reader = paddle.batch(paddle.dataset.mnist.test(),
                           batch_size=128)

MNIST 數據集下載鏈接:http://yann.lecun.com/exdb/mnist/

在這裏插入圖片描述

2. 配置網絡

先定義一個簡單的多層感知器,一共有三層,兩個大小爲100的隱層和一個大小爲10的輸出層,因爲MNIST數據集是手寫0到9的灰度圖像,類別有10個,所以最後的輸出大小是10。

最後輸出層的激活函數是Softmax,所以最後的輸出層相當於一個分類器。加上一個輸入層的話,多層感知器的結構是:輸入層–>>隱層–>>隱層–>>輸出層

def multilayer_perceptron(input):
    # 第一個全連接層,激活函數爲ReLU
    hidden1 = fluid.layers.fc(input=input, size=100, act='relu')
    # 第二個全連接層,激活函數爲
    hidden2 = fluid.layers.fc(input=hidden1, size=100, act='relu')
    # 以softmax 爲激活函數的全連接輸出層,大小爲10
    prediction = fluid.layers.fc(input=hidden2, size=10, act='softmax')
    return prediction

再定義一個輸入層,輸入的是圖像數據。圖像是28*28的灰度圖,所以輸入的形狀是[1, 28, 28]。

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)

接着是定義損失函數,這次使用的是交叉熵損失函數。

定義了一個損失函數之後,還有對它求平均值,因爲定義的是一個Batch的損失值。同時我們還可以定義一個準確率函數,這個可以在我們訓練的時候輸出分類的準確率。

cost = fluid.layers.cross_entropy(input=model, label=label)
avg_cost = fluid.layers.mean(cost)
acc = fluid.layers.accuracy(input=model, label=label)

再然後是定義優化方法,這次我們使用的是Adam優化方法,同時指定學習率爲0.001:

optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.001)
opts = optimizer.minimize(avg_cost)

在這裏插入圖片描述

3. 模型訓練 和 模型評估

定義一個解析器和初始化參數:

# 定義一個使用CPU 的解析器
place = fluid.CPUPlace()
exe = fluid.Executor(place)
# 進行參數初始化
exe.run(fluid.default_startup_program())

輸入的數據維度是圖像數據和圖像對應的標籤,每個類別的圖像都要對應一個標籤,這個標籤是從0遞增的整型數值。

# 定義輸入數據維度
feeder = fluid.DataFeeder(place=place, feed_list=[image, label])

開始訓練!

開始訓練和測試
for pass_id in range(5):
    # 進行訓練
    for batch_id, data in enumerate(train_reader()):                            # 遍歷train_reader
        train_cost, train_acc = exe.run(program=fluid.default_main_program(),   # 運行主程序
                                        feed=feeder.feed(data),                 # 給模型喂入數據
                                        fetch_list=[avg_cost, acc])             # fetch 誤差、準確率
        # 每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_reader    
        test_cost, test_acc = exe.run(program=fluid.default_main_program(), # 執行訓練程序
                                        feed=feeder.feed(data),             # 喂入數據
                                        fetch_list=[avg_cost, acc])         # fetch 誤差、準確率
        
        test_accs.append(test_acc[0])   # 每個batch的準確率
        test_costs.append(test_cost[0]) # 每個batch的誤差
    # 求測試結果的平均值
    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))

    # 保存模型
    # 如果保存路徑不存在就創建
    if not os.path.exists(model_save_dir):
        os.makedirs(model_save_dir)
    print ('save models to %s' % (model_save_dir))
    # 保存推理model的路徑,推理需要feed的數據,保存推理結果的Variables
    fluid.io.save_inference_model(model_save_dir,
                                    ['image'],
                                    [model],
                                    exe)

訓練結果:
在這裏插入圖片描述
在這裏插入圖片描述

4. 模型預測

在預測之前,要對圖像進行預處理,處理方式要跟訓練的時候一樣。首先進行灰度化,然後壓縮圖像大小爲28*28,接着將圖像轉換成一維向量,最後再對一維向量進行歸一化處理。

def load_image(file):
    im = Image.open(file).convert('L')                          # 將RGB轉化爲灰度圖像,L代表灰度圖像,灰度圖像的像素值在0-255之間
    im = im.resize((28, 28), Image.ANTIALIAS)                   # resize image with high-quality 圖像大小爲28*28
    im = np.array(im).reshape(1, 1, 28, 28).astype(np.float32)  # 返回新形狀的數組,把它變成一個numpy數組以匹配數據饋送格式
    im = im/255.0*2.0-1.0
    # print(im)
    return im

infer_exe = fluid.Executor(place)
inference_scope = fluid.core.Scope()

將圖像轉換成一維向量並進行預測,數據從feed中的image傳入。fetch_list的值是網絡模型的最後一層分類器,所以輸出的結果是10個標籤的概率值,這些概率值的總和爲1。

# 加載數據並開始預測
with fluid.scope_guard(inference_scope):
    # 獲取訓練好的模型
    # 從指定目錄中加載 推理model(inference model)
    [inference_program,                                             # 推理Program
    feed_target_names,                                              # 是一個str列表,它包含需要在推理Program中提供數據的變量的名稱
    fetch_targets] = fluid.io.load_inference_model(model_save_dir,  # fetch_targets: 是一個Variable列表,從中我們可以得到推斷結果。model_save_dir:模型保存的路徑
                                                    infer_exe)      # infer_exe: 運行inference model 的executor

    img = load_image(img_path)

    results = exe.run(program=inference_program,            # 運行預測程序
                        feed={feed_target_names[0]:img},    # 喂入需要預測的img
                        fetch_list=fetch_targets)           # 得到推測結果

拿到每個標籤的概率值之後,我們要獲取概率最大的標籤,並打印出來。

# 獲取概率最大的label
lab = np.argsort(results)                               # argsort函數返回的是result數組值從小到大的索引值
print("該圖片的預測結果的label爲: %d" % lab[0][0][-1])   # -1代表讀取數組中倒數第一列

三、結果

在這裏插入圖片描述
在這裏插入圖片描述 在這裏插入圖片描述
上面兩張照片都能夠正確識別👇
在這裏插入圖片描述
最後我自己寫了幾張,也可以正確識別:
在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述

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