天然語言處理

完整示例訓練過程用到的APIphp

 1 import tensorflow as tf
 2 
 3 dataset = tf.data.TextLineDataset(file_path)
 4 # tf.string_split
 5 # tf.string_to_number
 6 # tf.size
 7 # tf.data.Dataset.zip
 8 # tf.concat
 9 
10 # tf.logical_and
11 # tf.logical_not
12 # tf.logical_or
13 #
14 # tf.greater
15 # tf.greater_equal
16 # tf.less
17 # tf.less_equal
18 # tf.not_equal
19 
20 # dataset.map
21 # dataset.filter
22 # dataset.shuffle
23 # dataset.padded_batch
24 # dataset.make_one_shot_iterator
25 # dataset.make_initializable_iterator
26 
27 # tf.TensorShape
28 # tf.nn.rnn_cell.MultiRNNCell
29 # tf.nn.rnn_cell.BasicLSTMCell
30 # tf.transpose
31 # tf.random_uniform_initializer
32 # tf.nn.embedding_lookup
33 # tf.nn.dropout
34 # tf.nn.dynamic_rnn
35 # tf.nn.sparse_softmax_cross_entropy_with_logits
36 # tf.nn.softmax_cross_entropy_with_logits
37 # tf.sequence_mask
38 
39 # tf.reduce_sum
40 # tf.reduce_mean
41 # tf.reduce_max
42 # tf.reduce_min
43 # tf.reduce_any
44 # tf.reduce_all
45 # tf.reduce_prod
46 
47 # tf.to_float
48 # tf.to_double
49 # tf.to_int32
50 # tf.to_int64
51 # tf.to_bfloat16
52 
53 # optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0)
54 # optimizer.apply_gradients
55 
56 # tf.clip_by_global_norm
57 # tf.clip_by_value
58 # tf.clip_by_norm
59 # tf.clip_by_average_norm

tf.reduce_all和tf.reduce_anygit

 1 a = [[False, True], [True, True], [True, True]]
 2 l = tf.convert_to_tensor(a)
 3 s1 = tf.reduce_all(l)  # Computes the "logical and" of elements across dimensions of a tensor.
 4 s2 = tf.reduce_any(l)  # Computes the "logical or" of elements across dimensions of a tensor.
 5 
 6 print(l, s1, s2)
 7 with tf.Session() as sess:
 8     print(sess.run([l, s1, s2]))
 9 
10 # [array([[False,  True],
11 #        [ True,  True],
12 #        [ True,  True]]), False, True]

編碼過程用到的APIgithub

1 # tf.convert_to_tensor
2 # init_array = tf.TensorArray
3 # init_array.read  返回一個元素
4 # init_array.write  返回整個數組
5 # init_array.stack  一次性取出所有
6 #
7 # tf.reduce_all
8 # self.dec_cell.call
9 # tf.while_loop

 

這一章將介紹如何使用深度學習方法解決天然語言處理問題。算法

9.1 語言模型的背景知識數組

語言模型是天然語言處理問題中一類最基本的問題,它有着很是普遍的應用,也是理解更加複雜的天然語言處理問題的基礎。網絡

9.1.1 語言模型簡介session

假設一門語言中全部可能的句子服從某一個機率分佈,每一個句子出現的機率加起來爲1,那麼「語言模型」的任務就是預測每一個句子在語言中出現的機率。把句子當作單詞的序列,語言模型能夠表示爲一個計算p(w1, w2, w3,…, wm)的模型。語言模型僅僅對句子出現的機率進行建模,並不嘗試去「理解」句子的內容含義。app

語言模型有不少應用。不少生成天然語言文本的應用都依賴語言模型來優化輸出文本的流暢性。生成的句子在語言模型中的機率越高,說明其越有多是一個流程、天然的句子。例如在輸入法中,假設輸入的拼音串爲「xianzaiquna」,輸出多是」西安在去哪「,也多是」如今去哪「,這時輸入法就利用語言模型比較兩個輸出的機率,得出」如今去哪「更有多是用戶所須要的輸出。在統計機器翻譯的噪聲信道模型(Noise Channel Model)中,每一個候選翻譯的機率由一個翻譯模型和一個語言模型共同決定,其中的語言模型就起到了在目標語言中挑選較爲合理的句子的做用。在9.3小節將看到,神經網絡機器翻譯的Seq2Seq模型能夠看做是一個條件語言模型(Conditional Language Model),它至關因而在給定輸入的狀況下對目標語言的全部句子估算機率,並選擇其中機率最大的句子做爲輸出。框架

計算一個句子的機率:less

首先一個句子能夠被當作是一個單詞序列:

  S = (w1, w2, w3,…, wm),

其中m爲句子的長度。那麼,它的機率能夠表示爲

  p(S) = p(w1, w2, w3,…, wm) = 

     p(w1)p(w2|w1)p(w3|w1, w2,) … p(wm|w1, w2, w3,…, wm-1),

p(wm|w1, w2, w3,…, wm-1)表示,已知前m-1個單詞時,第m個單詞爲wm的條件機率。若是能對這一項建模,那麼只要把每一個位置的條件機率相乘,就能計算一個句子出現的機率。然而通常來講,任何一門語言的詞彙量都很大,詞彙的組合更是不可勝數。假設一門語言的詞彙量爲V,若是要將p(wm|w1, w2, w3,…, wm-1)的全部參數保存一個模型裏,將須要vm個參數,通常的句子長度遠遠超出了實際可行的範圍。爲了評估這些參數的取值,常見的方法由n-gram模型、決策樹、最大熵模型、條件隨機場、神經網絡模型等。這裏先以其中最簡單的n-gram模型來介紹語言模型問題。

爲了控制參數數量,n-gram模型作了一個有限歷史假設:當前單詞的出現機率僅僅與前面的n-1個單詞相關,所以以上公式能夠近似爲

  p(S) = p(w1, w2, w3,…, wm) = Πimp(wi| w1, w2, w3,…, wi-1)

n-gram模型裏的n指的是當前單詞依賴它前面的單詞的個數。一般n能夠取一、二、三、4。n-gram模型中須要預估的參數爲條件機率p(wi|wi-n+1,…, wi-1)。假設某種語言的單詞表大小爲V,那麼n-gram模型須要估計的不一樣參數數量爲O(vn)量級。當n越大時,模型在理論上越準確,但也越複雜,須要的計算量和訓練語料數據量也就越大,由於n取>=4的狀況很是少。

n-gram模型的參數通常採用最大似然估計(Maximum Likelihood Estimation, MLE)方法計算:

  p(wi|wi-n+1,…, wi-1) = C(wi-n+1,…, wi-1, wi) / C(wi-n+1,…, wi-1),

其中C(X)表示單詞序列X在訓練語料中出現的次數。訓練語料的規模越大,參數估計的結果越可靠。但即便訓練數據的規模很是大時,仍是有不少單詞序列在訓練語料中不會出現,這就會致使不少參數爲0。爲了不由於乘以0而致使整個句子機率爲0,使用最大似然估計方法時須要加入平滑避免參數取值爲0。

 

9.1.2 語言模型的評價方法

語言模型效果好壞的經常使用評價標準是複雜度(perplexity)。在測試集上獲得的複雜度越低,說明建模效果越好。計算perplexity值的公式以下:

  

簡單來講,perplexity值刻畫的是語言模型預測一個語言樣本的能力。

從上面的定義中能夠看出,perplexity實際是計算每一個單詞獲得的機率倒數的幾何平均,所以perplexity能夠理解爲平均分支系數(average branching factor),即模型預測下一個詞時的平都可選擇數量。例如,考慮一個由0~9這10個數字隨機組成的長度爲m的序列,因爲這10個數字出現的機率是隨機的,因此每一個數字出現的機率是1//10,所以,在任什麼時候刻,模型都有10個等機率的候選答案能夠選擇,因而perplexity就是10。計算過程以下:

  

在語言模型的訓練中,一般採用perplexity的對數表達形式:

  

相比先乘積再求平方根的方式,採用對數形式(由於能夠轉爲加法運算)能夠加速計算,同時避免機率乘積數值太小致使浮點數向下溢出的問題。

在數學上,log perplexity能夠當作真實分佈與預測分佈之間的交叉熵。假設x是一個離散變量,μ(x)和v(x)是兩個與x相關的機率分佈,那麼μ和v之間交叉熵的定義是在分佈μ下-log(v(x))的指望值:

  

把x看做單詞,μ(x)爲每一個位置上單詞的真實分佈,v(x)爲模型的預測分佈p(wi|wi-n+1,…, wi-1),就能夠看出log perplexity和交叉熵是等價的。惟一的區別在於,因爲語言的真實分佈是未知的,所以在log perplexity的定義中,真實分佈用測試語料中的取樣代替,即認爲在給定上文w1, w2, w3,…, wi-1的條件下,語料中出現單詞 w的機率爲1,出現其餘單詞的機率均爲0。

