學習RNN時原理理解起來不難,但是用TensorFlow去實現時被它各種數據的shape弄得暈頭轉向。現在就結合一個情感分析的案例來了解一下LSTM的操作流程。
一、深度學習在自然語言處理中的應用
自然語言處理是教會機器如何去處理或者讀懂人類語言的系統,主要應用領域:
- 對話系統 - 聊天機器人(小冰)
- 情感分析 - 對一段文本進行情感識別(我們一會要做的)
- 圖文映射 - CNN和RNN的融合
- 機器翻譯 - 將一種語言翻譯成另一種語言,現在谷歌做的太牛了
- 語音識別 - 讀懂人類的語音,如現在的正火熱的語音翻譯。
二、詞向量模型
計算機可只認識數字!
我們可以將一句話中的每一個詞都轉換成一個向量
你可以將輸入數據看成是一個 16*D 的一個矩陣。
詞向量是具有空間意義的並不是簡單的映射!例如,我們希望單詞 “love” 和 “adore” 這兩個詞在向量空間中是有一定的相關性的,因為他們有類似的定義,他們都在類似的上下文中使用。單詞的向量表示也被稱之為詞嵌入。
三、Word2Vec
為了去得到這些詞嵌入,我們使用一個非常牛逼的模型 “Word2Vec”。簡單的說,這個模型根據上下文的語境來推斷出每個詞的詞向量。如果兩個個詞在上下文的語境中,可以被互相替換,那么這兩個詞的距離就非常近。在自然語言中,上下文的語境對分析詞語的意義是非常重要的。比如,之前我們提到的 “adore” 和 “love” 這兩個詞,我們觀察如下上下文的語境。
從句子中我們可以看到,這兩個詞通常在句子中是表現積極的,而且一般比名詞或者名詞組合要好。這也說明了,這兩個詞可以被互相替換,他們的意思是非常相近的。對於句子的語法結構分析,上下文語境也是非常重要的。所以,這個模型的作用就是從一大堆句子(以 Wikipedia 為例)中為每個獨一無二的單詞進行建模,並且輸出一個唯一的向量。Word2Vec 模型的輸出被稱為一個嵌入矩陣。
這個嵌入矩陣包含訓練集中每個詞的一個向量。傳統來講,這個嵌入矩陣中的詞向量數據會很大。
Word2Vec 模型根據數據集中的每個句子進行訓練,並且以一個固定窗口在句子上進行滑動,根據句子的上下文來預測固定窗口中間那個詞的向量。然后根據一個損失函數和優化方法,來對這個模型進行訓練。
四、Recurrent Neural Networks (RNNs)
現在,我們已經得到了神經網絡的輸入數據 —— 詞向量,接下來讓我們看看需要構建的神經網絡。NLP 數據的一個獨特之處是它是時間序列數據。每個單詞的出現都依賴於它的前一個單詞和后一個單詞。由於這種依賴的存在,我們使用循環神經網絡來處理這種時間序列數據。
循環神經網絡的結構和你之前看到的那些前饋神經網絡的結構可能有一些不一樣。前饋神經網絡由三部分組成,輸入層,隱藏層和輸出層。
前饋神經網絡和 RNN 之前的主要區別就是 RNN 考慮了時間的信息。在 RNN 中,句子中的每個單詞都被考慮上了時間步驟。實際上,時間步長的數量將等於最大序列長度。
與每個時間步驟相關聯的中間狀態也被作為一個新的組件,稱為隱藏狀態向量 h(t) 。從抽象的角度來看,這個向量是用來封裝和匯總前面時間步驟中所看到的所有信息。就像 x(t) 表示一個向量,它封裝了一個特定單詞的所有信息。
隱藏狀態是當前單詞向量和前一步的隱藏狀態向量的函數。並且這兩項之和需要通過激活函數來進行激活。
五、Long Short Term Memory Units (LSTMs)
長短期記憶網絡單元,是另一個 RNN 中的模塊。從抽象的角度看,LSTM 保存了文本中長期的依賴信息。正如我們前面所看到的,H 在傳統的RNN網絡中是非常簡單的,這種簡單結構不能有效的將歷史信息鏈接在一起。舉個例子,在問答領域中,假設我們得到如下一段文本,那么 LSTM 就可以很好的將歷史信息進行記錄學習。
在這里,我們看到中間的句子對被問的問題沒有影響。然而,第一句和第三句之間有很強的聯系。對於一個典型的RNN網絡,隱藏狀態向量對於第二句的存儲信息量可能比第一句的信息量會大很多。但是LSTM,基本上就會判斷哪些信息是有用的,哪些是沒用的,並且把有用的信息在 LSTM 中進行保存。
我們從更加技術的角度來談談 LSTM 單元,該單元根據輸入數據 x(t) ,隱藏層輸出 h(t) 。在這些單元中,h(t) 的表達形式比經典的 RNN 網絡會復雜很多。這些復雜組件分為四個部分:輸入門,輸出門,遺忘門和一個記憶控制器。
每個門都將 x(t) 和 h(t-1) 作為輸入(沒有在圖中顯示出來),並且利用這些輸入來計算一些中間狀態。每個中間狀態都會被送入不同的管道,並且這些信息最終會匯集到 h(t) 。為簡單起見,我們不會去關心每一個門的具體推導。這些門可以被認為是不同的模塊,各有不同的功能。輸入門決定在每個輸入上施加多少強調,遺忘門決定我們將丟棄什么信息,輸出門根據中間狀態來決定最終的 h(t) 。
六、案例流程
1) 制作詞向量,可以使用gensim這個庫,也可以直接用現成的
2) 詞和ID的映射,常規套路了
3) 構建RNN網絡架構
4) 訓練我們的模型
5) 試試咋樣
1、導入數據
首先,我們需要去創建詞向量。為了簡單起見,我們使用訓練好的模型來創建。
作為該領域的一個最大玩家,Google 已經幫助我們在大規模數據集上訓練出來了 Word2Vec 模型,包括 1000 億個不同的詞!在這個模型中,谷歌能創建 300 萬個詞向量,每個向量維度為 300。
在理想情況下,我們將使用這些向量來構建模型,但是因為這個單詞向量矩陣相當大(3.6G),我們用另外一個現成的小一些的,該矩陣由 GloVe 進行訓練得到。矩陣將包含 400000 個詞向量,每個向量的維數為 50。
我們將導入兩個不同的數據結構,一個是包含 400000 個單詞的 Python 列表,一個是包含所有單詞向量值得 400000*50 維的嵌入矩陣。
import numpy as np wordsList = np.load('./training_data/wordsList.npy') print('Loaded the word list!') wordsList = wordsList.tolist() #Originally loaded as numpy array wordsList = [word.decode('UTF-8') for word in wordsList] #Encode words as UTF-8 wordVectors = np.load('./training_data/wordVectors.npy') print ('Loaded the word vectors!')
print(len(wordsList)) print(wordVectors.shape)
out:

