百度飛槳深度學習框架系列課程筆記(一)——數據讀取


本系列是針對於百度飛槳深度學習框架課程的筆記,主要是對百度官方課程資料的總結,內容是以入門項目——手寫數字識別為例介紹深度學習模型的搭建和飛槳框架的使用方法。由於水平實在有限,不免產生謬誤,歡迎讀者多多批評指正。如需要轉載請與博主聯系,謝謝

用飛槳搭建模型——以手寫數字識別為例


整體思路

利用飛槳搭建手寫數字識別模型的整體思路如下圖所示:

整個模型的代碼可以大致分為5個部分,即數據處理、模型設計、訓練配置、模型訓練、模型保存和測試,每個部分中又有着多個需要考慮的任務,這就大概總結了深度學習模型搭建所需的步驟。下面是針對每個部分的具體介紹。

數據處理

數據的預處理是模型解決問題的第一步,干凈而充實的訓練數據是獲得預測效果良好的模型的基本保證,實際工作中要根據所研究的任務不同而選擇合適的處理方式。一般來說需要考慮數據讀入、訓練/驗證集划分,生成批次數據、訓練樣本亂序及數據有效性校驗等問題。
MNIST手寫識別數據集分為訓練集train_set、驗證集val_set和測試集test_set,其中訓練集又包含訓練圖像train_images和訓練標簽train_labels兩個列表。train_image為[50000, 784]的二維列表,包含50000張圖片,每張圖片用一個長度為784的向量表示,內容是28*28尺寸的像素灰度值(黑白圖片)。train_labels為[50000, ]的列表,表示這些圖片對應的分類標簽,即0-9之間的一個數字。
相關實現代碼如下:

# 加載飛槳和相關數據處理的庫
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Linear
import numpy as np
import os
import gzip
import json
import random

# 數據讀取部分

# 聲明數據集文件位置
datafile = './work/mnist.json.gz'
print('loading mnist dataset from {} ......'.format(datafile))
# 加載json數據文件
data = json.load(gzip.open(datafile))
print('mnist dataset load done')
# 讀取到的數據區分訓練集,驗證集,測試集
train_set, val_set, eval_set = data
# 數據集相關參數,圖片高度IMG_ROWS, 圖片寬度IMG_COLS
IMG_ROWS = 28
IMG_COLS = 28

# 分批和亂序

imgs, labels = train_set[0], train_set[1]
# 獲得數據集長度
imgs_length = len(imgs)
# 定義數據集每個數據的序號,根據序號讀取數據
index_list = list(range(imgs_length))
# 讀入數據時用到的批次大小
BATCHSIZE = 100
# 隨機打亂訓練數據的索引序號
random.shuffle(index_list)
# 定義數據生成器,返回批次數據
def data_generator():

    imgs_list = []
    labels_list = []
    for i in index_list:
        # 將數據處理成期望的格式,比如類型為float32,shape為[1, 28, 28]
        img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')
        label = np.reshape(labels[i], [1]).astype('float32')
        imgs_list.append(img) 
        labels_list.append(label)
        if len(imgs_list) == BATCHSIZE:
            # 獲得一個batchsize的數據,並返回
            yield np.array(imgs_list), np.array(labels_list)
            # 清空數據讀取列表
            imgs_list = []
            labels_list = []

    # 如果剩余數據的數目小於BATCHSIZE,
    # 則剩余數據一起構成一個大小為len(imgs_list)的mini-batch
    if len(imgs_list) > 0:
        yield np.array(imgs_list), np.array(labels_list)
    return data_generator

# 數據校驗

# 聲明數據讀取函數,從訓練集中讀取數據
train_loader = data_generator
# 以迭代的形式讀取數據(這里只是校驗下圖像和標簽是否對應,一般在模型訓練時讀取)
for batch_id, data in enumerate(train_loader()):
    image_data, label_data = data
    if batch_id == 0:  # 這里只看一組,如果自動校驗可以遍歷所有數據
        # 人工校驗
        print("打印第一個batch數據的維度:")
        print("圖像維度: {}, 標簽維度: {}, 圖像數據類型: {}, 標簽數據類型: {}".format(image_data.shape, label_data.shape, type(image_data), type(label_data)))
        # 自動校驗(與人工校驗目的類似)
        assert len(image_data.shape[0]) == len(label_data.shape[0]), \
               "length of train_imgs({}) should be the same as train_labels({})".format(image_data.shape[0], label_data.shape[0])
    break

在實際工程中我們通常將上述數據預處理步驟提前封裝成函數,便於在訓練部分直接調用。這里僅對數據讀取部分進行封裝,並預留train/val/test三種模式。此外所需的批次數據生成函數data_generator前面已定義,就不再重復了。