在神經網絡模型中,p(wi| w1, w2, w3,…, wi-1)分佈一般是由一個softmax層產生的。這時tensorflow提供了兩個方便計算交叉熵的函數:tf.nn.softmax_cross_entropy_with_logits和tf.nn.sparse_softmax_cross_entropy_with_logits。

 1 import tensorflow as tf
 2 
 3 # tf.nn.softmax_cross_entropy_with_logits與tf.nn.sparse_softmax_cross_entropy_with_logits的區別在於
 4 # 前者須要預測目標(即label)以機率分佈的形式給出
 5 
 6 # 假設詞彙表大小爲3,語料包含有2個單詞,分別爲2和0
 7 word_labels = tf.constant([2, 0])
 8 
 9 # 假設模型對兩個單詞預測時,產生的logit分別爲[2.0, -1.0, 3.0]和[1.0, 0.0, -0.5]
10 predict_logits = tf.constant([[2.0, -1.0, 3.0], [1.0, 0.0, -0.5]])
11 
12 # 交叉熵
13 loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=word_labels, logits=predict_logits)
14 
15 # softmax_cross_entropy_with_logits須要將預測目標(即label)以機率分佈的形式給出
16 word_prob_distribution = tf.constant([[0.0, 0.0, 1.0], [1.0, 0.0, 0.0]])
17 loss2 = tf.nn.softmax_cross_entropy_with_logits(labels=word_prob_distribution, logits=predict_logits)
18 
19 with tf.Session() as sess:
20     print(sess.run([loss, loss2]))
21 
22 # 結果: 對應兩個預測的perplexity損失
23 # [array([0.32656264, 0.4643688 ], dtype=float32), array([0.32656264, 0.4643688 ], dtype=float32)]
24 
25 # 因爲softmax_cross_entropy_with_logits容許提供一個機率分佈,所以在使用時有更大的自由度。
26 # 例如,一種叫label smoothing的技巧是將正確數據的機率設爲一個比1.0略小的值,將錯誤數據的機率設爲比0.0略大的值,這樣能夠避免模型與數據過擬合,在某些時候能夠提升訓練效果。
27 word_prob_smooth = tf.constant([[0.01, 0.01, 0.98], [0.98, 0.01, 0.01]])
28 loss3 = tf.nn.softmax_cross_entropy_with_logits(labels=word_prob_smooth, logits=predict_logits)
29 
30 with tf.Session() as sess:
31     print(sess.run(loss3))
32 # [0.37656265 0.48936883]

 

9.2 神經語言模型

上節提到,n-gram模型爲了控制參數數量,須要將上下文信息控制在幾個單詞之內。也就是說,在預測下一個單詞時,n-gram模型只能考慮前n個單詞的信息,這對語言模型的能力形成了很大的限制。與之相比,循環神經網絡能夠將任意長度的上文信息存儲在隱藏狀態中。所以使用循環神經網絡做爲語言模型有着自然的優點。

因爲隱藏狀態的維度有限,它並不能真的存儲’全部‘的上文信息,一般來講,距離越遠的上文對下一個單詞的影響越小,所以存儲’全部‘信息也並不是必要。研究表面,在神經語言模型中保留13個單詞的上文信息大體能夠取得與保留全部上文信息相同的效果。

與圖像數據不一樣,天然語言文本數據沒法直接被當作數據值提供給神經網絡,須要進行預處理。

9.2.1 預處理

9.2.2 batching

9.2.3 完整示例

 

9.2.1 PTB數據集的預處理

 PTB(Penn Treebank Dataset)文本數據集是目前語言模型學習中使用最普遍的數據集。

下載地址:http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz

本節只需關心simple-example/data下的ptb.train.txt、ptb.valid.txt、ptb.test.txt三個文件。這三個數據文件已通過預處理,相鄰單詞之間用空格隔開。數據集中共包含9998個不一樣的單詞詞彙,加上稀有詞語的特殊符號<unk>和語句結束符在內,一共有10000個詞彙。在使用perplexity比較不一樣的語言模型時,文本的預處理和詞彙表必須保持一致。例如,若是一個語言模型將「don't」視爲一個單詞,而另外一個語言模型在預處理時將其拆爲「don」和「t」兩個單詞,那麼這兩個語言模型獲得的perplexity值就是不可比較的。近年來關於語言模型方面的論文大多采用了Mikolov提供的這一預處理後的數據版本,由此保證了論文之間具備可比性。
爲了將文本轉化爲模型能夠讀入的單詞序列,須要將不一樣的詞彙分別映射爲不一樣的整數編號。
1). 首先按照 詞頻順序爲每一個詞彙分配一個編號,而後將詞彙表保存到一個獨立的vocab文件中。
生成詞彙表:一行一個單詞,按詞頻從大到小排序,
 1 import collections
 2 from operator import itemgetter
 3 
 4 RAW_DATA = '/home/yangxl/files/ptb/ptb.train.txt'
 5 VOCAB_OUTPUT = '/home/yangxl/files/ptb/ptb.vocab'
 6 
 7 counter = collections.Counter()
 8 with open(RAW_DATA, 'r') as f:
 9     for line in f.readlines():
10         for word in line.strip().split():
11             counter[word] += 1
12 
13 # 按詞頻順序對單詞進行排序
14 sorted_word_to_cnt = sorted(counter.items(), key=itemgetter(1), reverse=True)
15 sorted_words = [x[0] for x in sorted_word_to_cnt]
16 
17 # 稍後咱們須要在文本換行處加入句子結束符'<eos>',這裏預先將其加入詞彙表。
# 這個句子結束符應該就至關於翻譯模型中的'_'吧。
18 sorted_words = ['<eos>'] + sorted_words 19 20 # 在9.3.2小節處理機器翻譯數據時,除了'<eos>',還須要將'<unk>'和句子起始符'<sos>'加入詞彙表,並從詞彙表中刪除低頻慈湖。
# 在PTB數據中,由於輸入數據已經將低頻詞彙替換爲'unk',所以不須要這一步驟。
# 剛好10000個詞彙。
21 # sorted_words = ['<unk>', '<sos>', '<eos>'] + sorted_words 22 # if len(sorted_words) > 10000: 23 # sorted_words = sorted_words[:10000] 24 25 with open(VOCAB_OUTPUT, 'w') as f_out: 26 for word in sorted_words: 27 f_out.write(word + '\n')

在肯定了詞彙表(就一個詞彙表)以後,再將訓練文件、測試文件等根據詞彙表文件轉化爲單詞編號。每一個單詞的編號就是它在詞彙文件中的行號。(說是替換,實際上是從新建立了一個新文件)

 1 RAW_DATA = '/home/error/simple-examples/data/ptb.test.txt'
 2 VACAB = '/home/error/ptb/test.vocab'
 3 OUTPUT_DATA = '/home/error/ptb/ptb.test'
 4 
 5 with codecs.open(VACAB, 'r', 'utf-8') as f:
 6     vocab = [word.strip() for word in f.readlines()]
 7 word_to_id = {v: k for k, v in enumerate(vocab)}
 8 print(word_to_id)
 9 print(word_to_id['<eos>'])
10 
11 def get_id(word):
12     return word_to_id[word] if word in word_to_id else word_to_id['<unk>']
13 
14 fin = codecs.open(RAW_DATA, 'r', 'utf-8')
15 fout = codecs.open(OUTPUT_DATA, 'w', 'utf-8')
16 
17 for line in fin:
18     words = line.strip().split() + ['<eos>']  # 讀取出來的行,每行都要加一個換行符(爲了替換爲序號),在讀取並strip時去掉了,因此得加上
19     out_line = ' '.join([str(get_id(w)) for w in words]) + '\n'  # 這個換行符只是爲了換行(上面那個是爲了替換爲序號)
20     fout.write(out_line)
21 
22 fin.close()
23 fout.close()

上面的實例使用了文本文件來保存通過處理的數據。在實際工程中,一般使用TFRecord格式來提升讀寫效率。

雖然預處理原則上能夠放在TF的Dataset框架中與讀取文本同時進行,但在工程實踐上,保存處理好的數據有幾個重要的優勢:第一,在調試模型的過程當中,能夠保證不一樣模型採起的預處理步驟相同;第二,減少文件體積,節省磁盤讀取的時間;第三,方便對預處理步驟自己進行debug,例如,在模型訓練效果不理想時,只需檢查最終的數據文件就能夠知道是否是預處理過程出了問題。

 

9.2.2 PTB數據的batching方法

在文本數據中,因爲每一個句子的長度不一樣,又沒法像圖像那樣調整到固定維度,由於在對文本數據進行batching時須要採起一些特殊操做。最多見的方法是使用填充(padding),將同一batch內的句子長度補齊。

可是,在PTB數據集中,每一個句子並不是隨機抽取的文本,而是在上下文之間有關聯的內容。語言模型爲了利用上下文信息,必須將前面句子的信息傳遞到後面的句子。爲了解決這個問題,一般採用的是另外一種batching方法。

若是模型大小沒有限制,那麼最理想的設計是將整個文檔先後鏈接起來,當作一個句子來訓練。但現實中這是沒法實現的。例如PTB數據總共約有19萬詞,若將整個文檔放入一個計算圖,循環神經網絡將展開成一個19萬層的前饋網絡。這樣會致使計算圖過大,另外序列過長可能形成訓練中梯度爆炸的問題。

