使用 paddle來進行文本生成

paddle 簡單介紹

    paddle 是百度在2016年9月份開源的深度學習框架。java

    就我最近體驗的感覺來講的它具備幾大優勢:python

         1. 自己內嵌了許多和實際業務很是貼近的模型好比個性化推薦,情感分析,詞向量,語義角色標註等模型還有更多實際已經內嵌了可是目前尚未出如今官方文檔上的模型好比物體檢測,文本生成,圖像分類,ctr預估等等,能夠快速應用到項目中去git

         2. 就實際體驗來看,訓練的速度相比於調用keras,在同等數據集上和相同網絡架構上要快上很多。固然也是由於keras自己也是基於在tensorflow或者theano上面的,二次調用的速度不如paddle直接調用底層迅速。github

    缺點也有不少:算法

         1. 一開始的安裝對新手極其的不友好,使用docker安裝感受這個開源框架走不長久,所幸這個問題已經解決。docker

         2. 目前不少的文檔並不完善,也許百度系的工程師目前對這方面其實並非很重視,新手教程看起來並不是那麼易懂。macos

     3. 層的封裝並不到位,不少神經網絡層得本身去寫,感受很是的不方便。ubuntu

    最後但願藉由本文,可讓你快速上手paddle。centos

一分鐘安裝paddle

     docker 安裝網絡

         以前paddle的安裝方式是使用docker安裝,感受很是的反人類。

         安裝命令:

     docker pull paddlepaddle/paddle:latest

     pip 安裝

         如今已經支持pip 安裝了。對(OS: centos 7, ubuntu 16.04, macos 10.12, python: python 2.7.x) 能夠直接使用

    pip install paddlepaddle     安裝cpu 版本。
    pip install paddlepaddle-gpu 安裝gpu 版本。

          安裝完之後,測試的代碼

import paddle.v2 as paddle
x = paddle.layer.data(name='x', type=paddle.data_type.dense_vector(13))
y = paddle.layer.fc(input=x, size=1, param_attr=paddle.attr.Param(name="fc.w"))
params = paddle.parameters.create(y)
print params["fc.w"].shape

         當輸出 [13,1],那麼恭喜你,已經成功安裝了paddle.  

     遇到的問題     

         當我在使用pip 安裝方式安裝了gpu版本的paddle之後,遇到了numpy 版本不兼容的問題。解決的辦法是:在把本地的numpy卸載之後,我首先把安裝的paddle卸載了,而後從新再安裝了一遍paddle。這樣在安裝的過程中,能夠藉由paddle的安裝過程來檢測你係統的其餘python包是否符合paddle須要的環境。其餘相似的python包的問題,均可以藉由這個辦法幫忙解決。

使用paddle中的循環神經網絡來生成文本

    背景簡介

        首先paddle實際上已經內嵌了這個項目:

https://github.com/PaddlePaddle/models/tree/develop/generate_sequence_by_rnn_lm

        文本生成有不少的應用,好比根據上文生成下一個詞,遞歸下去能夠生成整個句子,段落,篇章。目前主流生成文本的方式是使用rnn來生成文本。

        主要有兩個緣由:

            1. 由於RNN 是將一個結構反覆使用,即便輸入的文本很長,所需的network的參數都是同樣的。

            2. 由於RNN 是共用一個結構的,共用參數的。能夠用比較少的參數來訓練模型。這樣會比較難訓練,可是一旦訓練好之後,模型會比較難overfitting,效果也會比較好。

       對於RNN使用的這個結構,因爲原生的RNN的這個結構自己沒法解決長程依賴的問題,目前主要使用Lstm 和GRU來進行代替。可是具體到LSTM 和GRU,由於LSTM須要使用三個門結構也就是一般所說的遺忘門,更新門,輸出門。而GRU的表現和LSTM相似,卻只須要兩個門結構。訓練速度更快,對內存的佔用更小,目前看起來使用GRU是更好的選擇。

   項目實戰

  •        首先
    git clone https://github.com/PaddlePaddle/models/tree/develop/generate_sequence_by_rnn_lm
     到本地model 目錄下
  •        代碼結構以下
