天天看點

使用PaddleFluid和TensorFlow訓練序列标注模型

專欄介紹:Paddle Fluid 是用來讓使用者像 PyTorch 和 Tensorflow Eager Execution 一樣執行程式。在這些系統中,不再有模型這個概念,應用也不再包含一個用于描述 Operator 圖或者一系列層的符号描述,而是像通用程式那樣描述訓練或者預測的過程。

本專欄将推出一系列技術文章,從架構的概念、使用上對比分析 TensorFlow 和 Paddle Fluid,為對 PaddlePaddle 感興趣的同學提供一些指導。

上一篇我們通過 RNN 語言模型初識 PaddleFluid 和 TensorFlow 中的循環神經網絡模型。了解了:

在 PaddleFluid 和 TensorFlow 平台下如何組織序列輸入資料;

如何使用循環神經網絡單元;

使用中的注意事項。

可以看到 PaddleFluid 中的各種循環神經網絡單元都直接支援非填充序列作為輸入,使用者在使用時無需對 mini-batch 中的不等長序列進行填充,無需關心填充位是否會對代價(loss)計算産生影響,進而需要在計算損失時對填充位置進行過濾這樣的細節,對使用來說無疑是十分友善的。

循環神經網絡的是深度學習模型中最為重要的一部分,這一篇我們以序列标注任務為例将會構造一個更加複雜的循環神經網絡模型用于命名實體識别任務。我們的關注點始終放在:在兩個平台下:(1)如何組織序列資料;(2)如何使用序列處理單元(不限于循環神經網絡)。

這一篇會看到:

  1. PaddleFluid Data Feeder vs. 使用 TensorFlow r1.4 之後 release 的 Dataset API 讀取資料;
  2. 在 PaddleFluid 和 TensorFlow 中,使用條件随機場(Conditional Random Field,CRF)單元;
  3. 在 PaddleFluid 和 TensorFlow 中,通過資料并行方式使用多塊 GPU 卡進行訓練。

如何使用代碼

本篇文章配套有完整可運作的代碼, 請随時從 github [1] 上擷取最新代碼。代碼包括以下幾個檔案:

使用PaddleFluid和TensorFlow訓練序列标注模型

在執行訓練任務前,請首先在終端執行下面的指令進行訓練資料下載下傳以及預處理。

sh download.sh
           

在終端運作以下指令便可以使用預設結構和預設參數運作 PaddleFluid 訓練序列标注模型。

python sequence_tagging_fluid.py
           

在終端運作以下指令便可以使用預設結構和預設參數運作 TensorFlow 訓練序列标注模型。

python sequence_tagging_tensorflow.py
           

背景介紹

序列标注和命名實體識别

序列标注是自然語言處理任務中的重要基礎任務之一。常見的分詞,詞性标注,語義角色标注,命名實體識别,甚至自動問答(QA)都可以通過序列标注模型來實作。這一篇我們将訓練一個序列标注模型完成命名實體識别的任務。

我們先來看看,什麼是序列标注問題呢?請看下面一幅圖:

使用PaddleFluid和TensorFlow訓練序列标注模型

▲ 圖1. 序列标注問題

序列标注任務是為一個一維的線性輸入序列中的每個元素打上标簽集合中的某個标簽。在上面的例子中,序列标注就是為圖像序列中的每個元素貼上一個描述它們形狀的标簽。而序列标注任務的難點在于:序列中 元素的标記和 它們在序列中的位置密切相關。

那麼, 什麼是命名實體識别呢?命名實體識别(Named Entity Recognition,NER)又稱作“專名識别”,是指識别文本中具有特定意義的實體,主要包括:人名、地名、機構名、專有名詞等。

BIO 表示法