對此問題的解決方法是,將長序列截斷爲固定長度的子序列。循環神經網絡在處理完一個子序列後,它最終的隱藏狀態將複製到下一個序列做爲初始值,這樣在前向計算時,效果等同於一次性順序地讀取了整個文檔;而在反向傳播時,梯度則只在每一個子序列內部傳播。

爲了利用計算時的並行能力,咱們但願每一次計算能夠對多個句子進行並行處理,同時又要儘可能保證batch之間的上下文連續。解決方案是,先將整個文檔切分紅若干連續段落(能夠理解爲幾行),再讓batch中的每一個位置負責其中一段(能夠理解爲縱向,一個batch含有全部連續段落的一小部分)。例如,若是batch大小爲4,爲了讓batch的每一個位置負責一個子序列,須要將全文平均分爲4個子序列,這樣每一個子文檔內部的全部數據仍能夠被順序處理。

下面的代碼從文本文件中讀取數據,並按上面介紹的方案將數據整理成batch。因爲PTB數據集比較小,所以能夠直接將整個數據集一次性讀入內存。

 1 import numpy as np
 2 
 3 TRAIN_DATA = '/home/yangxl/files/ptb/ptb.train'
 4 TRAIN_BATCH_SIZE = 20
 5 TRAIN_NUM_STEP = 35
 6 
 7 
 8 # 從文件中讀取數據,並返回包含單詞編號的數組
 9 def read_data(file_path):
10     with open(file_path, 'r') as fin:
11         # 將整個文檔讀進一個長字符串
12         id_string = ' '.join([line.strip() for line in fin])
13     # 將讀取的單詞編號轉爲整數
14     id_list = [int(w) for w in id_string.split()]
15     return id_list
16 
17 
18 def make_batches(id_list, batch_size, num_step):
19     # batch的數量,即每個子序列在縱向上能切分爲多少小段:1327段;num_step爲每一小段的大小,即一個句子的長度;
20     # 每一個batch包含的單詞數量爲batch_size * num_step
21     num_batches = (len(id_list) - 1) // (batch_size * num_step)  # 這裏減1,是爲了預留一個數據看成label使用
22 
23     # 如9-4圖所示,將數據整理成一個維度爲[batch_size, num_batches * num_step]的二維數組
24     data = np.array(id_list[: num_batches * batch_size * num_step])  # 後面689個數據未用到。
25     data = np.reshape(data, [batch_size, num_batches * num_step])  #
26     # print(data.shape)  # (20, 46445)
27 
28     # 沿着第二個維度將數據切分紅num_batches個shape爲(batch_size, num_step)的batch,存入一個列表
29     data_batched = np.split(data, num_batches, axis=1)  # (1327, 20, 35)
30 
31     # label的處理與data同樣。
32     label = np.array(id_list[1: num_batches * batch_size * num_step + 1])
33     label = np.reshape(label, [batch_size, num_batches * num_step])
34     label_batches = np.split(label, num_batches, axis=1)
35 
36     # 返回一個長度爲num_batches的數組,
37     return list(zip(data_batched, label_batches))  # (1327, 2, 20, 35)
38 
39 
40 def main():
41     train_batches = make_batches(read_data(TRAIN_DATA), TRAIN_BATCH_SIZE, TRAIN_NUM_STEP)
42 
43 
44 if __name__ == '__main__':
45     main()

 

9.2.3 基於循環神經網絡的神經語言模型

 

如圖9-1所示,與第8章介紹的循環神經網絡相比,NLP應用中主要多了兩個層:詞向量層(embedding)和softmax層。

詞向量層

在輸入層,每一個單詞用一個實數向量表示,這個向量被稱爲「詞向量」。詞向量能夠形象地理解爲將詞彙表嵌入到一個固定維度的實數空間裏。將單詞編號轉化爲詞向量主要有兩大做用。

1. 下降輸入的維度。若是不使用詞向量,而直接將單詞以one-hot vector的形式輸入循環神經網絡,那麼輸入的維度大小將與詞彙表大小相同,一般在10000以上。而詞向量的維度一般在200~1000之間,這將大大減小循環神經網絡的參數數量與計算量。

2. 增長語義信息。簡單的單詞編號是不包含任何語義信息的。兩個單詞之間編號相近,並不意味着它們的含義有任何關聯。而詞向量層將稀疏的編號轉化爲稠密的向量表示,這使得詞向量有可能包含更爲豐富的信息。在天然語言應用中學習獲得的詞向量一般會將含義類似的詞賦予取值相近的詞向量值,使得上層的網絡能夠更容易地抓住類似單詞之間的共性。例如,由於貓和狗都須要吃東西,所以在預測下文中出現單詞「吃」的機率時,上文中出現「貓」或「狗」帶來的影響多是類似的。這樣的任務訓練出來的詞向量中,表明「貓」和「狗」的詞向量取值極可能是類似的。

假設詞向量的維度是EMB_SIZE,詞彙表的大小爲VOCAB_SIZE,那麼全部單詞的詞向量能夠放入一個大小爲VOCAB_SIZE × EME_SIZE的矩陣內。在讀取詞向量時,能夠調用tf.nn.embedding_lookup方法。

 1 import tensorflow as tf
 2 
 3 BATCH_SIZE = 20
 4 NUM_STEP = 35
 5 VOCAB_SIZE = 1000
 6 EMB_SIZE = 200
 7 
 8 input_data = tf.placeholder(dtype=tf.int64, shape=[BATCH_SIZE, NUM_STEP])  # int32, int64
 9 embedding = tf.get_variable('embedding', [VOCAB_SIZE, EMB_SIZE])
10 input_embedding = tf.nn.embedding_lookup(embedding, input_data)
11 # 輸出矩陣比輸入矩陣多一個維度,新增維度的大小爲EMB_SIZE。
12 # 在語言模型中,通常input_data的維度爲batch_size * num_steps,而輸出的input_embedding的維度是batch_size * num_steps * EMB_SIZE.
13 print(input_embedding)  # Tensor("embedding_lookup/Identity:0", shape=(20, 35, 200), dtype=float32)

 

softmax層

softmax層的做用是將循環神經網絡的輸出轉化爲一個單詞表中每一個單詞的輸出機率。

爲此須要兩個步驟:第一,使用一個線性映射將循環神經網絡的輸出映射爲一個維度與詞彙表大小相同的向量,這一步的輸出叫作logits.

 1 import tensorflow as tf
 2 
 3 BATCH_SIZE = 20
 4 NUM_STEP = 35
 5 HIDDEN_SIZE = 10
 6 VOCAB_SIZE = 1000
 7 
 8 # 定義線性映射用到的參數。
 9 weight = tf.get_variable('weight', shape=[HIDDEN_SIZE, VOCAB_SIZE])
10 bias = tf.get_variable('bias', [VOCAB_SIZE])
11 # 計算線性映射。output是RNN的輸出,shape=(batch_size * num_steps, HIDDEN_SIZE)
12 output = tf.placeholder(tf.float32, shape=[BATCH_SIZE * NUM_STEP, HIDDEN_SIZE])
13 logits = tf.nn.bias_add(tf.matmul(output, weight), bias)
14 print(logits)  # Tensor("BiasAdd:0", shape=(700, 1000), dtype=float32)

第二,調用softmax將logits轉化爲和爲1的機率分佈。語言模型的每一步輸出均可以看做一個分類問題:在VOCAB_SIZE個可能的類別中決定這一步最可能輸出的單詞

1 probs = tf.nn.softmax(logits)  # 機率;probs的維度與logits的維度相同

模型訓練一般並不關心機率的具體取值,而更關心最終的log perplexity,所以能夠調用tf.nn.sparse_softmax_cross_entropy_with_logits方法直接從logits計算log perplexity做爲損失函數:

1 # labels是一個大小爲[batch_size * num_step]的一維數組,包含每一個位置正確的單詞編號。
2 # logits的維度爲[batch_size * num_step, HIDDEN_SIZE VOCAB_SIZE](書上是錯的),loss的維度與labels相同,表明每一個位置上的log perplexity。
3 labels = tf.placeholder(tf.int64, shape=[BATCH_SIZE * NUM_STEP])
4 loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits)
5 print(loss)  # shape=(700,)

 

經過共享參數減小參數數量

softmax層和詞向量層的參數數量都與詞彙表大小VOCAB_SIZE成正比。VOCAB_SIZE的數值一般較大,而HIDDEN_SIZE相對較小,致使softmax和embedding在整個網絡的參數數量中佔很大比例。例如,VOCAB_SIZE爲10000,HIDDEN_SIZE和EMB_SIZE都爲512,循環神經網絡採用雙層LSTM,那麼詞向量層和Softmax層的參數數量均爲10000*512=5120000,而循環神經網絡自己的參數數量僅爲2*4*2*512*512=4194304(LSTM有4個參數矩陣,每一個參數矩陣的維度是[2*HIDDEN_SIZE, HIDDEN_SIZE],忽略偏置項, 見圖8-7),少於詞向量層和Softmax層的參數數量,僅佔總參數數量的29%。