Loaded the word list! Loaded the word vectors! 400000 (400000, 50)
我們也可以在詞庫中搜索單詞,比如 “baseball”,然后可以通過訪問嵌入矩陣來得到相應的向量,如下:
baseballIndex = wordsList.index('baseball') wordVectors[baseballIndex]
output:

array([-1.93270004, 1.04209995, -0.78514999, 0.91033 , 0.22711 , -0.62158 , -1.64929998, 0.07686 , -0.58679998, 0.058831 , 0.35628 , 0.68915999, -0.50598001, 0.70472997, 1.26639998, -0.40031001, -0.020687 , 0.80862999, -0.90565997, -0.074054 , -0.87674999, -0.62910002, -0.12684999, 0.11524 , -0.55685002, -1.68260002, -0.26291001, 0.22632 , 0.713 , -1.08280003, 2.12310004, 0.49869001, 0.066711 , -0.48225999, -0.17896999, 0.47699001, 0.16384 , 0.16537 , -0.11506 , -0.15962 , -0.94926 , -0.42833 , -0.59456998, 1.35660005, -0.27506 , 0.19918001, -0.36008 , 0.55667001, -0.70314997, 0.17157 ], dtype=float32)
現在我們有了向量,我們的第一步就是輸入一個句子,然后構造它的向量表示。假設我們現在的輸入句子是 “I thought the movie was incredible and inspiring”。為了得到詞向量,我們可以使用 TensorFlow 的嵌入函數。這個函數有兩個參數,一個是嵌入矩陣(在我們的情況下是詞向量矩陣),另一個是每個詞對應的索引。
import tensorflow as tf maxSeqLength = 10 #Maximum length of sentence numDimensions = 300 #Dimensions for each word vector firstSentence = np.zeros((maxSeqLength), dtype='int32') firstSentence[0] = wordsList.index("i") firstSentence[1] = wordsList.index("thought") firstSentence[2] = wordsList.index("the") firstSentence[3] = wordsList.index("movie") firstSentence[4] = wordsList.index("was") firstSentence[5] = wordsList.index("incredible") firstSentence[6] = wordsList.index("and") firstSentence[7] = wordsList.index("inspiring") #firstSentence[8] and firstSentence[9] are going to be 0 print(firstSentence.shape) print(firstSentence) #Shows the row index for each word
output:

(10,)
[ 41 804 201534 1005 15 7446 5 13767 0 0]
數據管道如下圖所示:
輸出數據是一個 10*50 的詞矩陣,其中包括 10 個詞,每個詞的向量維度是 50。就是去找到這些詞對應的向量
with tf.Session() as sess: print(tf.nn.embedding_lookup(wordVectors,firstSentence).eval().shape)
out:

(10, 50)
在整個訓練集上面構造索引之前,我們先花一些時間來可視化我們所擁有的數據類型。這將幫助我們去決定如何設置最大序列長度的最佳值。在前面的例子中,我們設置了最大長度為 10,但這個值在很大程度上取決於你輸入的數據。
訓練集我們使用的是 IMDB 數據集。這個數據集包含 25000 條電影數據,其中 12500 條正向數據,12500 條負向數據。這些數據都是存儲在一個文本文件中,首先我們需要做的就是去解析這個文件。正向數據包含在一個文件中,負向數據包含在另一個文件中。
from os import listdir from os.path import isfile, join positiveFiles = ['./training_data/positiveReviews/' + f for f in listdir('./training_data/positiveReviews/') if isfile(join('./training_data/positiveReviews/', f))] negativeFiles = ['./training_data/negativeReviews/' + f for f in listdir('./training_data/negativeReviews/') if isfile(join('./training_data/negativeReviews/', f))] numWords = [] for pf in positiveFiles: with open(pf, "r", encoding='utf-8') as f: line=f.readline() counter = len(line.split()) numWords.append(counter) print('Positive files finished') for nf in negativeFiles: with open(nf, "r", encoding='utf-8') as f: line=f.readline() counter = len(line.split()) numWords.append(counter) print('Negative files finished') numFiles = len(numWords) print('The total number of files is', numFiles) print('The total number of words in the files is', sum(numWords)) print('The average number of words in the files is', sum(numWords)/len(numWords))
output:

Positive files finished Negative files finished The total number of files is 25000 The total number of words in the files is 5844680 The average number of words in the files is 233.7872
import matplotlib.pyplot as plt %matplotlib inline plt.hist(numWords, 50) plt.xlabel('Sequence Length') plt.ylabel('Frequency') plt.axis([0, 1200, 0, 8000]) plt.show()
從直方圖和句子的平均單詞數,我們認為將句子最大長度設置為 250 是可行的。
maxSeqLength = 250
接下來,讓我們看看如何將單個文件中的文本轉換成索引矩陣,比如下面的代碼就是文本中的其中一個評論。
fname = positiveFiles[3] #Can use any valid index (not just 3) with open(fname) as f: for lines in f: print(lines) exit