序列标注任務一般都會采用 BIO 表示方式來定義序列标注的标簽集,B 代表句子的開始,I 代表句子中間,O 代表句子結束。通過 B、I、O 三種标記将不同的語塊賦予不同的标簽,例如:對于一個标記為 A 的命名實體,将它所包含的第一個語塊賦予标簽 B-A,将它所包含的其它語塊賦予标簽 I-A,不屬于任何命名實體的語塊賦予标簽 O。圖 2 是用 BIO 表示标注序列中命名實體的具體示例。

使用PaddleFluid和TensorFlow訓練序列标注模型

▲ 圖2. BIO标注方法示例

模型概覽

圖 3 是本篇模型的模型結構概覽。

使用PaddleFluid和TensorFlow訓練序列标注模型

▲ 圖3. 序列标注模型結構概覽

我們要訓練的序列标注模型,接受:一個文本序列作為輸入,另一個與輸入文本序列等長的标記序列作為學習的目标。首先通過上一篇介紹過的 word embedding 層的取詞作用得到詞向量, 接着經過一個雙向 LSTM 單元學習序列的特征表示,這個特别表示最終作為條件随機場 CRF 的輸入完成最終的序列标注任務。

下面是對各個子子產品的進一步說明。

雙向循環神經網絡

在循環神經網絡模型中,t 時刻輸出的隐藏層向量編碼了到 t 時刻為止所有輸入的資訊,但由于循環神經網絡單元計算的串行行:t 時刻循環神經網絡但願可以看到曆史(t 時刻之前),卻無法看到未來(t 時刻之後)。

一些自然語言處理任務總是能一次性拿到整個句子,這種情況下,在 t 時刻計算時,如果能夠像擷取曆史資訊一樣得到未來的資訊,對序列學習任務會有很大幫助,雙向循環神經網絡的出現正是為了解決這一問題。

它的思想簡單且直接:使用兩個循環神經網絡單元( simple RNN,GRU 或者 LSTM 均可)分别以正向和反向順序學習輸入序列,再将兩者的輸出 向量進行橫向拼接。這樣的一個輸出向量中就既包含了 t 時刻之前的資訊,也包含了 t 時刻之後的資訊。

條件随機場

使用神經網絡模型解決問題的思路通常都是:前層網絡學習輸入的特征表示,網絡的最後一層在特征基礎上完成最終任務。在序列 标注任務中,雙向循環神經網絡學習輸入的特征表示,條件随機場(Conditional Random Filed, CRF)正是在特征的基礎上完成序列标注的一種計算單元,處于整個網絡的末端。

CRF 是一種機率化結構模型,可以看作是一個機率無向圖模型(也叫作馬爾科夫随機場),結點表示随機變量,邊表示随機變量之間的機率依賴關系。簡單來講 CRF 學習條件機率:P(X|Y),其中 X=(x1,x2,...,xn) 是輸入序列,Y=(y1,y2,...,yn) 是标記序列;解碼過程是給定 X 序列求解令 P(Y|X) 最大的 Y 序列,即。

條件随機場是的定義:設 G=(V,E) 是一個無向圖, V 是結點的集合,E 是無向邊的集合。V 中的每個結點對應一個随機變量 Yv,,其取值範圍為可能的标記集合 {y},如果以随機變量 X 為條件,每個随機變量 Yv 都滿足以下馬爾科夫特性:

使用PaddleFluid和TensorFlow訓練序列标注模型

其中,ω∼v 表示兩個結點在圖 G 中是鄰近結點,那麼,(X,Y) 是一個條件随機場。

線性鍊條件随機場

上面的定義并沒有對 X 和 Y 的結構給出更多限制,理論上來講隻要标記序清單示了一定的條件獨立性,G 的圖結構可以是任意的。對序列标注任務,隻需要考慮 X 和 Y 都是一個序列,于是可以形成一個如圖 4 所示的簡單鍊式結構圖。在圖中,輸入序列 X 的元素之間并不存在圖結構,因為我們隻是将它作為條件,并不做任何條件獨立假設。

使用PaddleFluid和TensorFlow訓練序列标注模型

▲ 圖4. 輸入序列和标記序列具有相同結構的線性鍊條件随機場

