1. 程式人生 > >《PaddlePaddle從入門到煉丹》五——迴圈神經網路

《PaddlePaddle從入門到煉丹》五——迴圈神經網路

文章目錄

前言

除了卷積神經網路,深度學習中還有迴圈神經網路也是很常用的,迴圈神經網路更常用於自然語言處理任務上。我們在這一章中,我們就來學習如何使用PaddlePaddle來實現一個迴圈神經網路,並使用該網路完成情感分析的模型訓練。

訓練模型

首先匯入Python庫,fluid和numpy庫我們在前幾章都有使用過,這裡就不重複了。這裡主要結束是imdb庫,這個是一個數據集的庫,這個是資料集是一個英文的電影評論資料集,每一條資料都會有兩個分類,分別是正面和負面。

import paddle
import paddle.dataset.imdb as imdb
import
paddle.fluid as fluid import numpy as np

迴圈神經網路發展到現在,已經有不少效能很好的升級版的迴圈神經網路,比如長短期記憶網路等。一下的程式碼片段是一個比較簡單的迴圈神經網路,首先是經過一個fluid.layers.embedding(),這個是介面是接受資料的ID輸入,因為輸入資料時一個句子,但是在訓練的時候我們是把每個單詞轉換成對應的ID,再輸入到網路中,所以這裡使用到了embedding介面。然後是一個全連線層,接著是一個迴圈神經網路塊,在迴圈神經網路塊之後再經過一個sequence_last_step介面,這個介面通常是使用在序列函式的最後一步。最後的輸出層的啟用函式是Softmax,大小為2,因為資料的結果有2個,為正負面。

def rnn_net(ipt, input_dim):
    # 以資料的IDs作為輸入
    emb = fluid.layers.embedding(input=ipt, size=[input_dim, 128], is_sparse=True)
    sentence = fluid.layers.fc(input=emb, size=128, act='tanh')

    rnn = fluid.layers.DynamicRNN()
    with rnn.block():
        word = rnn.step_input(sentence)
        prev =
rnn.memory(shape=[128]) hidden = fluid.layers.fc(input=[word, prev], size=128, act='relu') rnn.update_memory(prev, hidden) rnn.output(hidden) last = fluid.layers.sequence_last_step(rnn()) out = fluid.layers.fc(input=last, size=2, act='softmax') return out

下面的程式碼片段是一個簡單的長短期記憶網路,這個網路是有迴圈神經網路演化過來的。當較長的序列資料,迴圈神經網路的訓練過程中容易出現梯度消失或爆炸現象,而長短期記憶網路就可以解決這個問題。在網路的開始同樣是經過一個embedding介面,接著是一個全連線層,緊接的是一個dynamic_lstm長短期記憶操作介面,有這個介面,我們很容易就搭建一個長短期記憶網路。然後是經過兩個序列池操作,該序列池的型別是最大化。最後也是一個大小為2的輸出層。

# 定義長短期記憶網路
def lstm_net(ipt, input_dim):
    # 以資料的IDs作為輸入
    emb = fluid.layers.embedding(input=ipt, size=[input_dim, 128], is_sparse=True)

    # 第一個全連線層
    fc1 = fluid.layers.fc(input=emb, size=128)
    # 進行一個長短期記憶操作
    lstm1, _ = fluid.layers.dynamic_lstm(input=fc1, size=128)

    # 第一個最大序列池操作
    fc2 = fluid.layers.sequence_pool(input=fc1, pool_type='max')
    # 第二個最大序列池操作
    lstm2 = fluid.layers.sequence_pool(input=lstm1, pool_type='max')

    # 以softmax作為全連線的輸出層,大小為2,也就是正負面
    out = fluid.layers.fc(input=[fc2, lstm2], size=2, act='softmax')
    return out

這裡可以先定義一個輸入層,這樣要注意的是我們使用的資料屬於序列資料,所以我們可以設定lod_level為1,當該引數不為0時,表示輸入的資料為序列資料,預設lod_level的值是0.

# 定義輸入資料, lod_level不為0指定輸入資料為序列資料
words = fluid.layers.data(name='words', shape=[1], dtype='int64', lod_level=1)
label = fluid.layers.data(name='label', shape=[1], dtype='int64')

然後是讀取資料字典,因為我們的資料是以資料標籤的放方式表示資料一個句子。所以每個句子都是以一串整數來表示的,每個數字都是對應一個單詞。所以這個資料集就會有一個數據集字典,這個字典是訓練資料中出現單詞對應的數字標籤。

# 獲取資料字典
print("載入資料字典中...")
word_dict = imdb.word_dict()
# 獲取資料字典長度
dict_dim = len(word_dict)

這裡可以獲取我們上面定義的網路作為我們之後訓練的網路模型,這兩個網路讀者都可以試試,可以對比它們的差別。

# 獲取長短期記憶網路
model = lstm_net(words, dict_dim)
# 獲取迴圈神經網路
# model = rnn_net(words, dict_dim)