注意,在上面的例子中,詞向量層和softmax層的參數數量是相等的,它們都爲每一個單詞分配了長度爲512的向量。若是共享詞向量層和softmax層的參數,不只能大幅減小參數數量,還能提供最終模型的效果。(可以共享參數的緣由,多是兩層都是線性變換,可使用任意參數系,那麼使用相同的參數系也無妨,進一步能夠說,它們是不可訓練的)。下面的完整代碼樣例中實現了這一方法。

語言模型完整實例:

  1 import numpy as np
  2 import tensorflow as tf
  3 
  4 TRAIN_DATA = '/home/yangxl/files/ptb/ptb.train'
  5 EVAL_DATA = '/home/yangxl/files/ptb/ptb.valid'
  6 TEST_DATA = '/home/yangxl/files/ptb/ptb.test'
  7 HIDDEN_SIZE = 300  # 隱藏層大小
  8 NUM_LAYERS = 2  # 深層循環神經網絡中LSTM結構的層數
  9 VOCAB_SIZE = 10000  # 詞彙表大小
 10 TRAIN_BATCH_SIZE = 20  # 訓練數據batch大小
 11 TRAIN_NUM_STEP = 35  # 訓練數據截斷長度
 12 
 13 EVAL_BATCH_SIZE = 1  # 測試數據batch大小
 14 EVAL_NUM_STEP = 1  # 測試數據截斷長度
 15 NUM_EPOCH = 10  # 使用訓練數據的輪數
 16 LSTM_KEEP_PROB = 0.9  # LSTM節點不被dropout的機率
 17 EMBEDDING_KEEP_PROB = 0.9  # 詞向量不被dropout的機率
 18 MAX_GRAD_NORM = 5  # 用於控制梯度膨脹的梯度大小上限
 19 SHARE_EMB_AND_SOFTMAX = True  # 在softmax層和詞向量層之間共享參數
 20 
 21 
 22 # 從文件中讀取數據,並返回包含單詞編號的數組
 23 def read_data(file_path):
 24     with open(file_path, 'r') as fin:
 25         # 將整個文檔讀進一個長字符串
 26         id_string = ' '.join([line.strip() for line in fin])
 27     # 將讀取的單詞編號轉爲整數
 28     id_list = [int(w) for w in id_string.split()]  # 長度爲929589
 29     return id_list
 30 
 31 
 32 def make_batches(id_list, batch_size, num_step):
 33     # batch的數量,即每個子序列在縱向上能切分爲多少小段:1327段;num_step爲每一小段的大小,即一個句子的長度;
 34     # 每一個batch包含的單詞數量爲batch_size * num_step
 35     num_batches = (len(id_list) - 1) // (batch_size * num_step)  # 這裏減1,是爲了預留一個數據看成label使用
 36 
 37     # 如9-4圖所示,將數據整理成一個維度爲[batch_size, num_batches * num_step]的二維數組
 38     data = np.array(id_list[: num_batches * batch_size * num_step])  # 後面689個數據未用到。
 39     data = np.reshape(data, [batch_size, num_batches * num_step])  #
 40     # print(data.shape)  # (20, 46445)
 41 
 42     # 沿着第二個維度將數據切分紅num_batches個shape爲(batch_size, num_step)的batch,存入一個列表
 43     data_batched = np.split(data, num_batches, axis=1)  # (1327, 20, 35)
 44 
 45     # label的處理與data同樣。
 46     label = np.array(id_list[1: num_batches * batch_size * num_step + 1])
 47     label = np.reshape(label, [batch_size, num_batches * num_step])
 48     label_batches = np.split(label, num_batches, axis=1)
 49 
 50     # 返回一個長度爲num_batches的數組,
 51     return list(zip(data_batched, label_batches))  # (1327, 2, 20, 35)
 52 
 53 
 54 # 經過一個PTBModel類來描述模型,這樣方便維護循環神經網絡中的狀態。
 55 class PTBModel(object):
 56     def __init__(self, is_training, batch_size, num_steps):
 57         self.batch_size = batch_size
 58         self.num_steps = num_steps  # 在run_epoch()中計算iters時會用到
 59 
 60         # 定義每一步的輸入和預期輸出,兩者維度相同。
 61         self.input_data = tf.placeholder(dtype=tf.int32, shape=[batch_size, num_steps])
 62         self.targets = tf.placeholder(dtype=tf.int32, shape=[batch_size, num_steps])
 63 
 64         # 定義以LSTM爲循環體結構且使用dropout的深層循環神經網絡。
 65         dropout_keep_prob = LSTM_KEEP_PROB if is_training else 1.0
 66         lstm_cells = [
 67             tf.nn.rnn_cell.DropoutWrapper(
 68                 tf.nn.rnn_cell.LSTMCell(HIDDEN_SIZE),
 69                 output_keep_prob=dropout_keep_prob)
 70             for _ in range(NUM_LAYERS)]
 71         cell = tf.nn.rnn_cell.MultiRNNCell(lstm_cells)
 72 
 73         # 初始化最初的狀態,即全零的向量。這個量只在每一個epoch初始化第一個batch時使用。
 74         # 和其餘神經網絡相似,在優化循環神經網絡時,每次也會使用一個batch的訓練樣本。 c和h的shape都爲[batch_size, HIDDEN_SIZE]
 75         self.initial_state = cell.zero_state(batch_size, tf.float32)
 76 
 77         # 定義單詞的詞向量矩陣
 78         embedding = tf.get_variable('embedding', [VOCAB_SIZE, HIDDEN_SIZE])
 79 
 80         # 將輸入單詞轉爲詞向量
 81         inputs = tf.nn.embedding_lookup(embedding, self.input_data)
 82 
 83         # 只在訓練時使用dropout
 84         if is_training:
 85             # 在輸入以前要dropout, 各個循環層也要dropout。不改變inputs.shape
 86             inputs = tf.nn.dropout(inputs, keep_prob=EMBEDDING_KEEP_PROB)
 87 
 88         # 定義輸出列表。在這裏先將不一樣時刻LSMT的輸出收集起來,再一塊兒提供給softmax層。
 89         # outputs = []
 90         # state = self.initial_state
 91         # with tf.variable_scope('RNN'):
 92         #     for time_step in range(num_steps):
 93         #         if time_step > 0:
 94         #             tf.get_variable_scope().reuse_variables()
 95         #         cell_output, state = cell(inputs[:, time_step, :], state)
 96         #         outputs.append(cell_output)  # 長度爲35,元素的shape=(20, 300)
 97         # 把輸出隊列展開爲[batch, num_steps*hidden_size]的形狀,而後在reshape成[batch*num_steps, hidden_size]的形狀
 98         # 比直接按行拼接區別在於可以讓同一片斷的數據放在一塊兒, 也是爲了在計算loss時方便使用reshape獲得labels
 99         # output = tf.reshape(tf.concat(outputs, axis=1), [-1, HIDDEN_SIZE])  # shape=(700, 300)