.
├── data
│   └── train_data_examples.txt         # 示例數據,可參考示例數據的格式,提供本身的數據
├── config.py                           # 配置文件,包括data、train、infer相關配置
├── generate.py                 # 預測任務腳本,即生成文本
├── beam_search.py                # beam search 算法實現
├── network_conf.py                 # 本例中涉及的各類網絡結構均定義在此文件中,但願進一步修改模型結構,請修改此文件
├── reader.py                           # 讀取數據接口
├── README.md
├── train.py                            # 訓練任務腳本
└── utils.py                            # 定義通用的函數,例如:構建字典、加載字典等

     運行說明

  •          首先執行python train.py 開始訓練模型,待模型訓練完畢之後。
  •          執行python generate.py 開始運行文本生成代碼。(默認的文本輸入爲data/train_data_example.txt,生成文本保存爲data/gen_result.txt)

     代碼解析

  •          paddle 的使用有幾個固定須要遵照的流程。
    •   大體須要4步。1:初始化,2:定義網絡結構,3:訓練,4:預測。
    •       其中定義網絡結構具體須要定義 1:定義具體的網絡結構,2:定義所須要的參數,3:定義優化的方法,4:定義event_handler 打印訓練信息。
    •       整體來講,paddle 的代碼上手難度其實對新手挺大的,但思路很是的清晰,耐心閱讀應該能夠明白。下面咱們具體介紹:

             1.首先須要加載paddle 進行初始化

import paddle.v2 as paddle
import numpy as np
paddle.init(use_gpu=False)

             2.定義網絡結構

# 變量說明
# vocab_dim: 輸入變量的維度數.
# type vocab_dim: int
# emb_dim: embedding vector的維度數
# type emb_dim: int
# rnn_type: RNN cell的類型.
# type rnn_type: int
# hidden_size: hidden unit的個數.
# type hidden_size: int
# stacked_rnn_num: 堆疊的rnn cell的個數.
# type stacked_rnn_num: int
    # 定義輸入層
    input = paddle.layer.data(
        name="input", type=paddle.data_type.integer_value_sequence(vocab_dim))
    if not is_infer:
        target = paddle.layer.data(
            name="target",
            type=paddle.data_type.integer_value_sequence(vocab_dim))
    
    # 定義embedding層
    # 該層將上層的輸出變量input 作爲本層的輸入灌入embedding層,將輸入input 向量化,方便後續處理
    input_emb = paddle.layer.embedding(input=input, size=emb_dim)

    # 定義rnn層
    # 若是 rnn_type 是lstm,則堆疊lstm層
    # 若是rnn_type 是gru,則堆疊gru層
    # 若是 i = 0的話,先將 input_emb作爲輸入,其他時刻則將上一時刻的rnn_cell做爲輸入進行堆疊
    # stack_rnn_num 等於多少就堆疊多少個 rnn層
    if rnn_type == "lstm":
        for i in range(stacked_rnn_num):
            rnn_cell = paddle.networks.simple_lstm(
                input=rnn_cell if i else input_emb, size=hidden_size)
    elif rnn_type == "gru":
        for i in range(stacked_rnn_num):
            rnn_cell = paddle.networks.simple_gru(
                input=rnn_cell if i else input_emb, size=hidden_size)
    else:
        raise Exception("rnn_type error!")
    
    # 定義全聯接層
    # 將上層最終定義獲得的輸出rnn_cell 作爲輸入灌入該全聯接層
    output = paddle.layer.fc(
        input=[rnn_cell], size=vocab_dim, act=paddle.activation.Softmax())
