《Python數據分析與機器學習實戰-唐宇迪》讀書筆記第20章--神經網絡項目實戰——影評情感分析

python數據分析我的學習讀書筆記-目錄索引 html

第20章神經網絡項目實戰——影評情感分析python

   以前講解神經網絡時,都是以圖像數據爲例,訓練過程當中,數據樣本之間是相互獨立的。可是在天然語言處理中就有些區別,例如,一句話中各個詞之間有明確的前後順序,或者一篇文章的上下文之間確定有聯繫,可是,傳統神經網絡卻沒法處理這種關係。遞歸神經網絡(Recurrent Neural Network,RNN)就是專門解決這類問題的,本章就遞歸神經網絡結構展開分析,並將其應用在真實的影評數據集中進行分類任務。git

20.1遞歸神經網絡github

   遞歸神經網絡與卷積神經網絡並稱深度學習中兩大傑出表明,分別應用於計算機視覺與天然語言處理中,本節介紹遞歸神經網絡的基本原理。算法

20.1.1RNN網絡架構網絡

   RNN網絡的應用十分普遍,任何與天然語言處理能掛鉤的任務基本都有它的影子,先來看一下它的總體架構,如圖20-1所示。架構

   邀月工做室

  圖20-1 RNN網絡總體架構app

  其實只要你們熟悉了基本的神經網絡結構,再來分析遞歸神經網絡就容易多了,它只比傳統網絡多作了一件事——保留各個輸入的中間信息。例如,有一個時間序列數據 [X0,X1,X2,...,Xt],若是直接用神經網絡去作,網絡會依次輸入各個數據,不會考慮它們之間的聯繫。dom

  在RNN網絡中,一個序列的輸入數據來了,不只要計算最終結果,還要保存中間結果,例如,總體操做須要2個全鏈接層獲得最後的結果,如今把通過第一個全鏈接層獲得的特徵單獨保存下來。機器學習

  在計算下一個輸入樣本時,輸入數就不只是當前的輸入樣本,還包括前一步獲得的中間特徵,至關於綜合考慮本輪的輸入和上一輪的中間結果。經過這種方法,能夠把時間序列的關係加入網絡結構中。

  例如,能夠把輸入數據想象成一段話,X0,X1,…,Xi就是其中每個詞語,此時要作一個分類任務,看一看這句話的情感是積極的仍是消極的。首先將X0最早輸入網絡,不只獲得當前輸出結果h0,還有其中間輸出特徵。接下來將X1輸入網絡,和它一塊兒進來的還有前一輪X0的中間特徵,以此類推,最終這段話最後一個詞語Xt輸入進來,依舊會結合前一輪的中間特徵(此時前一輪不只指Xt-1,由於Xt-1也會帶有Xt-2的特徵,以依類推,就比如綜合了前面所有的信息),獲得最終的結果ht就是想要的分類結果。

  能夠看到,在遞歸神經網絡中,只要沒到最後一步,就會把每一步的中間結果所有保存下來,以供後續過程使用。每一步也都會獲得相應的輸出,只不過中間階段的輸出用處並不大,由於尚未把全部內容都加載加來,一般都是把最後一步的輸出結果看成整個模型的最終輸出,由於它把前面全部的信息都考慮進來。

  例如,當前的輸入是一句影評數據,如圖20-2所示。

 邀月工做室

  圖20-2 影評輸入數據

  網絡結構展開如圖20-3所示。

 邀月工做室

  圖20-3 RNN網絡展開

  每一輪輸出結果爲:

 邀月工做室

  這就是遞歸神經網絡的總體架構,原理和計算方法都與神經網絡相似(全鏈接),只不過要考慮前一輪的結果。這就使得RNN網絡更適用於時間序列相關數據,它與語言和文字的表達十分類似,因此更適合天然語言處理任務。

20.1.2LSTM網絡

   RNN網絡看起來十分強大,那麼它有沒有問題呢?若是一句話過長,也就是輸入X序列過多的時候,最後一個輸入會把前面全部的中間特徵都考慮進來。此時能夠想象一下,一般狀況下,語言或者文字都是離着越近,相關性越高,例如:「今天我白天在家玩了一天,主要在玩遊戲,晚上照樣沒事幹,準備出去打球。」最後的詞語「打球」應該會和晚上沒事幹比較相關,而和前面的玩遊戲沒有多大關係,可是,RNN網絡會把不少無關信息所有考慮進來。實際的天然語言處理任務也會有類似的問題,越相關的應當先後越緊密,若是中間東西記得太多,就會使得總體網絡模型效果有所降低。

  因此最好的辦法就是讓網絡有選擇地記憶或遺忘一些內容,重要的東西須要記得更深入,價值不大的信息能夠遺忘掉,這就用到當下最流行的Long Short Term Memory Units,簡稱LSTM,它在RNN網絡的基礎上加入控制單元,以有選擇地保留或遺忘部分中間結果,如今來看一下它的總體架構,如圖20-4所示。

   邀月工做室

  圖20-4 LSTM總體架構

  它的主要組成部分有輸入門、輸出門、遺忘門和一個記憶控制器C,簡單概述,就是經過一個持續維護並進行更新的Ct來控制每次迭代須要記住或忘掉哪些信息,若是一個序列很長,相關的內容會選擇記憶下來,一些沒用的描述忘掉就好。

  LSTM網絡在處理問題時,總體流程仍是與RNN網絡相似,只不過每一步增長了選擇記憶的細節,這裏只向你們進行了簡單介紹,瞭解其基本原理便可,如圖20-5所示。隨着技術的升級,RNN網絡中各類新產品也是層出不窮。

   邀月工做室

  圖20-5 LSTM網絡展開

 

