前言¶
本系列教程中,前面介紹的都沒有保存模型,訓練之後也就結束了。那麼本章就介紹如果在訓練過程中保存模型,用於之後預測或者恢復訓練,又或者由於其他數據集的預訓練模型。本章會介紹三種保存模型和使用模型的方式。
訓練模型¶
在訓練模型的過程中我們可以隨時保存模型,當時也可以在訓練開始之前加載之前訓練過程的模型。爲了介紹這三個保存模型的方式,一共編寫了三個Python程序進行介紹,分別是save_infer_model.py、 save_use_params_model.py、save_use_persistables_model.py。
導入相關的依賴庫
import os
import shutil
import paddle as paddle
import paddle.dataset.cifar as cifar
import paddle.fluid as fluid
定義一個殘差神經網絡,這個是目前比較常用的一個網絡。該神經模型可以通過增加網絡的深度達到提高識別率,而不會像其他過去的神經模型那樣,當網絡繼續加深時,反而會損失精度。
# 定義殘差神經網絡(ResNet)
def resnet_cifar10(ipt, class_dim):
def conv_bn_layer(input,
ch_out,
filter_size,
stride,
padding,
act='relu',
bias_attr=False):
tmp = fluid.layers.conv2d(
input=input,
filter_size=filter_size,
num_filters=ch_out,
stride=stride,
padding=padding,
bias_attr=bias_attr)
return fluid.layers.batch_norm(input=tmp, act=act)
def shortcut(input, ch_in, ch_out, stride):
if ch_in != ch_out:
return conv_bn_layer(input, ch_out, 1, stride, 0, None)
else:
return input
def basicblock(input, ch_in, ch_out, stride):
tmp = conv_bn_layer(input, ch_out, 3, stride, 1)
tmp = conv_bn_layer(tmp, ch_out, 3, 1, 1, act=None, bias_attr=True)
short = shortcut(input, ch_in, ch_out, stride)
return fluid.layers.elementwise_add(x=tmp, y=short, act='relu')
# 殘差塊
def layer_warp(block_func, input, ch_in, ch_out, count, stride):
tmp = block_func(input, ch_in, ch_out, stride)
for i in range(1, count):
tmp = block_func(tmp, ch_out, ch_out, 1)
return tmp
conv1 = conv_bn_layer(ipt, ch_out=16, filter_size=3, stride=1, padding=1)
res1 = layer_warp(basicblock, conv1, 16, 16, 5, 1)
res2 = layer_warp(basicblock, res1, 16, 32, 5, 2)
res3 = layer_warp(basicblock, res2, 32, 64, 5, 2)
pool = fluid.layers.pool2d(input=res3, pool_size=8, pool_type='avg', pool_stride=1)
predict = fluid.layers.fc(input=pool, size=class_dim, act='softmax')
return predict
定義輸出成,這裏使用的數據集是cifar數據集,這個數據集的圖片是寬高都爲32的3通道圖片,所以這裏定義的圖片輸入層的shape是[3, 32, 32]。
# 定義輸入層
image = fluid.layers.data(name='image', shape=[3, 32, 32], dtype='float32')
label = fluid.layers.data(name='label', shape=[1], dtype='int64')
獲取殘差神經網絡的分類器,並指定分類大小是10,因爲這個數據集有10個類別。
# 獲取分類器
model = resnet_cifar10(image, 10)
獲取交叉熵損失函數和平均準確率,模型獲取的準確率是Top1的。
# 獲取損失函數和準確率函數
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)
定義優化方法。
# 定義優化方法
optimizer = fluid.optimizer.AdamOptimizer(learning_rate=1e-3)
opts = optimizer.minimize(avg_cost)
獲取訓練和測試數據,使用的是cifar數據集,cifar數據集有兩種,一種是100個類別的,一種是10個類別的,這裏使用的是10個類別的。
# 獲取CIFART數據
train_reader = paddle.batch(cifar.train10(), batch_size=32)
test_reader = paddle.batch(cifar.test10(), batch_size=32)
創建執行器,因爲我們使用的網絡是一個比較大的網絡,而且圖片也比之前的灰度圖要大很多。之前的MNIST數據集的每張圖片大小784,而現在的是3072。當然主要是網絡比之前的要大很多很多,如果使用CPU訓練,速度是非常慢的,所以最好使用GPU進行訓練。
# 創建執行器,最好使用GPU,CPU速度太慢了
# place = fluid.CPUPlace()
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
# 進行參數初始化
exe.run(fluid.default_startup_program())
加載模型¶
創建執行器之後,就可以加載之前訓練的模型了,有兩種加載模型的方式,對應着兩種保存模型的方式。這兩種模型,可以只使用一種就可以。
save_use_params_model.py加載之前訓練保存的參數模型,對應的保存接口是fluid.io.save_params。使用這些模型參數初始化網絡參數,進行訓練
# 加載之前訓練過的參數模型
save_path = 'models/params_model/'
if os.path.exists(save_path):
print('使用參數模型作爲預訓練模型')
fluid.io.load_params(executor=exe, dirname=save_path)
save_use_persistables_model.py加載之前訓練保存的持久化變量模型,對應的保存接口是fluid.io.save_persistables。使用這些模型參數初始化網絡參數,進行訓練。
# 加載之前訓練過的檢查點模型
save_path = 'models/persistables_model/'
if os.path.exists(save_path):
print('使用持久化變量模型作爲預訓練模型')
fluid.io.load_persistables(executor=exe, dirname=save_path)
開始訓練模型。
# 定義輸入數據維度
feeder = fluid.DataFeeder(place=place, feed_list=[image, label])
for pass_id in range(10):
# 進行訓練
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))
沒有加載之前保存的模型
Pass:0, Batch:0, Cost:2.73460, Accuracy:0.03125
Pass:0, Batch:100, Cost:1.93663, Accuracy:0.25000
Pass:0, Batch:200, Cost:2.02943, Accuracy:0.12500
Pass:0, Batch:300, Cost:1.94425, Accuracy:0.25000
Pass:0, Batch:400, Cost:1.87802, Accuracy:0.21875
Pass:0, Batch:500, Cost:1.71312, Accuracy:0.25000
Pass:0, Batch:600, Cost:1.94090, Accuracy:0.18750
Pass:0, Batch:700, Cost:2.08904, Accuracy:0.12500
Pass:0, Batch:800, Cost:1.89128, Accuracy:0.12500
Pass:0, Batch:900, Cost:1.95716, Accuracy:0.21875
Pass:0, Batch:1000, Cost:1.65181, Accuracy:0.34375
使用參數模型作爲預訓練模型訓練時輸出的信息:
使用參數模型作爲預訓練模型
Pass:0, Batch:0, Cost:0.27627, Accuracy:0.90625
Pass:0, Batch:100, Cost:0.40026, Accuracy:0.87500
Pass:0, Batch:200, Cost:0.54928, Accuracy:0.78125
Pass:0, Batch:300, Cost:0.56526, Accuracy:0.84375
Pass:0, Batch:400, Cost:0.53501, Accuracy:0.78125
Pass:0, Batch:500, Cost:0.18596, Accuracy:0.93750
Pass:0, Batch:600, Cost:0.23747, Accuracy:0.96875
Pass:0, Batch:700, Cost:0.45520, Accuracy:0.84375
Pass:0, Batch:800, Cost:0.86205, Accuracy:0.71875
Pass:0, Batch:900, Cost:0.36981, Accuracy:0.87500
Pass:0, Batch:1000, Cost:0.37483, Accuracy:0.81250
持久性變量模型作爲預訓練模型訓練時輸出的信息:
使用持久性變量模型作爲預訓練模型
Pass:0, Batch:0, Cost:0.51357, Accuracy:0.81250
Pass:0, Batch:100, Cost:0.64380, Accuracy:0.78125
Pass:0, Batch:200, Cost:0.69049, Accuracy:0.62500
Pass:0, Batch:300, Cost:0.52201, Accuracy:0.87500
Pass:0, Batch:400, Cost:0.47289, Accuracy:0.81250
Pass:0, Batch:500, Cost:0.15821, Accuracy:1.00000
Pass:0, Batch:600, Cost:0.36470, Accuracy:0.87500
Pass:0, Batch:700, Cost:0.25326, Accuracy:0.90625
Pass:0, Batch:800, Cost:0.92556, Accuracy:0.78125
Pass:0, Batch:900, Cost:0.27470, Accuracy:0.93750
Pass:0, Batch:1000, Cost:0.34562, Accuracy:0.87500
保存模型¶
訓練結束之後,就可以進行保存模型。當然也不一樣要全部訓練結束才保存模型,我們可以在每一個Pass訓練結束之後保存一次模型。這裏使用三個程序分別保存,當然也可以一次全部保存。
save_infer_model.py保存預測模型,之後用於預測圖像。通過使用這個方式保存的模型,之後預測是非常方便的,具體可以閱讀預測部分。
# 保存預測模型
save_path = 'models/infer_model/'
# 刪除舊的模型文件
shutil.rmtree(save_path, ignore_errors=True)
# 創建保持模型文件目錄
os.makedirs(save_path)
# 保存預測模型
fluid.io.save_inference_model(save_path, feeded_var_names=[image.name], target_vars=[model], executor=exe)
save_use_params_model.py保存參數模型,之後用於初始化模型,進行訓練。
# 保存參數模型
save_path = 'models/params_model/'
# 刪除舊的模型文件
shutil.rmtree(save_path, ignore_errors=True)
# 創建保持模型文件目錄
os.makedirs(save_path)
# 保存參數模型
fluid.io.save_params(executor=exe, dirname=save_path)
save_use_persistables_model.py保存持久化變量模型,之後用於初始化模型,進行訓練。
# 保存持久化變量模型
save_path = 'models/persistables_model/'
# 刪除舊的模型文件
shutil.rmtree(save_path, ignore_errors=True)
# 創建保持模型文件目錄
os.makedirs(save_path)
# 保存持久化變量模型
fluid.io.save_persistables(executor=exe, dirname=save_path)
預測¶
在訓練的時候使用fluid.io.save_inference_model接口保存的模型,可以通過以下use_infer_model.py程序預測,通過這個程序,讀者會發現通過這個接口保存的模型,再次預測是非常簡單的。
導入相關的依賴庫
import paddle.fluid as fluid
from PIL import Image
import numpy as np
創建一個執行器,預測圖片可以使用CPU執行,這個速度不會太慢。
# 創建執行器
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
加載模型,這個是整個預測程序的重點,通過加載預測模型我們就可以輕鬆獲取得到一個預測程序,輸出參數的名稱,以及分類器的輸出。
# 保存預測模型路徑
save_path = 'models/infer_model/'
# 從模型中獲取預測程序、輸入數據名稱列表、分類器
[infer_program, feeded_var_names, target_var] = fluid.io.load_inference_model(dirname=save_path, executor=exe)
定義一個圖像預處理的函數,這個函數可以統一圖像大小,修改圖像的存儲順序和圖片的通道順序,轉換成numpy數據。
# 預處理圖片
def load_image(file):
im = Image.open(file)
im = im.resize((32, 32), Image.ANTIALIAS)
im = np.array(im).astype(np.float32)
# PIL打開圖片存儲順序爲H(高度),W(寬度),C(通道)。
# PaddlePaddle要求數據順序爲CHW,所以需要轉換順序。
im = im.transpose((2, 0, 1))
# CIFAR訓練圖片通道順序爲B(藍),G(綠),R(紅),
# 而PIL打開圖片默認通道順序爲RGB,因爲需要交換通道。
im = im[(2, 1, 0), :, :] # BGR
im = im / 255.0
im = np.expand_dims(im, axis=0)
return im
獲取數據並進行預測。這裏對比之前的預測方式,不需要再輸入一個模擬的標籤,因爲在保存模型的時候,已經對這部分進行修剪,去掉了這部分不必要的輸入。
# 獲取圖片數據
img = load_image('image/cat.png')
# 執行預測
result = exe.run(program=infer_program,
feed={feeded_var_names[0]: img},
fetch_list=target_var)
執行預測之後,得到一個數組,這個數組是表示每個類別的概率,獲取最大概率的標籤,並根據標籤獲取獲取該類的名稱。
# 顯示圖片並輸出結果最大的label
lab = np.argsort(result)[0][0][-1]
names = ['飛機', '汽車', '鳥', '貓', '鹿', '狗', '青蛙', '馬', '船', '卡車']
print('預測結果標籤爲:%d, 名稱爲:%s, 概率爲:%f' % (lab, names[lab], result[0][0][lab]))
預測輸出結果:
預測結果標籤爲:3, 名稱爲:貓, 概率爲:0.864919
關於模型的保存和使用就介紹到這裏,讀者可以使用這個方式保存之前學過的模型。在這個基礎上,下一章我們介紹如何使用預訓練模型。
同步到百度AI Studio平臺:https://aistudio.baidu.com/aistudio/projectDetail/38741
同步到科賽網K-Lab平臺:https://www.kesci.com/home/project/5c3f495589f4aa002b845d6b
項目代碼GitHub地址:https://github.com/yeyupiaoling/LearnPaddle2/tree/master/note8
注意: 最新代碼以GitHub上的爲準
上一章:《PaddlePaddle從入門到煉丹》七——強化學習¶
下一章:《PaddlePaddle從入門到煉丹》九——遷移學習¶
參考資料¶
- https://blog.csdn.net/qq_33200967/article/details/79095224
- http://www.paddlepaddle.org/documentation/docs/zh/1.2/api_cn/io_cn.html