前言

在第二章,我們已經學習瞭如何使用PaddlePaddle來進行加法計算,從這個小小的例子中,我們掌握了PaddlePaddle的使用方式。在本章中,我們將介紹使用PaddlePaddle完成一個深度學習非常常見的入門例子——線性迴歸,我們將分別使用自定義數據集和使用PaddlePaddle提供的數據集接口來訓練一個線性迴歸模型。

使用自定義數據

在這一部分,我們將介紹整個線性迴歸從定義網絡到使用自定義的數據進行訓練,最後驗證我們網絡的預測能力。

首先導入PaddlePaddle庫和一些工具類庫。

import paddle.fluid as fluid
import paddle
import numpy as np

定義一個簡單的線性網絡,這個網絡非常簡單,結構是:輸出層-->>隱層-->>輸出層,這個網絡一共有2層,因爲輸入層不算網絡的層數。更具體的就是一個大小爲100,激活函數是ReLU的全連接層和一個輸出大小爲1的全連接層,就這樣構建了一個非常簡單的網絡。這裏使用輸入fluid.layers.data()定義的輸入層類似fluid.layers.create_tensor(),也是有name屬性,之後也是根據這個屬性來填充數據的。這裏定義輸入層的形狀爲13,這是因爲波士頓房價數據集的每條數據有13個屬性,我們之後自定義的數據集也是爲了符合這一個維度。

# 定義一個簡單的線性網絡
x = fluid.layers.data(name='x', shape=[13], dtype='float32')
hidden = fluid.layers.fc(input=x, size=100, act='relu')
net = fluid.layers.fc(input=hidden, size=1, act=None)

定義損失函數之後,可以在主程序(fluid.default_main_program)中克隆一個程序作爲預測程序,用於訓練完成之後使用這個預測程序進行預測數據。這個定義的順序不能錯,因爲我們只需要網絡結構,不需要損失函數等等。主程序定義了神經網絡模型,前向反向計算,以及優化算法對網絡中可學習參數的更新,是我們整個程序的核心,這個是PaddlePaddle已經幫我們實現的了,我們只需注重網絡的構建和訓練即可。

# 複製一個主程序,方便之後使用
test_program = fluid.default_main_program().clone(for_test=True)

接着定義神經網絡的損失函數,這裏同樣使用了fluid.layers.data()這個接口,這個可以理解爲數據對應的結果,上面namexfluid.layers.data()爲屬性數據。這裏使用了平方差損失函數(square_error_cost),PaddlePaddle提供了很多的損失函數的接口,比如交叉熵損失函數(cross_entropy)。因爲本項目是一個線性迴歸任務,所以我們使用的是平方差損失函數。因爲fluid.layers.square_error_cost()求的是一個Batch的損失值,所以我們還要對他求一個平均值。

# 定義損失函數
y = fluid.layers.data(name='y', shape=[1], dtype='float32')
cost = fluid.layers.square_error_cost(input=net, label=y)
avg_cost = fluid.layers.mean(cost)

接着是定義訓練使用的優化方法,這裏使用的是隨機梯度下降優化方法。PaddlePaddle提供了大量的優化函數接口,除了本項目使用的隨機梯度下降法(SGD),還有Momentum、Adagrad、Adagrad等等,讀者可以更加自己項目的需求使用不同的優化方法。

# 定義優化方法
optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01)
opts = optimizer.minimize(avg_cost)

然後是創建一個解析器,我們同樣是使用CPU來進行訓練。創建解析器之後,使用解析器來執行fluid.default_startup_program()初始化參數。

# 創建一個使用CPU的執行器
place = fluid.CPUPlace()
exe = fluid.Executor(place)
# 進行參數初始化
exe.run(fluid.default_startup_program())