100         # print('output', output)
101         # print('state', state)
102 
103         # 下面這樣自動計算的更牛
104         outputs, state = tf.nn.dynamic_rnn(cell, inputs, dtype=tf.float32)
105         output = tf.reshape(outputs, [-1, HIDDEN_SIZE])
106         print('output2', output)
107         print('state2', state)
108 
109         # Softmax層:將RNN在每一個位置上的輸出轉化爲各個單詞的logits.
110         if SHARE_EMB_AND_SOFTMAX:  # 共享參數,不會建立新的變量
111             weight = tf.transpose(embedding)
112         else:
113             weight = tf.get_variable('weight', [HIDDEN_SIZE, VOCAB_SIZE])
114         bias = tf.get_variable('bias', [VOCAB_SIZE])
115         logits = tf.matmul(output, weight) + bias  # output、weight的前後問題??
116 
117         # 定義交叉熵損失函數和平均損失
118         # loss = tf.losses.mean_squared_error(labels=tf.reshape(self.targets, [-1]), predictions=logits)
119         loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=tf.reshape(self.targets, [-1]), logits=logits)
120         self.cost = tf.reduce_sum(loss) / batch_size
121         self.final_state = state  # 保存了每次batch的狀態
122 
123         if not is_training:
124             return
125 
126         trainable_variables = tf.trainable_variables()
127         # print('train', trainable_variables)
128         # [<tf.Variable 'language_model/embedding:0' shape=(10000, 300) dtype=float32_ref>,
129         #  <tf.Variable 'language_model/RNN/multi_rnn_cell/cell_0/lstm_cell/kernel:0' shape=(600, 1200) dtype=float32_ref>,  每層有4個維度爲[2n, n]的參數矩陣,因此kernel.shape=[600, 1200]
130         #  <tf.Variable 'language_model/RNN/multi_rnn_cell/cell_0/lstm_cell/bias:0' shape=(1200,) dtype=float32_ref>,
131         #  <tf.Variable 'language_model/RNN/multi_rnn_cell/cell_1/lstm_cell/kernel:0' shape=(600, 1200) dtype=float32_ref>,
132         #  <tf.Variable 'language_model/RNN/multi_rnn_cell/cell_1/lstm_cell/bias:0' shape=(1200,) dtype=float32_ref>,
133         #  <tf.Variable 'language_model/bias:0' shape=(10000,) dtype=float32_ref>]
134         # 控制梯度大小,定義優化方法和訓練步驟
135         grads, _ = tf.clip_by_global_norm(tf.gradients(self.cost, trainable_variables), MAX_GRAD_NORM)
136         optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0)
137         self.train_op = optimizer.apply_gradients(zip(grads, trainable_variables))
138 
139 
140 def run_epoch(session, model, batches, train_op, output_log, step):
141     # train_op做爲一個專門的參數而不是使用model.train_op,是爲了區分訓練過程和測試/驗證過程
142 
143     # 計算平均perplexity的輔助變量
144     total_costs = 0.0
145     iters = 0
146     state = session.run(model.initial_state)
147     # 訓練一個epoch
148     for x, y in batches:
149         # 在當前batch上運行train_op並計算損失值。交叉熵損失函數計算的就是下一個單詞爲給定單詞的機率
150         cost, state, _ = session.run(
151             # 最須要注意的是state,每一個epoch都要從新提供初始化狀態。
152             [model.cost, model.final_state, train_op], feed_dict={model.input_data: x, model.targets: y, model.initial_state: state}  # feed_dict裏面的值不僅是placeholder!
153         )
154         total_costs += cost
155         iters += model.num_steps
156 
157         # 只有在訓練時輸出日誌
158         if output_log and step % 100 == 0:
159             print('After %d steps, perplexity is %.3f' % (step, np.exp(total_costs / iters)))
160 
161         step += 1
162 
163     return step, np.exp(total_costs / iters)
164 
165 
166 def main():
167     # 定義初始化函數
168     initializer = tf.random_normal_initializer(-0.05, 0.05)
169 
170     # 定義訓練用的循環神經網絡模型
171     with tf.variable_scope('language_model', reuse=None, initializer=initializer):
172         train_model = PTBModel(True, TRAIN_BATCH_SIZE, TRAIN_NUM_STEP)
173 
174     # 定義測試用的循環神經網絡模型,它與train_model共用參數,但沒有dropout
175     with tf.variable_scope('language_model', reuse=True, initializer=initializer):
176         eval_model = PTBModel(False, EVAL_BATCH_SIZE, EVAL_NUM_STEP)
177 
178     # 訓練模型
179     with tf.Session() as session:
180         tf.global_variables_initializer().run()
181         train_batches = make_batches(read_data(TRAIN_DATA), TRAIN_BATCH_SIZE, TRAIN_NUM_STEP)
182         eval_batches = make_batches(read_data(EVAL_DATA), EVAL_BATCH_SIZE, EVAL_NUM_STEP)
183         test_batches = make_batches(read_data(TEST_DATA), EVAL_BATCH_SIZE, EVAL_NUM_STEP)
184 
185         step = 0
186 
187         for i in range(NUM_EPOCH):
188             print('In iteration: %d' % (i+1))
189             step, train_pplx = run_epoch(session, train_model, train_batches, train_model.train_op, True, step)
190             print('Epoch: %d Train PerPlexity: %.3f' % (i+1, train_pplx))
191 
192             _, eval_pplx = run_epoch(session, eval_model, eval_batches, tf.no_op(), False, 0)
193             print('Epoch: %d Eval Perplexity: %.3f' % (i+1, eval_pplx))
194 
195             _, test_pplx = run_epoch(session, eval_model, test_batches, tf.no_op(), False, 0)
196             print('Test Perplexity: %.3f' % test_pplx)
197 
198 
199 if __name__ == '__main__':
200     main()

 

也就吃個飯的時間(一個多小時),TIME+都到478了,看來還真不是常規的時間。

top命令的TIME/TIME+是指的進程所使用的CPU時間,不是進程啓動到如今的時間,所以,若是一個進程使用的cpu不多,那即便這個進程已經存在N長時間,TIME/TIME+也是很小的數值。此外,若是你的系統有多個CPU,或者是多核CPU的話,那麼,進程佔用多個cpu的時間是累加的,上邊的示例,一個多小時對應478,就說明機器是8核的。

 

經過調整LSTM隱藏層的節點個數和大小以及訓練迭代的輪數還能夠將perplexity值降到最低。

很是多的天然語言處理應用的技術都是基於語言模型。

 

9.3 神經網絡機器翻譯

最基礎的機器翻譯算法——Seq2Seq模型。

9.3.1 機器翻譯背景與Seq2Seq模型介紹

Seq2Seq模型的基本思想:使用一個循環神經網絡讀取輸入句子,將整個句子的信息壓縮到一個固定維度的編碼中,再使用另外一個循環神經網絡讀取這個編碼,將其「解壓」爲目標語言的一個句子。這兩個循環神經網絡分別成爲編碼器(Encoder)和解碼器(Decoder),這個模型也稱爲encoder-decoder模型。

解碼器部分的結構與語言模型幾乎徹底相同:輸入爲單詞的詞向量,輸出爲softmax層產生的單詞機率,損失函數爲log perplexity。解碼器能夠理解爲一個以輸入編碼爲前提的語言模型。語言模型中使用的一些技巧,如共享softmax層和詞向量的參數,均可以直接應用到Seq2Seq模型的解碼器中。

編碼器分佈則更爲簡單。它與解碼器同樣擁有詞向量層和循環神經網絡,可是因爲編碼階段並未輸出,所以不須要softmax層。

在訓練過程當中,編碼器順序讀入每一個單詞的詞向量,而後將最終的隱藏狀態複製到解碼器做爲初始狀態解碼器的第一個輸入是一個特殊的<sos>字符(start-of-sentence),每一步預測的單詞是訓練數據的目標句子,預測序列的最後一個單詞是與語言模型相同的'<eos>'字符(end-of-sentence)

在機器翻譯應用中,真實應用場景中的測試步驟與語言模型的測試步驟有所不一樣。語言模型中測試的標準是給定目標句子上的perplexity。而機器翻譯的測試方法是,讓解碼器在沒有「正確答案」的狀況下自主生成一個翻譯句子,而後採用人工或自動的方法對翻譯句子的質量進行評測

在解碼過程當中,每一步預測的單詞中機率最大的單詞被選爲這一步的輸出,並複製到下一步的輸入中(圖9-3中用虛線表示)。這裏描述的是最簡單的貪心算法,在真實應用中廣泛採用集束搜索(Beam Search)方法來得到最好的翻譯效果。

 

9.3.2 機器翻譯文本數據的預處理

機器翻譯領域最重要的公開數據集是WMT數據集(Workshop on Statistical Machine Translation),下載地址:http://data.statmt.org/wmt17/translation-task/,該會議從2016年起改成Conference on Machine Translation。每一年,該會議就會組織一次機器翻譯領域的競賽,其提供的訓練和測試數據也成爲了機器翻譯領域論文的標準數據集。然而因爲WMT數據集較大,訓練時間較長,所以本節採用一個較小的IWLST TED演講數據集做爲示例,下載地址:https://wit3.fbk.eu/mt.php?release=2015-01

點擊'0.51'下載的就是了。

它的英文-中文訓練數據包含21萬個句子對,內容是TED演講的中英字幕。

對於平行語料的預處理,其步驟和9.2.1小節中關於PTB數據的預處理基本同樣。首先,須要統計語料中出現的單詞,爲每一個單詞分配一個ID,將詞彙表存入一個vocab文件,而後將文本轉化爲用單詞編號的形式來表示。

與前面不一樣的地方主要在於,下載的文本沒有通過預處理,尤爲沒有通過切詞。例如,因爲每一個英文單詞和標點符號之間緊密相連,致使不能像處理PTB數據那樣直接用空格對單詞進行切割。爲此須要用一些獨立的工具來進行切詞操做。

最經常使用的切詞工具是moses:(書上的地址不對)

1 git clone https://github.com/moses-smt/mosesdecoder.git

 

切詞操做:

 1 # 英文切詞
 2 error@error-F14CU27:~/moses/mosesdecoder/scripts/tokenizer$ perl ./tokenizer.perl -no-escape -l en < /home/error/en-zh/train.tags.en-zh.en > /home/error/codes/train.tags.en-zh.en
 3 Tokenizer Version 1.1
 4 Language: en
 5 Number of threads: 1
 6 
 7 # 中文切詞
 8 # 先把漢字和漢字切分開
 9 error@error-F14CU27:~/en-zh$ sed 's/ //g; s/\B/ /g' ./train.tags.en-zh.zh > /home/error/codes/train.tags.en-zh.zh
10 
11 # 切分漢字和標點符號
12 error@error-F14CU27:~/moses/mosesdecoder/scripts/tokenizer$ perl ./tokenizer.perl -no-escape -l en < /home/error/codes/train.tags.en-zh.zh > /home/error/codes/train.tags.en-zh.zh2
13 Tokenizer Version 1.1
14 Language: en
15 Number of threads: 1

 

