深度学习在情感分析中的应用
然語言情感分析簡介
情感分析無處不在,它是一種基于自然語言處理的分類技術。其主要解決的問題是給定一段話,判斷這段話是正面的還是負面的。例如在亞馬遜網站或者推特網站中,人們會發表評論,談論某個商品、事件或人物。商家可以利用情感分析工具知道用戶對自己的產品的使用體驗和評價。當需要大規模的情感分析時,肉眼的處理能力就變得十分有限了。情感分析的本質就是根據已知的文字和情感符號,推測文字是正面的還是負面的。處理好了情感分析,可以大大提升人們對于事物的理解效率,也可以利用情感分析的結論為其他人或事物服務,比如不少基金公司利用人們對于某家公司、某個行業、某件事情的看法態度來預測未來股票的漲跌。
進行情感分析有如下難點:
- 第一,文字非結構化,有長有短,很難適合經典的機器學習分類模型。
- 第二,特征不容易提取。文字可能是談論這個主題的,也可能是談論人物、商品或事件的。人工提取特征耗費的精力太大,效果也不好。
- 第三,詞與詞之間有聯系,把這部分信息納入模型中也不容易。
本章探討深度學習在情感分析中的應用。深度學習適合做文字處理和語義理解,是因為深度學習結構靈活,其底層利用詞嵌入技術可以避免文字長短不均帶來的處理困難。使用深度學習抽象特征,可以避免大量人工提取特征的工作。深度學習可以模擬詞與詞之間的聯系,有局部特征抽象化和記憶功能。正是這幾個優勢,使得深度學習在情感分析,乃至文本分析理解中發揮著舉足輕重的作用。
順便說一句,推特已經公開了他們的情感分API(http://help.sentiment140.com/api)。讀者可以把其整合到自己的應用程序中,也可以試著開發一套自己的API。下面通過一個電影評論的例子詳細講解深度學習在情感分析中的關鍵技術。
首先下載http://ai.stanford.edu/~amaas/data/sentiment/中的數據。
輸入下文安裝必要的軟件包:
pip install numpy scipy pip install scikit-learn pip install pillow pip install h5py下面處理數據。Keras 自帶了imdb 的數據和調取數據的函數,直接調用load.data()就可以了。
import keras import numpy as np from keras.datasets import imdb (X_train, y_train), (X_test, y_test) = imdb.load_data()先看一看數據長什么樣子的。輸入命令:
X_train[0]我們可以看到結果:
原來,Keras 自帶的load_data 函數幫我們從亞馬遜S3 中下載了數據,并且給每個詞標注了一個索引(index),創建了字典。每段文字的每個詞對應了一個數字。
print(y[:10])得到array([1, 0, 0, 1, 0, 0, 1, 0, 1, 0]),可見y 就是標注,1 表示正面,0 表示負面。
print(X_train.shape) print(y_train.shape)我們得到的兩個張量的維度都為(25000,)。
接下來可以看一看平均每個評論有多少個字:
avg_len = list(map(len, X_train)) print(np.mean(avg_len))可以看到平均字長為238.714。
為了直觀顯示,這里畫一個分布圖(見圖7.1):
import matplotlib.pyplot as plt plt.hist(avg_len, bins = range(min(avg_len), max(avg_len) + 50, 50)) plt.show()注意,如果遇到其他類型的數據,或者自己有數據,那么就得自己寫一套處理數據的腳本。大致步驟如下。
- 第一,文字分詞。英語分詞可以按照空格分詞,中文分詞可以參考jieba。
- 第二,建立字典,給每個詞標號。
- 第三,把段落按字典翻譯成數字,變成一個array。
接下來就開始建模了。
文字情感分析建模
詞嵌入技術
為了克服文字長短不均和將詞與詞之間的聯系納入模型中的困難,人們使用了一種技術——詞嵌入。簡單說來,就是給每個詞賦一個向量,向量代表空間里的點,含義接近的詞,其向量也接近,這樣對于詞的操作就可以轉化為對于向量的操作了,在深度學習中,這被叫作張量(tensor)。用張量表示詞的好處在于:第一,可以克服文字長短不均的問題,因為如果每個詞已經有對應的詞向量,那么對于長度為N 的文本,只要選取對應的N 個詞所代表的向量并按文本中詞的先后順序排在一起,就是輸入張量了,其中每個詞向量的維度都是一樣的。第二,詞本身無法形成特征,但是張量就是抽象的量化,它是通過多層神經網絡的層層抽象計算出來的。第三,文本是由詞組成的,文本的特征可以由詞的張量組合。文本的張量蘊含了多個詞之間的組合含義,這可以被認為是文本的特征工程,進而為機器學習文本分類提供基礎。
詞的嵌入最經典的作品是Word2Vec,可以參見:https://code.google.com/archive/p/word2vec/。通過對具有數十億詞的新聞文章進行訓練,Google 提供了一組詞向量的結果,可以從http://word2vec.googlecode.com/svn/trunk/獲取。其主要思想依然是把詞表示成向量的形式,而不是One Hot 編碼。圖7.2展示了這個模型里面詞與詞的關系。
多層全連接神經網絡訓練情感分析
不同于已經訓練好的詞向量,Keras 提供了設計嵌入層(Embedding Layer)的模板。只要在建模的時候加一行Embedding Layer 函數的代碼就可以。注意,嵌入層一般是需要通過數據學習的,讀者也可以借用已經訓練好的嵌入層比如Word2Vec 中預訓練好的詞向量直接放入模型,或者把預訓練好的詞向量作為嵌入層初始值,進行再訓練。Embedding 函數定義了嵌入層的框架,其一般有3 個變量:字典的長度(即文本中有多少詞向量)、詞向量的維度和每個文本輸入的長度。注意,前文提到過每個文本可長可短,所以可以采用Padding 技術取最長的文本長度作為文本的輸入長度,而不足長度的都用空格填滿,即把空格當成一個特殊字符處理。空格本身一般也會被賦予詞向量,這可以通過機器學習訓練出來。Keras 提供了sequence.pad_sequences 函數幫我們做文本的處理和填充工作。
先把代碼進行整理:
from keras.models import Sequential from keras.layers import Dense from keras.layers import Flatten from keras.layers.embeddings import Embedding from keras.preprocessing import sequence import keras import numpy as np from keras.datasets import imdb (X_train, y_train), (X_test, y_test) = imdb.load_data()使用下面的命令計算最長的文本長度:
m = max(list(map(len, X_train)), list(map(len, X_test))) print(m)從中我們會發現有一個文本特別長,居然有2494 個字符。這種異常值需要排除,考慮到文本的平均長度為230 個字符,可以設定最多輸入的文本長度為400 個字符,不足400 個字符的文本用空格填充,超過400 個字符的文本截取400 個字符,Keras 默認截取后400 個字符。
maxword = 400 X_train = sequence.pad_sequences(X_train, maxlen = maxword) X_test = sequence.pad_sequences(X_test, maxlen = maxword) vocab_size = np.max([np.max(X_train[i]) for i in range(X_train.shape[0])]) + 1這里1 代表空格,其索引被認為是0。
下面先從最簡單的多層神經網絡開始嘗試:
首先建立序列模型,逐步往上搭建網絡。
model = Sequential() model.add(Embedding(vocab_size, 64, input_length = maxword))第一層是嵌入層,定義了嵌入層的矩陣為vocab_size 64。每個訓練段落為其中的maxword 64 矩陣,作為數據的輸入,填入輸入層。
model.add(Flatten())把輸入層壓平,原來是maxword × 64 的矩陣,現在變成一維的長度為maxword × 64的向量。
接下來不斷搭建全連接神經網絡,使用relu 函數。relu 是簡單的非線性函數:f(x) =max(0; x)。注意到神經網絡的本質是把輸入進行非線性變換。
model.add(Dense(2000, activation = 'relu')) model.add(Dense(500, activation = 'relu')) model.add(Dense(200, activation = 'relu')) model.add(Dense(50, activation = 'relu')) model.add(Dense(1, activation = 'sigmoid'))這里最后一層用Sigmoid,預測0,1 變量的概率,類似于logistic regression 的鏈接函數,目的是把線性變成非線性,并把目標值控制在0~1。因此這里計算的是最后輸出的是0 或者1 的概率。
model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy']) 2 print(model.summary())這里有幾個概念要提一下:交叉熵(Cross Entropy)和Adam Optimizer。
交叉熵主要是衡量預測的0,1 概率分布和實際的0,1 值是不是匹配,交叉熵越小,說明匹配得越準確,模型精度越高。
其具體形式為
這里把交叉熵作為目標函數。我們的目的是選擇合適的模型,使這個目標函數在未知數據集上的平均值越低越好。所以,我們要看的是模型在測試數據(訓練時需要被屏蔽)上的表現。
Adam Optimizer 是一種優化辦法,目的是在模型訓練中使用的梯度下降方法中,合理地動態選擇學習速度(Learning Rate),也就是每步梯度下降的幅度。直觀地說,如果在訓練中損失函數接近最小值了,則每步梯度下降幅度自然需要減小,而如果損失函數的曲線還很陡,則下降幅度可以稍大一些。從優化的角度講,深度學習網絡還有其他一些梯度下降優化方法,比如Adagrad 等。它們的本質都是解決在調整神經網絡模型過程中如何控制學習速度的問題。
Keras 提供的建模API 讓我們既能訓練數據,又能在驗證數據時看到模型測試效果。
model.fit(X_train, y_train, validation_data = (X_test, y_test), epochs = 20,batch_size = 100, verbose = 1) score = model.evaluate(X_test, y_test)其精確度大約在85%。如果多做幾次迭代,則精確度會更高。讀者可以試著嘗試一下多跑幾個循環。
以上提到的是最常用的多層全連接神經網絡模型。它假設模型中的所有上一層和下一層是互相連接的,是最廣泛的模型。
卷積神經網絡訓練情感分析
全連接神經網絡幾乎對網絡模型沒有任何限制,但缺點是過度擬合,即擬合了過多噪聲。全連接神經網絡模型的特點是靈活、參數多。在實際應用中,我們可能會對模型加上一些限制,使其適合數據的特點。并且由于模型的限制,其參數會大幅減少。這降低了模型的復雜度,模型的普適性進而會提高。
接下來我們介紹卷積神經網絡(CNN)在自然語言的典型應用。
在自然語言領域,卷積的作用在于利用文字的局部特征。一個詞的前后幾個詞必然和這個詞本身相關,這組成該詞所代表的詞群。詞群進而會對段落文字的意思進行影響,決定這個段落到底是正向的還是負向的。對比傳統方法,利用詞包(Bag of Words),和TF-IDF 等,其思想有相通之處。但最大的不同點在于,傳統方法是人為構造用于分類的特征,而深度學習中的卷積讓神經網絡去構造特征。
以上便是卷積在自然語言處理中有著廣泛應用的原因。
接下來介紹如何利用Keras 搭建卷積神經網絡來處理情感分析的分類問題。下面的代碼構造了卷積神經網絡的結構。
from keras.layers import Dense, Dropout, Activation, Flatten from keras.layers import Conv1D, MaxPooling1D model = Sequential() model.add(Embedding(vocab_size, 64, input_length = maxword)) model.add(Conv1D(filters = 64, kernel_size = 3, padding = 'same', activation= 'relu')) model.add(MaxPooling1D(pool_size = 2)) model.add(Dropout(0.25)) model.add(Conv1D(filters = 128, kernel_size = 3, padding = 'same',activation= 'relu')) model.add(MaxPooling1D(pool_size = 2)) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(64, activation = 'relu')) model.add(Dense(32, activation = 'relu')) model.add(Dense(1, activation = 'sigmoid')) model.compile(loss = 'binary_crossentropy', optimizer = 'rmsprop', metrics =['accuracy']) print(model.summary())下面對模型進行擬合。
model.fit(X_train, y_train, validation_data = (X_test, y_test), epochs = 20,batch_size = 100) scores = model.evaluate(X_test, y_test, verbose = 1)print(scores)精確度提高了一點,在85.5% 左右。讀者可以試著調整模型的參數,增加訓練次數等,或者使用其他的優化方法。這里還要提一句,代碼里用了一個Dropout 的技巧,大致意思是在每個批量訓練過程中,對每個節點,不論是在輸入層還是隱藏層,都有獨立的概率讓節點變成0。這樣的好處在于,每次批量訓練相當于在不同的小神經網絡中進行計算,當訓練數據大的時候,每個節點的權重都會被調整過多次。另外,在每次訓練的時候,系統會努力在有限的節點和小神經網絡中找到最佳的權重,這樣可以最大化地找到重要特征,避免過度擬合。這就是為什么Dropout 會得到廣泛的應用。
循環神經網絡訓練情感分析
下面介紹如何用長短記憶模型(LSTM)處理情感分類。
LSTM 是循環神經網絡的一種。本質上,它按照時間順序,把信息進行有效的整合和篩選,有的信息得到保留,有的信息被丟棄。在時間t,你獲得到的信息(比如對段落文字的理解)理所應當會包含之前的信息(之前提到的事件、人物等)。LSTM 說,根據我手里的訓練數據,我得找出一個方法來如何進行有效的信息取舍,從而把最有價值的信息保留到最后。那么最自然的想法是總結出一個規律用來處理前一時刻的信息。由于遞歸性,在處理前一個時刻信息時,會考慮到再之前的信息,所以到時間t 時,所有從時間點1 到現在的信息都或多或少地被保留一部分,也會被丟棄一部分。LSTM 對信息的處理主要通過矩陣的乘積運算來實現的(見圖7.3)。
構造LSTM 神經網絡的結構可以使用如下的代碼。
from keras.layers import LSTM model = Sequential() model.add(Embedding(vocab_size, 64, input_length = maxword)) model.add(LSTM(128, return_sequences=True)) model.add(Dropout(0.2)) model.add(LSTM(64, return_sequences=True)) model.add(Dropout(0.2)) model.add(LSTM(32)) model.add(Dropout(0.2)) model.add(Dense(1, activation = 'sigmoid'))然后把模型打包。
model.compile(loss = 'binary_crossentropy', optimizer = 'rmsprop', metrics =['accuracy']) print(model.summary())最后輸入數據集訓練模型。
model.fit(X_train, y_train, validation_data = (X_test, y_test), epochs = 5,batch_size = 100) scores = model.evaluate(X_test, y_test) print(scores)預測的精確度大致為86.7%,讀者可以試著調試不同參數和增加循環次數,從而得到更好的效果。
總結
本章介紹了不同種類的神經網絡,有多層神經網絡(MLP),卷積神經網絡(CNN)和長短記憶模型(LSTM)。它們的共同點是有很多參數,需要通過后向傳播來更新參數。CNN 和LSTM 作為神經網絡的不同類型的模型,需要的參數相對較少,這也反映了它們的一個共性:參數共享。這和傳統的機器學習原理很類似:對參數或者模型加的限制越多,模型的自由度越小,越不容易過度擬合。反過來,模型參數越多,模型越靈活,越容易擬合噪聲,從而對預測造成負面影響。通常,我們通過交叉驗證技術選取最優參數(比如,幾層模型、每層節點數、Dropout 概率等)。最后需要說明的是,情感分析本質是一個分類問題,是監督學習的一種。除了上述模型,讀者也可以試試其他經典機器學習模型,比如SVM、隨機森林、邏輯回歸等,并和神經網絡模型進行比較。
總結
以上是生活随笔為你收集整理的深度学习在情感分析中的应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AI影响将远超互联网 不在乎与AT市值差
- 下一篇: 利用TensorFlow和神经网络来处理