[深度學習]-基於tensorflow的CNN和RNN-LSTM文本情感分析對比

1. 背景介紹

如今科技日益發展、網絡技術不斷深入到大衆生活中,貼吧、網站、電子郵件,用戶評論等使得人們有更多的便捷方式在網絡中發表自己的意見和看法。這些數量龐大的文本中的情感信息有着極大的研究價值和實用價值。而如何能夠從衆多文本信息和數據中準確而高效地分析出文本中所蘊含的情感,並判斷情感極性,對情感做出分類,是目前自然語言處理中的一個熱門研究課題。關於文本情感分析,在此之前國內外專家和學者們都已經做了大量的研究,其中運用深度學習來對文本進行情感分析是常用的文本情感分析方法。神經網絡模型通過學習和訓練後,能有效地模擬人腦的學習方式,對大量的輸入文本信息進行高效的分析,並對文本中的情感進行判斷,非常適合用於文本情感分析的研究中。

2. 數據集介紹

Google 已經幫助我們在大規模數據集上訓練出來了 Word2Vec 模型,它包括 1000 億個不同的詞,在這個模型中,谷歌能創建300萬個詞向量,每個向量維度爲 300。在理想情況下,我們將使用這些向量來構建模型,但是因爲這個單詞向量矩陣太大了**(3.6G**),因此在此次研究中我們將使用一個更加易於管理的矩陣,該矩陣由 GloVe 進行訓練得到。矩陣將包含 400000 個詞向量,每個向量的維數爲 50

我們將導入兩個不同的數據結構,一個是包含 400000 個單詞的 Python 列表(wordsList.npy),一個是包含所有單詞向量值的 400000*50 維的嵌入矩陣 (wordVectors.npy)。

數據和代碼可以從下面連接中下載
https://github.com/adeshpande3/LSTM-Sentiment-Analysis
在這裏插入圖片描述

2.0 wordsList.npy

一個是包含 400000 個單詞的 Python 列表,它裏面每個單詞對應的位置就是 wordVectors裏相應詞向量的位置
例子:如果我們要查找baseball這個詞的相應詞向量

import numpy as np
import tensorflow as tf
import os as os
import matplotlib.pyplot as plt
from os import listdir
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(wordsList)
print(wordVectors.shape)

baseballIndex = wordsList.index('baseball')
print(baseballIndex)
print(wordVectors[baseballIndex])

執行結果

Loaded the word list!
Loaded the word vectors!
400000
(400000, 50)
1444
[-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 ]

2.1 wordVectors.npy

一個是包含所有單詞向量值的 400000*50 維的嵌入矩陣
假如我們有這麼一句話“I thought the movie was incredible and inspiring” ,一個10個詞,那麼它們在wordVectors中的詞向量是什麼?
代碼實現去查找它們相應的詞向量

import numpy as np
import tensorflow as tf
import os as os
import matplotlib.pyplot as plt
from os import listdir
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')
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
with tf.Session() as sess:
    print(tf.nn.embedding_lookup(wordVectors, firstSentence).eval().shape)

執行結果

(10,)
[    41    804 201534   1005     15   7446      5  13767      0      0]
(10, 50)

2.2 idsMatrix.npy

IMDB(Internet Movie Database互聯網電影數據庫)數據集,我們在這個數據集上做的訓練和測試。這個數據集包含 25000 條電影數據,其中 12500 條正向數據在positiveReviews目錄下,12500 條負向數據在negativeReviews目錄下。我們將25000個文本中的23000個文本評論作爲訓練集,將剩下的2000個文本評論作爲測試集

下面代碼是把positiveReviews和negativeReviews目錄下電影評論數據轉化爲Numpy的數據集
25000 x 250, 一共25000條電影評論,最長的評論有250單詞

import numpy as np
import tensorflow as tf
import os as os
import matplotlib.pyplot as plt
from os import listdir

file_path = 'D:/BaiduNetdiskDownload/'
positiveReviews_path = file_path+'positiveReviews/'
negativeReviews_path = file_path +'negativeReviews/'
positiveFiles = [positiveReviews_path + f for f in listdir(positiveReviews_path) if os.path.isfile(os.path.join(positiveReviews_path, f))]
negativeFiles = [negativeReviews_path + f for f in listdir(negativeReviews_path) if os.path.isfile(os.path.join(negativeReviews_path, f))]

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

maxSeqLength = 250

ids = np.zeros((25000, maxSeqLength), dtype='int32')
fileCounter = 0
for pf in positiveFiles:
    with open(pf, "r", encoding='utf-8') 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", encoding='utf-8') 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

np.save('idsMatrix', ids)

2.2.0 文本預處理

文本預處理
在這裏插入圖片描述
輸入文本,在將輸入文本轉化成向量之前,我們需要將標點符號、括號、問號等刪去,只留下字母、數字和字符, 同時將大寫字母轉化爲小寫。

效果如下圖
在這裏插入圖片描述

2.2.0 爲什麼把詞轉化爲詞向量