def load_data(mode='train'):
    datafile = './work/mnist.json.gz'
    print('loading mnist dataset from {} ......'.format(datafile))
    # 加載json數據文件
    data = json.load(gzip.open(datafile))
    print('mnist dataset load done')
   
    # 讀取到的數據區分訓練集,驗證集,測試集
    train_set, val_set, eval_set = data
    if mode=='train':
        # 獲得訓練數據集
        imgs, labels = train_set[0], train_set[1]
    elif mode=='valid':
        # 獲得驗證數據集
        imgs, labels = val_set[0], val_set[1]
    elif mode=='eval':
        # 獲得測試數據集
        imgs, labels = eval_set[0], eval_set[1]
    else:
        raise Exception("mode can only be one of ['train', 'valid', 'eval']")
    print("訓練數據集數量: ", len(imgs))
    
    # 校驗數據
    imgs_length = len(imgs)

    assert len(imgs) == len(labels), \
          "length of train_imgs({}) should be the same as train_labels({})".format(len(imgs), len(label))
    
    # 獲得數據集長度
    imgs_length = len(imgs)
    
    # 定義數據集每個數據的序號,根據序號讀取數據
    index_list = list(range(imgs_length))
    # 讀入數據時用到的批次大小
    BATCHSIZE = 100

為了提升模型整體運行效率,我們通常采取異步策略讀取數據,即設立一個異步隊列作為緩存區,將新讀取的數據不斷存入,而模型則從中取出前期存儲的數據進行訓練,從而實現數據讀取和模型訓練的並行運行。飛槳中相關實現方法如下:

# 定義數據讀取后存放的位置,CPU或者GPU,這里使用CPU,place = fluid.CUDAPlace(0) 時,數據才讀取到GPU上
place = fluid.CPUPlace()
with fluid.dygraph.guard(place):
    # 聲明數據加載函數,使用訓練模式
    train_loader = load_data(mode='train')
    # 定義DataLoader對象用於加載Python生成器產生的數據,數據會由Python線程預先讀取,並異步送入一個隊列中(如果去掉下面兩句則代碼變為同步讀取策略)
    # 參數capacity表示在DataLoader中維護的隊列容量,如果讀取數據的速度很快,建議設置為更大的值;return_list:在動態圖模式下需要設置為“True”,詳細參數介紹見飛槳技術文檔
    data_loader = fluid.io.DataLoader.from_generator(capacity=5, return_list=True)
    # 設置數據生成器,輸入的參數是一個Python數據生成器train_loader和服務器資源類型place(標明CPU還是GPU)
    data_loader.set_batch_generator(train_loader, places=place)
    # 迭代的讀取數據並打印數據的形狀
    for i, data in enumerate(data_loader):
        image_data, label_data = data
        print(i, image_data.shape, label_data.shape)
        if i>=5:
            break

利用上述數據讀取部分,我們可以搭建一個最簡單的一層神經網絡進行訓練,以此展示讀取的數據應當如何使用。即使今后搭建非常復雜的模型,整體思路也是一樣的。

class MNIST(fluid.dygraph.Layer):   # 定義神經網絡模型
    def __init__(self):
        super(MNIST, self).__init__()
        self.fc = Linear(input_dim=784, output_dim=1, act=None)

    def forward(self, inputs):
        inputs = fluid.layers.reshape(inputs, (-1, 784))
        outputs = self.fc(inputs)
        return outputs

with fluid.dygraph.guard():
    model = MNIST()
    model.train()
    # 調用加載數據的函數
    train_loader = load_data('train')
    # 創建異步數據讀取器
    place = fluid.CPUPlace()
    data_loader = fluid.io.DataLoader.from_generator(capacity=5, return_list=True)
    data_loader.set_batch_generator(train_loader, places=place)
    
    optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001, parameter_list=model.parameters())
    EPOCH_NUM = 3
    for epoch_id in range(EPOCH_NUM):
        for batch_id, data in enumerate(data_loader):
            # 准備數據,變得更加簡潔
            image_data, label_data = data
            image = fluid.dygraph.to_variable(image_data)
            label = fluid.dygraph.to_variable(label_data)
            
            # 前向計算的過程
            predict = model(image)
            
            # 計算損失,取一個批次樣本損失的平均值
            loss = fluid.layers.square_error_cost(predict, label)
            avg_loss = fluid.layers.mean(loss)
            
            # 每訓練了200批次的數據,打印下當前Loss的情況
            if batch_id % 200 == 0:
                print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))
            
            # 后向傳播,更新參數的過程
            avg_loss.backward()
            optimizer.minimize(avg_loss)
            model.clear_gradients()

    #保存模型參數
    fluid.save_dygraph(model.state_dict(), 'mnist')

參考資料:

  1. https://aistudio.baidu.com/aistudio/course 百度AI Studio課程
  2. https://www.paddlepaddle.org.cn/documentation/docs/zh/beginners_guide/index_cn.html 百度飛槳技術文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM