不久之前,機器之心聯合百度推出 PaddlePaddle 專欄,為想要學習這一平臺的技術人員推薦相關教程與資源。在框架解析和安裝教程的介紹之後,本次專欄將教你如何在 PaddlePaddle 上實現 MNIST 手寫數字識別。
目錄
- 目錄
- 資料集的介紹
- 定義神經網路
- 開始訓練模型
- 匯入依賴包
- 初始化 Paddle
- 獲取訓練器
- 開始訓練
- 使用引數預測
- 初始化 PaddlePaddle
- 獲取訓練好的引數
- 讀取圖片
- 開始預測
- 所有程式碼
- 專案程式碼
- 參考閱讀
資料集的介紹
如題目所示, 本次訓練使用到的是 MNIST 資料庫的手寫數字, 這個資料集包含 60,000 個示例的訓練集以及 10,000 個示例的測試集. 圖片是 28x28 的畫素矩陣,標籤則對應著 0~9 的 10 個數字。每張圖片都經過了大小歸一化和居中處理. 該資料集的圖片是一個黑白的單通道圖片, 其中圖片如下:
該資料集非常小, 很適合影像識別的入門使用, 該資料集一共有 4 個檔案, 分別是訓練資料和其對應的標籤, 測試資料和其對應的標籤. 檔案如表所示:
檔名稱 | 大小 | 說明 |
---|---|---|
train-images-idx3-ubyte | 9.9M | 訓練資料圖片,60,000 條資料 |
train-labels-idx1-ubyte | 28.9K | 訓練資料標籤,60,000 條資料 |
t10k-images-idx3-ubyte | 1.6M | 測試資料圖片,10,000 條資料 |
t10k-labels-idx1-ubyte | 4.5K | 測試資料標籤,10,000 條資料 |
這個資料集針對 170 多 M 的 CIFAR 資料集來說, 實在是小太多了. 這使得我們訓練起來非常快, 這能一下子激發開發者的興趣.
在訓練時, 開發者不需要單獨去下載該資料集, PaddlePaddle 已經幫我們封裝好了, 在我們呼叫paddle.dataset.mnist
的時候, 會自動在下載到快取目錄/home/username/.cache/paddle/dataset/mnist
下, 當以後再使用的時候, 可以直接在快取中獲取, 就不會去下載了。
定義神經網路
我們這次使用的是卷積神經網路 LeNet-5,官方一共提供了 3 個分類器,分別是 Softmax 迴歸,多層感知器,卷積神經網路 LeNet-5,在影像識別問題上,一直是使用卷積神經網路較多。我們建立一個cnn.py
的 Python 檔案來定義一個 LeNet-5 神經網路,程式碼如下:
# coding=utf-8
import paddle.v2 as paddle
# 卷積神經網路LeNet-5,獲取分類器
def convolutional_neural_network():
# 定義資料模型,資料大小是28*28,即784
img = paddle.layer.data(name="pixel",
type=paddle.data_type.dense_vector(784))
# 第一個卷積--池化層
conv_pool_1 = paddle.networks.simple_img_conv_pool(input=img,
filter_size=5,
num_filters=20,
num_channel=1,
pool_size=2,
pool_stride=2,
act=paddle.activation.Relu())
# 第二個卷積--池化層
conv_pool_2 = paddle.networks.simple_img_conv_pool(input=conv_pool_1,
filter_size=5,
num_filters=50,
num_channel=20,
pool_size=2,
pool_stride=2,
act=paddle.activation.Relu())
# 以softmax為啟用函式的全連線輸出層,輸出層的大小必須為數字的個數10
predict = paddle.layer.fc(input=conv_pool_2,
size=10,
act=paddle.activation.Softmax())
return predict
開始訓練模型
我們建立一個train.py
的 Python 檔案來做訓練模型。
匯入依賴包
首先要先匯入依賴包, 其中就包含了最重要的 PaddlePaddle 的 V2 包
# encoding:utf-8
import os
import sys
import paddle.v2 as paddle
from cnn import convolutional_neural_network
初始化 Paddle
然後我們建立一個類, 再在類中建立一個初始化函式, 在初始化函式中來初始化我們的 PaddlePaddle,在初始化 PaddlePaddle 的時候,就要指定是否使用 GPU 來訓練我們的模型,同時使用多少個執行緒來訓練。
class TestMNIST:
def __init__(self):
# 該模型執行在CUP上,CUP的數量為2
paddle.init(use_gpu=False, trainer_count=2)
獲取訓練器
透過上面一步獲取的分類器和圖片的標籤來生成一個損失函式, 透過損失函式就可以建立訓練引數了。
之後也要建立一個最佳化方法,這個最佳化方法是定義學習率等等在訓練中的處理。
最後透過訓練引數,最佳化方法,損失函式這 3 個引數建立訓練器
# *****************獲取訓練器********************************
def get_trainer(self):
# 獲取分類器
out = convolutional_neural_network()
# 定義標籤
label = paddle.layer.data(name="label",
type=paddle.data_type.integer_value(10))
# 獲取損失函式
cost = paddle.layer.classification_cost(input=out, label=label)
# 獲取引數
parameters = paddle.parameters.create(layers=cost)
"""
定義最佳化方法
learning_rate 迭代的速度
momentum 跟前面動量最佳化的比例
regularzation 正則化,防止過擬合
:leng re
"""
optimizer = paddle.optimizer.Momentum(learning_rate=0.1 / 128.0,
momentum=0.9,
regularization=paddle.optimizer.L2Regularization(rate=0.0005 * 128))
'''
建立訓練器
cost 損失函式
parameters 訓練引數,可以透過建立,也可以使用之前訓練好的引數
update_equation 最佳化方法
'''
trainer = paddle.trainer.SGD(cost=cost,
parameters=parameters,
update_equation=optimizer)
return trainer
開始訓練
最後就可以的開始訓練了, 透過上一步得到的訓練器開始訓練, 訓練的時候要用到 3 個引數.
第一個是訓練資料, 這個訓練資料就是我們的 MNIST 資料集.
第二個是訓練的輪數, 表示我們要訓練多少輪, 次數越多準確率越高, 最終會穩定在一個固定的準確率上.
第三個是訓練過程中的一些事件處理, 比如會在每個 batch 列印一次日誌, 在每個 pass 之後儲存一下引數和測試一下測試資料集的預測準確率.
# *****************開始訓練********************************
def start_trainer(self):
# 獲取訓練器
trainer = self.get_trainer()
# 定義訓練事件
def event_handler(event):
lists = []
if isinstance(event, paddle.event.EndIteration):
if event.batch_id % 100 == 0:
print "\nPass %d, Batch %d, Cost %f, %s" % (
event.pass_id, event.batch_id, event.cost, event.metrics)
else:
sys.stdout.write('.')
sys.stdout.flush()
if isinstance(event, paddle.event.EndPass):
# 儲存訓練好的引數
model_path = '../model'
if not os.path.exists(model_path):
os.makedirs(model_path)
with open(model_path + "/model.tar", 'w') as f:
trainer.save_parameter_to_tar(f=f)
result = trainer.test(reader=paddle.batch(paddle.dataset.mnist.test(), batch_size=128))
print "\nTest with Pass %d, Cost %f, %s\n" % (event.pass_id, result.cost, result.metrics)
lists.append((event.pass_id, result.cost, result.metrics['classification_error_evaluator']))
# 獲取資料
reader = paddle.batch(paddle.reader.shuffle(paddle.dataset.mnist.train(), buf_size=20000),
batch_size=128)
'''
開始訓練
reader 訓練資料
num_passes 訓練的輪數
event_handler 訓練的事件,比如在訓練的時候要做一些什麼事情
'''
trainer.train(reader=reader,
num_passes=100,
event_handler=event_handler)
然後在main
入口中呼叫我們的訓練函式, 就可以訓練了
if __name__ == "__main__":
testMNIST = TestMNIST()
# 開始訓練
testMNIST.start_trainer()
在訓練過程中會輸出這樣的日誌:
Pass 0, Batch 0, Cost 2.991905, {'classification_error_evaluator': 0.859375}
...................................................................................................
Pass 0, Batch 100, Cost 0.891881, {'classification_error_evaluator': 0.3046875}
...................................................................................................
Pass 0, Batch 200, Cost 0.309183, {'classification_error_evaluator': 0.0859375}
...................................................................................................
Pass 0, Batch 300, Cost 0.289464, {'classification_error_evaluator': 0.078125}
...................................................................................................
Pass 0, Batch 400, Cost 0.131645, {'classification_error_evaluator': 0.03125}
....................................................................
Test with Pass 0, Cost 0.117626, {'classification_error_evaluator': 0.03790000081062317}
使用引數預測
我們建立一個infer.py
的 Python 檔案,用來做模型預測的。
初始化 PaddlePaddle
在預測的時候也是要初始化 PaddlePaddle 的
class TestMNIST:
def __init__(self):
# 該模型執行在CUP上,CUP的數量為2
paddle.init(use_gpu=False, trainer_count=2)
獲取訓練好的引數
在訓練的時候, 我們在 pass 訓練結束後都會儲存他的引數, 儲存這些引數我們現在就可以使用它來預測了
# *****************獲取引數********************************
def get_parameters(self):
with open("../model/model.tar", 'r') as f:
parameters = paddle.parameters.Parameters.from_tar(f)
return parameters
讀取圖片
在使用圖片進行預測時,我們要對圖片進行處理,,處理成跟訓練的圖片一樣,28*28 的灰度圖,最後影像會轉化成一個浮點陣列。
# *****************獲取你要預測的引數********************************
def get_TestData(self):
def load_images(file):
# 對圖進行灰度化處理
im = Image.open(file).convert('L')
# 縮小到跟訓練資料一樣大小
im = im.resize((28, 28), Image.ANTIALIAS)
im = np.array(im).astype(np.float32).flatten()
im = im / 255.0
return im
test_data = []
test_data.append((load_images('../images/infer_3.png'),))
return
開始預測
透過傳入分類器,訓練好的引數,預測資料這個 3 個引數就可以進行預測了。這個分類器就是我們之前定義的。
# *****************使用訓練好的引數進行預測********************************
def to_prediction(self, out, parameters, test_data):
# 開始預測
probs = paddle.infer(output_layer=out,
parameters=parameters,
input=test_data)
# 處理預測結果並列印
lab = np.argsort(-probs)
print "預測結果為: %d" % lab[0][0]
在main
入口中呼叫預測函式
if __name__ == "__main__":
testMNIST = TestMNIST()
out = convolutional_neural_network()
parameters = testMNIST.get_parameters()
test_data = testMNIST.get_TestData()
# 開始預測
testMNIST.to_prediction(out=out, parameters=parameters, test_data=test_data)
輸出的預測結果是:
預測結果為: 3
所有程式碼
infer.py
程式碼:
# coding=utf-8
import paddle.v2 as paddle
# 卷積神經網路LeNet-5,獲取分類器
def convolutional_neural_network():
# 定義資料模型,資料大小是28*28,即784
img = paddle.layer.data(name="pixel",
type=paddle.data_type.dense_vector(784))
# 第一個卷積--池化層
conv_pool_1 = paddle.networks.simple_img_conv_pool(input=img,
filter_size=5,
num_filters=20,
num_channel=1,
pool_size=2,
pool_stride=2,
act=paddle.activation.Relu())
# 第二個卷積--池化層
conv_pool_2 = paddle.networks.simple_img_conv_pool(input=conv_pool_1,
filter_size=5,
num_filters=50,
num_channel=20,
pool_size=2,
pool_stride=2,
act=paddle.activation.Relu())
# 以softmax為啟用函式的全連線輸出層,輸出層的大小必須為數字的個數10
predict = paddle.layer.fc(input=conv_pool_2,
size=10,
act=paddle.activation.Softmax())
return predict
train.py
程式碼:
# encoding:utf-8
import os
import sys
import paddle.v2 as paddle
from cnn import convolutional_neural_network
class TestMNIST:
def __init__(self):
# 該模型執行在CUP上,CUP的數量為2
paddle.init(use_gpu=False, trainer_count=2)
# *****************獲取訓練器********************************
def get_trainer(self):
# 獲取分類器
out = convolutional_neural_network()
# 定義標籤
label = paddle.layer.data(name="label",
type=paddle.data_type.integer_value(10))
# 獲取損失函式
cost = paddle.layer.classification_cost(input=out, label=label)
# 獲取引數
parameters = paddle.parameters.create(layers=cost)
"""
定義最佳化方法
learning_rate 迭代的速度
momentum 跟前面動量最佳化的比例
regularzation 正則化,防止過擬合
:leng re
"""
optimizer = paddle.optimizer.Momentum(learning_rate=0.1 / 128.0,
momentum=0.9,
regularization=paddle.optimizer.L2Regularization(rate=0.0005 * 128))
'''
建立訓練器
cost 分類器
parameters 訓練引數,可以透過建立,也可以使用之前訓練好的引數
update_equation 最佳化方法
'''
trainer = paddle.trainer.SGD(cost=cost,
parameters=parameters,
update_equation=optimizer)
return trainer
# *****************開始訓練********************************
def start_trainer(self):
# 獲取訓練器
trainer = self.get_trainer()
# 定義訓練事件
def event_handler(event):
lists = []
if isinstance(event, paddle.event.EndIteration):
if event.batch_id % 100 == 0:
print "\nPass %d, Batch %d, Cost %f, %s" % (
event.pass_id, event.batch_id, event.cost, event.metrics)
else:
sys.stdout.write('.')
sys.stdout.flush()
if isinstance(event, paddle.event.EndPass):
# 儲存訓練好的引數
model_path = '../model'
if not os.path.exists(model_path):
os.makedirs(model_path)
with open(model_path + "/model.tar", 'w') as f:
trainer.save_parameter_to_tar(f=f)
# 使用測試進行測試
result = trainer.test(reader=paddle.batch(paddle.dataset.mnist.test(), batch_size=128))
print "\nTest with Pass %d, Cost %f, %s\n" % (event.pass_id, result.cost, result.metrics)
lists.append((event.pass_id, result.cost, result.metrics['classification_error_evaluator']))
# 獲取資料
reader = paddle.batch(paddle.reader.shuffle(paddle.dataset.mnist.train(), buf_size=20000),
batch_size=128)
'''
開始訓練
reader 訓練資料
num_passes 訓練的輪數
event_handler 訓練的事件,比如在訓練的時候要做一些什麼事情
'''
trainer.train(reader=reader,
num_passes=100,
event_handler=event_handler)
if __name__ == "__main__":
testMNIST = TestMNIST()
# 開始訓練
testMNIST.start_trainer()
infer.py
程式碼:
# encoding:utf-8
import numpy as np
import paddle.v2 as paddle
from PIL import Image
from cnn import convolutional_neural_network
class TestMNIST:
def __init__(self):
# 該模型執行在CUP上,CUP的數量為2
paddle.init(use_gpu=False, trainer_count=2)
# *****************獲取引數********************************
def get_parameters(self):
with open("../model/model.tar", 'r') as f:
parameters = paddle.parameters.Parameters.from_tar(f)
return parameters
# *****************獲取你要預測的引數********************************
def get_TestData(self ,path):
def load_images(file):
# 對圖進行灰度化處理
im = Image.open(file).convert('L')
# 縮小到跟訓練資料一樣大小
im = im.resize((28, 28), Image.ANTIALIAS)
im = np.array(im).astype(np.float32).flatten()
im = im / 255.0
return im
test_data = []
test_data.append((load_images(path),))
return test_data
# *****************使用訓練好的引數進行預測********************************
def to_prediction(self, out, parameters, test_data):
# 開始預測
probs = paddle.infer(output_layer=out,
parameters=parameters,
input=test_data)
# 處理預測結果並列印
lab = np.argsort(-probs)
print "預測結果為: %d" % lab[0][0]
if __name__ == "__main__":
testMNIST = TestMNIST()
# 開始預測
out = convolutional_neural_network()
parameters = testMNIST.get_parameters()
test_data = testMNIST.get_TestData('../images/infer_3.png')
testMNIST.to_prediction(out=out, parameters=parameters, test_data=test_data)
專案程式碼
GitHub 地址:https://github.com/yeyupiaoling/LearnPaddle