$ cat train.tags.en-zh.en | grep -v '<url>'| grep -v '<keywords>' | grep -v '<speaker>' | grep -v '<talkid>' | grep -v '<translator' | grep -v '<reviewer' > train.en

 

yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/ //g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/\B/ /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/,/ , /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/。 / 。/g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/!/ !/g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/!/ !/g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/,/ , /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/\./ \. /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/?/ ? /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/?/ ? /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/《/ 《 /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/》/ 》 /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/"/ " /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/— —/ —— /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/•/ • /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/、/ 、 /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/·/ · /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/:/ : /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/…/ … /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/【/ 【 /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/】/ 】 /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/(/ ( /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/)/ ) /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/-/ - /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/- -/--/g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/- -/--/g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/— —/ —— /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/- -/ - - /g' train.zh.bk yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/- -/--/g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/-/ - /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/- -/--/g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/(/ ( /g' train.zh.bk
yangxl@yangxl-Lenovo-ideapad-330C-15IKB:~/files/trans$ sed -i 's/)/ ) /g' train.zh.bk

 

完成切詞後,再使用以前處理PTB數據的方法,分別生成英文文本和中文文本詞彙文件,並將文本轉化爲單詞編號。生成詞彙文件時,須要注意將<sos>、<eos>、<unk>這3個詞手動加入到詞彙表中,而且要限制詞匯表大小,將詞頻太低的次替換爲<unk>。假定英文詞彙表大小爲10000,中文詞彙表大小爲4000。

在PTB 數據中,因爲句子之間有上下文關聯,所以能夠直接將連續的句子鏈接起來稱爲一個大的段落。而在機器翻譯的訓練樣本中,每一個句子對一般是做爲獨立的數據來訓練的。因爲每一個句子的長短不一致,所以在將這些句子放入同一個batch 時,須要將較短的句子補齊到與同batch內最長句子相同的長度。用於填充長度而填入的位置叫做填充(padding)。在TensorFlow中,tf.data.Dataset.padded_ batch函數提供了這一功能。

循環神經網絡在讀取數據時會將填充位置的內容與其餘內容同樣歸入計算,所以爲了避免讓填充影響訓練,有兩方面須要注意:
第一,循環神經網絡在讀取填充時,應當跳過這一位置的計算。以編碼器爲例,若是編碼器在讀取填充時,像正常輸入同樣處理填充輸入,那麼在讀取「B1B200」以後產生的最後一位隱藏狀態就和讀取「B1B2」以後的隱藏狀態不一樣,會產生錯誤的結果。
TensorFlow提供了tf.nn.dynamic_rnn方法來實現這一功能。dynamic_rnn對每個batch的數據讀取兩個輸入:輸入數據的內容(維度爲[batch_size, time])和輸入數據的長度(維度爲[time])。對於輸入batch裏的每一條數據,在讀取了相應長度的內容後,dynamic_rnn就跳事後面的輸入,直接把前一步的計算結果複製到後面的時刻。這樣能夠保證padding是否存在不影響模型效果。

另外值得注意的是,使用dyanmic_rnn時每一個batch的最大序列長度不須要相同。例如,在上面的例子中,第一個batch的維度是2×4,而第二個batch的維度是2×7。在訓練中dynamic_rnn會根據每一個batch的最大長度動態展開到須要的層數,這就是它被稱爲「dynamic」的緣由。

第二,在設計損失函數時須要特別將填充位置的損失的權重設置爲0,這樣在填充位置產生的預測不會影響梯度的計算。

下面的代碼使用tf.data.Dataset.padded_batch來進行填充和batching,並記錄每一個句子的序列長度以用做dynamic_rnn的輸入。與前面PTB的例子不一樣,這裏沒有將全部數據讀入內存,而是使用Dataset從磁盤動態讀取數據。

數據處理的示例見完整代碼的前面部分。

 

sequence_mask方法:

 1 g = tf.sequence_mask([8, 9, 7, 5, 8], maxlen=10, dtype=tf.float32)
 2 
 3 with tf.Session() as sess:
 4     print(sess.run(g))
 5 
 6 # 結果:
 7 # [[1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]
 8 #  [1. 1. 1. 1. 1. 1. 1. 1. 1. 0.]
 9 #  [1. 1. 1. 1. 1. 1. 1. 0. 0. 0.]
10 #  [1. 1. 1. 1. 1. 0. 0. 0. 0. 0.]
11 #  [1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]]

 

seq2seq模型完整實例:

  1 #!coding:utf8
  2 
  3 import tensorflow as tf
  4 import time
  5 
  6 MAX_LEN = 50  # 限定句子的最大單詞數量
  7 SOS_ID = 1  # 目標語言詞彙表中<sos>的ID
  8 
  9 SRC_TRAIN_DATA = 'D:\\files\\tf\\train.txt.en.num'
 10 TRG_TRAIN_DATA = 'D:\\files\\tf\\train.txt.zh2.num'
 11 CHECKPOINT_PATH = 'D:\\files\\tf\\seq222seq_ckpt'
 12 HIDDEN_SIZE = 1024  # 隱藏層大小
 13 NUM_LAYERS = 2  # 層數
 14 SRC_VOCAB_SIZE = 10000  # 源語言詞彙表大小
 15 TRG_VOCAB_SIZE = 4000  # 目標語言詞彙表大小
 16 BATCH_SIZE = 100
 17 NUM_EPOCH = 1
 18 KEEP_PROB = 0.8  # 節點不被dropout的機率
 19 MAX_GRAD_NORM = 5  # 用於控制梯度膨脹的梯度大小上限
 20 SHARE_EMB_AND_SOFTMAX = True
 21 
 22 
 23 # 使用Dataset從一個文件中讀取一個語言的數據。
 24 # 數據格式爲每行一句話,單詞已經轉化爲單詞編號
 25 # 行數據,當前行單詞個數
 26 def MakeDataset(file_path):
 27     dataset = tf.data.TextLineDataset(file_path)  # 取出來的是一行,bytes類型
 28     # b'95 13 1590 0 4 11 90 4870 0 4 2'
 29 
 30     # 根據空格將單詞編號切分開並放入一個一維向量
 31     dataset = dataset.map(lambda string: tf.string_split([string]).values)  # '[]'不能丟
 32     # [b'95' b'13' b'1590' b'0' b'4' b'11' b'90' b'4870' b'0' b'4' b'2']
 33 
 34     # 將字符串形式的單詞編號轉化爲整數
 35     dataset = dataset.map(lambda string: tf.string_to_number(string, tf.int32))
 36     # [  95   13 1590    0    4   11   90 4870    0    4    2]
 37 
 38     # 統計每一個句子的單詞數量,並與句子內容一塊兒放入Dataset中
 39     dataset = dataset.map(lambda x: (x, tf.size(x)))  # ([  95   13 1590    0    4   11   90 4870    0    4    2], 11)
 40     return dataset
 41 
 42 
 43 # 從源語言文件src_path和目標語言文件trg_path中分別讀取數據,並進行填充和batching操做
 44 def MakeSrcTrgDataset(src_path, trg_path, batch_size):
 45     # 首先分別讀取源語言數據和目標語言數據
 46     src_data = MakeDataset(src_path)  # ([  95   13 1590    0    4   11   90 4870    0    4    2], 11)
 47     trg_data = MakeDataset(trg_path)  # ([  40, 5545,  610,  118,   10,  261,    7,  171, 4827,  507,    4, 5,    7,   40, 5545,  610,    6,    2], 18)
 48 
 49     # 經過zip操做將兩個Dataset合併爲一個Dataset。
 50     dataset = tf.data.Dataset.zip((src_data, trg_data))
 51 
 52     # 刪除內容爲空的句子和長度過長的句子
 53     def FilterLength(src_tuple, trg_tuple):
 54         ((src_input, src_len), (trg_label, trg_len)) = (src_tuple, trg_tuple)
 55         src_len_ok = tf.logical_and(tf.greater(src_len, 1), tf.less_equal(src_len, MAX_LEN))  # 句子長度大於1且不大於50
 56         trg_len_ok = tf.logical_and(tf.greater(trg_len, 1), tf.less_equal(trg_len, MAX_LEN))
 57         return tf.logical_and(src_len_ok, trg_len_ok)
 58     dataset = dataset.filter(FilterLength)  # 做爲過濾器的函數必須可以返回布爾值。
 59 
 60     # 解碼器須要兩種格式的目標句子。
 61         # 1. 解碼器的輸入(trg_input), 形式如同'<sos> X Y Z'
 62         # 2. 解碼器的目標輸出(trg_label), 形式如同'X Y Z <eos>'
 63         # 上面從目標文件中讀到的目標句子是'X Y Z <eos>'的形式,須要從中生成'<sos> X Y Z'形式並加入到dataset中。
 64     def MakeTrgInput(src_tuple, trg_tuple):
 65         ((src_input, src_len), (trg_label, trg_len)) = (src_tuple, trg_tuple)  # [  40, 5545,  610,  118,   10,  261,    7,  171, 4827,  507,    4, 5,    7,   40, 5545,  610,    6,    2]
 66         trg_input = tf.concat([[SOS_ID], trg_label[:-1]], axis=0)
 67         return ((src_input, src_len), (trg_input, trg_label, trg_len))
 68     dataset = dataset.map(MakeTrgInput)
 69 
 70     # 隨機打亂循環數據
 71     dataset = dataset.shuffle(10000)
 72 
 73     # 規定填充後輸出的數據維度
 74     padded_shapes = (
 75         (tf.TensorShape([None]),  # 源句子是長度未知的向量
 76          tf.TensorShape([])),  # 源句子長度是單個數字
 77 
 78         (tf.TensorShape([None]),  # 目標句子(解碼器輸入)是長度未知的向量
 79          tf.TensorShape([None]),  # 目標句子(解碼器目標輸出)是長度未知的向量
 80          tf.TensorShape([]))  # 目標句子長度是單個數字
 81     )
 82 
 83     # batching操做,
 84     # Defaults are `0` for numeric types and the empty string for string types.
 85     batched_dataset = dataset.padded_batch(batch_size, padded_shapes)  # src.shape=(batch_size, max_length)
 86     return batched_dataset
 87 
 88 
 89 # 定義模型
 90 class NMTModel(object):
 91     def __init__(self):
 92         self.enc_cell = tf.nn.rnn_cell.MultiRNNCell(
 93             [tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE) for _ in range(NUM_LAYERS)]
 94         )
 95         self.dec_cell = tf.nn.rnn_cell.MultiRNNCell(
 96             [tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE) for _ in range(NUM_LAYERS)]
 97         )
 98 
 99         # 爲源語言和目標語言分別定義詞向量