接著定義損失函式,這裡同樣是一個分類任務,所以使用的損失函式也是交叉熵損失函式。這裡也可以使用fluid.layers.accuracy()介面定義一個輸出分類準確率的函式,可以方便在訓練的時候,輸出測試時的分類準確率,觀察模型收斂的情況。

# 獲取損失函式和準確率
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)

然後是定義優化方法,這裡使用的時Adagrad優化方法,Adagrad優化方法多用於處理稀疏資料,設定學習率為0.002。

# 定義優化方法
optimizer = fluid.optimizer.AdagradOptimizer(learning_rate=0.002)
opt = optimizer.minimize(avg_cost)

接著建立一個解析器,這次是的資料集比之前使用的資料集要大不少,所以訓練起來先對比較慢,如果讀取有GPU環境,可以嘗試使用GPU來訓練,使用方式是使用fluid.CUDAPlace(0)來建立解析器。

# 建立一個解析器
place = fluid.CPUPlace()
# place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
# 進行引數初始化
exe.run(fluid.default_startup_program())

然後把訓練資料和測試資料讀取到記憶體中,因為資料集比較大,為了加快資料的資料,使用paddle.reader.shuffle()介面來將資料先按照設定的大小讀取到快取中。讀入快取的大小可以根據硬體環境記憶體大小來設定。

# 獲取訓練和預測資料
print("載入訓練資料中...")
train_reader = paddle.batch(paddle.reader.shuffle(imdb.train(word_dict), 25000), batch_size=128)
print("載入測試資料中...")
test_reader = paddle.batch(imdb.test(word_dict), batch_size=128)

定義資料資料的維度,資料的順序是一條句子資料對應一個標籤。

# 定義輸入資料的維度
feeder = fluid.DataFeeder(place=place, feed_list=[words, label])

現在就可以開始訓練了,這裡設定訓練的迴圈是1次,讀者可以根據情況設定更多的訓練輪數,來讓模型完全收斂。我們在訓練中,每40個Batch列印一層訓練資訊和進行一次測試,測試是使用測試集進行預測並輸出損失值和準確率,測試完成之後,對之前預測的結果進行求平均值。

# 開始訓練
for pass_id in range(1):
    # 進行訓練
    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])

        if batch_id % 40 == 0:
            print('Pass:%d, Batch:%d, Cost:%0.5f' % (pass_id, batch_id, train_cost[0]))
            # 進行測試
            test_costs = []
            test_accs = []
            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_costs.append(test_cost[0])
                test_accs.append(test_acc[0])
            # 計算平均預測損失在和準確率
            test_cost = (sum(test_costs) / len(test_costs))
            test_acc = (sum(test_accs) / len(test_accs))
            print('Test:%d, Cost:%0.5f, ACC:%0.5f' % (pass_id, test_cost, test_acc))

預測資料

我們先定義三個句子,第一句是中性的,第二句偏向正面,第三句偏向負面。然後把這些句子讀取到一個列表中。

# 定義預測資料
reviews_str = ['read the book forget the movie', 'this is a great movie', 'this is very bad']
# 把每個句子拆成一個個單詞
reviews = [c.split() for c in reviews_str]

然後把句子轉換成編碼,根據資料集的字典,把句子中的單詞轉換成對應標籤。

# 獲取結束符號的標籤
UNK = word_dict['<unk>']
# 獲取每句話對應的標籤
lod = []
for c in reviews:
    # 需要把單詞進行字串編碼轉換
    lod.append([word_dict.get(words.encode('utf-8'), UNK) for words in c])

獲取輸入資料的維度和大小。

# 獲取每句話的單詞數量
base_shape = [[len(c) for c in lod]]

將要預測的資料轉換成張量,準備開始預測。

# 生成預測資料
tensor_words = fluid.create_lod_tensor(lod, base_shape, place)

開始預測,使用的program是克隆的測試程式。預測資料是通過feed鍵值對的方式傳入到預測程式中,為了符合輸入資料的格式,label中使用了一個假的label輸入到程式中。fetch_list的值是網路的分類器。

# 預測獲取預測結果,因為輸入的是3個數據,所以要模擬3個label的輸入
results = exe.run(program=test_program,
                  feed={'words': tensor_words, 'label': np.array([[0], [0], [0]]).astype('int64')},
                  fetch_list=[model])

最後可以把預測結果輸出,因為我們使用了3條資料進行預測,所以輸出也會有3個結果。每個結果是類別的概率。

# 列印每句話的正負面概率
for i, r in enumerate(results[0]):
    print("\'%s\'的預測結果為:正面概率為:%0.5f,負面概率為:%0.5f" % (reviews_str[i], r[0], r[1]))

到處為止,本章就結束了。希望讀者經過學習完這一章,可以對PaddlePaddle的使用有更深一步的認識。在下一章中,我們來使用PaddlePaddle實現一個生成對抗網路,生成對抗網路這一兩年中可以說時非常火的,同樣也非長有趣。那麼我們下一章見吧。

本文章由夜雨飄零製作

有疑問可以加QQ群:432676488(PaddlePaddle 交流社群)或518588005(百度AI Studio)交流哦

參考資料