# 最後一層cost中記錄了神經網絡的全部拓撲結構,經過組合不一樣的layer,咱們便可完成神經網絡的搭建。 cost = paddle.layer.classification_cost(input=output, label=target)

                 paddle的網絡結構從這裏能夠看出其實定義起來須要本身寫很是多的代碼,感受很是的冗餘,雖然一樣也是搭建積木自上而下一層層來寫,代碼開發的工做量其實蠻大的。

            3.訓練模型

                在完成神經網絡的搭建以後,咱們首先須要根據神經網絡結構來建立所須要優化的parameters(也就是網絡結構的參數),並建立optimizer(求解網絡結構參數的優化方法好比Sgd,Adam,Rmstrop)以後,咱們能夠建立trainer來對網絡進行訓練。在這裏咱們使用adam算法來做爲咱們優化的算法,L2正則項來做爲正則項。並根據cost 中記錄的網絡拓撲結構來建立神經網絡所須要的參數。

    # create optimizer
    adam_optimizer = paddle.optimizer.Adam(
        learning_rate=1e-3,
        regularization=paddle.optimizer.L2Regularization(rate=1e-3),
        model_average=paddle.optimizer.ModelAverage(
            average_window=0.5, max_average_window=10000))

    # create parameters
    parameters = paddle.parameters.create(cost)
    # create trainer
    trainer = paddle.trainer.SGD(
        cost=cost, parameters=parameters, update_equation=adam_optimizer)

               其中,trainer接收三個參數,包括神經網絡拓撲結構 cost神經網絡參數 parameters以及迭代方程 adam_optimizer。在搭建神經網絡的過程當中,咱們僅僅對神經網絡的輸入進行了描述。而trainer須要讀取訓練數據進行訓練,PaddlePaddle中經過reader來加載數據。

    # define reader
    reader_args = {
        "file_name": conf.train_file,
        "word_dict": word_dict,
    }
    # 讀取訓練數據
    train_reader = paddle.batch(
        paddle.reader.shuffle(
            reader.rnn_reader(**reader_args), buf_size=102400),
        batch_size=conf.batch_size)
    # 讀取測試數據
    test_reader = None
    if os.path.exists(conf.test_file) and os.path.getsize(conf.test_file):
        test_reader = paddle.batch(
            paddle.reader.shuffle(
                reader.rnn_reader(**reader_args), buf_size=65536),
            batch_size=conf.batch_size)

                 最終咱們能夠調用trainer的train方法啓動訓練:

    
    # define the event_handler callback
    # event_handler 主要負責打印訓練的進度信息,訓練的損失值,這裏能夠本身定製
    def event_handler(event):
        if isinstance(event, paddle.event.EndIteration):
            if not event.batch_id % conf.log_period:
                logger.info("Pass %d, Batch %d, Cost %f, %s" % (
                    event.pass_id, event.batch_id, event.cost, event.metrics))

            if (not event.batch_id %
                    conf.save_period_by_batches) and event.batch_id:
                save_name = os.path.join(model_save_dir,
                                         "rnn_lm_pass_%05d_batch_%03d.tar.gz" %
                                         (event.pass_id, event.batch_id))
                with gzip.open(save_name, "w") as f:
                    parameters.to_tar(f)

        if isinstance(event, paddle.event.EndPass):
            if test_reader is not None:
                result = trainer.test(reader=test_reader)
                logger.info("Test with Pass %d, %s" %
                            (event.pass_id, result.metrics))
            save_name = os.path.join(model_save_dir, "rnn_lm_pass_%05d.tar.gz" %
                                     (event.pass_id))
            with gzip.open(save_name, "w") as f:
                parameters.to_tar(f)

    # 開始訓練
    trainer.train(
        reader=train_reader, event_handler=event_handler, num_passes=num_passes)

                 至此,咱們的訓練代碼定義結束,開始進行訓練 

python train.py

                 pass 至關於咱們日常所使用的 epoch(即一次迭代), batch是咱們每次訓練加載的輸入變量的個數,cost 是衡量咱們的網絡結構損失函數大小的具體值,越小越好,最後一項 classification_error_evaluator 是代表咱們目前的分類偏差的損失率,也是越小越好。

            4.生成文本

                 當等待若干時間之後,訓練完畢之後。開始進行文本生成。

python generate.py

 

                 生成文本展現

81    若隱若現 地像 幽靈 , 像 死神
-12.2542    同樣 。 他 是 個 怪物 <e>
-12.6889    同樣 。 他 是 個 英雄 <e>
-13.9877    同樣 。 他 是 我 的 敵人 <e>
-14.2741    同樣 。 他 是 我 的 <e>
-14.6250    同樣 。 他 是 我 的 朋友 <e>

其中:

  1. 第一行 81 若隱若現 地像 幽靈 , 像 死神\t爲分隔,共有兩列:
    • 第一列是輸入前綴在訓練樣本集中的序號。
    • 第二列是輸入的前綴。
  2. 第二 ~ beam_size + 1 行是生成結果,一樣以 \t 分隔爲兩列:
    • 第一列是該生成序列的對數機率(log probability)。
    • 第二列是生成的文本序列,正常的生成結果會以符號<e>結尾,若是沒有以<e>結尾,意味着超過了最大序列長度,生成強制終止

     總結:

            咱們此次說明了如何安裝paddle。如何使用paddle開始一段項目。整體來講paddle 的文檔目前是很是的不規範,閱讀的體驗也不是很好,須要開發者耐心細緻的閱讀源代碼來掌握paddle的使用方法。第二不少層的封裝感受寫法很是的冗餘,好比必定要用paddle做爲前綴,把python寫出了java的感受。可是瑕不掩瑜,從使用的角度來看,一旦掌握了其使用方法之後,本身定義網絡結構感受很是的方便。訓練的速度也是挺快的。

相關文章
相關標籤/搜索