序列标注問題使用的是以上這種定義線上性鍊上的特殊條件随機場,稱之為線性鍊條件随機場(Linear Chain Conditional Random Field)。下面,我們給出線性鍊條件随機場的數學定義:

定義 2 :線性鍊條件随機場 :設 X=(x1,x2,...,xn),Y=(y1,y2,...,yn) 均為線性連結清單示的随機變量序列,若在給定随機變量序列 X 的條件下,随機變量序列 Y 的條件機率分布 P(Y|X) 滿足馬爾科夫性:

使用PaddleFluid和TensorFlow訓練序列标注模型

i=1,2,...,n(在i=1和n時隻考慮單邊)則稱 P(Y|X) 為線性鍊條件随機場。X 表示輸入序列,Y 表示與之對應的标記序列。

根據線性鍊條件随機場上的因子分解定理,在給定觀測序列 X 時,一個特定标記序列 Y 的機率可以定義為:

使用PaddleFluid和TensorFlow訓練序列标注模型

其中:

使用PaddleFluid和TensorFlow訓練序列标注模型

是規範化因子。

上面的式子中 tj 是定義在邊上的特征函數,依賴于目前和前一個位置,稱為轉移特征,表示對于觀察序列 X 及其标注序列在 i 及 i−1 位置上标記的轉移機率。sk 是定義在結點上的特征函數,稱為狀态特征,依賴于目前位置,表示對于觀察序列 X 及其 i 位置的标記機率。λj 和 μk 分别是轉移特征函數和狀态特征函數對應的權值。

線性鍊條件随機場的優化目标

實際上 ,t 和 s 可以用相同的數學形式表示,s 可以同樣也寫為以下形式:

使用PaddleFluid和TensorFlow訓練序列标注模型

假設有 K1 個轉移特征,K2 個狀态特征,定義特征函數:

使用PaddleFluid和TensorFlow訓練序列标注模型

再對轉移特征和狀态特在各個位置 i 求和有:

使用PaddleFluid和TensorFlow訓練序列标注模型

于是條件機率 P(Y|X) 可以寫為:

使用PaddleFluid和TensorFlow訓練序列标注模型
使用PaddleFluid和TensorFlow訓練序列标注模型

我們把 f 統稱為特征函數,ω 是權值,是 CRF 模型要求解的參數。

學習時,對于給定的輸入序列和對應的标記序列的集合 D=[(X1,Y1),(X2,Y2),...,(XN,YN)] ,通過正則化的極大似然估計,可以得到如下優化目标:

使用PaddleFluid和TensorFlow訓練序列标注模型

這個優化目标,可以通過反向傳播算法和整個神經網絡一起更新求解。

解碼時,對于給定的輸入序列 X,通過解碼算法(通常有:維特比算法、Beam Search)求令出條件機率最大的輸出序列。

CRF小結

條件随機場是這一篇網絡中一個相對複雜的計算單元。值得慶幸的是,在各個深度學習架構的幫助下,大多數情況下,我們隻需要知道其原理便可以非常友善的使用,而不必過于關注 其内部的實作細節。

這裡我們再對上面的内容進行一個簡單的總結,友善大家使用 CRF 單元:

  1. 在序列标注網絡中, CRF 以循環神經網絡單元輸出向量作為輸入,學習狀态特征和轉移特征。
  2. 狀态特征隻與當然輸入有關;轉移特征是一個矩陣,刻畫了标記兩兩之間互相轉移的強度。
  3. 假設循環神經網絡單元輸出向量次元為 h ,序列中含有 t 個詞語,共有 d 個标記:

循環神經網絡輸入矩陣的大小為:Out=t×h;

CRF 層以 Out 為輸入學習轉移特征:通過一個 全連接配接層将 Out 映射為一個 t×d 的矩陣,也就是轉移特征;

