情感分析之电影评论分析-基于Tensorflow的LSTM
1. 深度學習在自然語言處理中的應用
自然語言處理是教會機器如何去處理或者讀懂人類語言的系統,目前比較熱門的方向,包括如下幾類:
對話系統 - 比較著名的案例有:Siri,Alexa 和 Cortana。
情感分析 - 對一段文本進行情感識別。
圖文映射 - 用一句話來描述一張圖片。
機器翻譯 - 將一種語言翻譯成另一種語言。
語音識別 - 讓電腦識別口語。
2. 情感分析基本方法
目前對情感分析的研究主要集中在兩個方面:識別給定的文本實體是主觀的還是客觀的,以及識別主觀的文本的極性。大多數情感分析研究都使用機器學習方法。
分析方法主要有以下三種:
- 詞法分(基于詞典的打分機制)
- 基于機器學習的分析
- 混合分析
下面簡單介紹下這三種方法:
詞法分析運用了由預標記詞匯組成的字典,使用詞法分析將器將輸入文本轉化為單詞序列。將新的單詞與詞典中的詞匯進行匹配。如果有一個積極的匹配,正分數就加到分數總池中。如果有一個消極的匹配,輸入文本的總分數就會減少。最后文本的分類就取決于文本的總得分。目前有大量的工作致力于度量詞法信息的有效性。
機器學習具有高的適應性和準確性,也因此受到了越來越多的關注。在情感分析中主要使用的是監督學習方法。可以分為三個階段:數據收集、預處理、訓練分類。并且在訓練過程中,需要提供一個標記語料庫作為訓練數據。分類器使用一系列特征向量對目標數據進行分類。
目前情感分析研究將兩種方法進行組合,這樣既可以利用機器學習方法的高準確性,又可以利用詞法分析的快速的特性。有研究者利用由兩個詞組成的詞匯和一個未標記的數據作為訓練集。思想是:首先將這些由兩個詞組成的詞匯劃分為積極的類和消極的類,然后把這些詞匯集合進行向量化表示,每個類都有一個中心向量代表該類,然后計算未標記詞和中心詞向量之間的余弦相似度,根據相似度將該文件劃分為積極和消極的類,最后把這些全部送進分類器進行分類。個人理解這里其實有點半監督學習的意思。。
3. 實戰電影評論情感分析
該任務可以被認為是從一個句子,一段話,或者是從一個文檔中,將作者的情感分為積極的,消極的或者中性的。這篇教程由多個主題組成,包括詞向量,循環神經網絡和LSTM,tensorflow框架的搭建。
3.1 關于詞向量:
我們需要考慮不同形式的數據,這些數據被用來作為機器學習或者深度學習模型的輸入數據。卷積神經網絡使用像素值作為輸入,logistic回歸使用一些可以量化的特征值作為輸入,強化學習模型使用獎勵信號來進行更新。通常的輸入數據是需要被標記的標量值。
詞向量模型:
我們可以將一句話中的每一個詞都轉換成一個向量:輸入數據可以看成是一個 16*D 的矩陣
Word2Vec:
為了去得到這些詞嵌入,我們使用模型 “Word2Vec”。簡單的說,這個模型根據上下文的語境來推斷出每個詞的詞向量。如果兩個個詞在上下文的語境中,可以被互相替換,那么這兩個詞的距離就非常近。在自然語言中,上下文的語境對分析詞語的意義是非常重要的。這個模型的作用就是從一大堆句子(以 Wikipedia 為例)中為每個獨一無二的單詞進行建模,并且輸出一個唯一的向量。Word2Vec 模型的輸出被稱為一個嵌入矩陣。
這個嵌入矩陣包含訓練集中每個詞的一個向量。傳統來講,這個嵌入矩陣中的詞向量數據會很大。
Word2Vec 模型根據數據集中的每個句子進行訓練,并且以一個固定窗口在句子上進行滑動,根據句子的上下文來預測固定窗口中間那個詞的向量。然后根據一個損失函數和優化方法,來對這個模型進行訓練。
具體的可以參考:
https://blog.csdn.net/lilong117194/article/details/82182869
https://blog.csdn.net/lilong117194/article/details/81979522
3.2 循環神經網絡和LSTM
這方面的資料很多,這里不再重復講lstm相關的知識。
3.3 代碼實現
有了上面的基礎,下面就進行貼出代碼,整體流程如下:
1) 制作詞向量,可以使用gensim這個庫,也可以直接用現成的訓練好的
2) 詞和ID的映射
3) 構建RNN網絡架構
4) 訓練模型
5) 測試模型效果
整體的代碼為:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Wed Aug 8 14:13:14 2018 @author: lilong """import numpy as np import time import datetime import os import re from random import randint from os.path import isfile, join import tensorflow as tf""" 使用訓練好的詞向量模型,該矩陣包含400000x50的數據;還有一個是400000的詞典 """wordsList = np.load('wordsList.npy') print('載入word列表:',np.shape(wordsList),type(wordsList)) # 這里轉化為列表,但是是[b'and', b'in', b'a', b'"', b"'s", b'for']的形式,即是二進制編碼。 wordsList = wordsList.tolist() # 轉化為utf-8編碼的形式,['of', 'to', 'and', 'in', 'a', '"', "'s", 'for', '-',] wordsList = [word.decode('UTF-8') for word in wordsList] # 400000x50的嵌入矩陣,這個是訓練好的詞典向量模型 wordVectors = np.load('wordVectors.npy') print('載入文本向量:',wordVectors.shape) #print(wordVectors[home_ndex]) # 得到對應詞典中詞的50維向量""" 解析文件,數據預處理,得到正負評價下的文件 """# 這里得到了所有的正面評價文件夾pos下的文件路徑 pos_files = ['pos/' + f for f in os.listdir('pos/') if isfile(join('pos/', f))] # 這里得到了所有的neg文件夾下的文件路徑 neg_files = ['neg/' + f for f in os.listdir('neg/') if isfile(join('neg/', f))] num_words = [] # 這里的每個txt文件都是一行文本,12500都是正面評價 for pf in pos_files:with open(pf, "r", encoding='utf-8') as f:line = f.readline()counter = len(line.split())num_words.append(counter) print('正面評價完結。。。') # 這里是12500負面評價 for nf in neg_files:with open(nf, "r", encoding='utf-8') as f:line = f.readline()counter = len(line.split())num_words.append(counter) print('負面評價完結。。。') num_files = len(num_words) print('文件總數:', num_files) print('所有的詞的數量:', sum(num_words)) print('平均文件的詞的長度:', sum(num_words) / len(num_words))""" 輔助函數:返回一個數據集的迭代器,用于返回一批訓練集合""" max_seq_num = 250 num_dimensions = 50 # 每個單詞向量的維度,這里和嵌入矩陣的每個詞的維度相同# arr:24 x 250的矩陣 def get_train_batch():labels = []arr = np.zeros([batch_size, max_seq_num])for i in range(batch_size):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 get_test_batch():labels = []arr = np.zeros([batch_size, max_seq_num])for i in range(batch_size):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""" 構建tensorflow圖"""batch_size = 24 # batch的尺寸 lstm_units = 64 # lstm的單元數量 num_labels = 2 # 輸出的類別數 iterations = 200000 # 迭代的次數 # 載入正負樣本的詞典映射 ids = np.load('idsMatrix.npy') print('載入IDS:',ids.shape) tf.reset_default_graph() # 確定好單元的占位符:輸入是24x300,輸出是24x2 labels = tf.placeholder(tf.float32, [batch_size, num_labels]) input_data = tf.placeholder(tf.int32, [batch_size, max_seq_num])# 必須先定義該變量 data = tf.Variable(tf.zeros([batch_size, max_seq_num, num_dimensions]), dtype=tf.float32) # 調用tf.nn.lookup()接口獲得文本向量,該函數返回batch_size個文本的3D張量,用于后續的訓練 data = tf.nn.embedding_lookup(wordVectors, input_data)# 使用tf.contrib.rnn.BasicLSTMCell細胞單元配置lstm的數量 lstmCell = tf.contrib.rnn.BasicLSTMCell(lstm_units) # 配置dropout參數,以此避免過擬合 lstmCell = tf.contrib.rnn.DropoutWrapper(cell=lstmCell, output_keep_prob=0.75) # 最后將LSTM cell和數據輸入到tf.nn.dynamic_rnn函數,功能是展開整個網絡,并且構建一整個RNN模型 # 這里的value認為是最后的隱藏狀態,該向量將重新確定維度,然后乘以一個權重加上偏置,最終獲得lable value, _ = tf.nn.dynamic_rnn(lstmCell, data, dtype=tf.float32)weight = tf.Variable(tf.truncated_normal([lstm_units, num_labels])) bias = tf.Variable(tf.constant(0.1, shape=[num_labels])) value = tf.transpose(value, [1, 0, 2]) last = tf.gather(value, int(value.get_shape()[0]) - 1) prediction = (tf.matmul(last, weight) + bias)# 定義正確的預測函數和正確率評估參數 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_v2(logits=prediction, labels=labels)) optimizer = tf.train.AdamOptimizer().minimize(loss)sess = tf.InteractiveSession() saver = tf.train.Saver() sess.run(tf.global_variables_initializer())tf.summary.scalar('loss',loss) tf.summary.scalar('Accrar',accuracy) merged=tf.summary.merge_all() logdir='tensorboard/'+ datetime.datetime.now().strftime("%Y%m%d-%H%M%S")+"/" writer=tf.summary.FileWriter(logdir,sess.graph)iterations = 100000 for i in range(iterations):# 下個批次的數據next_batch, next_batch_labels = get_train_batch()sess.run(optimizer,{input_data: next_batch, labels: next_batch_labels}) # 每50次寫入一次leadboardif(i%50==0):summary=sess.run(merged,{input_data: next_batch, labels: next_batch_labels})writer.add_summary(summary,i)if (i%1000==0):loss_ = sess.run(loss, {input_data: next_batch, labels: next_batch_labels})accuracy_=(sess.run(accuracy, {input_data: next_batch, labels: next_batch_labels})) * 100print("iteration:{}/{}".format(i+1, iterations),"\nloss:{}".format(loss_),"\naccuracy:{}".format(accuracy_)) print('..........') # 每10000次保存一下模型if(i%10000==0 and i!=0):save_path=saver.save(sess,"models/pretrained_lstm.ckpt",gloal_step=i)print("saved to %s"% save_path)writer.close()運行結果:
載入word列表: (400000,) <class 'numpy.ndarray'> 載入文本向量: (400000, 50) 正面評價完結。。。 負面評價完結。。。 文件總數: 25000 所有的詞的數量: 5844680 平均文件的詞的長度: 233.7872 載入IDS: (25000, 250) iteration:1/100000 loss:0.7998574376106262 accuracy:58.33333134651184 .......... iteration:1001/100000 loss:0.6967594027519226 accuracy:54.16666865348816 .......... iteration:2001/100000 loss:0.6576123237609863 accuracy:62.5 .......... iteration:3001/100000 loss:0.6840474605560303 accuracy:62.5 .......... iteration:4001/100000 loss:0.6713441014289856 accuracy:58.33333134651184 .......... iteration:5001/100000 loss:0.6841540932655334 accuracy:54.16666865348816 .......... iteration:6001/100000 loss:0.7225241661071777 accuracy:66.66666865348816 .......... . . .注意:這里的所有運行中用到的準備文件都是提前訓練好的,下面會有介紹。
由于時間問題這里沒有跑完所有的訓練迭代輪數,但可以看到在每個batch下的損失率和準確率。
下面拆解整體的流程:
(1) 導入數據
首先,我們需要去創建詞向量。為了簡單起見,我們使用訓練好的模型來創建。
作為該領域的一個最大玩家,Google 已經幫助我們在大規模數據集上訓練出來了 Word2Vec 模型,包括 1000 億個不同的詞!在這個模型中,谷歌能創建 300 萬個詞向量,每個向量維度為 300。
在理想情況下,我們將使用這些向量來構建模型,但是因為這個單詞向量矩陣相當大(3.6G),我們用另外一個現成的小一些的,該矩陣由 GloVe 進行訓練得到。矩陣將包含 400000 個詞向量,每個向量的維數為 50。上面的代碼也是用的這個詞向量。
我們將導入兩個不同的數據結構,一個是包含 400000 個單詞的 Python 列表,這個相當于詞典,主要是為了產生新輸入的句子中的每一個詞的索引值。一個是包含所有單詞向量值得 400000*50 維的嵌入矩陣。
也可以在詞庫中搜索單詞,比如 “baseball”,然后可以通過訪問嵌入矩陣來得到相應的向量,如下:
baseballIndex = wordsList.index('baseball') wordVectors[baseballIndex]所以詞向量庫的詞和列表的詞的索引是一一對應的。
(2) 訓練數據中的每個樣本的長度的確定:
在整個訓練集上面構造索引之前,我們先花一些時間來可視化我們所有的數據。這將幫助我們去決定如何設置最大序列長度的最佳值。訓練集我們使用的是 IMDB 數據集。但這個值在很大程度上取決于你輸入的數據。這里的數據集包含 25000 條電影數據,其中 12500 條正向數據,12500 條負向數據。這些數據都是存儲在一個文本文件中,首先我們需要做的就是去解析這個文件。正向數據包含在一個文件中,負向數據包含在另一個文件中。
可視化為:
可視化圖:
由圖可以看出大部分的文本都在230之內,這里我的設置是250,也就是為訓練樣本中的每個樣本提前開辟一個空間,用于存儲樣本向量。
(3) 得到索引矩陣
這是一個計算成本非常高的過程,這里使用的是提前運行保存的索引矩陣文件。當然了如果是自己的項目,那肯定要自己跑代碼,等運行結果了。代碼如下:
運行會得到全部電影訓練集的索引矩陣,大小是25000 * 250 的矩陣
(4) 得到詞向量:
對于一個新輸入的句子或者文本,為了得到其詞向量,我們可以使用 TensorFlow 的嵌入函數:tf.nn.embedding_lookup。這個函數有兩個參數,一個是嵌入矩陣(在我們的情況下是詞向量矩陣),另一個是每個詞對應的索引。如下:
運行結果:
輸出數據是一個 10*50 的詞矩陣,其中包括 10 個詞,每個詞的向量維度是 50,就是去找到這些詞對應的向量,這里只是作為一個示例。
數據管道如下:
(5) 批訓練集函數:
def getTrainBatch():和def getTestBatch():
上面兩個函數的功能就是構造訓練集和測試集的每一批樣本,可以發現有如下區間:
[1?11500]([11500?12500][12500?13500])[13500?25000][1?11500]([11500?12500][12500?13500])[13500?25000]
訓練集中的每一批次正負樣本都有,又能保證測試集里的樣本沒有在訓練集中出現過。
(6) RNN Model
- 當確定好好兩個占位符,一個用于數據輸入,另一個用于標簽數據。對于占位符,最重要的一點就是確定好維度。這里的標簽占位符代表一組值,每一個值都為 [1,0] 或者 [0,1],這個取決于數據是正向的還是負向的。輸入占位符,是一個整數化的索引數組。一旦,我們設置了我們的輸入數據占位符,我們可以調用 tf.nn.embedding_lookup() 函數來得到我們的詞向量。該函數最后將返回一個三維向量,第一個維度是批處理大小,第二個維度是句子長度,第三個維度是詞向量長度。
- 堆棧 LSTM 網絡是一個比較好的網絡架構。也就是前一個LSTM 隱藏層的輸出是下一個LSTM的輸入。堆棧LSTM可以幫助模型記住更多的上下文信息,但是帶來的弊端是訓練參數會增加很多,模型的訓練時間會很長,過擬合的幾率也會增加。如果你想了解更多有關堆棧LSTM,可以查看TensorFlow的官方教程。
- dynamic RNN 函數的第一個輸出可以被認為是最后的隱藏狀態向量。這個向量將被重新確定維度,然后乘以最后的權重矩陣和一個偏置項來獲得最終的輸出值。
- 標準的交叉熵損失函數來作為損失值。對于優化器,我們選擇 Adam,并且采用默認的學習率
(7) 超參數調整
選擇合適的超參數來訓練你的神經網絡是至關重要的。你會發現你的訓練損失值與你選擇的優化器(Adam,Adadelta,SGD,等等),學習率和網絡架構都有很大的關系。特別是在RNN和LSTM中,單元數量和詞向量的大小都是重要因素。
- 學習率:RNN最難的一點就是它的訓練非常困難,因為時間步驟很長。那么,學習率就變得非常重要了。如果我們將學習率設置的很大,那么學習曲線就會波動性很大,如果我們將學習率設置的很小,那么訓練過程就會非常緩慢。根據經驗,將學習率默認設置為 0.001 是一個比較好的開始。如果訓練的非常緩慢,那么你可以適當的增大這個值,如果訓練過程非常的不穩定,那么你可以適當的減小這個值。
- 優化器:這個在研究中沒有一個一致的選擇,但是 Adam 優化器被廣泛的使用。
- LSTM單元的數量:這個值很大程度上取決于輸入文本的平均長度。而更多的單元數量可以幫助模型存儲更多的文本信息,當然模型的訓練時間就會增加很多,并且計算成本會非常昂貴。
- 詞向量維度:詞向量的維度一般我們設置為50到300。維度越多意味著可以存儲更多的單詞信息,但是你需要付出的是更昂貴的計算成本。
(8) 訓練過程
最好使用GPU進行訓練,訓練會用幾個小時的時間,當然了,這要看硬件。。
上面也就是訓練的過程,也可以同時在訓練的同時,通過保存的模型來進行測試集的測試,但是我這里一直報錯:
NotFoundError (see above for traceback): Key rnn/basic_lstm_cell/bias not found in checkpoint我的這個心啊。。。
改天再搞!!!
4. 語法使用
在使用中需要注意的地方有:
4.1 tensorflow的tf.argmax() 用法:
tf.argmax(input, dimension, name=None)
- dimension=0 按列找
- dimension=1 按行找
- tf.argmax()返回最大數值的下標
通常和tf.equal()一起使用,計算模型準確度,下面是使用:
輸出:
4 [3 2 2] [2 0 2 0]4.2 tf.gather用法
簡單說就是獲得相應下標的數據:
import tensorflow as tftemp = tf.range(0,10)*10 + tf.constant(1,shape=[10]) temp2 = tf.gather(temp,[1,5,9]) with tf.Session() as sess:print (tf.range(0,10)*10)print (tf.constant(1,shape=[10]))print (sess.run(temp))print (sess.run(temp2))輸出:
Tensor("mul_1:0", shape=(10,), dtype=int32) Tensor("Const_1:0", shape=(10,), dtype=int32) [ 1 11 21 31 41 51 61 71 81 91] [11 51 91]注意:
macOS下的新建文件夾會自動生成一個.DS_Store的文件,會影響文件的讀取,下面簡單介紹下:
DS_Store 是給Finder用來存儲這個文件夾的顯示屬性的:比如文件圖標的擺放位置。刪除以后的副作用就是這些信息的失去。
2 關閉.DS_Store的方法:
步驟一:刪除所有隱藏.DS_store文件,打開命令行窗口
步驟二: 設置不再產生選項, 執行如下命令
defaults write com.apple.desktopservices DSDontWriteNetworkStores true本文的代碼是學習自:《pytho自然語言處理實戰 核心技術與算法》
https://github.com/nlpinaction/learning-nlp/tree/master/chapter-8/sentiment-analysis
參考:https://www.aliyun.com/jiaocheng/517649.html
總結
以上是生活随笔為你收集整理的情感分析之电影评论分析-基于Tensorflow的LSTM的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高管的定义是什么意思(高管的定义)
- 下一篇: 笔记本线程数是什么意思(原始地址线程数是