20.2影評數據特徵工程

   如今要對電影評論數據集進行分類任務(二分類),創建一個LSTM網絡模型,以識別哪些評論是積極確定的情感、哪些是消極批判的情感。下面先來看看數據(見圖20-6)。

One of the very best Three Stooges shorts ever. A spooky house full of evil guys and 「The Goon」 challenge the Alert Detective Agency’s best men. Shemp is in top form in the famous in-the-dark scene. Emil Sitka provides excellent support in his Mr. Goodrich role,as the target of a murder plot. Before it’s over,Shemp’s 「trusty little shovel」 is employed to great effect. This 16 minute gem moves about as fast as any Stooge’s short and packs twice the wallop. Highly recommended.

   邀月工做室

  圖20-6 影評數據分類任務

  這就是其中一條影評數據,因爲英文數據自己以空格爲分隔符,因此直接處理詞語便可。可是這裏有一個問題——如何構建文本特徵呢?若是直接利用詞袋模型或者TF-IDF方法計算整個文本向量,很可貴到比較好的效果,由於一篇文章實在太長。

  另外一個問題就是RNN網絡的輸入要求是什麼?在原理講解中已經指出,須要把整個句子分解成一個個詞語,所以每個詞就是一個輸入,即x0,x1,…,xt。因此須要考慮每個詞的特徵表示。

  在數據處理階段,必定要弄清楚最終網絡須要的輸入是什麼,按照這個方向去處理數據。

20.2.1詞向量

   特徵一直是機器學習中的難點,爲了使得整個模型效果更好,必需要把詞的特徵表示作好,也就是詞向量。

  如圖20-7所示,每個詞都須要轉換成相應的特徵向量,並且維度必須一致,關於詞向量的組成,可不是簡單的詞頻統計,而是須要有實際的含義。

   邀月工做室

  圖20-7 詞向量的組成

  若是基於統計的方法來製做向量,love和adore是兩個徹底不一樣的向量,由於統計的方法很難考慮詞語自己以及上下文的含義,如圖20-8所示。若是用詞向量模型(word2vec)來製做,結果就大不相同。

  圖20-9爲詞向量的特徵空間意義。類似的詞語在向量空間上也會很是相似,這纔是但願獲得的結果。因此,當拿到文本數據以後,第一步要對語料庫進行詞向量建模,以獲得每個詞的向量。

   邀月工做室

  圖20-8 詞向量的意義

   邀月工做室

  圖20-9 詞向量的特徵空間意義

因爲訓練詞向量的工做量很大,在不少通用任務中,例如常見的新聞數據、影評數據等,均可以直接使用前人用大規模語料庫訓練以後的結果。由於此時但願獲得每個詞的向量,確定是預料越豐富,獲得的結果越好(見圖20-10)。

 邀月工做室

圖20-10 詞向量製做

  詞語能通用的緣由在於,語言自己就是能夠跨內容使用的,這篇文章中使用的每個詞語的含義換到下一篇文章中基本不會發生變化。可是,若是你的任務是專門針對某一領域,例如醫學實驗,這裏面確定會有大量的專有名詞,此時就須要單獨訓練詞向量模型來解決專門問題。

  接下來簡單介紹一下詞向量的基本原理,也就是Word2Vec模型,在天然語言處理中常常用到這個模型,其目的就是獲得各個詞的向量表示。

  Word2Vec模型如圖20-11所示,總體的結構仍是神經網絡,只不過此時要訓練的不只是網絡的權重參數,還有輸入數據。首先對每一個詞進行向量初始化,例如隨機建立一個300維的向量。在訓練過程當中,既能夠根

  據上下文預測某一箇中間詞,例如文本是:今每天氣不錯,上下文就是今天不錯,預測結果爲:天氣,如圖20-11(a)所示;也能夠由一個詞去預測其上下文結果。最終經過神經網絡不斷迭代,以訓練出每個詞向量結果。

   邀月工做室

  圖20-11 Word2Vec模型

  在word2vec模型中,每一次迭代更新,輸入的詞向量都會發生變化,至關於既更新網絡權重參數,也更新輸入數據。

  關於詞向量的建模方法,Gensim工具包中已經給出了很是不錯的文檔教程,若是要親自動手建立一份詞向量,能夠參考其使用方法,只需先將數據進行分詞,而後把分詞後的語料庫傳給Word2Vec函數便可,方法仍是很是簡單的。