This is easily the most underrated film inn the Brooks cannon. Sure, its flawed. It does not give a realistic view of homelessness (unlike, say, how Citizen Kane gave a realistic view of lounge singers, or Titanic gave a realistic view of Italians YOU IDIOTS). Many of the jokes fall flat. But still, this film is very lovable in a way many comedies are not, and to pull that off in a story about some of the most traditionally reviled members of society is truly impressive. Its not The Fisher King, but its not crap, either. My only complaint is that Brooks should have cast someone else in the lead (I love Mel as a Director and Writer, not so much as a lead).
接下來,我們將它轉換成一個索引矩陣。
# 刪除標點符號、括號、問號等,只留下字母數字字符 import re strip_special_chars = re.compile("[^A-Za-z0-9 ]+") def cleanSentences(string): string = string.lower().replace("<br />", " ") return re.sub(strip_special_chars, "", string.lower()) firstFile = np.zeros((maxSeqLength), dtype='int32') with open(fname) as f: indexCounter = 0 line=f.readline() cleanedLine = cleanSentences(line) split = cleanedLine.split() for word in split: try: firstFile[indexCounter] = wordsList.index(word) except ValueError: firstFile[indexCounter] = 399999 #Vector for unknown words indexCounter = indexCounter + 1 print(firstFile)