狀态特征是一個:(d+2)×d 維的矩陣,刻畫了标記之前轉移的強度。 這裡的 +2 是需要學習序列開始 向句子首詞轉移和和句子末尾詞向序列結束 轉移這樣兩種特殊的狀态轉移;

CRF 本質上計算了一個 softmax:給定标記序列出現的機率。但困難之處在于 softmax 的歸一化分母是所有可能标記序列,計算量很大。但由于引入了馬爾科夫假設,這個歸一化分母可以巧妙地通過一個動态規劃算法求解。

  1. CRF 的學習準則是令 negative log likelihood 最大化。

資料集介紹

這一篇我們使用 Standford CS224d 課程中作業 2 [2] 的 NER 任務資料作為訓練資料源。 進入 data 目錄運作 data/download.sh 腳本下載下傳資料并預處理訓練資料。預處理包括:1. 為輸入文本序列建立詞典;2. 組織輸入資料格式。

運作結束将會在 data 目錄下看到如下内容。

data
├── dev
├── dev_src.txt
├── dev_src.vocab
├── dev_trg.txt
├── dev_trg.vocab
├── download.sh
├── preprocess.py
├── train
├── train_src.txt
├── train_src.vocab
├── train_trg.txt
└── train_trg.vocab
           

其中需要重點關注的是 train_src.txt 、 train_trg.txt 、 train_src.vocab和train_trg.vocab 檔案。它們分别是:輸入文本序列;文本對應的标記序列;輸入文本序列的詞典以及标記序列詞典。 train_src.txt 和 train_trg.txt 的一行是一條訓練樣本,他們嚴格一一對應。分别執行 head -n 1 train_src.txt 和 head -n 1 train_trg.t xt 會看到如下内容:

EU rejects German call to boycott British lamb .
           

B-ORG O B-MISC O O O B-MISC O O

程式結構

我們首先在此整體回顧一下使用 PaddleFluid 平台和 TensorFlow 運作神經網絡模型的整體流程。

PaddleFluid

  1. 調用 PaddleFluid API 描述神經網絡模型。PaddleFluid 中 一個神經網絡訓練任務被稱之為一段 Fluid Program 。
  2. 定義 Fluid Program 執行裝置: place 。常見的有 fluid.CUDAPlace(0) 和 fluid.CPUPlace() 。
place = fluid.CUDAPlace(0) if conf.use_gpu else fluid.CPUPlace()
           

注:PaddleFluid 支援混合裝置運作,一些 運算(operator)沒有特定裝置實作,或者為了提高全局資源使用率,可以為他們指定不同的計算裝置。

  1. 建立 PaddleFluid 執行器(Executor),需要為執行器指定運作裝置。
exe = fluid.Executor(place)
           
  1. 讓執行器執行 fluid.default_startup_program() ,初始化神經網絡中的可學習參數,完成必要的初始化工作。
  2. 定義 DataFeeder,編寫 data reader,隻需要關注如何傳回一條訓練/測試資料。
  3. 進入訓練的雙層循環(外層在 epoch 上循環,内層在 mini-batch 上循環),直到訓練結束。

TensorFlow

  1. 調用 TensorFlow API 描述神經網絡模型。 TensorFlow 中一個神經網絡模型是一個 Computation Graph。
  2. 建立 TensorFlow Session 用來執行計算圖。
sess = tf.Session()
           
  1. 調用 sess.run(tf.global_variables_initializer()) 初始化神經網絡中的可學習參數。
  2. 編寫傳回每個 mini-batch 資料的資料讀取腳本。

如果不顯示地指定使用何種裝置進行訓練,TensorFlow 會對機器硬體進行檢測(是否有 GPU), 選擇能夠盡可能利用機器硬體資源的方式運作。

建構網絡

基于 PaddleFluid 和 TensorFlow 的序列标注網絡分别定義在 sequence_tagging_fluid.py 和 sequence_tagging_tensorflow.py 的 NER_net 類中,詳細資訊請參考完整代碼,這裡對重要部分進行說明。

