> 原文博客:Doi技術團隊

鏈接地址:https://blog.doiduoyi.com/authors/1584446358138
初心:記錄優秀的Doi技術團隊學習經歷

*本篇文章基於 PaddlePaddle 0.11.0、Python 2.7

數據集的介紹


如題目所示,本次訓練使用到的是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

所有代碼


cnn.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)


上一章:《我的PaddlePaddle學習之路》筆記一——PaddlePaddle的安裝
下一章:《我的PaddlePaddle學習之路》筆記三——CIFAR彩色圖像識別


項目代碼


GitHub地址:https://github.com/yeyupiaoling/LearnPaddle

參考資料


  1. http://paddlepaddle.org/
  2. http://yann.lecun.com/exdb/mnist/
小夜