array([ 37, 14, 2407, 201534, 96, 37314, 319, 7158, 201534, 6469, 8828, 1085, 47, 9703, 20, 260, 36, 455, 7, 7284, 1139, 3, 26494, 2633, 203, 197, 3941, 12739, 646, 7, 7284, 1139, 3, 11990, 7792, 46, 12608, 646, 7, 7284, 1139, 3, 8593, 81, 36381, 109, 3, 201534, 8735, 807, 2983, 34, 149, 37, 319, 14, 191, 31906, 6, 7, 179, 109, 15402, 32, 36, 5, 4, 2933, 12, 138, 6, 7, 523, 59, 77, 3, 201534, 96, 4246, 30006, 235, 3, 908, 14, 4702, 4571, 47, 36, 201534, 6429, 691, 34, 47, 36, 35404, 900, 192, 91, 4499, 14, 12, 6469, 189, 33, 1784, 1318, 1726, 6, 201534, 410, 41, 835, 10464, 19, 7, 369, 5, 1541, 36, 100, 181, 19, 7, 410, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
現在,我們用相同的方法來處理全部的 25000 條評論。我們將導入電影訓練集,並且得到一個 25000 * 250 的矩陣。這是一個計算成本非常高的過程,可以直接使用理好的索引矩陣文件。
# ids = np.zeros((numFiles, maxSeqLength), dtype='int32') # fileCounter = 0 # for pf in positiveFiles: # with open(pf, "r") as f: # indexCounter = 0 # line=f.readline() # cleanedLine = cleanSentences(line) # split = cleanedLine.split() # for word in split: # try: # ids[fileCounter][indexCounter] = wordsList.index(word) # except ValueError: # ids[fileCounter][indexCounter] = 399999 #Vector for unkown words # indexCounter = indexCounter + 1 # if indexCounter >= maxSeqLength: # break # fileCounter = fileCounter + 1 # for nf in negativeFiles: # with open(nf, "r") as f: # indexCounter = 0 # line=f.readline() # cleanedLine = cleanSentences(line) # split = cleanedLine.split() # for word in split: # try: # ids[fileCounter][indexCounter] = wordsList.index(word) # except ValueError: # ids[fileCounter][indexCounter] = 399999 #Vector for unkown words # indexCounter = indexCounter + 1 # if indexCounter >= maxSeqLength: # break # fileCounter = fileCounter + 1 # #Pass into embedding function and see if it evaluates. # np.save('idsMatrix', ids)
ids = np.load('./training_data/idsMatrix.npy')
2、輔助函數
from random import randint def getTrainBatch(): labels = [] arr = np.zeros([batchSize, maxSeqLength]) for i in range(batchSize): if (i % 2 == 0): num = randint(1,11499) labels.append([1,0]) else: num = randint(13499,24999) labels.append([0,1]) arr[i] = ids[num-1:num] return arr, labels def getTestBatch(): labels = [] arr = np.zeros([batchSize, maxSeqLength]) for i in range(batchSize): num = randint(11499,13499) if (num <= 12499): labels.append([1,0]) else: labels.append([0,1]) arr[i] = ids[num-1:num] return arr, labels
4、RNN Model
現在,我們可以開始構建我們的 TensorFlow 圖模型。首先,我們需要去定義一些超參數,比如批處理大小,LSTM的單元個數,分類類別和訓練次數。
batchSize = 24 lstmUnits = 64 numClasses = 2 iterations = 50000
與大多數 TensorFlow 圖一樣,現在我們需要指定兩個占位符,一個用於數據輸入,另一個用於標簽數據。對於占位符,最重要的一點就是確定好維度。
標簽占位符代表一組值,每一個值都為 [1,0] 或者 [0,1],這個取決於數據是正向的還是負向的。輸入占位符,是一個整數化的索引數組。
import tensorflow as tf tf.reset_default_graph() labels = tf.placeholder(tf.float32, [batchSize, numClasses]) input_data = tf.placeholder(tf.int32, [batchSize, maxSeqLength])
一旦,我們設置了我們的輸入數據占位符,我們可以調用 tf.nn.embedding_lookup() 函數來得到我們的詞向量。該函數最后將返回一個三維向量,第一個維度是批處理大小,第二個維度是句子長度,第三個維度是詞向量長度。更清晰的表達,如下圖所示:
data = tf.Variable(tf.zeros([batchSize, maxSeqLength, numDimensions]),dtype=tf.float32)
data = tf.nn.embedding_lookup(wordVectors,input_data)
現在我們已經得到了我們想要的數據形式,那么揭曉了我們看看如何才能將這種數據形式輸入到我們的 LSTM 網絡中。首先,我們使用 tf.nn.rnn_cell.BasicLSTMCell 函數,這個函數輸入的參數是一個整數,表示需要幾個 LSTM 單元。這是我們設置的一個超參數,我們需要對這個數值進行調試從而來找到最優的解。然后,我們會設置一個 dropout 參數,以此來避免一些過擬合。
最后,我們將 LSTM cell 和三維的數據輸入到 tf.nn.dynamic_rnn ,這個函數的功能是展開整個網絡,並且構建一整個 RNN 模型。
lstmCell = tf.contrib.rnn.BasicLSTMCell(lstmUnits) lstmCell = tf.contrib.rnn.DropoutWrapper(cell=lstmCell, output_keep_prob=0.75) value, _ = tf.nn.dynamic_rnn(lstmCell, data, dtype=tf.float32)
堆棧 LSTM 網絡是一個比較好的網絡架構。也就是前一個LSTM 隱藏層的輸出是下一個LSTM的輸入。堆棧LSTM可以幫助模型記住更多的上下文信息,但是帶來的弊端是訓練參數會增加很多,模型的訓練時間會很長,過擬合的幾率也會增加。
dynamic RNN 函數的第一個輸出可以被認為是最后的隱藏狀態向量。這個向量將被重新確定維度,然后乘以最后的權重矩陣和一個偏置項來獲得最終的輸出值。
weight = tf.Variable(tf.truncated_normal([lstmUnits, numClasses])) bias = tf.Variable(tf.constant(0.1, shape=[numClasses])) value = tf.transpose(value, [1, 0, 2]) #取最終的結果值 last = tf.gather(value, int(value.get_shape()[0]) - 1) prediction = (tf.matmul(last, weight) + bias)
接下來,我們需要定義正確的預測函數和正確率評估參數。正確的預測形式是查看最后輸出的0-1向量是否和標記的0-1向量相同。
correctPred = tf.equal(tf.argmax(prediction,1), tf.argmax(labels,1))
accuracy = tf.reduce_mean(tf.cast(correctPred, tf.float32))
之后,我們使用一個標准的交叉熵損失函數來作為損失值。對於優化器,我們選擇 Adam,並且采用默認的學習率。
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=labels))
optimizer = tf.train.AdamOptimizer().minimize(loss)
5、超參數調整
選擇合適的超參數來訓練你的神經網絡是至關重要的。你會發現你的訓練損失值與你選擇的優化器(Adam,Adadelta,SGD,等等),學習率和網絡架構都有很大的關系。特別是在RNN和LSTM中,單元數量和詞向量的大小都是重要因素。
- 學習率:RNN最難的一點就是它的訓練非常困難,因為時間步驟很長。那么,學習率就變得非常重要了。如果我們將學習率設置的很大,那么學習曲線就會波動性很大,如果我們將學習率設置的很小,那么訓練過程就會非常緩慢。根據經驗,將學習率默認設置為 0.001 是一個比較好的開始。如果訓練的非常緩慢,那么你可以適當的增大這個值,如果訓練過程非常的不穩定,那么你可以適當的減小這個值。
- 優化器:這個在研究中沒有一個一致的選擇,但是 Adam 優化器被廣泛的使用。
- LSTM單元的數量:這個值很大程度上取決於輸入文本的平均長度。而更多的單元數量可以幫助模型存儲更多的文本信息,當然模型的訓練時間就會增加很多,並且計算成本會非常昂貴。
- 詞向量維度:詞向量的維度一般我們設置為50到300。維度越多意味着可以存儲更多的單詞信息,但是你需要付出的是更昂貴的計算成本。
6、訓練
訓練過程的基本思路是,我們首先先定義一個 TensorFlow 會話。然后,我們加載一批評論和對應的標簽。接下來,我們調用會話的 run 函數。這個函數有兩個參數,第一個參數被稱為 fetches 參數,這個參數定義了我們感興趣的值。我們希望通過我們的優化器來最小化損失函數。第二個參數被稱為 feed_dict 參數。這個數據結構就是我們提供給我們的占位符。我們需要將一個批處理的評論和標簽輸入模型,然后不斷對這一組訓練數據進行循環訓練。
sess = tf.InteractiveSession() saver = tf.train.Saver() sess.run(tf.global_variables_initializer()) for i in range(iterations): #Next Batch of reviews nextBatch, nextBatchLabels = getTrainBatch(); sess.run(optimizer, {input_data: nextBatch, labels: nextBatchLabels}) if (i % 1000 == 0 and i != 0): loss_ = sess.run(loss, {input_data: nextBatch, labels: nextBatchLabels}) accuracy_ = sess.run(accuracy, {input_data: nextBatch, labels: nextBatchLabels}) print("iteration {}/{}...".format(i+1, iterations), "loss {}...".format(loss_), "accuracy {}...".format(accuracy_)) #Save the network every 10,000 training iterations if (i % 10000 == 0 and i != 0): save_path = saver.save(sess, "models/pretrained_lstm.ckpt", global_step=i) print("saved to %s" % save_path)