1 from gensim.models.word2vec import Word2Vec
2 model=Word2Vec(sentences_list,works=num_workers,size=num_features,min_count=min_word_count,window=context)

  使用時,須要指定好每個參數值。

  • sentences:分好詞的語料庫,能夠是一個list。
  • sg:用於設置訓練算法,默認爲0,對應CBOW算法;sg=1則採用skip-gram算法。
  • size:是指特徵向量的維度,默認爲100。大的size須要更多的訓練數據,可是效果會更好,推薦值爲幾十到幾百。
  • window:表示當前詞與預測詞在一個句子中的最大距離是多少。
  • alpha:是學習速率。
  • seed:用於隨機數發生器,與初始化詞向量有關。
  • min_count:能夠對字典作截斷,詞頻少於min_count次數的單詞會被丟棄掉,默認值爲5。
  • max_vocab_size:設置詞向量構建期間的RAM限制。若是全部獨立單詞個數超過這個,則就消除掉其中最不頻繁的一個。每1000萬個單詞須要大約1GB的RAM。設置成None,則沒有限制。
  • workers:控制訓練的並行數。
  • hs:若是爲1,則會採用hierarchica softmax技巧。若是設置爲0(defaut),則negative sampling會被使用。
  • negative:若是>0,則會採用negative samping,用於設置多少個noise words。
  • iter:迭代次數,默認爲5。

  訓練完成後獲得的詞向量如圖20-12所示,基本上都是較小的數值,其含義如同降維獲得的結果,仍是很難進行解釋。

 邀月工做室

  圖20-12 詞向量結果

  製做好詞向量以後,還能夠動手試試其效果,看一下到底有沒有空間中的實際含義:

 邀月工做室

邀月工做室

  經過實驗結果能夠看出,使用語料庫訓練獲得的詞向量確實有着實際含義,而且具備相同含義的詞在特徵空間中是很是接近的。關於詞向量的維度,一般狀況下,50~300維比較常見,谷歌官方給出的word2vec模型的詞向量是300維,能解決絕大多數任務。

20.2.2數據特徵製做

   影評數據集中涉及的詞語都是常見詞,因此徹底能夠利用前人訓練好的詞向量模型,英文數據集中有不少訓練好的結果,最經常使用的就是谷歌官方給出的詞向量結果,可是,它的詞向量是300維度,也就是說,在RNN模型中,每一次輸入的數據都是300維的,若是你們用筆記本電腦來跑程序會比較慢,因此這裏選擇另一份詞向量結果,每一個詞只有50維特徵,一共包含40萬個經常使用詞。

 1 import numpy as np
 2 #讀取詞數據集
 3 wordsList = np.load('./training_data/wordsList.npy')
 4 print('Loaded the word list!')
 5 #已經訓練好的詞向量模型
 6 wordsList = wordsList.tolist() 
 7 #給定相應格式
 8 wordsList = [word.decode('UTF-8') for word in wordsList] 
 9 #讀取詞向量數據集
10 wordVectors = np.load('./training_data/wordVectors.npy')
11 print ('Loaded the word vectors!')
12 
13 print(len(wordsList))
14 print(wordVectors.shape)
400000
(400000, 50)

  關於詞向量的製做,也能夠本身用Gensim工具包訓練,若是你們想處理一份300維的特徵數據,不妨本身訓練一番,文本數據較少時,很快就能獲得各個詞的向量表示。

若是你們想看看詞向量的模樣,能夠實際傳入一些單詞試一試:

1 baseballIndex = wordsList.index('baseball')
2 wordVectors[baseballIndex]
array([-1.9327  ,  1.0421  , -0.78515 ,  0.91033 ,  0.22711 , -0.62158 ,
       -1.6493  ,  0.07686 , -0.5868  ,  0.058831,  0.35628 ,  0.68916 ,
       -0.50598 ,  0.70473 ,  1.2664  , -0.40031 , -0.020687,  0.80863 ,
       -0.90566 , -0.074054, -0.87675 , -0.6291  , -0.12685 ,  0.11524 ,
       -0.55685 , -1.6826  , -0.26291 ,  0.22632 ,  0.713   , -1.0828  ,
        2.1231  ,  0.49869 ,  0.066711, -0.48226 , -0.17897 ,  0.47699 ,
        0.16384 ,  0.16537 , -0.11506 , -0.15962 , -0.94926 , -0.42833 ,
       -0.59457 ,  1.3566  , -0.27506 ,  0.19918 , -0.36008 ,  0.55667 ,
       -0.70315 ,  0.17157 ], dtype=float32)

  上述代碼返回的結果就是一個50維的向量,其中每個數值的含義根本理解不了,可是計算機卻能看懂它們的總體含義。

  如今已經有各個詞的向量,可是手裏拿到的是一篇文章,須要對應地找到其各個詞的向量,而後再組合在一塊兒,先來總體看一下流程,如圖20-13所示。

   邀月工做室

  圖20-13 詞向量讀取

  由圖可見,先獲得一句話,而後取其在詞庫中的對應索引位置,再對照詞向量錶轉換成相應的結果,例如輸入10個詞,最終獲得的結果就是[10,50],表示每一個詞都轉換成其對應的向量。

  Embedding Matrix表示總體的詞向量大表,要在其中尋找所需的結果,TensorFlow提供了一個很是便捷的函數tf.nn.embedding_lookup(),能夠快速完成查找工做,若是任務與天然語言處理相關,那會常常用到這個函數。

  總體流程看起來有點麻煩,其實就是對照輸入中的每個詞將其轉換成相應的詞向量便可,在數據量較少時,也能夠用字典的方法查找替換,可是,當數據量與詞向量矩陣都較大時,最好使用embedding_lookup()函數,速度起碼快一個數量級。

  在將全部影評數據替換爲詞向量以前,須要考慮不一樣的影評數據長短不一所致使的問題,要不要規範它們?

 1 import tensorflow as tf  2 #能夠設置文章的最大詞數來限制  3 maxSeqLength = 10  4 #每一個單詞的最大維度  5 numDimensions = 300  6 firstSentence = np.zeros((maxSeqLength), dtype='int32')  7 firstSentence[0] = wordsList.index("i")  8 firstSentence[1] = wordsList.index("thought")  9 firstSentence[2] = wordsList.index("the") 10 firstSentence[3] = wordsList.index("movie") 11 firstSentence[4] = wordsList.index("was") 12 firstSentence[5] = wordsList.index("incredible") 13 firstSentence[6] = wordsList.index("and") 14 firstSentence[7] = wordsList.index("inspiring") 15 #若是長度沒達到設置的標準,用0來佔位 16 print(firstSentence.shape) 17 #結果 18 print(firstSentence) 19 20 with tf.Session() as sess: 21 print(tf.nn.embedding_lookup(wordVectors,firstSentence).eval().shape)

  修正代碼爲:

1 with tf.compat.v1.Session() as sess:
2     print(tf.nn.embedding_lookup(wordVectors,firstSentence).eval().shape)
3     
4 # AttributeError: module 'tensorflow' has no attribute 'Session'
5 # with tf.Session() as sess:
6 #     print(tf.nn.embedding_lookup(wordVectors,firstSentence).eval().shape)        
(10,)
[    41    804 201534   1005     15   7446      5  13767      0      0]
(10, 50)

  對一篇影評數據來講,首先找到其對應索引位置(以後要經過索引獲得其對應的詞向量結果),再利用embedding_lookup()函數就能獲得其詞向量結果,其中wordVectors是製做好的詞向量庫,firstSentence就是要尋找的詞向量的這句話。(10,50)表示將10個單詞轉換成對應的詞向量結果。

  這裏須要注意,以後設計的RNN網絡必須適用於全部文章。例如一篇文章的長度是200(x1,x2,…,x200),另外一篇是300(x1,x2,…,x300),此時輸入數據大小不一致,這是根本不行的,在網絡訓練中,必須保證結構是同樣的(這是全鏈接操做的前提)。

  此時須要對文本數據進行預處理操做,基本思想就是選擇一個合適的值來限制文本的長度,例如選250(須要根據實際任務來選擇)。若是一篇影評數據中詞語數量比250多,那就從第250個詞開始截斷,後面的就不須要了;少於250個詞的,缺失部分所有用0來填充便可。

  影評數據一共包括25000篇評論,其中消極和積極的數據各佔一半,以前說到須要定義一個合適的篇幅長度來設計RNN網絡結構,這裏先來統計一下每篇文章的平均長度,因爲數據存儲在不一樣文件夾中,因此須要分別讀取不一樣類別中的每一條影評數據(見圖20-14)。

 邀月工做室

  圖20-14 數據存儲格式

 1 from os import listdir
 2 from os.path import isfile, join
 3 #指定好數據集位置,因爲提供的數據都一個個單獨的文件,因此還得一個個讀取
 4 positiveFiles = ['./training_data/positiveReviews/' + f for f in listdir('./training_data/positiveReviews/') if isfile(join('./training_data/positiveReviews/', f))]
 5 negativeFiles = ['./training_data/negativeReviews/' + f for f in listdir('./training_data/negativeReviews/') if isfile(join('./training_data/negativeReviews/', f))]
 6 numWords = []
 7 #分別統計積極和消極情感數據集
 8 for pf in positiveFiles:
 9     with open(pf, "r", encoding='utf-8') as f:
10         line=f.readline()
11         counter = len(line.split())
12         numWords.append(counter)       
13 print('情感積極數據集加載完畢')
14 
15 for nf in negativeFiles:
16     with open(nf, "r", encoding='utf-8') as f:
17         line=f.readline()
18         counter = len(line.split())
19         numWords.append(counter)  
20 print('情感消極數據集加載完畢')
21 
22 numFiles = len(numWords)
23 print('總共文件數量', numFiles)
24 print('所有詞語數量', sum(numWords))
25 print('平均每篇評論詞語數量', sum(numWords)/len(numWords)
情感積極數據集加載完畢
情感消極數據集加載完畢
總共文件數量 25000
所有詞語數量 5844680
平均每篇評論詞語數量 233.7872

  能夠將平均長度233看成RNN中序列的長度,最好仍是繪圖觀察其分佈狀況:

1 import matplotlib.pyplot as plt
2 %matplotlib inline
3 plt.hist(numWords, 50)
4 plt.xlabel('Sequence Length')
5 plt.ylabel('Frequency')
6 plt.axis([0, 1200, 0, 8000])
7 plt.show()

   邀月工做室

  從總體上觀察,絕大多數評論的長度都在300之內,因此暫時設置RNN序列長度爲250沒有問題,這也能夠看成是總體模型的一個參數,你們也能夠用實驗來對比不一樣長度對結果的影響。

 

 1 maxSeqLength = 250
 2 
 3 #隨便哪一篇評論來看看結果
 4 fname = positiveFiles[3] 
 5 with open(fname) as f:
 6     for lines in f:
 7         print(lines)
 8         exit
 9 
10 # 刪除標點符號、括號、問號等,只留下字母數字字符
11 import re
12 strip_special_chars = re.compile("[^A-Za-z0-9 ]+")
13 
14 def cleanSentences(string):
15     string = string.lower().replace("<br />", " ")
16     return re.sub(strip_special_chars, "", string.lower())
17 
18 firstFile = np.zeros((maxSeqLength), dtype='int32')
19 with open(fname) as f:
20     indexCounter = 0
21     line=f.readline()
22     cleanedLine = cleanSentences(line)
23     split = cleanedLine.split()
24     for word in split:
25         try:
26             firstFile[indexCounter] = wordsList.index(word)
27         except ValueError:
28             firstFile[indexCounter] = 399999 #Vector for unknown words
29         indexCounter = indexCounter + 1
30 firstFile

邀月工做室

   上述輸出就是對文章截斷後的結果,長度不夠的時候,指定0進行填充。接下來是一個很是耗時間的過程,須要先把全部文章中的每個詞轉換成對應的索引,而後再把這些矩陣的結果返回。

  若是你們的筆記本電腦性能通常,可能要等上大半天,這裏直接給出一份轉換結果,實驗的時候,能夠直接讀取轉換好的矩陣:

# 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)
1 ids = np.load('./training_data/idsMatrix.npy')

  在RNN網絡進行迭代的時候,須要指定每一次傳入的batch數據,這裏先作好數據的選擇方式,方便以後在網絡中傳入數據。

 1 from random import randint
 2 # 製做batch數據,經過數據集索引位置來設置訓練集和測試集
 3 #而且讓batch中正負樣本各佔一半,同時給定其當前標籤
 4 def getTrainBatch():
 5     labels = []
 6     arr = np.zeros([batchSize, maxSeqLength])
 7     for i in range(batchSize):
 8         if (i % 2 == 0): 
 9             num = randint(1,11499)
10             labels.append([1,0])
11         else:
12             num = randint(13499,24999)
13             labels.append([0,1])
14         arr[i] = ids[num-1:num]
15     return arr, labels
16 
17 def getTestBatch():
18     labels = []
19     arr = np.zeros([batchSize, maxSeqLength])
20     for i in range(batchSize):
21         num = randint(11499,13499)
22         if (num <= 12499):
23             labels.append([1,0])
24         else:
25             labels.append([0,1])
26         arr[i] = ids[num-1:num]
27     return arr, labels

  構造好batch數據後,數據和標籤就肯定了。

  圖20-15所示爲數據最終預處理後的結果,構建RNN模型的時候,還需再將詞索引轉換成對應的向量。如今再向你們強調一下輸入數據的格式,傳入RNN網絡中的數據需是一個三維的形式,即[batchsize,文本長度,詞向量維度],例如一次迭代訓練10個樣本數據,每一個樣本長度爲250,每一個詞的向量維度爲50,輸入就是[10,250,50]。

   邀月工做室

  圖20-15 數據預處理結果

  在數據預處理時,最好的方法就是先倒着來思考,想想最終網絡模型要求輸入什麼,而後對照目標進行預處理和特徵提取。

 

20.3構建RNN模型

      邀月:本篇如下示例所有基於tensorflow 1.15.2版本運行,最初原想用tensorflow tensorflow-2.1.0運行,折騰兩個晚上,放棄了。

https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md 看這裏,google的版本何時都會給人驚喜和絕望。

/*
D:\tools\Python37>pip install tensorflow==1.15.2
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting tensorflow==1.15.2
........
Installing collected packages: tensorboard, tensorflow-estimator, tensorflow
  Attempting uninstall: tensorboard
    Found existing installation: tensorboard 2.1.1
    Uninstalling tensorboard-2.1.1:
      Successfully uninstalled tensorboard-2.1.1
  Attempting uninstall: tensorflow-estimator
    Found existing installation: tensorflow-estimator 2.1.0
    Uninstalling tensorflow-estimator-2.1.0:
      Successfully uninstalled tensorflow-estimator-2.1.0
Successfully installed tensorboard-1.15.0 tensorflow-1.15.2 tensorflow-estimator-1.15.1
*/

 

  首先須要設置模型所需參數,在RNN網絡中,其基本計算方式仍是全鏈接,因此須要指定隱層神經元數量:

  • batchSize=24
  • lstmUnits=64
  • numClasses=2
  • iterations=50000

  其中,batchSize能夠根據本身機器性能來選擇,若是以爲迭代過程有些慢,能夠再下降一些;lstmUnits表示其中每個隱層的神經元數量;numClasses就是最終要獲得的輸出結果,也就是一個二分類問題;在迭代過程當中,iterations就是最大迭代次數。

  網絡模型的搭建方法都是相同的,仍是先指定輸入數據的格式,而後定義RNN網絡結構訓練迭代:

 1 batchSize = 24
 2 lstmUnits = 64
 3 numClasses = 2
 4 iterations = 50000

  

1 import tensorflow as tf
2 tf.reset_default_graph()
3 
4 labels = tf.placeholder(tf.float32, [batchSize, numClasses])
5 input_data = tf.placeholder(tf.int32, [batchSize, maxSeqLength])

  依舊用placeholder()進行佔位,此時只獲得二維的結果,即[batchSize,maxSeqLength],還需將文本中每個詞由其索引轉換成相應的詞向量。

1 data = tf.Variable(tf.zeros([batchSize, maxSeqLength, numDimensions]),dtype=tf.float32)
2 data = tf.nn.embedding_lookup(wordVectors,input_data)

 

  使用embedding_lookup函數完成最後的詞向量讀取轉換工做,就搞定了輸入數據,你們在建模時,必定要清楚[batchSize,maxSeqLength,numDimensions]這三個維度的含義,不能只會調用工具包函數,還須要理解其中細節。

  構建LSTM網絡模型,須要分幾步走:

1 lstmCell = tf.contrib.rnn.BasicLSTMCell(lstmUnits)
2 lstmCell = tf.contrib.rnn.DropoutWrapper(cell=lstmCell, output_keep_prob=0.75)
3 value, _ = tf.nn.dynamic_rnn(lstmCell, data, dtype=tf.float32)
WARNING:tensorflow:
The TensorFlow contrib module will not be included in TensorFlow 2.0.
For more information, please see:
  * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md
  * https://github.com/tensorflow/addons
  * https://github.com/tensorflow/io (for I/O related ops)
If you depend on functionality not listed there, please file an issue.

WARNING:tensorflow:From <ipython-input-17-db6a6fc2c55e>:1: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This class is equivalent as tf.keras.layers.LSTMCell, and will be replaced by that in Tensorflow 2.0.
WARNING:tensorflow:From <ipython-input-17-db6a6fc2c55e>:3: dynamic_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `keras.layers.RNN(cell)`, which is equivalent to this API
WARNING:tensorflow:From d:\tools\python37\lib\site-packages\tensorflow_core\python\ops\rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `layer.add_weight` method instead.
WARNING:tensorflow:From d:\tools\python37\lib\site-packages\tensorflow_core\python\ops\rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Call initializer instance with the dtype argument instead of passing it to the constructor

  首先建立基本的LSTM單元,也就是每個輸入走的網絡結構都是相同的,再把這些基本單元和輸入的序列數據組合起來,還能夠加入Dropout功能。關於RNN網絡,還有不少種建立方法,這些在TensorFlow官網中都有實例說明,用的時候最好先參考一下其API文檔。

1 #權重參數初始化
2 weight = tf.Variable(tf.truncated_normal([lstmUnits, numClasses]))
3 bias = tf.Variable(tf.constant(0.1, shape=[numClasses]))
4 value = tf.transpose(value, [1, 0, 2])
5 #取最終的結果值
6 last = tf.gather(value, int(value.get_shape()[0]) - 1)
7 prediction = (tf.matmul(last, weight) + bias)

  RNN網絡的權重參數初始化方法與傳統神經網絡一致,都是全鏈接的操做,須要注意網絡輸出會有多個結果,能夠參考圖20-1,每個輸入的詞向量都與當前輸出結果相對應,最終選擇最後一個詞所對應的結果,而且經過一層全鏈接操做轉換成對應的分類結果。

  網絡模型和輸入數據肯定後,接下來與以前訓練方法一致,給定損失函數和優化器,而後迭代求解便可:

 1 correctPred = tf.equal(tf.argmax(prediction,1), tf.argmax(labels,1))
 2 accuracy = tf.reduce_mean(tf.cast(correctPred, tf.float32))
 3 
 4 
 5 loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=labels))
 6 optimizer = tf.train.AdamOptimizer().minimize(loss)
 7 
 8 sess = tf.InteractiveSession()
 9 saver = tf.train.Saver()
