1. 程式人生 > >《PaddlePaddle從入門到煉丹》三——線性迴歸

《PaddlePaddle從入門到煉丹》三——線性迴歸

前言

在第二章,我們已經學習瞭如何使用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.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)

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

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

接著是定義訓練使用的優化方法,這裡使用的是隨機梯度下降優化方法。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,讀者可根據情況設定更多的訓練輪數,通常來說訓練的次數越多,模型收斂的越好。同樣我們使用的時proframfluid.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]))

訓練完成之後,我們使用上面克隆主程式得到的預測程式了預測我們剛才定義的預測資料。預測資料同樣作為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, 'y': np.array([[0.0]]).astype('float32')},
                 fetch_list=[net])
print("當x為6.0時,y為:%0.5f:" % result[0][0][0])

使用房價資料集訓練

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

在此之前,我們已經完整訓練深度學習模型,並使用這個模型來進行預測。而上面使用的是我們自己定義的資料,而且這個資料非常小。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]))

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

參考資料