加載訓練資料

PaddleFluid:編寫Data Reader

PaddleFluid 模型通過 fluid.layers.data 來接收輸入資料。序列标注網絡以圖檔以及圖檔對應的類别标簽作為網絡的輸入:

self.source = fluid.layers.data(
    name="source", shape=[1], dtype="int64", lod_level=1)
self.target = fluid.layers.data(
    name="target", shape=[1], dtype="int64", lod_level=1)
           

定義 data layer 的核心是指定輸入 Tensor 的形狀( shape )和類型。

序列标注中,輸入文本序列和标記序列都使用 one-hot 特征作為輸入,一個詞用一個和字典大小相同的向量表示,每一個位置對應了字典中的 一個詞語。one-hot 向量僅有一個次元為 1, 其餘全部為 0。在上面定義的 data layer 中 source 和 target 的形狀都是 1,類型是 int64 。

PaddleFluid 支援非填充的序列輸入,這是通過 LoD Tensor 實作的。關于什麼是 LoD Tensor 請參考上一篇使用 PaddleFluid 和 TensorFlow 訓練 RNN 語言模型中的介紹,這一篇不再贅述。有了 LoD Tensor 的概念後,在 PaddleFluid 中,通過 DataFeeder 子產品來為網絡中的 data layer 提供資料,調用方式如下面的代碼所示:

train_reader = paddle.batch(
    paddle.reader.shuffle(
        data_reader(conf.train_src_file_name, conf.train_trg_file_name,
                    conf.src_vocab_file, conf.trg_vocab_file),
        buf_size=1024000),
    batch_size=conf.batch_size)

place = fluid.CUDAPlace(0) if conf.use_gpu else fluid.CPUPlace()
feeder = fluid.DataFeeder(feed_list=[net.source, net.target], place=place)
           

觀察以上代碼,需要使用者完成的僅有:編寫一個實作讀取一條資料的 python 函數: data_reader 。 data_reader 的代碼非常簡單,我們再來看一下它的具體實作:

def data_reader(src_file_name, trg_file_name, src_vocab_file, trg_vocab_file):
    def __load_dict(dict_file_path):
        word_dict = {}
        with open(dict_file_path, "r") as fdict:
            for idx, line in enumerate(fdict):
                if idx < 2: continue
                word_dict[line.strip().split("\t")[0]] = idx - 2

        return word_dict

    def __reader():
        src_dict = __load_dict(src_vocab_file)
        trg_dict = __load_dict(trg_vocab_file)

        with open(src_file_name, "r") as fsrc, open(trg_file_name,
                                                    "r") as ftrg:
            for src, trg in izip(fsrc, ftrg):
                src_words = src.strip().split()
                trg_words = trg.strip().split()

                src_ids = [src_dict[w] for w in src_words]
                trg_ids = [trg_dict[w] for w in trg_words]
                yield src_ids, trg_ids

    return __reader
           

在上面的代碼中:

  1. data_reader 是一個 python generator ,函數名字可以任意指定,無需固定。
  2. data_reader 打開輸入序列檔案和标記序列檔案,每次從這兩個檔案讀取一行,一行既是一條訓練資料,傳回一個 python list,這個 python list 既是序列中所有時間步。具體的資料組織方式如下表所示,其中 i 代表一個整數:
使用PaddleFluid和TensorFlow訓練序列标注模型
  1. paddle.batch() 接口用來構造 mini-batch 輸入,會調用 data_reader 将資料讀入一個 pool 中,對 pool 中的資料進行 shuffle,然後依次傳回每個 mini-batch 的資料。

TensorFlow:使用Dataset API

在之前的篇章中我們都使用 TensorFlow 的 placeholder 接入訓練資料,這一篇我們使用一種新的方式 TensorFlow 在 r1.3 版本之後引入的 Dataset API 來讀取資料。