10 sess.run(tf.global_variables_initializer())
11 
12 for i in range(iterations):
13     #以前已經定義好的拿到batch數據函數
14     nextBatch, nextBatchLabels = getTrainBatch();
15     sess.run(optimizer, {input_data: nextBatch, labels: nextBatchLabels}) 
16     #每隔1000次打印一下當前的結果
17     if (i % 1000 == 0 and i != 0):
18         loss_ = sess.run(loss, {input_data: nextBatch, labels: nextBatchLabels})
19         accuracy_ = sess.run(accuracy, {input_data: nextBatch, labels: nextBatchLabels})
20         
21         print("iteration {}/{}...".format(i+1, iterations),
22               "loss {}...".format(loss_),
23               "accuracy {}...".format(accuracy_))    
24     #每一個1W次保存一下當前模型
25     if (i % 10000 == 0 and i != 0):
26         save_path = saver.save(sess, "models/pretrained_lstm.ckpt", global_step=i)
27         print("saved to %s" % save_path)

  這裏不只打印當前迭代結果,每隔1萬次還會保存當前的網絡模型。TensorFlow中保存模型最簡單的方法,就是用saver.save()函數指定保存的模型,以及保存的路徑。保存好訓練的權重參數,當預測任務來臨時,直接讀取模型便可。

  可能有同窗會問,爲何不能只保存最後一次的結果?因爲網絡在訓練過程當中,其效果可能發生浮動變化,並且不必定迭代次數越多,效果就越好,可能第3萬次的效果要比第5萬次的還要強,所以須要保存中間結果。

  訓練網絡須要耐心,這份數據集中,因爲給定的網絡結構和詞向量維度都比較小,因此訓練起來很快:

