當前位置:
首頁 > 最新 > 現代情感分析方法

現代情感分析方法

編譯:伯樂在線 - Ree Ray

情感分析(Sentiment analysis)是自然語言處理(NLP)方法中常見的應用,尤其是以提煉文本情緒內容為目的的分類。利用情感分析這樣的方法,可以通過情感評分對定性數據進行定量分析。雖然情感充滿了主觀性,但情感定量分析已經有許多實用功能,例如企業藉此了解用戶對產品的反映,或者判別在線評論中的仇恨言論。

情感分析最簡單的形式就是藉助包含積極和消極詞的字典。每個詞在情感上都有分值,通常 +1 代表積極情緒,-1 代表消極。接著,我們簡單累加句子中所有詞的情感分值來計算最終的總分。顯而易見,這樣的做法存在許多缺陷,最重要的就是忽略了語境(context)和鄰近的詞。例如一個簡單的短語「not good」最終的情感得分是 0,因為「not」是 -1,「good」是 +1。正常人會將這個短語歸類為消極情緒,儘管有「good」的出現。

另一個常見的做法是以文本進行「詞袋(bag of words)」建模。我們把每個文本視為 1 到 N 的向量,N 是所有辭彙(vocabulary)的大小。每一列是一個詞,對應的值是這個詞出現的次數。比如說短語「bag of bag of words」可以編碼為 [2, 2, 1]。這個值可以作為諸如邏輯回歸(logistic regression)、支持向量機(SVM)的機器學習演算法的輸入,以此來進行分類。這樣可以對未知的(unseen)數據進行情感預測。注意這需要已知情感的數據通過監督式學習的方式(supervised fashion)來訓練。雖然和前一個方法相比有了明顯的進步,但依然忽略了語境,而且數據的大小會隨著辭彙的大小增加。

Word2Vec 和 Doc2Vec

近幾年,Google 開發了名為 Word2Vec 新方法,既能獲取詞的語境,同時又減少了數據大小。Word2Vec 實際上有兩種不一樣的方法:CBOW(Continuous Bag of Words,連續詞袋)和 Skip-gram。對於 CBOW,目標是在給定鄰近詞的情況下預測單獨的單詞。Skip-gram 則相反:我們希望給定一個單獨的詞(見圖 1)來預測某個範圍的詞。兩個方法都使用人工神經網路(Artificial Neural Networks)來作為它們的分類演算法。首先,辭彙表中的每個單詞都是隨機的 N 維向量。在訓練過程中,演算法會利用 CBOW 或者 Skip-gram 來學習每個詞的最優向量。

圖 1:CBOW 以及 Skip-Gram 結構圖,選自《Efficient Estimation of Word Representations in Vector Space》。W(t) 代表當前的單詞,而w(t-2), w(t-1) 等則是鄰近的單詞。

這些詞向量現在可以考慮到上下文的語境了。這可以看作是利用基本的代數式來挖掘詞的關係(例如:「king」 – 「man」 + 「woman」 = 「queen」)。這些詞向量可以作為分類演算法的輸入來預測情感,有別於詞袋模型的方法。這樣的優勢在於我們可以聯繫詞的語境,並且我們的特徵空間(feature space)的維度非常低(通常約為 300,相對於約為 100000 的辭彙)。在神經網路提取出這些特徵之後,我們還必須手動創建一小部分特徵。由於文本長度不一,將以全體詞向量的均值作為分類演算法的輸入來歸類整個文檔。

然而,即使使用了上述對詞向量取均值的方法,我們仍然忽略了詞序。Quoc Le 和 Tomas Mikolov 提出了 Doc2Vec 的方法對長度不一的文本進行描述。這個方法除了在原有基礎上添加 paragraph / document 向量以外,基本和 Word2Vec 一致,也存在兩種方法:DM(Distributed Memory,分布式內存)和分布式詞袋(DBOW)。DM 試圖在給定前面部分的詞和 paragraph 向量來預測後面單獨的單詞。即使文本中的語境在變化,但 paragraph 向量不會變化,並且能保存詞序信息。DBOW 則利用paragraph 來預測段落中一組隨機的詞(見圖 2)。