參考 Google 官方給出的 Dataset API 中的類圖 [3],使用 TensorFlow 的 Dataset API,首先引入兩個抽象概念:

  1. tf.data.Dataset 表示一系列元素,其中每個元素包含一個或多個 Tensor 對象。
  2. tf.data.Iterator 提供了從資料集中取出元素的方法。 Iterator.get_next() 會在執行時生成 Dataset 的下一個 /mini-batch 元素。
使用PaddleFluid和TensorFlow訓練序列标注模型

定義 Dataset

目前 Dataset API 還提供了三種預定義好的定義 Dataset 的方式。這一篇中我們主要面向文本資料的處理,使用其中的 TextLineDataset 接口。

tf.data.TextLineDataset:接口的輸入是一個檔案清單,輸出是一個 TensorFlow dataset , dataset 中的每一個元素就對應了檔案中的一行。通過下面的調用傳入輸入序列文本路徑和标記序列文本路徑便可傳回一個 Dataset 。

src_dataset = tf.data.TextLineDataset(src_file_name)
trg_dataset = tf.data.TextLineDataset(trg_file_name)
           

擷取 Iterator

需要說明的是,TensorFlow 中的循環神經網絡要求一個 mini-batch 之内序列長度相等,使用 Dynamic RNN 時,batch 和 batch 之間序列長度可以不相等,是以對一個 mini-batch 之内的資料需要進行填充。

Dataset API 提供了 padded_batch 幫助構造填充後的 mini-batch 資料。

提示:使用 bucket 分桶,從桶内取 mini-batch 資料,填充至一個 batch 中的最長序列長度能夠有效提高 dynamic rnn 的計算效率。

下面的代碼傳回 Iterator ,使用先分桶,然後再取 mini-batch 資料填充至 batch 中最長序列長度的方式。完整代碼請參考:iterator_helper_tf [4]。

def get_data_iterator(src_file_name,
                      trg_file_name,
                      src_vocab_file,
                      trg_vocab_file,
                      batch_size,
                      pad_token="</p>",
                      max_sequence_length=None,
                      unk_id=1,
                      num_parallel_calls=4,
                      num_buckets=5,
                      output_buffer_size=102400,
                      is_training=True):

    def __get_word_dict(vocab_file_path, unk_id):
        return tf.contrib.lookup.index_table_from_file(
            vocabulary_file=vocab_file_path,
            key_column_index=0,
            default_value=unk_id)

    src_dataset = tf.data.TextLineDataset(src_file_name)
    trg_dataset = tf.data.TextLineDataset(trg_file_name)

    dataset = tf.data.Dataset.zip((src_dataset, trg_dataset))
    if is_training:
        dataset = dataset.shuffle(
            buffer_size=output_buffer_size, reshuffle_each_iteration=True)

    src_trg_dataset = dataset.map(
        lambda src, trg: (tf.string_split([src]).values, \
                tf.string_split([trg]).values),
        num_parallel_calls=num_parallel_calls).prefetch(output_buffer_size)

    src_dict = __get_word_dict(src_vocab_file, unk_id)
    trg_dict = __get_word_dict(trg_vocab_file, unk_id)

    src_pad_id = tf.cast(src_dict.lookup(tf.constant(pad_token)), tf.int32)
    trg_pad_id = tf.cast(trg_dict.lookup(tf.constant(pad_token)), tf.int32)

    # convert word string to word index
    src_trg_dataset = src_trg_dataset.map(
        lambda src, trg: (
                tf.cast(src_dict.lookup(src), tf.int32),
                tf.cast(trg_dict.lookup(trg), tf.int32)),
        num_parallel_calls=num_parallel_calls).prefetch(output_buffer_size)

    # Add in sequence lengths.
    src_trg_dataset = src_trg_dataset.map(
        lambda src, trg: (src, trg, tf.size(src)),
        num_parallel_calls=num_parallel_calls).prefetch(output_buffer_size)

    def __batching_func(x):
        return x.padded_batch(
            batch_size,
            padded_shapes=(
                tf.TensorShape([None]),  # src
                tf.TensorShape([None]),  # trg
                tf.TensorShape([]),  #seq_len
            ),
            padding_values=(src_pad_id, trg_pad_id, 0, ))

    if num_buckets > 1:

        def __key_func(unused_1, unused_2, seq_len):
            if max_sequence_length:
                bucket_width = (
                    max_sequence_length + num_buckets - 1) // num_buckets
            else:
                bucket_width = 10

            bucket_id = seq_len // bucket_width,
            return tf.to_int64(tf.minimum(num_buckets, bucket_id))

        def __reduce_func(unused_key, windowed_data):
            return __batching_func(windowed_data)

        batched_dataset = src_trg_dataset.apply(
            tf.contrib.data.group_by_window(
                key_func=__key_func,
                reduce_func=__reduce_func,
                window_size=batch_size))

    else:
        batched_dataset = __batching_func(curwd_nxtwd_dataset)

    batched_iter = batched_dataset.make_initializable_iterator()
    src_ids, trg_ids, seq_len = batched_iter.get_next()

    return BatchedInput(
        initializer=batched_iter.initializer,
        source=src_ids,
        target=trg_ids,
        sequence_length=seq_len)
           