# output iteration 1001/50000... loss 0.6308178901672363... accuracy 0.5... iteration 2001/50000... loss 0.7168402671813965... accuracy 0.625... iteration 3001/50000... loss 0.7420873641967773... accuracy 0.5... iteration 4001/50000... loss 0.650059700012207... accuracy 0.5416666865348816... iteration 5001/50000... loss 0.6791467070579529... accuracy 0.5... iteration 6001/50000... loss 0.6914048790931702... accuracy 0.5416666865348816... iteration 7001/50000... loss 0.36072710156440735... accuracy 0.8333333134651184... iteration 8001/50000... loss 0.5486791729927063... accuracy 0.75... iteration 9001/50000... loss 0.41976991295814514... accuracy 0.7916666865348816... iteration 10001/50000... loss 0.10224487632513046... accuracy 1.0... saved to models/pretrained_lstm.ckpt-10000 iteration 11001/50000... loss 0.37682783603668213... accuracy 0.8333333134651184... iteration 12001/50000... loss 0.266050785779953... accuracy 0.9166666865348816... iteration 13001/50000... loss 0.40790924429893494... accuracy 0.7916666865348816... iteration 14001/50000... loss 0.22000855207443237... accuracy 0.875... iteration 15001/50000... loss 0.49727579951286316... accuracy 0.7916666865348816... iteration 16001/50000... loss 0.21477992832660675... accuracy 0.9166666865348816... iteration 17001/50000... loss 0.31636106967926025... accuracy 0.875... iteration 18001/50000... loss 0.17190784215927124... accuracy 0.9166666865348816... iteration 19001/50000... loss 0.11049345880746841... accuracy 1.0... iteration 20001/50000... loss 0.06362085044384003... accuracy 1.0... saved to models/pretrained_lstm.ckpt-20000 iteration 21001/50000... loss 0.19093847274780273... accuracy 0.9583333134651184... iteration 22001/50000... loss 0.06586482375860214... accuracy 0.9583333134651184... iteration 23001/50000... loss 0.02577809803187847... accuracy 1.0... iteration 24001/50000... loss 0.0732395276427269... accuracy 0.9583333134651184... iteration 25001/50000... loss 0.30879321694374084... accuracy 0.9583333134651184... iteration 26001/50000... loss 0.2742778956890106... accuracy 0.9583333134651184... iteration 27001/50000... loss 0.23742587864398956... accuracy 0.875... iteration 28001/50000... loss 0.04694415628910065... accuracy 1.0... iteration 29001/50000... loss 0.031666990369558334... accuracy 1.0... iteration 30001/50000... loss 0.09171193093061447... accuracy 1.0... saved to models/pretrained_lstm.ckpt-30000 iteration 31001/50000... loss 0.03852967545390129... accuracy 1.0... iteration 32001/50000... loss 0.06964454054832458... accuracy 1.0... iteration 33001/50000... loss 0.12447216361761093... accuracy 0.9583333134651184... iteration 34001/50000... loss 0.008963108994066715... accuracy 1.0... iteration 35001/50000... loss 0.04129207879304886... accuracy 0.9583333134651184... iteration 36001/50000... loss 0.0081111378967762... accuracy 1.0... iteration 37001/50000... loss 0.022405564785003662... accuracy 1.0... iteration 38001/50000... loss 0.03473325073719025... accuracy 1.0... iteration 39001/50000... loss 0.09315425157546997... accuracy 0.9583333134651184... iteration 40001/50000... loss 0.3166258931159973... accuracy 0.9583333134651184... saved to models/pretrained_lstm.ckpt-40000 iteration 41001/50000... loss 0.03648881986737251... accuracy 1.0... iteration 42001/50000... loss 0.2616865932941437... accuracy 0.9583333134651184... iteration 43001/50000... loss 0.013914794661104679... accuracy 1.0... iteration 44001/50000... loss 0.020460862666368484... accuracy 1.0... iteration 45001/50000... loss 0.15876878798007965... accuracy 0.9583333134651184... iteration 46001/50000... loss 0.007766606751829386... accuracy 1.0... iteration 47001/50000... loss 0.02079685777425766... accuracy 1.0... iteration 48001/50000... loss 0.017801295965909958... accuracy 1.0... iteration 49001/50000... loss 0.017789073288440704... accuracy 1.0...
查看上面的訓練曲線,我們發現這個模型的訓練結果還是不錯的。損失值在穩定的下降,正確率也不斷的在接近 100% 。然而,當分析訓練曲線的時候,我們應該注意到我們的模型可能在訓練集上面已經過擬合了。過擬合是機器學習中一個非常常見的問題,表示模型在訓練集上面擬合的太好了,但是在測試集上面的泛化能力就會差很多。也就是說,如果你在訓練集上面取得了損失值是 0 的模型,但是這個結果也不一定是最好的結果。當我們訓練 LSTM 的時候,提前終止是一種常見的防止過擬合的方法。基本思路是,我們在訓練集上面進行模型訓練,同事不斷的在測試集上面測量它的性能。一旦測試誤差停止下降了,或者誤差開始增大了,那么我們就需要停止訓練了。因為這個跡象表明,我們網絡的性能開始退化了。
導入一個預訓練的模型需要使用 TensorFlow 的另一個會話函數,稱為 Server ,然后利用這個會話函數來調用 restore 函數。這個函數包括兩個參數,一個表示當前的會話,另一個表示保存的模型。
sess = tf.InteractiveSession() saver = tf.train.Saver() saver.restore(sess, tf.train.latest_checkpoint('models'))

INFO:tensorflow:Restoring parameters from models\pretrained_lstm.ckpt-40000
然后,從我們的測試集中導入一些電影評論。請注意,這些評論是模型從來沒有看見過的。
iterations = 10 for i in range(iterations): nextBatch, nextBatchLabels = getTestBatch(); print("Accuracy for this batch:", (sess.run(accuracy, {input_data: nextBatch, labels: nextBatchLabels})) * 100)

Accuracy for this batch: 91.6666686535 Accuracy for this batch: 79.1666686535 Accuracy for this batch: 87.5 Accuracy for this batch: 87.5 Accuracy for this batch: 91.6666686535 Accuracy for this batch: 75.0 Accuracy for this batch: 91.6666686535 Accuracy for this batch: 70.8333313465 Accuracy for this batch: 83.3333313465 Accuracy for this batch: 95.8333313465