本文基於百度PaddlePaddle教程:https://aistudio.baidu.com/aistudio/projectdetail/350646
一、準備數據
- uci-housing 數據集介紹
數據集共506行,每行14列。前13列用來描述房屋的各種信息,最後一列爲該類房屋的價格中位數。
PaddlePaddle提供了讀取uci-housing 訓練集和測試集的接口,分別爲paddle.dataset.uci_housing.train()
和paddle.dataset.uci_housing.test()
。
- train_reader 和test_reader
paddle.reader.shuffle()
表示每次緩存BUF_SIZE個數據項,並進行打亂。
paddle.batch()
表示每BATCH_SIZE組成一個batch。
import paddle.fluid as fluid
import paddle
import numpy as np
import os
import matplotlib.pyplot as plt
BUF_SIZE = 500
BATCH_SIZE = 20
# 用於訓練的數據提供器,每次從緩存中隨機讀取批次大小的數據
train_reader = paddle.batch(
paddle.reader.shuffle(paddle.dataset.uci_housing.train(),
buf_size=BUF_SIZE),
batch_size=BATCH_SIZE)
# 用於測試的數據提供器,每次從緩存中隨機讀取批次大小的數據
test_reader = paddle.batch(
paddle.reader.shuffle(paddle.dataset.uci_housing.test(),
buf_size=BUF_SIZE),
batch_size=BATCH_SIZE)
二、網絡配置
1. 網絡搭建
對於波士頓房價數據集,假設屬性和房價之間的關係可以被屬性間的線性組合描述。
# 定義張量變量x,表示13維的特徵值
x = fluid.layers.data(name='x', shape=[13], dtype='float32')
# 定義張量y,表示目標值
y = fluid.layers.data(name='y', shape=[1], dtype='float32')
# 定義一個簡單的線性網絡,連接輸入和輸出的全連接層
# input: 輸入tensor
# size: 該層輸出單元的數目
# act: 激活函數
hidden1 = fluid.layers.fc(input=x, size=100, act="relu")
y_predict = fluid.layers.fc(input=hidden1, size=1, act=None)
2. 定義損失函數
這裏使用均方差損失函數。
square_error_cost(input,lable)
接收輸入預測值和目標值,並返回方差估計,即爲**(y-y_predict)**的平方
# 定義損失函數
cost = fluid.layers.square_error_cost(input=y_predict, label=y)
avg_cost = fluid.layers.mean(cost)
3. 定義優化函數
這裏使用隨機梯度下降。
# 定義優化函數
optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.005)
opts = optimizer.minimize(avg_cost)
test_program = fluid.default_main_program().clone(for_test=True)
上述模型配置完畢後,得到兩個fluid.Program
:
-
fluid.default_startup_program()
參數初始化操作會被寫入該Program 中。 -
fluid.default_main_program()
此Program 用於獲取默認或全局main program(主程序),該主程序用於訓練和測試模型。
fluid.layers
中所有layer函數可以向default_main_program
中添加算子和變量。
default_main_program
是fluid
的許多編程接口(API)的Program 參數的默認值(當用戶program 沒有傳入的時候,Executor.run()
會默認執行 default_main_program
三、模型訓練 和 模型評估
1. 創建Executor
定義運算場所fluid.CPUPlace()
和fluid.CUDAPlace(0)
分別表示運算場所爲CPU和GPU。
Executor:接收傳入的program,通過run()
方法運行program。
# 創建Executor
use_cuda = False
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
exe = fluid.Executor(place) # 創建一個Executor實例exe
exe.run(fluid.default_startup_program()) # 參數初始化
2. 定義輸入數據維度
DataFeeder負責將數據提供器(train_reader
, test_reader
)返回的數據轉成一種特殊的數據結構,使其可以輸入到Executor中。
# 定義輸入數據維度
# feed_list: 向模型輸入的變量表或變量表名
feeder = fluid.DataFeeder(place=place, feed_list=[x, y])
3. 定義繪製訓練過程中的損失值變化趨勢
由於我這由於某些未知原因,導致plt 圖像不能顯示,所以我只能將其保存在本地進行查看。
iter = 0
iters = []
train_costs = []
def draw_train_process(iters, train_costs):
title = 'Training cost'
plt.title(title, fontsize=24)
plt.xlabel('iter', fontsize=14)
plt.ylabel('cost', fontsize=14)
plt.plot(iters, train_costs, color='red', label='training cost')
plt.grid()
plt.savefig("C:/Users/waao_wuyou/Desktop/pic.jpg")
4. 訓練並保存模型
Executor 接收傳入的program, 並根據feed map(輸入映射表)和fetch_list(結果獲取表) 向program中添加feed operators(數據輸入算子)和fetch operators(結果獲取算子)。
feed map爲該program提供輸入數據。fetch_list提供program訓練結束後用戶預期的變量。
enumerate() 函數用於將一個可遍歷的數據對象(如列表、元組或字符串)組合爲一個索引序列,同時列出數據和數據下標
# 訓練並保存模型
EPOCH_NUM = 100
model_save_dir = "C:/Users/waao_wuyou/Desktop/fit_a_line.inference.model"
for pass_id in range(EPOCH_NUM):
# 開始訓練並輸出最後一個batch的損失值
train_cost = 0
for batch_id, data in enumerate(train_reader()): # 遍歷train_reader 迭代器
train_cost = exe.run(program=fluid.default_main_program(), # 運行主程序
feed=feeder.feed(data), # 喂入一個batch 的訓練數據,根據feed_list 和data 提供的信息,將輸入數據轉成一種特殊的數據結構
fetch_list=[avg_cost])
if batch_id % 40 == 0:
print("Pass:%d, Cost:%0.5f" % (pass_id, train_cost[0][0])) # 打印最後一個batch 的損失值
iter = iter + BATCH_SIZE
iters.append(iter)
train_costs.append(train_cost[0][0])
# 開始測試並輸出最後一個batch的損失值
test_cost = 0
for batch_id, data in enumerate(test_reader()): # 遍歷test_reader 迭代器
test_cost = exe.run(program=test_program,
feed=feeder.feed(data), # 喂入一個batch 的測試數據
fetch_list=[avg_cost]) # fetch 均方誤差
print('Test:%d, Cost:%0.5f' % (pass_id, test_cost[0][0])) # 打印最後一個batch 的損失值
# 保存模型
if not os.path.exists(model_save_dir):
os.makedirs(model_save_dir)
print('save models to %s' % (model_save_dir))
# 保存訓練參數到指定路徑中,構建一個專門預測的program
fluid.io.save_inference_model(model_save_dir, # 保存推理model 的路徑
['x'], # 推理需要feed 的數據
[y_predict], # 保存推理結果的Variables
exe) # exe 保存inference model
draw_train_process(iters, train_costs)
輸出結果:
Pass:90, Cost:23.69488
Test:90, Cost:17.02254
Pass:91, Cost:6.22767
Test:91, Cost:12.09491
Pass:92, Cost:7.67705
Test:92, Cost:95.80173
Pass:93, Cost:5.76839
Test:93, Cost:7.67893
Pass:94, Cost:14.50994
Test:94, Cost:6.25279
Pass:95, Cost:34.86055
Test:95, Cost:29.38514
Pass:96, Cost:19.19988
Test:96, Cost:15.92449
Pass:97, Cost:27.39356
Test:97, Cost:4.28481
Pass:98, Cost:5.60616
Test:98, Cost:0.79274
Pass:99, Cost:9.20047
Test:99, Cost:15.64438
四、模型預測
1. 創建預測用的Executor
# 模型預測
infer_exe = fluid.Executor(place) # 創建推測用的executor
inference_scope = fluid.core.Scope() # Scope 指定作用域
2. 可視化真實值與預測值方法定義
# 可視化真實值與預測值方法定義
infer_results = []
ground_truths = []
# 繪製真實值和預測值對比圖
def draw_infer_result(ground_truths, infer_results):
title = 'Boston'
plt.title(title, fontsize=24)
x = np.arange(1, 20)
y = x
plt.plot(x, y)
plt.xlabel('ground truth', fontsize=14)
plt.ylabel('infer result', fontsize=14)
plt.scatter(ground_truths, infer_results, color='green', label='training cost')
plt.grid()
plt.savefig("C:/Users/waao_wuyou/Desktop/result.jpg")
3. 開始預測
通過fluid.io.load_inference_model
,預測器會從params_dirname中讀取已經訓練好的模型,來對從未遇見過的數據進行預測。
# 開始預測
with fluid.scope_guard(inference_scope): # 修改全局/默認作用域,運行時中的所有變量都將分配給新的scope
# 從指定目錄中加載 推理model(inference model)
[inference_program, # 推理的program
feed_target_names, # 需要在推理program中提供數據的變量名稱
fetch_targets] = fluid.io.load_inference_model( # 推斷結果
model_save_dir, # 模型路徑
infer_exe) # 預測用executor
# 獲取預測數據
infer_reader = paddle.batch(paddle.dataset.uci_housing.test(), # 獲取uci_housing 的測試數據
batch_size=200) # 從測試數據中讀取一個大小爲200的batch 的數據
# 從test_reader中分割x
test_data = next(infer_reader())
test_x = np.array([data[0] for data in test_data]).astype("float32")
test_y = np.array([data[1] for data in test_data]).astype("float32")
results = infer_exe.run(inference_program, # 預測模型
feed={feed_target_names[0]: np.array(test_x)}, # 喂入要預測的x值
fetch_list=fetch_targets) # 得到推測結果
print("infer results: (House Price)")
for idx, val in enumerate(results[0]):
print("%d: %0.2f" % (idx, val))
infer_results.append(val)
print("ground truth:")
for idx, val in enumerate(test_y):
print("%d: %.2f" % (idx, val))
ground_truths.append(val)
draw_infer_result(ground_truths, infer_results)
輸出結果:
90: 24.50
91: 23.10
92: 19.70
93: 18.30
94: 21.20
95: 17.50
96: 16.80
97: 22.40
98: 20.60
99: 23.90
100: 22.00
101: 11.90