圖 2: Doc2Vec 方法結構圖,選自《Distributed Representations of Sentences and Documents》。

一旦經過訓練,paragraph 向量就可以作為情感分類器的輸入而不需要所有單詞。這是目前對 IMDB 電影評論數據集進行情感分類最先進的方法,錯誤率只有 7.42%。當然,如果這個方法不實用,說這些都沒有意義。幸運的是,一個 Python 第三方庫 gensim 提供了 Word2Vec 和 Doc2Vec 的優化版本。

基於 Python 的 Word2Vec 舉例

在本節我們將會展示怎麼在情感分類任務中使用詞向量。gensim 這個庫是 Anaconda 發行版中的標配,你同樣可以利用 pip 來安裝。利用它你可以在自己的語料庫(一個文檔數據集)中訓練詞向量或者導入 C text 或二進位格式的已經訓練好的向量。

我發現讀取谷歌已經訓練好的詞向量尤其管用,這些向量來自谷歌新聞(Google News),由超過千億級別的詞訓練而成,「已經訓練過的詞和短語向量」可以在這裡找到。注意未壓縮的文件有 3.5 G。通過 Google 詞向量我們能夠發現詞與詞之間有趣的關聯:

model.most_similar(positive=[ woman , king ],negative=[ man ],topn=5)

有趣的是它可以發現語法關係,例如識別最高級(superlatives)和動詞詞幹(stems):

「biggest」 – 「big」 + 「small」 = 「smallest」

model.most_similar(positive=[ biggest , small ],negative=[ big ],topn=5)

(u largest ,0.6007465720176697),

「ate」 – 「eat」 + 「speak」 = 「spoke」

model.most_similar(positive=[ ate , speak ],negative=[ eat ],topn=5)

由以上例子可以清楚認識到 Word2Vec 能夠學習詞與詞之間的有意義的關係。這也就是為什麼它對於許多 NLP 任務有如此大的威力,包括在本文中的情感分析。在我們用它解決起情感分析問題以前,讓我們先測試一下 Word2Vec 對詞分類(separate)和聚類(cluster)的本事。我們會用到三個示例詞集:食物類(food)、運動類(sports)和天氣類(weather),選自一個非常棒的網站 Enchanted Learning。因為這些向量有 300 個維度,為了在 2D 平面上可視化,我們會用到 Scikit-Learn』s 中叫作「t-SNE」的降維演算法操作

首先必須像下面這樣取得詞向量:

importnumpyasnp

withopen( food_words.txt , r )asinfile:

food_words=infile.readlines()

withopen( sports_words.txt , r )asinfile:

sports_words=infile.readlines()

withopen( weather_words.txt , r )asinfile:

weather_words=infile.readlines()

defgetWordVecs(words):

vecs=[]

forwordinwords:

word=word.replace( n , )

try:

vecs.append(model[word].reshape((1,300)))

exceptKeyError:

continue

vecs=np.concatenate(vecs)

returnnp.array(vecs,dtype= float )#TSNE expects float type values

food_vecs=getWordVecs(food_words)

sports_vecs=getWordVecs(sports_words)

weather_vecs=getWordVecs(weather_words)

我們接著使用 TSNE 和 matplotlib 可視化聚類,代碼如下:

fromsklearn.manifoldimportTSNE

importmatplotlib.pyplotasplt

ts=TSNE(2)

reduced_vecs=ts.fit_transform(np.concatenate((food_vecs,sports_vecs,weather_vecs)))

#color points by word group to see if Word2Vec can separate them

foriinrange(len(reduced_vecs)):

ifi

#food words colored blue

color= b

elifi>=len(food_vecs)andi<(len(food_vecs)+len(sports_vecs)):

#sports words colored red

color= r

else:

#weather words colored green

color= g