100         # 注意VOCAB_SIZE是詞彙表大小,不是文本大小。
101         # 初始化的變量取什麼值都無所謂吧
102         self.src_embedding = tf.get_variable('src_emb', [SRC_VOCAB_SIZE, HIDDEN_SIZE])
103         self.trg_embedding = tf.get_variable('trg_emb', [TRG_VOCAB_SIZE, HIDDEN_SIZE])
104 
105         # softmax層的變量都是trg的,由於是輸出層進行分類
106         if SHARE_EMB_AND_SOFTMAX:
107             self.softmax_weight = tf.transpose(self.trg_embedding)
108         else:
109             self.softmax_weight = tf.get_variable('weight', [HIDDEN_SIZE, TRG_VOCAB_SIZE])
110         self.softmax_bias = tf.get_variable('softmax_bias', [TRG_VOCAB_SIZE])
111 
112     def forward(self, src_input, src_size, trg_input, trg_label, trg_size):
113         # src_input.shape=(5, 17) src_size=[10  9 13 11 17]  # (5, 17)是由於沒有去掉文件頭部幾行,看了不下一遍了。
114         # trg_label.shape=(5, 30) trg_size=[30 21 26 18 21]
115         batch_size = tf.shape(src_input)[0]
116 
117         src_emb = tf.nn.embedding_lookup(self.src_embedding, src_input)  # (5, 17, 1024)
118         trg_emb = tf.nn.embedding_lookup(self.trg_embedding, trg_input)  # (5, 30, 1024)  # 解碼器的輸入trg_input
119 
120         # 在詞向量上進行dropout,dropout不會改變shape。
121         src_emb = tf.nn.dropout(src_emb, KEEP_PROB)  # (5, 17, 1024)
122         trg_emb = tf.nn.dropout(trg_emb, KEEP_PROB)  # (5, 30, 1024)
123 
124         # 使用dynamic_rnn構造編碼器
125         # 不改變shape
126         with tf.variable_scope('encoder'):
127             # 由於編碼器是一個雙層LSTM結構,所以enc_state是一個包含兩個LSTMStateTuple類的tuple,每一個類對應編碼器中一層的狀態。
128                 # state_c和state_h的shape都爲[batch_size, HIDDEN_SIZE],即(5, 1024)
129             # enc_outputs是頂層LSTM在每一步的輸出,維度爲[batch_size, max_time, HIDDEN_SIZE],同輸入(5, 17, 1024)
130                 # 後兩個參數的做用??
131             enc_outputs, enc_state = tf.nn.dynamic_rnn(
132                 self.enc_cell, src_emb, src_size, dtype=tf.float32
133             )
134 
135         # 構造解碼器
136         with tf.variable_scope('decoder'):
137             # dec_outputs.shape=(5, 30, 1024), 同輸入
138             # 輸出隱藏狀態,4個都是(5, 1024)
139                 # 解碼器的初始狀態爲編碼器的輸出狀態
140             dec_outputs, _ = tf.nn.dynamic_rnn(
141                 self.dec_cell, trg_emb, trg_size, initial_state=enc_state  # 編碼器的最終狀態,做爲解碼器的初始狀態。
142             )
143 
144         # 計算解碼器每一步的log perplexity
145         output = tf.reshape(dec_outputs, [-1, HIDDEN_SIZE])  # shape=(150, 1024)
146 
147         # softmax層輸出
148         logits = tf.matmul(output, self.softmax_weight) + self.softmax_bias  # shape=(150, 4000)
149         loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=tf.reshape(trg_label, [-1]), logits=logits)  # shape=(150,)  一個batch(150個單詞編號)的總損失。
150 
151         # 在計算平均損失時,須要將填充位置的權重設置爲0,以免無效位置的預測干擾模型的訓練
152         label_weight = tf.sequence_mask(
153             trg_size, maxlen=tf.shape(trg_label)[1], dtype=tf.float32
154         )  # 假如trg_size=[30 21 26 18 21], maxlen=30, 那麼結果的第二個元素爲[21個1.0,(30-21)個0.0]
155 
156         label_weight = tf.reshape(label_weight, [-1])  # shape=(150,)
157 
158         cost = tf.reduce_sum(loss * label_weight)  # 一個batch的損失,1035.8269  # 把填充位置的損失過濾掉了
159         cost_per_token = cost / tf.reduce_sum(label_weight)  # 一個單詞編號對應的損失,8.704428  # 也是把填充位置排除了
160 
161         # 定義反向傳播
162         trainable_variables = tf.trainable_variables()
163 
164         # 控制梯度大小,定義優化方法和訓練步驟
165         grads = tf.gradients(cost / tf.to_float(batch_size), trainable_variables)  # 習慣上,一個batch有batch_size個數據,除以batch_size表示一個`數據`(一個句子)的損失
166         grads, _ = tf.clip_by_global_norm(grads, MAX_GRAD_NORM)
167         optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0)
168         train_op = optimizer.apply_gradients(zip(grads, trainable_variables))
169         return cost_per_token, train_op
170         # return src_input, src_size, src_emb, dropout_src_emb, enc_outputs, trg_input, trg_label, trg_size, trg_emb, dropout_trg_emb, dec_outputs, output, logits, loss, cost_per_token, train_op
171 
172 
173 def run_epoch(sess, cost_op, train_op, saver, step):
174     # 重複訓練步驟直至遍歷完dataset中的全部數據
175     # 訓練以前不知道須要訓練多少輪
176     while True:
177         try:
178             cost, _ = sess.run([cost_op, train_op])
179             if step % 10 == 0:
180                 print('After %s steps, per token cost is %.3f' % (step, cost))
181             # 每200步保存一個checkpoint
182             if step % 200 == 0:
183                 saver.save(sess, CHECKPOINT_PATH, global_step=step)
184             step += 1
185         except tf.errors.OutOfRangeError:
186             break
187     return step
188 
189 
190 def main():
191     initializer = tf.random_uniform_initializer(-0.05, 0.05)
192 
193     # 定義訓練用的循環神經網絡模型
194     with tf.variable_scope('nmt_model', reuse=None, initializer=initializer):
195         train_model = NMTModel()
196 
197     # 定義輸入數據
198     data = MakeSrcTrgDataset(SRC_TRAIN_DATA, TRG_TRAIN_DATA, BATCH_SIZE)
199     iter = data.make_initializable_iterator()
200     (src, src_size), (trg_input, trg_label, trg_size) = iter.get_next()  # trg_input的shape=[batch_size, max_length]
201 
202     cost_op, train_op = train_model.forward(src, src_size, trg_input, trg_label, trg_size)
203     # src_input, src_size, src_emb, dropout_src_emb, enc_outputs, trg_input, trg_label, trg_size, trg_emb, dropout_trg_emb, dec_outputs, output, logits, loss, cost_per_token, train_op = train_model.forward(None, src, src_size, trg_input, trg_label, trg_size)
204 
205     # 訓練模型
206     saver = tf.train.Saver()
207     step = 0
208     with tf.Session() as sess:
209         tf.global_variables_initializer().run()
210 
211         # src_input, src_size, src_emb, dropout_src_emb, enc_outputs, trg_input, trg_label, trg_size, trg_emb, dropout_trg_emb, dec_outputs, output, logits, loss, cost_per_token, train_op = sess.run([src_input, src_size, src_emb, dropout_src_emb, enc_outputs, trg_input, trg_label, trg_size, trg_emb, dropout_trg_emb, dec_outputs, output, logits, loss, cost_per_token, train_op])
212         # print(src_input.shape, src_size, src_emb.shape, dropout_src_emb.shape, enc_outputs.shape, trg_input.shape, trg_label.shape, trg_size, trg_emb.shape, dropout_trg_emb.shape, dec_outputs.shape, output.shape, logits.shape, loss.shape, cost_per_token, train_op)
213         # return
214 
215         for i in range(NUM_EPOCH):
216             print('In iterator: %d' % (i+1))
217             # iterator爲什麼放在循環中,遍歷第二遍時還要從新初始化??
218             sess.run(iter.initializer)  # 取值以前必定不要忘記迭代器初始化
219             step = run_epoch(sess, cost_op, train_op, saver, step)
220             print('In interator %d is end, step is %d' % (i+1, step))
221 
222 if __name__ == '__main__':
223     print(int(time.time()))
224     main()
225     print(int(time.time()))

