基于tensorflow的RNN自然语言建模
關于循環神經網絡的介紹可以參考:
https://blog.csdn.net/lilong117194/article/details/82958326
這里強烈建議先搞明白上述參考鏈接中的一些基本概念和例子,再讀下面的代碼。
PTB 數據集下載:http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz
1. 自然語言建模
簡單的說,自然語言建模的目的就是為了計算一個句子出現的概率。在這里把句子看做是單詞的序列,于是自然語言模型需要計算的就是p(w1,w2,..wn)p(w_1,w_2,..w_n)p(w1?,w2?,..wn?)。然后利用語言模型,可以確定哪個單詞序列出現的可能性更大,或者給定若干個單詞,可以預測下一個最可能出現的詞語。
舉個音字轉換的例子:假設輸入的拼音字符串為“xianzaiquna”,它的輸出可以是“西安在去哪”,同樣也可以是“現在去哪”。根據語言常識可以知道,應該轉換為后者的概率更大,而這時的語言模型就可以得到后者的概率大于前者,這就是語言模型的作用。
那么如何計算一個句子的概率呢?首先一個句子可以被看做單詞組成的序列:
s=(w1,w2,w3...wm)s = (w_1,w_2,w_3 ... w_m)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,...,wm?1)p(s) = p(w_1,w_2,w_3 ... w_m) = p(w_1)p(w_2|w_1)p(w_3|w_1,w_2) ... p(w_m|w_1,w_2, ... ,w_{m-1})p(s)=p(w1?,w2?,w3?...wm?)=p(w1?)p(w2?∣w1?)p(w3?∣w1?,w2?)...p(wm?∣w1?,w2?,...,wm?1?)
要計算句子出現的概率,就得知道上面公式中等式右邊每一項的取值。但是任何一門語言的詞匯量都是極大的,這樣我們無法根據這個公式計算概率,因為計算復雜度無可估量。于是我們采用估算的方法,常用的是:n-gram方法、決策樹、最大熵模型、條件隨機場、神經網絡語言模型。這里只介紹 n-gram 模型和循環神經網絡的方法。
1.1 n-gram 模型的及其評價標準
n-gram 模型基于一個重要的有限歷史假設:當前單詞出現的概率僅僅取決于它的前 n-1 個單詞,即p(wm∣w1,w2,...,wm?1)≈p(wi∣wi?n+1,...,wi?1)p(w_m|w_1,w_2, ... ,w_{m-1})\approx p(w_i|w_{i-n+1}, ... ,w_{i-1})p(wm?∣w1?,w2?,...,wm?1?)≈p(wi?∣wi?n+1?,...,wi?1?)因此上面的P(S)可以近似為p(S)=p(w1,w2,w3...wm)=∏imp(wi∣wi?n+1,...,wi?1)p(S) = p(w_1,w_2,w_3 ... w_m) =\prod _i ^m p(w_i|w_{i-n+1}, ... ,w_{i-1})p(S)=p(w1?,w2?,w3?...wm?)=∏im?p(wi?∣wi?n+1?,...,wi?1?),而這里通常 n 指的是當前單詞依賴它前面的單詞個數。通常可以取 1,2,3 對應的模型分別稱為 unigram,bigram和 trigram 模型。所以n—gram模型需要估計的參數為條件概率:p(wi∣wi?n+1,...,wi?1)p(w_i|w_{i-n+1}, ... ,w_{i-1})p(wi?∣wi?n+1?,...,wi?1?),所以有:
- 在1-gram模型下:
P(w1,w2,w3,…,wn)=P(w1)P(w2∣w1)P(w3∣w1w2)P(w4∣w1w2w3)…P(wn∣w1w2…wn?1)≈P(w1)P(w2)P(w3)P(w4)…P(wn)P(w_1, w_2, w_3, … , w_n)=P(w_1)P(w_2|w_1)P(w_3|w_1w_2)P(w_4|w_1w_2w_3)…P(w_n|w_1w_2…w_{n-1})≈P(w_1)P(w_2)P(w_3)P(w_4)…P(w_n)P(w1?,w2?,w3?,…,wn?)=P(w1?)P(w2?∣w1?)P(w3?∣w1?w2?)P(w4?∣w1?w2?w3?)…P(wn?∣w1?w2?…wn?1?)≈P(w1?)P(w2?)P(w3?)P(w4?)…P(wn?) - 在2-gram模型下:
P(w1,w2,w3,…,wn)=P(w1)P(w2∣w1)P(w3∣w1w2)P(w4∣w1w2w3)…P(wn∣w1w2…wn?1)≈P(w1)P(w2∣w1)P(w3∣w2)P(w4∣w3)…P(wn∣wn?1)P(w_1, w_2, w_3, … , w_n)=P(w_1)P(w_2|w_1)P(w_3|w_1w_2)P(w_4|w_1w_2w_3)…P(w_n|w_1w_2…w_{n-1})≈P(w_1)P(w_2|w_1)P(w_3|w_2)P(w_4|w_3)…P(w_n|w_{n-1})P(w1?,w2?,w3?,…,wn?)=P(w1?)P(w2?∣w1?)P(w3?∣w1?w2?)P(w4?∣w1?w2?w3?)…P(wn?∣w1?w2?…wn?1?)≈P(w1?)P(w2?∣w1?)P(w3?∣w2?)P(w4?∣w3?)…P(wn?∣wn?1?) - 在3-gram模型下:
P(w1,w2,w3,…,wn)=P(w1)P(w2∣w1)P(w3∣w1w2)P(w4∣w1w2w3)…P(wn∣w1w2…wn?1)≈P(w1)P(w2∣w1)P(w3∣w1w2)P(w4∣w2w3)…P(wn∣wn?2wn?1)P(w_1, w_2, w_3, … , w_n)=P(w_1)P(w_2|w_1)P(w_3|w_1w_2)P(w_4|w_1w_2w_3)…P(w_n|w_1w_2…w_{n-1}) ≈P(w_1)P(w_2|w_1)P(w_3|w_1w_2)P(w_4|w_2w_3)…P(w_n|w_{n-2}w_{n-1}) P(w1?,w2?,w3?,…,wn?)=P(w1?)P(w2?∣w1?)P(w3?∣w1?w2?)P(w4?∣w1?w2?w3?)…P(wn?∣w1?w2?…wn?1?)≈P(w1?)P(w2?∣w1?)P(w3?∣w1?w2?)P(w4?∣w2?w3?)…P(wn?∣wn?2?wn?1?) - 在4-gram模型下:
P(w1,w2,w3,…,wn)=P(w1)P(w2∣w1)P(w3∣w1w2)P(w4∣w1w2w3)…P(wn∣w1w2…wn?1)≈P(w1)P(w2∣w1)P(w3∣w1w2)P(w4∣w1w2w3)…P(wn∣wn?3wn?2wn?1)P(w_1, w_2, w_3, … , w_n)=P(w_1)P(w_2|w_1)P(w_3|w_1w_2)P(w_4|w_1w_2w_3)…P(w_n|w_1w_2…w_{n-1})≈P(w_1)P(w_2|w_1)P(w_3|w_1w_2)P(w_4|w_1w_2w_3)…P(w_n|w_{n-3}w_{n-2}w_{n-1})P(w1?,w2?,w3?,…,wn?)=P(w1?)P(w2?∣w1?)P(w3?∣w1?w2?)P(w4?∣w1?w2?w3?)…P(wn?∣w1?w2?…wn?1?)≈P(w1?)P(w2?∣w1?)P(w3?∣w1?w2?)P(w4?∣w1?w2?w3?)…P(wn?∣wn?3?wn?2?wn?1?)
假設某種語言的單詞表大小為 k,那么我們可以計算出 n-gram 模型需要估計的不同參數數量為k的n次方(慶幸不是k的k次方)。高于四元的用的很少,因為訓練它需要更龐大的語料,而且數據稀疏嚴重,時間復雜度高,精確度卻提高的不多。而n-gram模型的參數估計一般采用最大似然估計(maximun likelihood estimation,MLE)的計算方法:
p(wi∣wi?n+1,...,wi?1)=C(wi,wi?n+1,...,wi?1)C(wi?n+1,...,wi?1)p(w_i|w_{i-n+1}, ... ,w_{i-1})=\frac{C(w_i,w_{i-n+1}, ... ,w_{i-1})}{C(w_{i-n+1}, ... ,w_{i-1})}p(wi?∣wi?n+1?,...,wi?1?)=C(wi?n+1?,...,wi?1?)C(wi?,wi?n+1?,...,wi?1?)?
其中C(X)C(X)C(X)表示單詞序列X在訓練語料中出現的次數。也就是說,訓練語料越大,參數估計的結果越可靠。但是還有一個問題存在,那就是即使訓練的語料再大,也會有在訓練中沒有出現過的n-gram 序列出現,這就會導致很多參數為0,為了避免乘以0而導致整個概率為0,使用最大似然估計方法時都需要加入平滑避免參數取值為 0 。
語言模型的好壞常用的評價指標是復雜度 perplexity 。perplexity 值刻畫的就是通過某一個語言模型估計的一句話出現的概率。比如當已知(w1,w2,w3,…,Wm)這句話出現在語料庫之中,那么通過語言模型計算得到這句話的概率就越高越好,也就是 perplexity 值越小越好。令 perplexity 為:
mperplexity(S)=p(w1,w2,w3...wm)?1m=1p(w1,w2,w3...wm)m=1∏i=1mp(wi∣w1,w2...,wi?1)m=1∏i=1mp(wi∣wi?n+1,...,wi?1)mmperplexity(S)= p(w_1,w_2,w_3 ... w_m)^{-\frac{1}{m}}=\sqrt[m]{\frac{1}{p(w_1,w_2,w_3 ... w_m)}}=\sqrt[m]{\frac{1}{\prod_{i=1}^mp(w_i|w_1,w_2 ... ,w_{i-1})}}=\sqrt[m]{\frac{1}{\prod_{i=1}^mp(w_i|w_{i-n+1}, ... ,w_{i-1})}}mperplexity(S)=p(w1?,w2?,w3?...wm?)?m1?=mp(w1?,w2?,w3?...wm?)1??=m∏i=1m?p(wi?∣w1?,w2?...,wi?1?)1??=m∏i=1m?p(wi?∣wi?n+1?,...,wi?1?)1??
其中n為n-gram模型中的n。
另一種表示方式:
log(perplexity(s))=?∑p(wi∣w1,w2...wi?1)mlog(perplexity(s))=\frac{-\sum p(w_i|w_1,w_2...w_{i-1})}{m}log(perplexity(s))=m?∑p(wi?∣w1?,w2?...wi?1?)?
相比第一種乘積開根號的表示形式,這里使用加法的形式可以加速計算,這也有效的避免了概率為0時導致整個計算結果為0的問題。
復雜度perplexity表示的是平均分支系數(average branch factor),即模型預測下一個詞時的平均可選擇數量。
例如:考慮一個由0-9這10個數字隨機組成的長度為m的序列。由于這10個數字出現的概率是隨機的,所以每個數字出現的概率是110\frac{1}{10}101?。因此在任意時刻,模型都有10個等概率的候選答案可以選擇,于是perplexity的值就是10,計算過程:perplexity(s)=1∏i=1m110mperplexity(s)=\sqrt[m]{\frac{1}{\prod_{i=1}^m \frac{1}{10}}}perplexity(s)=m∏i=1m?101?1??。因此有一個語言模型的perplexity是89,就表示平均情況下,模型預測下一個詞時,有89個詞等可能地可以作為下一個詞的選擇。
1.2 rnn自然語言建模
除了 n-gram 模型,RNN 也可以用來對自然語言建模,如下圖所示:
每個時刻的輸入為一個句子中的單詞,而每個時刻的輸出為一個概率分布,表示句子中下一個位置為不同單詞的概率。通過這種方式,對于給定的句子,就可以通過RNN的前向傳播計算出p(wi∣w1,w2...wi?1)p(w_i|w_1,w_2...w_{i-1})p(wi?∣w1?,w2?...wi?1?)。
比如 “大海的顏色是藍色” 這句話的概率,知道第一個單詞是 “大海” 后,計算 p(“的”|“大海”) = 0.8,然后計算 p(x|“大海”,"的“) 、p(x|“大海”,“的”,“顏色”)、p(x|“大海”,“的”,“顏色”,“是”)。以此類推,就可以求出整句話 “大海的顏色是藍色” 的概率。
2. 基于PTB文本數據集的自然語言模型
PTB(penn treebank dataset)文本數據集是語言模型學習中最廣泛使用的數據集。下載 PTB 數據集:http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz 解壓后獲取 data 目錄下的三個文件:
ptb.train.txt 訓練集
ptb.valid.txt 驗證集
ptb.test.txt 測試集
這三個數據文件夾中的數據已經經過了預處理,包含了1000個不同的詞語和語句結束標記符。TensorFlow 提供了 ptb_raw_data函數來讀取PTB的數據,并將原始數據中的單詞轉換成單詞 ID。
代碼:
from tensorflow.models.tutorials.rnn.ptb import readerDATA_PATH="PTB_data" train_data,valid_data,test_data,_=reader.ptb_raw_data(DATA_PATH)# 讀取數據原始數據 print(len(train_data)) print(train_data[:100])結果:
929589 [9970, 9971, 9972, 9974, 9975, 9976, 9980, 9981, 9982, 9983, 9984, 9986, 9987, 9988, 9989, 9991, 9992, 9993, 9994, 9995, 9996, 9997, 9998, 9999, 2, 9256, 1, 3, 72, 393, 33, 2133, 0, 146, 19, 6, 9207, 276, 407, 3, 2, 23, 1, 13, 141, 4, 1, 5465, 0, 3081, 1596, 96, 2, 7682, 1, 3, 72, 393, 8, 337, 141, 4, 2477, 657, 2170, 955, 24, 521, 6, 9207, 276, 4, 39, 303, 438, 3684, 2, 6, 942, 4, 3150, 496, 263, 5, 138, 6092, 4241, 6036, 30, 988, 6, 241, 760, 4, 1015, 2786, 211, 6, 96, 4]訓練數據總共包括929589個單詞,而這些單詞被組成了一個很長的序列。這個序列通過特殊的標識符給出了每句話結束的位置。而這個數據集中,句子結束的標識符id為2,從運行結果可以看出句子結束的位置。
構造訓練集需要的batch:
import tensorflow as tf from tensorflow.models.tutorials.rnn.ptb import readerDATA_PATH = "PTB_data" train_data, valid_data, test_data, _ = reader.ptb_raw_data(DATA_PATH) print ('Total length:',len(train_data)) print (train_data[:100]) print('..........................')tt=train_data[:100] print(len(tt)) # 100個id分成4組,每組就是25個,截斷長度為5,也就是25/5=5每組5批id,第25個id是9256 print('id:',tt[25]) """ 將訓練數據組織成batch大小為4、截斷長度為5的數據組。并使用隊列讀取前3個batch。 即是:把原始的序列從第一個算起,平均分成4組,每組的大小是平均后的長度。而截斷長度是從每組中從第一個開始截取長度為5 """ # ptb_producer返回的為一個二維的tuple數據。 result = reader.ptb_producer(tt, 4, 5) print(type(result)) # 通過隊列依次讀取batch。 with tf.Session() as sess:coord = tf.train.Coordinator()threads = tf.train.start_queue_runners(sess=sess, coord=coord)for i in range(5):x, y = sess.run(result)print ("X%d: "%i, x)print ("Y%d: "%i, y)coord.request_stop()coord.join(threads)運行結果:
Total length: 929589 [9970, 9971, 9972, 9974, 9975, 9976, 9980, 9981, 9982, 9983, 9984, 9986, 9987, 9988, 9989, 9991, 9992, 9993, 9994, 9995, 9996, 9997, 9998, 9999, 2, 9256, 1, 3, 72, 393, 33, 2133, 0, 146, 19, 6, 9207, 276, 407, 3, 2, 23, 1, 13, 141, 4, 1, 5465, 0, 3081, 1596, 96, 2, 7682, 1, 3, 72, 393, 8, 337, 141, 4, 2477, 657, 2170, 955, 24, 521, 6, 9207, 276, 4, 39, 303, 438, 3684, 2, 6, 942, 4, 3150, 496, 263, 5, 138, 6092, 4241, 6036, 30, 988, 6, 241, 760, 4, 1015, 2786, 211, 6, 96, 4] .......................... 100 id: 9256 <class 'tuple'> X0: [[9970 9971 9972 9974 9975][9256 1 3 72 393][1596 96 2 7682 1][3684 2 6 942 4]] Y0: [[9971 9972 9974 9975 9976][ 1 3 72 393 33][ 96 2 7682 1 3][ 2 6 942 4 3150]] X1: [[9976 9980 9981 9982 9983][ 33 2133 0 146 19][ 3 72 393 8 337][3150 496 263 5 138]] Y1: [[9980 9981 9982 9983 9984][2133 0 146 19 6][ 72 393 8 337 141][ 496 263 5 138 6092]] X2: [[9984 9986 9987 9988 9989][ 6 9207 276 407 3][ 141 4 2477 657 2170][6092 4241 6036 30 988]] Y2: [[9986 9987 9988 9989 9991][9207 276 407 3 2][ 4 2477 657 2170 955][4241 6036 30 988 6]] X3: [[9991 9992 9993 9994 9995][ 2 23 1 13 141][ 955 24 521 6 9207][ 6 241 760 4 1015]] Y3: [[9992 9993 9994 9995 9996][ 23 1 13 141 4][ 24 521 6 9207 276][ 241 760 4 1015 2786]] X4: [[9970 9971 9972 9974 9975][9256 1 3 72 393][1596 96 2 7682 1][3684 2 6 942 4]] Y4: [[9971 9972 9974 9975 9976][ 1 3 72 393 33][ 96 2 7682 1 3][ 2 6 942 4 3150]]這里主要是為了送入循環神經網絡而做的數據處理。將一個長序列分成batch并截斷的操作示意圖如下:
注意:
報錯信息:
'AttributeError: module 'tensorflow.models.tutorials.rnn.ptb.reader' has no attribute 'ptb_iterator'
把讀取 PTB 數據集的 reader的 ptb_iterator() 修改成ptb_producer()。
但還是會報錯:
AttributeError: 'tuple' object has no attribute 'next',這是因為 ptb_producer() 返回的是 tuple,這時可以使用上述的方式通過建立session,使用隊列來解決。
完整的RNN語言模型為:
import numpy as np import tensorflow as tf from tensorflow.models.tutorials.rnn.ptb import readerDATA_PATH = "PTB_data" # 數據存儲路徑 HIDDEN_SIZE = 200 # 隱藏層規模 NUM_LAYERS = 2 # 深層循環網絡中lstm結構的層數 VOCAB_SIZE = 10000 # 詞典規模,加上語句結束標示符和稀有單詞標示符,總共一萬多個單詞LEARNING_RATE = 1.0 # 學習速率 TRAIN_BATCH_SIZE = 20 # 訓練數據batch的大小 TRAIN_NUM_STEP = 35 # 截斷長度# 在測試時不需要使用截斷,可以將測試數據看成一個超長的序列 EVAL_BATCH_SIZE = 1 # 測試數據batch的大小 EVAL_NUM_STEP = 1 # 測試數據截斷的長度 NUM_EPOCH = 2 # 使用訓練數據的輪數 KEEP_PROB = 0.5 # 節點不被dropout的概率 MAX_GRAD_NORM = 5 # 用于控制梯度膨脹的參數# 通過一個PTBModel類來描述模型,這樣方便維護循環神經網絡中的狀態 class PTBModel(object):# 記錄使用的batch的大小和截斷長度def __init__(self, is_training, batch_size, num_steps):# 使用batch的大小和截斷長度self.batch_size = batch_sizeself.num_steps = num_steps # 定義輸入層:維度為batch_size x num_steps,這和ptb_producer的函數輸出的數據batch是一致的。self.input_data = tf.placeholder(tf.int32, [batch_size, num_steps])# 定義預期輸出:它和輸入是對應的,標簽就是該詞匯對應的下一個詞匯self.targets = tf.placeholder(tf.int32, [batch_size, num_steps])# 定義一個基礎的LSTM結構作為循環體的基礎結構,深層循環神經網絡也支持使用其他的循環體。 lstm_cell = tf.contrib.rnn.BasicLSTMCell(HIDDEN_SIZE)if is_training:# 訓練時使用dropout的深層循環神經網絡lstm_cell = tf.contrib.rnn.DropoutWrapper(lstm_cell, output_keep_prob=KEEP_PROB)# 通過MultiRNNCell類實現深層循環網絡中的每一個時刻的前向傳播過程cell = tf.contrib.rnn.MultiRNNCell([lstm_cell]*NUM_LAYERS)# 初始化最初的狀態:也就是全0的向量self.initial_state = cell.zero_state(batch_size, tf.float32)"""下面是tensorflow中word2vec的構建"""# 將單詞id轉換為單詞向量:因為總共有VOCAB_SIZE個單詞,每個單詞的向量維度為hidde_size,# 所以embedding參數的維度為10000x200embedding = tf.get_variable("embedding", [VOCAB_SIZE, HIDDEN_SIZE]) # 將原本batch_size x numk_steps個單詞ID轉為單詞向量,# 轉化后的輸入層維度為batch_sizexnum_stepsxhidden_sizeinputs = tf.nn.embedding_lookup(embedding, self.input_data)# 只在訓練時使用dropoutif is_training:inputs = tf.nn.dropout(inputs, KEEP_PROB)# 定義輸出列表:在這里先將不同時刻的lstm結構的輸出列表收集起來,再通過一個全連接層得到最終的輸出outputs = []# state存儲不同batch中lstm的“狀態”,將其初始化為0state = self.initial_statewith tf.variable_scope("RNN"):# 這里num_steps是截斷長度為35:為了避免梯度消散問題,會規定一個最大的序列長度,就是num_stepsfor time_step in range(num_steps): # 在第一個時刻需要聲明網絡中的變量,在之后的時刻都需要復用之前定義好的變量。if time_step > 0: tf.get_variable_scope().reuse_variables()# 每一步處理時間序列中的一個時刻。將當前的輸入和前一時刻狀態傳入定義好的網絡結構,# 可以得到當前的輸出和更新的狀態cell_output, state = cell(inputs[:, time_step, :], state)# 將當前輸出加入到輸出隊列outputs.append(cell_output) # 把輸出隊列展開成[batch,hidden_size*num_steps]的形狀,然后再reshape成[batch*num_steps,hidden_size]的形狀# tf.concat(concat_dim, values, name='concat'),這里完全是為了后面全連接層的運算(3D化為2D)output = tf.reshape(tf.concat(outputs, 1), [-1, HIDDEN_SIZE])# 將從lstm中得到的輸出再經過一個全連接層得到最后的預測結果,# 最終的預測結果在每一個時刻上都是一個長度為vocab_size的數組,經過softamx層之后表示下一個位置是不同單詞的概率weight = tf.get_variable("weight", [HIDDEN_SIZE, VOCAB_SIZE])bias = tf.get_variable("bias", [VOCAB_SIZE])logits = tf.matmul(output, weight) + bias # [20x35,200]*[200x10000]+[10000]# 定義交叉熵損失函數:tf使用sequence_loss_by_example函數來計算一個序列的交叉熵的和loss = tf.contrib.legacy_seq2seq.sequence_loss_by_example([logits], # 預測的結果[tf.reshape(self.targets, [-1])], # 期望的正確答案,這里將[batch,num_step]的二維數組壓縮成一維數組# 損失的權重:這里所有的權重都為1,也就是說不同batch和不同時刻的重要程度是一樣的[tf.ones([batch_size * num_steps], dtype=tf.float32)])# 計算得到每個batch的平均損失self.cost = tf.reduce_sum(loss) / batch_size self.final_state = state# 只在訓練模型時定義反向傳播操作。if not is_training: return trainable_variables = tf.trainable_variables()# 通過clip_by_global_norm函數控制梯度大小,避免梯度膨脹的問題。grads, _ = tf.clip_by_global_norm(tf.gradients(self.cost, trainable_variables), MAX_GRAD_NORM)# 定義優化方法optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE)# 定義訓練步驟self.train_op = optimizer.apply_gradients(zip(grads, trainable_variables))# 使用給定的模型model在數據data上運行train_op并返回在全部數據上的perolexity值 def run_epoch(session, model, data, train_op, output_log, epoch_size):# 計算perplexity的輔助變量total_costs = 0.0iters = 0state = session.run(model.initial_state)# 使用當前數據訓練或者測試模型for step in range(epoch_size):x, y = session.run(data)# 在當前batch上運行train_op并計算損失值,交叉熵損失函數計算的就是下一個單詞為給定單詞的概率cost, state, _ = session.run([model.cost, model.final_state, train_op],{model.input_data: x, model.targets: y, model.initial_state: state}) # 將不同時刻、不同batch的概率加起來再將這個和做指數運算就可以得到perplexity值(公式二)total_costs += costiters += model.num_steps# 只有在訓練時輸出日志if output_log and step % 100 == 0:print("After %d steps, perplexity is %.3f" % (step, np.exp(total_costs / iters)))# 返回給定模型在給定數據上的perplexity值return np.exp(total_costs / iters)def main():# 獲得原始數據train_data, valid_data, test_data, _ = reader.ptb_raw_data(DATA_PATH)print ('length of traindata:',len(train_data))# 計算一個epoch需要訓練的次數train_data_len = len(train_data)# 這里的‘//’是取整的意思train_batch_len = train_data_len // TRAIN_BATCH_SIZE# 這里得到的是每個batch可以分成多少個steptrain_epoch_size = (train_batch_len - 1) // TRAIN_NUM_STEPprint('train_epoch_size:',train_epoch_size)# 同上valid_data_len = len(valid_data)valid_batch_len = valid_data_len // EVAL_BATCH_SIZEvalid_epoch_size = (valid_batch_len - 1) // EVAL_NUM_STEPprint('valid_epoch_size:',valid_epoch_size)# 同上test_data_len = len(test_data)test_batch_len = test_data_len // EVAL_BATCH_SIZEtest_epoch_size = (test_batch_len - 1) // EVAL_NUM_STEPprint('test_epoch_size:',test_epoch_size)# 定義初始化函數initializer = tf.random_uniform_initializer(-0.05, 0.05)# 定義訓練用的循環神經網絡模型with tf.variable_scope("language_model", reuse=None, initializer=initializer):train_model = PTBModel(True, TRAIN_BATCH_SIZE, TRAIN_NUM_STEP)# 定義測試用的循環神經網絡模型with tf.variable_scope("language_model", reuse=True, initializer=initializer):eval_model = PTBModel(False, EVAL_BATCH_SIZE, EVAL_NUM_STEP)# 訓練模型。with tf.Session() as session:tf.global_variables_initializer().run()train_queue = reader.ptb_producer(train_data, train_model.batch_size, train_model.num_steps)eval_queue = reader.ptb_producer(valid_data, eval_model.batch_size, eval_model.num_steps)test_queue = reader.ptb_producer(test_data, eval_model.batch_size, eval_model.num_steps)coord = tf.train.Coordinator()threads = tf.train.start_queue_runners(sess=session, coord=coord)# 使用訓練數據訓練模型for i in range(NUM_EPOCH):print("In iteration: %d" % (i + 1))# 在所有的訓練數據上訓練循環神經網絡run_epoch(session, train_model, train_queue, train_model.train_op, True, train_epoch_size)# 使用驗證數據集評測數據效果valid_perplexity = run_epoch(session, eval_model, eval_queue, tf.no_op(), False, valid_epoch_size)print("Epoch: %d Validation Perplexity: %.3f" % (i + 1, valid_perplexity))# 使用測試數據集測試模型效果test_perplexity = run_epoch(session, eval_model, test_queue, tf.no_op(), False, test_epoch_size)print("Test Perplexity: %.3f" % test_perplexity)coord.request_stop()coord.join(threads)""" 主函數入口 """ if __name__ == "__main__":main()運行結果:
length of traindata: 929589 train_epoch_size: 1327 valid_epoch_size: 73759 test_epoch_size: 82429 In iteration: 1 After 0 steps, perplexity is 9980.495 After 100 steps, perplexity is 1389.408 After 200 steps, perplexity is 1021.756 After 300 steps, perplexity is 853.102 After 400 steps, perplexity is 746.115 After 500 steps, perplexity is 673.331 After 600 steps, perplexity is 621.486 After 700 steps, perplexity is 577.891 After 800 steps, perplexity is 538.988 After 900 steps, perplexity is 508.633 After 1000 steps, perplexity is 485.270 After 1100 steps, perplexity is 462.591 After 1200 steps, perplexity is 443.829 After 1300 steps, perplexity is 426.913 Epoch: 1 Validation Perplexity: 244.784 In iteration: 2 After 0 steps, perplexity is 370.560 After 100 steps, perplexity is 259.848 After 200 steps, perplexity is 264.565 After 300 steps, perplexity is 265.816 After 400 steps, perplexity is 263.193 After 500 steps, perplexity is 260.594 After 600 steps, perplexity is 260.226 After 700 steps, perplexity is 257.938 After 800 steps, perplexity is 253.309 After 900 steps, perplexity is 250.747 After 1000 steps, perplexity is 249.277 After 1100 steps, perplexity is 246.004 After 1200 steps, perplexity is 243.725 After 1300 steps, perplexity is 241.052 Epoch: 2 Validation Perplexity: 198.344 Test Perplexity: 191.840通過輸出可以看出,在迭代開始時perplexity值為9980.495,這也就是說相當于從10000個單詞中隨機選擇下一個單詞,而在訓練結束后,perplexity的值降低到了 ,這說明通過訓練將選擇下一個單詞的范圍從10000個減少到大約190,當然還可以通過修改參數,把perplexity降低到更低。
總結
以上是生活随笔為你收集整理的基于tensorflow的RNN自然语言建模的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是五险一金(说一说什么是五险一金的简
- 下一篇: 中国杰出女性代表人物(资改派代表人物)