我們使用numpy定義一組數據,這組數據的每一條數據有13個,這是因爲我們在定義網絡的輸入層時,shape是13,但是每條數據的後面12個數據是沒意義的,因爲筆者全部都是使用0來填充,純粹是爲了符合數據的格式而已。這組數據是符合y = 2 * x + 1,但是程序是不知道的,我們之後使用這組數據進行訓練,看看強大的神經網絡是否能夠訓練出一個擬合這個函數的模型。最後定義了一個預測數據,是在訓練完成,使用這個數據作爲x輸入,看是否能夠預測於正確值相近結果。

# 定義訓練和測試數據
x_data = np.array([[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 
                   [2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 
                   [3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 
                   [4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 
                   [5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]).astype('float32')
y_data = np.array([[3.0], [5.0], [7.0], [9.0], [11.0]]).astype('float32')
test_data = np.array([[6.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]).astype('float32')

定義數據之後,我們就可以使用數據進行訓練了。我們這次訓練了10個pass,讀者可根據情況設置更多的訓練輪數,通常來說訓練的次數越多,模型收斂的越好。同樣我們使用的時programfluid.default_main_program()feed中是在訓練時把數據傳入fluid.layers.data()定義的變量中,及那個鍵值對的key對用的就是fluid.layers.data()中的name的值。我們讓訓練過程中輸出avg_cost的值。

在訓練過程中,我們可以看到輸出的損失值在不斷減小,證明我們的模型在不斷收斂。

# 開始訓練100個pass
for pass_id in range(10):
    train_cost = exe.run(program=fluid.default_main_program(),
                         feed={'x': x_data, 'y': y_data},
                         fetch_list=[avg_cost])
    print("Pass:%d, Cost:%0.5f" % (pass_id, train_cost[0]))

輸出信息:

Pass:0, Cost:65.61024
Pass:1, Cost:26.62285
Pass:2, Cost:7.78299
Pass:3, Cost:0.59838
Pass:4, Cost:0.02781
Pass:5, Cost:0.02600
Pass:6, Cost:0.02548
Pass:7, Cost:0.02496
Pass:8, Cost:0.02446
Pass:9, Cost:0.02396

訓練完成之後,我們使用上面克隆主程序得到的預測程序了預測我們剛纔定義的預測數據。預測數據同樣作爲xfeed輸入,在預測時,理論上是不用輸入y的,但是要符合輸入格式,我們模擬一個y的數據值,這個值並不會影響我們的預測結果。fetch_list的值,也就是我們執行預測之後要輸出的結果,這是網絡的最後一層,而不是平均損失函數(avg_cost),因爲我們是想要預測程序輸出預測結果。根據我們上面定義數據時,滿足規律y = 2 * x + 1,所以當x爲6時,y應該時13,最後輸出的結果也是應該接近13的。

# 開始預測
result = exe.run(program=test_program,
                 feed={'x': test_data},
                 fetch_list=[net])
print("當x爲6.0時,y爲:%0.5f:" % result[0][0][0])

輸出信息:

當x爲6.0時,y爲:13.23651:

使用房價數據集訓練

在這一部分,我們還是使用上面定義的網絡結構,使用波士頓房價數據集進行訓練。

在此之前,我們已經完整訓練深度學習模型,並使用這個模型來進行預測。而上面使用的是我們自己定義的數據,而且這個數據非常小。PaddlePaddle提供了大量的數據集API,我們可使用這些API來使用一些比較常用的數據集,比如在深度學習中,線性迴歸最常用的是波士頓房價數據集(UCI Housing Data Set),uci_housing就是PaddlePaddle提供的一個波士頓房價數據集。

而且這次我們的數據集不是一下子全部都丟入到訓練中,而已把它們分成一個個Batch的小數據集,而每個Batch的大小我們都可以通過batch_size進行設置,這個大小一般是2的N次方。這裏定義了訓練和測試兩個數據集。

import paddle.dataset.uci_housing as uci_housing
# 使用房價數據進行訓練和測試
# 從paddle接口中獲取房價數據集
train_reader = paddle.batch(reader=uci_housing.train(), batch_size=128)
test_reader = paddle.batch(reader=uci_housing.test(), batch_size=128)

接着定義數據的維度,在使用自定義數據的時候,我們是使用鍵值對的方式添加數據的,但是我們調用API來獲取數據集時,已經是將屬性數據和結果放在一個Batch中,如果再對數據拆分在訓練進行填充,那就更麻煩了,所以PaddlePaddle提供了一個fluid.DataFeeder()這個接口,這裏可以定義輸入數據每個維度是屬於哪一個fluid.layers.data().

# 定義輸入數據維度
feeder = fluid.DataFeeder(place=place, feed_list=[x, y])

接下來我們是使用波士頓房價數據集來進行訓練,在訓練時,我們是通過一個循環迭代器把reader中的數據按照一個個Batch提取出來加入到訓練中。加入訓練時使用上面定義的數據維度feeder.feed()添加的。

當每一個Pass訓練完成之後,都執行一次測試,測試與預測的作用不同,測試是爲了使用於測試數據集預測並與真實結果對比,評估當前模型的好壞。因爲測試集不屬於訓練集,所以測試集的預測結果的好壞能狗體現模型的泛化能力。

# 開始訓練和測試
for pass_id in range(10):
    # 開始訓練並輸出最後一個batch的損失值
    train_cost = 0
    for batch_id, data in enumerate(train_reader()):
        train_cost = exe.run(program=fluid.default_main_program(),
                             feed=feeder.feed(data),
                             fetch_list=[avg_cost])
    print("Pass:%d, Cost:%0.5f" % (pass_id, train_cost[0][0]))

    # 開始測試並輸出最後一個batch的損失值
    test_cost = 0
    for batch_id, data in enumerate(test_reader()):
        test_cost = exe.run(program=fluid.default_main_program(),
                            feed=feeder.feed(data),
                            fetch_list=[avg_cost])
    print('Test:%d, Cost:%0.5f' % (pass_id, test_cost[0][0]))

輸出信息:

Pass:0, Cost:35.61119
Test:0, Cost:92.18690
Pass:1, Cost:121.56089
Test:1, Cost:51.94175
Pass:2, Cost:44.66270
Test:2, Cost:34.46148
Pass:3, Cost:33.25787
Test:3, Cost:30.89449
Pass:4, Cost:29.12044
Test:4, Cost:28.29573
Pass:5, Cost:26.75469
Test:5, Cost:26.66773
Pass:6, Cost:24.98260
Test:6, Cost:25.11611
Pass:7, Cost:23.55230
Test:7, Cost:23.84240
Pass:8, Cost:22.41704
Test:8, Cost:22.65791
Pass:9, Cost:21.51291
Test:9, Cost:21.71775

到此爲止,本章知識已經學完。本章我們學會了如何使用PaddlePaddle完成了深度學習入門的常見例子,相信讀者經過學習本章之後,對深度學習和PaddlePaddle的使用有了非常深刻的瞭解,也恭喜讀者正式加入到人工智能行列中,希望讀者能夠堅定信心,在自己喜歡的領域一直走下去。在下一章,我們將會介紹使用卷積神經網絡進行訓練MNIST圖像數據集,相信下一章你更加喜歡深度學習的,準備學習下一章了嗎。

同步到百度AI Studio平臺:http://aistudio.baidu.com/projectdetail/29342
同步到科賽網K-Lab平臺:https://www.kesci.com/home/project/5bf8c7a6954d6e001066d72c
項目代碼GitHub地址:https://github.com/yeyupiaoling/LearnPaddle2/tree/master/note3

注意: 最新代碼以GitHub上的爲準


上一章:《PaddlePaddle從入門到煉丹》二——計算1+1
下一章:《PaddlePaddle從入門到煉丹》四——卷積神經網絡


參考資料

  1. http://www.paddlepaddle.org/documentation/docs/zh/1.0/beginners_guide/quick_start/fit_a_line/README.cn.html
小夜