iteration 1001/50000... loss 0.5977783799171448... accuracy 0.5...
iteration 2001/50000... loss 0.6750070452690125... accuracy 0.5833333134651184...
iteration 3001/50000... loss 0.6415902972221375... accuracy 0.3333333432674408...
iteration 4001/50000... loss 0.6623604893684387... accuracy 0.5416666865348816...
iteration 5001/50000... loss 0.6792729496955872... accuracy 0.5833333134651184...
iteration 6001/50000... loss 0.6929864883422852... accuracy 0.5...
iteration 7001/50000... loss 0.6421437859535217... accuracy 0.5416666865348816...
iteration 8001/50000... loss 0.6045424938201904... accuracy 0.7083333134651184...
iteration 9001/50000... loss 0.49526092410087585... accuracy 0.875...
iteration 10001/50000... loss 0.23377956449985504... accuracy 0.9166666865348816...
saved to models/pretrained_lstm.ckpt-10000
iteration 11001/50000... loss 0.22206246852874756... accuracy 0.9166666865348816...
iteration 12001/50000... loss 0.49464142322540283... accuracy 0.7916666865348816...
iteration 13001/50000... loss 0.26161226630210876... accuracy 0.9166666865348816...
iteration 14001/50000... loss 0.3195655047893524... accuracy 0.9166666865348816...
iteration 15001/50000... loss 0.2544494867324829... accuracy 0.7916666865348816...
iteration 16001/50000... loss 0.4504941403865814... accuracy 0.7916666865348816...
iteration 17001/50000... loss 0.14206933975219727... accuracy 0.9583333134651184...
iteration 18001/50000... loss 0.28434768319129944... accuracy 0.875...
iteration 19001/50000... loss 0.2196163386106491... accuracy 0.875...
iteration 20001/50000... loss 0.1411515176296234... accuracy 0.9166666865348816...
saved to models/pretrained_lstm.ckpt-20000
iteration 21001/50000... loss 0.10852870345115662... accuracy 0.9166666865348816...
iteration 22001/50000... loss 0.17102549970149994... accuracy 0.875...
iteration 23001/50000... loss 0.2163759469985962... accuracy 0.9583333134651184...
iteration 24001/50000... loss 0.40744829177856445... accuracy 0.9583333134651184...
iteration 25001/50000... loss 0.12172506004571915... accuracy 0.875...
iteration 26001/50000... loss 0.22618673741817474... accuracy 0.9166666865348816...
iteration 27001/50000... loss 0.29675474762916565... accuracy 0.9583333134651184...
iteration 28001/50000... loss 0.028712689876556396... accuracy 1.0...
iteration 29001/50000... loss 0.06705771386623383... accuracy 0.9583333134651184...
iteration 30001/50000... loss 0.06020817533135414... accuracy 1.0...
saved to models/pretrained_lstm.ckpt-30000
iteration 31001/50000... loss 0.053414225578308105... accuracy 1.0...
iteration 32001/50000... loss 0.018836012110114098... accuracy 1.0...
iteration 33001/50000... loss 0.22417615354061127... accuracy 0.9583333134651184...
iteration 34001/50000... loss 0.11704185605049133... accuracy 0.9583333134651184...
iteration 35001/50000... loss 0.01906798779964447... accuracy 1.0...
iteration 36001/50000... loss 0.11806797981262207... accuracy 0.9166666865348816...
iteration 37001/50000... loss 0.03285054862499237... accuracy 1.0...
iteration 38001/50000... loss 0.10801851749420166... accuracy 0.9166666865348816...
iteration 39001/50000... loss 0.026212451979517937... accuracy 1.0...
iteration 40001/50000... loss 0.047293175011873245... accuracy 1.0...
saved to models/pretrained_lstm.ckpt-40000
iteration 41001/50000... loss 0.015445593744516373... accuracy 1.0...
iteration 42001/50000... loss 0.06675603985786438... accuracy 0.9583333134651184...
iteration 43001/50000... loss 0.026207834482192993... accuracy 1.0...
iteration 44001/50000... loss 0.012909072451293468... accuracy 1.0...
iteration 45001/50000... loss 0.019438063725829124... accuracy 1.0...
iteration 46001/50000... loss 0.01888447254896164... accuracy 1.0...
iteration 47001/50000... loss 0.010169420391321182... accuracy 1.0...
iteration 48001/50000... loss 0.04857032373547554... accuracy 0.9583333134651184...
iteration 49001/50000... loss 0.009694952517747879... accuracy 1.0...

 

  隨着網絡迭代的進行,模型也愈來愈收斂,基本上2萬次就可以達到完美的效果,可是不要高興得太早,這只是訓練集的結果,還要看測試集上的效果。

  如圖20-1六、圖20-17所示,雖然只用了很是簡單的LSTM結構,收斂效果仍是不錯的,其實最終模型的效果在很大程度上仍是與輸入數據有關,若是不使用詞向量模型,訓練的效果可能就要大打折扣。

 邀月工做室

  ▲圖20-16 訓練時準備率變化狀況

 邀月工做室

  ▲圖20-17 訓練時損失變化狀況

  接下來再看看測試的效果,這裏先給你們演示一下如何加載已經保存好的模型:

1 sess = tf.InteractiveSession()
2 saver = tf.train.Saver()
3 saver.restore(sess, tf.train.latest_checkpoint('models'))

  這裏加載的是最後保存的模型,固然也能夠指定具體的名字來加載指定的模型文件,讀取的就是以前訓練網絡時候所獲得的各個權重參數,接下來只須要在batch裏面傳入實際的測試數據集便可:

1 iterations = 10
2 for i in range(iterations):
3     nextBatch, nextBatchLabels = getTestBatch();
4     print("Accuracy for this batch:", (sess.run(accuracy, {input_data: nextBatch, labels: nextBatchLabels})) * 100)

 

Accuracy for this batch: 87.5
Accuracy for this batch: 79.16666865348816
Accuracy for this batch: 83.33333134651184
Accuracy for this batch: 83.33333134651184
Accuracy for this batch: 91.66666865348816
Accuracy for this batch: 83.33333134651184
Accuracy for this batch: 75.0
Accuracy for this batch: 87.5
Accuracy for this batch: 91.66666865348816
Accuracy for this batch: 91.66666865348816

  爲了使測試效果更穩定,選擇10個batch數據,在二分類任務中,獲得的結果只能說總體還湊合,能夠明顯發現網絡模型已經有些過擬合。在訓練數據集中,基本都是100%,然而實際測試時卻有所折扣。你們在實驗的時候,也能夠嘗試改變其中的參數,以調節網絡模型,再對比最終的結果。

  在神經網絡訓練過程當中,能夠調節的細節比較多,一般都是先調整學習率,致使過擬合最可能的緣由就是學習率過大。網絡結構與輸出數據也會對結果產生影響,這些都須要經過大量的實驗進行對比觀察。

 

項目小結:

   該章從總體上介紹了RNN網絡結構及其升級版本LSTM網絡,針對天然語言處理,其實很大程度上拼的是如何進行特徵構造,詞向量模型能夠說是當下最好的解決方案之一,對詞的維度進行建模要比總體文章建模更實用。針對影評數據集,首先進行數據格式處理,這也是按照後續網絡模型的要求輸入的,TensorFlow當中有不少便捷的API能夠完成處理任務,例如經常使用的embedding_lookup(),至於其具體用法,官網的解釋確定是最好的,因此千萬不要忽視最直接的資源。在處理序列數據上,RNN網絡結構有着先天的優點,因此,其在文本處理任務上,尤爲涉及上下文和序列相關任務的時候,仍是儘量優先選擇深度學習算法,雖然速度要慢一些,可是總體效果還不錯。

  20章的機器學習算法與實戰的學習到這裏就結束了,其中經歷了數學推導的考驗與案例中反覆的實驗,相信你們已經掌握了機器學習的核心思想與實踐方法。算法自己並無高低之分,不少時候拼的是如何對數據進行合適的特徵提取,結合特徵工程,將最合適的算法應用到最適合的數據中才是上策。學習應當是反覆的過程,每一次都會有更深的理解,機器學習算法自己較爲複雜,時常複習也是必不可缺的。案例的利用也是如此,光看不練終歸不是本身的,觸類旁通才能提高本身的實戰技能。在後續的學習和工做中,根據業務需求,還能夠結合實際論文來探討解決方案,善用資料,加以理解,並應用到本身的任務中,纔是最佳的提高路線。

 

第20章完。全書完

python數據分析我的學習讀書筆記-目錄索引

 

該書資源下載,請至異步社區:https://www.epubit.com

相關文章
相關標籤/搜索