建構網絡結構及運作

建構網絡結構及運作的過程對兩個平台上都是正常流程。

  1. 建構網絡時調用相關的 API 接口,令一個 計算單元的輸出成為下一個計算單元的輸入建立起網絡的連通性;具體請參考 sequence_tagging_fluid.py 和 sequence_tagging_tensorflow.py 中 NER_net 類的實作。
  2. 運作訓練以及解碼具體請參考 sequence_tagging_fluid.py 和 sequence_tagging_tensorflow.py 中 train 函數的實作。

模型中核心子產品:LSTM 單元在兩個平台下的差異及注意事項請參考上一篇:使用 PaddleFluid 和 TensorFlow 訓練 RNN 語言模型,這裡不再贅述。

總結

這一篇繼續在序列标注模型中了解 PaddleFluid 和 TensorFlow 在接受序列輸入,序列處理政策上的不同。

  1. PaddleFluid 引入了 LoD Tensor 的概念,所有序列處理子產品(包括所有循環神經網絡單元,文本卷積)都支援非填充的序列輸入,使用時無需對 mini-batch 資料進行填充,也就避免了對填充位的各種特殊處理,這一點非常友善。
  2. TensorFlow 中的 Dynamic RNN 支援 mini-batch 之間序列不等長,但仍要求一個 mini-batch 内的資料填充至一樣長。
  3. PaddleFluid 中通過 Data Feeder 提供訓練資料,隻需要編寫一個 python generator 實作從原始輸入檔案中讀取一條訓練樣本, 架構會完成資料 shuffle 群組織 mini-batchd 工作。
  4. 這一篇使用了 TensorFlow r1.3 後 release 的 Dataset API,資料讀取部分也是一個 computation graph,能夠提高 I/O 效率,使用相對複雜一些。

本篇代碼中提供了通過資料并行政策在 PaddleFluid 平台下使用多塊 GPU 卡進行訓練,在 TensorFlow 中使用多卡相對複雜一些,這些主題會在下面繼續讨論。

參考文獻

[1]. 本文配套代碼

https://github.com/JohnRabbbit/TF2Fluid/tree/master/05_sequence_tagging

[2]. Standford CS224d課程作業2

http://cs224d.stanford.edu/assignment2/index.html

[3]. Google官方Dataset API

https://developers.googleblog.com/2017/09/introducing-tensorflow-datasets.html

[4]. iterator_helper_tf

https://github.com/JohnRabbbit/TF2Fluid/blob/master/05_sequence_tagging/iterator_helper_tf.py

原文釋出時間為:2018-07-11

本文作者:讓你更懂AI

本文來自雲栖社群合作夥伴“

PaperWeekly

”,了解相關資訊可以關注“

”。

繼續閱讀