在解碼的程序中,解碼器的實現與訓練時有很大不一樣。由於訓練時解碼器能夠從輸入中讀取完整的目標訓練句子,所以能夠用dynamic_rnn簡單地展開成前饋網絡。而在解碼過程當中,模型只能看到輸入句子,卻不能看到目標句子。解碼器的第一步讀取<sos>符,預測目標句子的第一個單詞,而後須要將這個預測的單詞複製到第二步做爲輸入,再預測第二個單詞,知道預測的單詞爲<eos>爲止。這個過程須要使用一個循環結構來實現。tf.while_loop。

翻譯過程:

  1 #!coding:utf8
  2 
  3 import tensorflow as tf
  4 
  5 CHECKPOINT_PATH = '/home/yangxl/codes/seq2seq_ckpt-400'
  6 
  7 # 必需要與訓練模型的參數一致
  8 HIDDEN_SIZE = 1024  # 隱藏層大小
  9 NUM_LAYERS = 2  # 層數
 10 SRC_VOCAB_SIZE = 10000  # 源語言詞彙表大小
 11 TRG_VOCAB_SIZE = 10000  # 目標語言詞彙表大小
 12 SHARE_EMB_AND_SOFTMAX = True
 13 
 14 SOS_ID = 1
 15 EOS_ID = 2
 16 
 17 class NMTModel(object):
 18     def __init__(self):
 19         # 與訓練模型中的__init__函數相同。
 20         self.enc_cell = tf.nn.rnn_cell.MultiRNNCell(
 21             [tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE) for _ in range(NUM_LAYERS)]
 22         )
 23         self.dec_cell = tf.nn.rnn_cell.MultiRNNCell(
 24             [tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE) for _ in range(NUM_LAYERS)]
 25         )
 26 
 27         # 爲源語言和目標語言分別定義詞向量
 28         self.src_embedding = tf.get_variable('src_emb', [SRC_VOCAB_SIZE, HIDDEN_SIZE])
 29         self.trg_embedding = tf.get_variable('trg_emb', [TRG_VOCAB_SIZE, HIDDEN_SIZE])
 30 
 31         # softmax層的變量
 32         if SHARE_EMB_AND_SOFTMAX:
 33             self.softmax_weight = tf.transpose(self.trg_embedding)
 34         else:
 35             self.softmax_weight = tf.get_variable('weight', [HIDDEN_SIZE, TRG_VOCAB_SIZE])
 36         self.softmax_bias = tf.get_variable('softmax_bias', [TRG_VOCAB_SIZE])
 37 
 38     def inference(self, src_input):
 39         # dynamic_rnn要求輸入是batch形式,所以這裏須要把輸入整理爲大小爲1的batch
 40         src_size = tf.convert_to_tensor([len(src_input)], dtype=tf.int32)  # 加上`[]`使其batch爲1  # shape=(1,)
 41         src_input = tf.convert_to_tensor([src_input], dtype=tf.int32)      # 這也要加, 與上面對應,shape=(1, 6)
 42         src_emb = tf.nn.embedding_lookup(self.src_embedding, src_input)  # (1, 6, 1024)
 43 
 44         # 構造編碼器,與訓練時一致
 45         with tf.variable_scope('encoder'):
 46             # 輸入數據的rank至少爲3(batch_size, max_time, ...)
 47             # enc_output.shape=(1, 6, 1024)
 48             enc_output, enc_state = tf.nn.dynamic_rnn(
 49                 self.enc_cell, src_emb, src_size, dtype=tf.float32
 50             )
 51 
 52         # 設置解碼的最大步數,以免極端狀況下出現無限循環問題。
 53         MAX_DEC_LEN = 100
 54 
 55         with tf.variable_scope('decoder/rnn/multi_rnn_cell'):  # 這樣寫是爲了與加載文件中的一致
 56             # 使用一個變長的tensorarray來存儲生成的句子
 57             init_array = tf.TensorArray(dtype=tf.int32, size=0, dynamic_size=True, clear_after_read=False)
 58             # 填入第一個單詞<sos>做爲解碼器的輸入
 59             # init_array.read()
 60             # init_array.write()
 61             # init_array.stack()
 62             init_array = init_array.write(0, SOS_ID)
 63             # 構建初始的循環狀態,循環狀態包含循環神經網絡的隱藏狀態,保存生成句子的tensorarry,以及記錄解碼步數step
 64             init_loop_var = (enc_state, init_array, 0)
 65 
 66             # tf.while_loop的循環條件: 直到解碼器輸出<sos>,或者達到最大步數
 67             def continue_loop_condition(state, trg_ids, step):
 68                 # trg_ids.read(step), 即init_array.read(step)
 69                 # tf.reduce_all
 70                 return tf.reduce_all(tf.logical_and(  # 去掉reduce_all也沒問題
 71                     tf.not_equal(trg_ids.read(step), EOS_ID),
 72                     tf.less(step, MAX_DEC_LEN-1)
 73                 ))
 74 
 75             def loop_body(state, trg_ids, step):
 76                 # 讀取最後一步輸出的單詞,並讀取其詞向量
 77                 trg_input = [trg_ids.read(step)]  # 加一箇中括號  # (1,1)
 78                 trg_emb = tf.nn.embedding_lookup(self.trg_embedding, trg_input)  # (1, 1, 1024)
 79                 # 這裏不使用dynamic_rnn,而是直接調用dec_cell向前計算一步
 80                 dec_outputs, next_state = self.dec_cell.call(state=state, inputs=trg_emb)  # (1, 1, 1024)
 81                 # 計算每一個可能的輸出單詞對應的logits
 82                 output = tf.reshape(dec_outputs, [-1, HIDDEN_SIZE])  # (1, 1024)
 83                 logits = tf.matmul(output, self.softmax_weight) + self.softmax_bias  # (1, 4000)
 84                 # 選取logit值最大的單詞做爲這一步的輸出
 85                 next_id = tf.argmax(logits, axis=1, output_type=tf.int32)
 86                 # 將這一步輸出的單詞寫入循環狀態的trg_ids中
 87                 trg_ids = trg_ids.write(step+1, next_id[0])  # 寫入,而後下一步輸出
 88                 return next_state, trg_ids, step+1
 89 
 90             # 執行循環tf.while_loop,返回最終狀態
 91             state, trg_ids, step = tf.while_loop(continue_loop_condition, loop_body, init_loop_var)
 92             return trg_ids.stack()
 93 
 94 
 95 def main(x):
 96     with tf.variable_scope('nmt_model', reuse=None):
 97         model = NMTModel()
 98 
 99     test_sentence = [90, 13, 9, 689, 4, 2]
100     output_op = model.inference(test_sentence)
101 
102     saver = tf.train.Saver()
103     with tf.Session() as sess:
104 
105         saver.restore(sess, CHECKPOINT_PATH)
106 
107         # 讀取翻譯結果
108         output = sess.run(output_op)
109         print('result: ', output)
110 
111 if __name__ == '__main__':
112     tf.app.run()

API:

tf.string_split()、tf.string_join()

 1 strs = tf.string_split([b'95 13 1590 0 4 11 90 4870 0 4 2'])
 2 SparseTensor(indices=Tensor("StringSplit:0", shape=(?, 2), dtype=int64), values=Tensor("StringSplit:1", shape=(?,), dtype=string), dense_shape=Tensor("StringSplit:2", shape=(2,), dtype=int64))  # 有3個屬性:indices、values、dense_shape
 3 
 4 with tf.Session() as sess:
 5     print(sess.run(strs))
 6 SparseTensorValue(indices=array([[ 0,  0],
 7        [ 0,  1],
 8        [ 0,  2],
 9        [ 0,  3],
10        [ 0,  4],
11        [ 0,  5],
12        [ 0,  6],
13        [ 0,  7],
14        [ 0,  8],
15        [ 0,  9],
16        [ 0, 10]], dtype=int64), values=array([b'95', b'13', b'1590', b'0', b'4', b'11', b'90', b'4870', b'0',
17        b'4', b'2'], dtype=object), dense_shape=array([ 1, 11], dtype=int64))

 

驗證是否改變shape:embedding改變、dropout、dynastic_rnn不變。具體代碼看訓練過程。

相關文章
相關標籤/搜索