plt.plot(reduced_vecs[i,],reduced_vecs[i,1],marker= o ,color=color,markersize=8)

importnumpyasnp

withopen( food_words.txt , r )asinfile:

food_words=infile.readlines()

withopen( sports_words.txt , r )asinfile:

sports_words=infile.readlines()

withopen( weather_words.txt , r )asinfile:

weather_words=infile.readlines()

defgetWordVecs(words):

vecs=[]

forwordinwords:

word=word.replace( n , )

try:

vecs.append(model[word].reshape((1,300)))

exceptKeyError:

continue

vecs=np.concatenate(vecs)

returnnp.array(vecs,dtype= float )#TSNE 要求浮點型的值

food_vecs=getWordVecs(food_words)

sports_vecs=getWordVecs(sports_words)

weather_vecs=getWordVecs(weather_words)

結果如下:

圖 3:食物類單詞(藍色),運動類單詞(紅色)和天氣類單詞(綠色)T-SNE 集群效果圖。

我們可以從上面的例子看到,Word2Vec 不僅能有效分類不相關的單詞,同樣也能聚類類似的詞。

推特 Emoji 情感分析

現在我們進入下一個常式,利用符號表情作為搜索詞的推特情感分析。我們把這些符號表情作為我們數據的「模糊(fuzzy)」標籤;微笑表情(:-))與積極情緒對應,而皺眉表情(:-()則對應消極情緒。在大約 400,000 條推特數據中,積極和消極的各佔一半(even split)。我們對積極和消極情緒的推特進行了隨機採樣,並按80 / 20 的比例分為了訓練集/ 測試集。我們接著在 Word2Vec 模型上訓練推特。為了避免數據泄露(data leakage),在訓練數據集分類完成以前我們都不會在 Word2Vec 上訓練。為了結構化分類器的輸入,我們對所有推特詞向量取均值。我們會用到 Scikit-Learn 這個第三方庫做大量的機器學習。

我們首先導入我們的數據並訓練 Word2Vec 模型

fromsklearn.cross_validationimporttrain_test_split

withopen( twitter_data/pos_tweets.txt , r )asinfile:

pos_tweets=infile.readlines()

withopen( twitter_data/neg_tweets.txt , r )asinfile:

neg_tweets=infile.readlines()

# 1 代表積極情緒,0 代表消極情緒

y=np.concatenate((np.ones(len(pos_tweets)),np.zeros(len(neg_tweets))))

x_train,x_test,y_train,y_test=train_test_split(np.concatenate((pos_tweets,neg_tweets)),y,test_size=0.2)

# 零星的預處理

defcleanText(corpus):

corpus=[z.lower().replace( n , ).split()forzincorpus]

returncorpus

x_train=cleanText(x_train)

x_test=cleanText(x_test)

n_dim=300

# 初始化模型並創建辭彙表(vocab)

imdb_w2v.build_vocab(x_train)

# 訓練模型 (會花費幾分鐘)

imdb_w2v.train(x_train)

下面我們必須對輸入文本創建詞向量,為了平均推特中的所有詞向量,將用到如下的函數:

# 對訓練數據集創建詞向量,接著進行比例縮放(scale)。

defbuildWordVector(text,size):

vec=np.zeros(size).reshape((1,size))

count=0.

forwordintext:

try:

count+=1.

exceptKeyError:

continue

ifcount!=:

vec/=count

returnvec

對我們的數據集進行縮放是標準化處理的一部分。通過均值為零的高斯分布,意味著大於均值則為積極,小於則為消極。許多機器學習模型要求使用縮放過的數據集來獲得更好的處理效果,尤其是多特徵(例如文本分類)。

fromsklearn.preprocessingimportscale

train_vecs=np.concatenate([buildWordVector(z,n_dim)forzinx_train])

train_vecs=scale(train_vecs)

# 在測試推特數據集中訓練 Word2Vec

imdb_w2v.train(x_test)

最終我們必須創建測試數據向量並進行比例縮放來評估。

# 創建測試推特向量並縮放

test_vecs=np.concatenate([buildWordVector(z,n_dim)forzinx_test])

test_vecs=scale(test_vecs)

下面我們想通過計算測試數據的預測精度來驗證我們的分類器,同時測試它們的 ROC 曲線(Receiver Operating Characteristic,受試者操作特徵曲線)。當模型參數調節時,ROC 曲線會測試分類器的真陽性(true-positive)以及假陽性(false-positive)。本例中,我們通過調節邊界閾值概率(cut-off threshold probability)將某條推特分類為積極或消極情緒。通常,更希望得到最大化的真陽性和最小化的假陽性,也就是 ROC 曲線下方最大的區域(AUC)。通過這裡更多地了解 ROC 曲線。

開始訓練我們的分類器,本例對邏輯回歸(Logistic Regression)使用隨機梯度下降(Stochastic Gradient Descent)。

# 使用分類演算法(例如:隨機邏輯回歸(Stochastic Logistic Regression)來訓練數據集,接著從 sklearn.linear_model 導入 SGDClassifier 進行模型處理)

lr=SGDClassifier(loss= log ,penalty= l1 )

lr.fit(train_vecs,y_train)

print Test Accuracy: %.2f %lr.score(test_vecs,y_test)

我們利用 matplotlib 和 Scikit-Learn 的 metric 包中的 roc_curve 創建 ROC 曲線來評估。

# 創建 ROC 曲線

fromsklearn.metricsimportroc_curve,auc

importmatplotlib.pyplotasplt

pred_probas=lr.predict_proba(test_vecs)[:,1]

fpr,tpr,_=roc_curve(y_test,pred_probas)

roc_auc=auc(fpr,tpr)

plt.plot(fpr,tpr,label= area = %.2f %roc_auc)

plt.plot([,1],[,1], k-- )

plt.xlim([0.0,1.0])

plt.ylim([0.0,1.05])

plt.legend(loc= lower right )

plt.show()

曲線結果如下:

圖 4:邏輯分類器對推特訓練數據的 ROC 曲線

沒有創建任何特徵以及最小化的文本預處理,利用 Scikit-Learn 提供的簡單線性模型我們已經實現了 73% 的測試準確率。有趣的是,移除了標點符號實際上反而降低了準確率,說明當「?」或「!」出現時,Word2Vec 能夠找到有趣的特徵。將這些標點視為獨立的單詞,訓練更長的時間,做更多的預處理,調節 Word2Vec 和分類器中的參數這些方法都有助於準確率的提升。我已經發現配合使用人工神經網路(ANN)能夠提高大概 5% 的準確率。因為 Scikit-Learn 沒有提供 ANN 分類器的實現工具,所以我自己寫了一個:

fromNNetimportNeuralNet

nnet=NeuralNet(100,learn_rate=1e-1,penalty=1e-8)

maxiter=1000

batch=150

_=nnet.fit(train_vecs,y_train,fine_tune=False,maxiter=maxiter,SGD=True,batch=batch,rho=0.9)

print Test Accuracy: %.2f %nnet.score(test_vecs,y_test)

最終準確率為 77%。不論什麼機器學習任務,選對模型的藝術性大於科學性。如果你想用我寫的庫你可以在這找到。友情提示,它看起來比較亂並且沒有定期維護!如果你想貢獻代碼歡迎 fork 我的代碼倉。它非常需要被寵幸(TLC)。

基於 Doc2Vec 的電影評論分析

在推特的例子中,使用詞向量的均值效果良好。這是因為推特通常是幾十個詞的長度,即使取均值也能保留相關的特徵。然而,一旦我們上升到段落的規模,忽略詞序和上下文信息將面臨丟失大量特徵的風險。這樣的情況下更適合使用 Doc2Vec 創建輸入特徵。我們將使用 IMDB 電影評論數據集 作為示例來測試 Word2Vec 在情感分析中的有效性。數據集中包含了 25,000 條積極評論,25,000 條消極評論和 50,000 條未標記的電影評論。我們首先利用 Doc2Vec 對未標記評論進行訓練。除了同時使用 DM 和 DBOW 向量作為輸入以外,方法和上一節 Word2Vec 例子相同。

importgensim

fromsklearn.cross_validationimporttrain_test_split

importnumpyasnp

withopen( IMDB_data/pos.txt , r )asinfile:

pos_reviews=infile.readlines()

withopen( IMDB_data/neg.txt , r )asinfile:

neg_reviews=infile.readlines()

withopen( IMDB_data/unsup.txt , r )asinfile:

unsup_reviews=infile.readlines()

# 1 代表積極情緒,0 代表消極情緒

y=np.concatenate((np.ones(len(pos_reviews)),np.zeros(len(neg_reviews))))

x_train,x_test,y_train,y_test=train_test_split(np.concatenate((pos_reviews,neg_reviews)),y,test_size=0.2)

# 零星的預處理

defcleanText(corpus):

punctuation=""".,?!:;(){}[]"""

corpus=[z.lower().replace( n , )forzincorpus]

corpus=[z.replace(

, )forzincorpus]

# 將標點視為一個單詞

forcinpunctuation:

corpus=[z.replace(c, %s %c)forzincorpus]

corpus=[z.split()forzincorpus]

returncorpus

x_train=cleanText(x_train)

x_test=cleanText(x_test)

unsup_reviews=cleanText(unsup_reviews)

# Gensim 的 Doc2Vec 工具要求每個文檔/段落包含一個與之關聯的標籤。我們利用 LabeledSentence 進行處理。格式形如 「TRAIN_i」 或者 「TEST_i」,其中 「i」 是假的評論索引。

deflabelizeReviews(reviews,label_type):

labelized=[]

fori,vinenumerate(reviews):

label= %s_%s %(label_type,i)

labelized.append(LabeledSentence(v,[label]))

returnlabelized

x_train=labelizeReviews(x_train, TRAIN )

x_test=labelizeReviews(x_test, TEST )

unsup_reviews=labelizeReviews(unsup_reviews, UNSUP )

這麼一來創建了 LabeledSentence 類型對象:

下面我們實例化兩個 Doc2Vec 模型,DM 和 DBOW。gensim 文檔建議多次訓練數據,並且在每一步(pass)調節學習率(learning rate)或者用隨機順序輸入文本。接著我們收集了通過模型訓練後的電影評論向量。

importrandom

size=400

# 實例化 DM 和 DBOW 模型

# 對所有評論創建辭彙表

model_dm.build_vocab(np.concatenate((x_train,x_test,unsup_reviews)))

model_dbow.build_vocab(np.concatenate((x_train,x_test,unsup_reviews)))

# 多次傳入數據集,通過每次滑動(shuffling)來提高準確率。

all_train_reviews=np.concatenate((x_train,unsup_reviews))

forepochinrange(10):

model_dm.train(all_train_reviews[perm])

model_dbow.train(all_train_reviews[perm])

# 從我們的模型中獲得訓練過的向量

defgetVecs(model,corpus,size):

vecs=[np.array(model[z.labels[]]).reshape((1,size))forzincorpus]

returnnp.concatenate(vecs)

train_vecs_dm=getVecs(model_dm,x_train,size)

train_vecs_dbow=getVecs(model_dbow,x_train,size)

train_vecs=np.hstack((train_vecs_dm,train_vecs_dbow))

# 訓練測試數據集

x_test=np.array(x_test)

forepochinrange(10):

model_dm.train(x_test[perm])

model_dbow.train(x_test[perm])

# 創建測試數據集向量

test_vecs_dm=getVecs(model_dm,x_test,size)

test_vecs_dbow=getVecs(model_dbow,x_test,size)

test_vecs=np.hstack((test_vecs_dm,test_vecs_dbow))

現在我們準備對我們的評論向量訓練一個分類器。我們再次使用 sklearn 的 SGDClassifier。

fromsklearn.linear_modelimportSGDClassifier

lr=SGDClassifier(loss= log ,penalty= l1 )

lr.fit(train_vecs,y_train)

print Test Accuracy: %.2f %lr.score(test_vecs,y_test)

這個模型的測試準確率達到了 0.86。我們也構建了如下的分類器 ROC 曲線:

#Create ROC curve

fromsklearn.metricsimportroc_curve,auc

%matplotlibinline

importmatplotlib.pyplotasplt

pred_probas=lr.predict_proba(test_vecs)[:,1]

fpr,tpr,_=roc_curve(y_test,pred_probas)

roc_auc=auc(fpr,tpr)

plt.plot(fpr,tpr,label= area = %.2f %roc_auc)

plt.plot([,1],[,1], k-- )

plt.xlim([0.0,1.0])

plt.ylim([0.0,1.05])

plt.legend(loc= lower right )

plt.show()

圖 5:基於 IMDB 電影評論訓練數據的邏輯分類器(logistic classifier)的 ROC 曲線

原始論文 強調了用有 50 個結點的神經網路加上一個簡單的邏輯回歸分類器,效果會有提高:

fromNNetimportNeuralNet

nnet=NeuralNet(50,learn_rate=1e-2)

maxiter=500

batch=150

_=nnet.fit(train_vecs,y_train,fine_tune=False,maxiter=maxiter,SGD=True,batch=batch,rho=0.9)

print Test Accuracy: %.2f %nnet.score(test_vecs,y_test)

有趣的是,我們在這兒並沒有看到什麼提高。測試準確率是 0.85,我們也沒能達到他們所說的 7.42% 的測試錯誤率。原因有很多:我們在每一步(epochs)對於訓練/測試數據沒有訓練足夠,他們實現 Doc2Vec 和 ANN 的方式不同,他們的超參數不同等等。因為論文中並沒有談及細節,所以難以確知真正原因。不管怎樣,在進行了零星預處理以及沒有構造和選取特徵的情況下,我們還是得到了 86% 的準確率。並不需要花哨的卷積(convolutions)和樹庫(treebanks)!

結論

我希望已經你不僅見識了 Word2Vec 和 Doc2Vec 的強大,而且能夠通過標準工具諸如 Python 和 gensim 來應用它們。只需要非常簡單的演算法我們即可得到豐富的詞和段落向量,足以在所有 NLP 應用中使用。另外更棒的是 Google 發布了基於超大規模數據集預訓練(pre-train)的詞向量。如果你想在大規模數據集中訓練自己的詞向量,可以利用 Apache Spark』s MLlib 的 Word2Vec 來實現。Happy NLP』ing!

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發者 的精彩文章:

共享經濟+大數據?這或許是一個值得考慮的模式
如何在3個月內成為數據科學家,挑戰起薪30萬
我是這樣挑戰不用 for 循環的
程序員做不到 35 歲?看看他們
養成一個女朋友,不如調教一個Chatbot?

TAG:Python開發者 |

您可能感興趣

雙相情感障礙的診斷分類方法
情感論
女命從殺格的婚姻情感看法與分析
情感表現
家人患有雙相情感障礙:分辨躁狂行為的方法提示
控制情感的三個方法
漢字形態情感!與創意表達方法
惜情情感黃老師挽回婚姻愛情 婚姻危機4個處理方法 幸福女人情感機構黃老師
情感概述
後悔分手,想要挽回,良姻情感教你方法
大數據情感分析
【升魅情感】戀人間如何談戀愛的不分手方法
情感挽回的方法有哪些?良姻情感為你介紹挽回情感挽回的三種方法
巨蟹座一周愛情情感分析
論親情友情感情
卜卦與本命占星感情案例分析:好感、友情感情曖昧與真愛
情感挽回最有效的方法
情感 情感語錄集
為什麼要分析情感問題?
女友要分手,該怎麼挽回她?良姻情感告訴你正確的方法