我們希望創建這種詞向量的方式是可以表示單詞及其在上下文中意義的。例如,我們希望單詞 “love” 和 “adore” 這兩個詞在向量空間中是有一定的相關性的,因爲他們的意思相似,而且都在類似的上下文中使用,因此他們的空間相距距離會相對較小。而“love”、“adore”這兩個單詞與單詞“baseball”的意思有很大的不同,詞性也不相同,那麼“love”、“adore”這兩個單詞的向量與單詞“baseball”的向量相距距離就會相對較大。單詞的向量表示也被稱之爲詞嵌入。

在這裏插入圖片描述
爲了得到這些詞嵌入,我們採用一個很著名的模型 “Word2Vec”。“Word2Vec”是近幾年很火的算法,它通過神經網絡機器學習算法來訓練N-gram 語言模型,並在訓練過程中求出word所對應的vector的方法。它是將詞表徵爲實數值向量的一種高效的算法模型,其利用深度學習的思想,可以通過訓練,把對文本內容的處理簡化爲 K 維向量空間中的向量運算,而向量空間上的相似度可以用來表示文本語義上的相似。在這個模型中,每個詞的詞向量是根據上下文的語境來進行推斷的,如果兩個詞在上下文的語境中可以被互換,那麼這就表示這兩個詞的意思相似,詞性相似,那麼他們的詞向量中相距距離就非常近。在自然語言中,上下文的語境對分析詞語的意義是非常重要的。

簡單來說,Word2Vec這個模型的作用就是從一大堆句子(以 Wikipedia 爲例)中爲每個獨一無二的單詞進行建模,並且輸出一個唯一的向量,Word2Vec 模型的輸出被稱爲一個嵌入矩陣。該嵌入矩陣將包含訓練語料庫中每個不同單詞的向量。 傳統上,嵌入矩陣可以包含超過300萬個單詞向量。

Word2Vec模型是通過對數據集中的每個句子進行訓練,在其上滑動一個固定大小的窗口,並試圖預測窗口的中心詞,給出其他詞。使用損失函數和優化程序,該模型爲每個唯一字生成向量。這個訓練過程的細節可能會變得有點複雜,所以我們現在要跳過細節,但這裏主要的一點是,任何深度學習方法對於NLP任務的輸入可能都會有單詞向量作爲輸入。

Google 已經幫助我們在大規模數據集上訓練出來了 Word2Vec 模型,它包括 1000 億個不同的詞,在這個模型中,谷歌能創建300萬個詞向量,每個向量維度爲 300。在理想情況下,我們將使用這些向量來構建模型,但是因爲這個單詞向量矩陣太大了(3.6G),因此在此次研究中我們將使用一個更加易於管理的矩陣,該矩陣由 GloVe 進行訓練得到。矩陣將包含 400000 個詞向量,每個向量的維數爲 50

2.3 Helper Functions

from random import randint
import numpy as np

maxSeqLength = 250
batchSize = 24
ids = np.load('./training_data/idsMatrix.npy')

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

3. RNN網絡訓練

import tensorflow as tf
import numpy as np
import datetime
from random import randint

maxSeqLength = 250
batchSize = 24
lstmUnits = 64
numClasses = 2
iterations = 100000
numDimensions = 50 #Dimensions for each word vector

maxSeqLength = 250
batchSize = 24
ids = np.load('./training_data/idsMatrix.npy')

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


wordVectors = np.load('./training_data/wordVectors.npy')
print('Loaded the word vectors!')

tf.reset_default_graph()

labels = tf.placeholder(tf.float32, [batchSize, numClasses])
input_data = tf.placeholder(tf.int32, [batchSize, maxSeqLength])

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

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)

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)

correctPred = tf.equal(tf.argmax(prediction,1), tf.argmax(labels,1))
accuracy = tf.reduce_mean(tf.cast(correctPred, tf.float32))

loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=labels))
optimizer = tf.train.AdamOptimizer().minimize(loss)

tf.summary.scalar('Loss', loss)
tf.summary.scalar('Accuracy', accuracy)
merged = tf.summary.merge_all()
logdir = "tensorboard/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + "/"


sess = tf.InteractiveSession()
saver = tf.train.Saver()
sess.run(tf.global_variables_initializer())

writer = tf.summary.FileWriter(logdir, sess.graph)

for i in range(iterations):
    #Next Batch of reviews
    nextBatch, nextBatchLabels = getTrainBatch();
    sess.run(optimizer, {input_data: nextBatch, labels: nextBatchLabels})

    #Write summary to Tensorboard
    if (i % 50 == 0):
        summary = sess.run(merged, {input_data: nextBatch, labels: nextBatchLabels})
        writer.add_summary(summary, i)

    #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)
writer.close()

4. CNN網絡訓練

5. CNN與RNN訓練結果對比

參考文獻

https://blog.csdn.net/qq_33547191/article/details/86075275
https://www.oreilly.com/content/perform-sentiment-analysis-with-lstms-using-tensorflow/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章