文本情感分析
文本情感分析(也稱為意見挖掘)是指用自然語言處理藏畅、文本挖掘以及計算機語言學等方法來識別和提取原素材中的主觀信息。
通常來說功咒,情感分析的目的是為了找出說話者/作者在某些話題上或者針對一個文本兩極的觀點的態(tài)度愉阎。這個態(tài)度或許是他或她的個人判斷或是評估,也許是他當時的情感狀態(tài)(就是說力奋,作者在做出這個言論時的情緒狀態(tài))榜旦,或是作者有意向的情感交流(就是作者想要讀者所體驗的情緒)。
書籍評論數(shù)據(jù)
我們將使用從豆瓣網(wǎng)收集的《解憂雜貨店》書籍相關(guān)評論數(shù)據(jù)景殷,書籍整體評分在8.5分溅呢,大多數(shù)都是4星或者5星。選取該書籍的原因主要在于收集足夠的樣本進行建模猿挚,考慮該書籍是東野圭吾的熱門小說咐旧,主頁顯示接近40w的評價,短評約13w亭饵,初略估計每個星類別都有幾千的評論休偶。當然這本書的內(nèi)容也是挺不錯的,有興趣的讀者可以去看看哦辜羊。踏兜。
收集的數(shù)據(jù)包含文本評論和對應的星級評分,文本評論用來做情感分類的輸入數(shù)據(jù)八秃,豆瓣評分用來表示該評論是“正面”還是“負面”碱妆。豆瓣的評分包含1~5星的打分,為了簡化建模過程昔驱,我們將評論打分處理成二分類疹尾,評分1分和2分的評論標記為“負面”,評分4分和5分的評論標記為“正面”骤肛,3分歸類為中性評論纳本,所以不包含在數(shù)據(jù)集中。這里就不討論3分不包含數(shù)據(jù)集中的處理方式腋颠,有興趣的讀者可以看看3分的相關(guān)評論繁成。
加載數(shù)據(jù)集
使用Pandas可以很方便的讀取評論數(shù)據(jù),我們用Jupyter notebook來執(zhí)行代碼淑玫,方便我們查看數(shù)據(jù)情況巾腕。加載數(shù)據(jù)集后查看前五條數(shù)據(jù),對于中文文本有時候會出現(xiàn)編碼問題絮蒿,確認目前數(shù)據(jù)沒有發(fā)生該問題尊搬。
import pandas as pd
# 讀取評論數(shù)據(jù)集
df = pd.read_excel('data/comment.xlsx')
df.head()
數(shù)據(jù)探索
接下來我們查看整個數(shù)據(jù)集的記錄數(shù)以及每個評分的記錄數(shù)⊥晾裕總共有4352條評論佛寿,其中一半為1分或2分,一半為4分或5分狗准。原始數(shù)據(jù)中絕大部分評論數(shù)據(jù)的評分都是4分或5分,為了防止訓練模型的時候因數(shù)據(jù)集分類數(shù)量差異導致模型“記憶”更多的“正面”評論腔长,經(jīng)過處理使得目前使用的數(shù)據(jù)集二分類各占一半。
## 查看數(shù)據(jù)形狀
print(df.shape)
# 查看分數(shù)分布
print(df['rating'].value_counts())
(4352, 2)
2 1735
5 1250
4 926
1 441
最后捞附,檢查數(shù)據(jù)集有無缺失值,遍歷后所有列均沒有缺失值鸟召。
# 查看有無空值
for col in df.columns:
print(col, ':', len(df[df[col].isnull()]))
id : 0
comment : 0
rating : 0
數(shù)據(jù)預處理
由于這是一個二分類問題,我們需要將評分轉(zhuǎn)化為只含兩種標簽的數(shù)據(jù)欧募。rating
列大于3的標記為1,其余標記為0仆抵,并指定新的label
列跟继。
df['label'] = df['rating'].map(lambda x: 1 if x > 3 else 0)
中文分詞
用于機器學習文本最簡單且最常用的方法是使用詞袋(bag-of-words)表示种冬。為了表示詞袋需要將評論分成一個個單詞,并計算每個單詞在評論中出現(xiàn)的頻次舔糖。這種表示方法有個缺點娱两,即舍棄了文本中的大部分結(jié)構(gòu),段落金吗、句子十兢、格式。英文每個單詞都用空格隔開比較好處理摇庙,對于中文句子我們需要使用分詞庫將句子分割成單詞旱物,不同分詞庫具體案例可以看我之前寫過的文章《Python中文分詞及詞頻統(tǒng)計》。
這里我們使用jieba分詞庫進行快速分詞卫袒,由于可能有全數(shù)字的文本評論會導致文本分詞報錯宵呛,這里先進行類型轉(zhuǎn)換。
import jieba
# jieba分詞
df['comment'] = df['comment'].map(str)
df['cuted'] = df['comment'].map(lambda x: ' '.join(jieba.cut(x)))
訓練集和測試集
為了驗證數(shù)據(jù)準確率玛臂,對于文本數(shù)據(jù)同樣需要進行劃分數(shù)據(jù)集烤蜕。大寫X表示輸入,小寫y表示輸出迹冤。sklearn
機器學習庫的train_test_split
可以很方便進行劃分數(shù)據(jù)集讽营,默認25%做為測試集,random_state
指定隨機種子保證每次劃分的結(jié)果是一致的泡徙。
# 輸入和輸出
X = df['cuted']
y = df['label']
from sklearn.model_selection import train_test_split
# 劃分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
# 查看訓練集
X_train.shape
CountVectorizer
類可以實現(xiàn)文本的詞袋表示橱鹏,實例化對象后進行擬合,vocabulary_
屬性可以看到詞表堪藐,每個單詞對應的索引莉兰,不是頻次。從打印的信息可以看到我們總共有7349個單詞礁竞。
from sklearn.feature_extraction.text import CountVectorizer
# 變換器
vect = CountVectorizer()
vect.fit(X_train)
# 詞表數(shù)量
print(len(vect.vocabulary_))
# 打印詞表
print(vect.vocabulary_)
我們可以調(diào)用transform
方法來創(chuàng)建詞袋的稀疏矩陣糖荒,矩陣中每個特征對應詞表中的單詞,如果沒有這個單詞會用0進行填充模捂。
words_matrix = pd.DataFrame(vect.transform(X).toarray(),
columns=vect.get_feature_names())
words_matrix.head()
構(gòu)建模型
在提取特征后捶朵,我們通過構(gòu)建LogisticRegression
邏輯回歸分類器來擬合訓練集數(shù)據(jù),并使用交叉驗證對LogisticRegression
進行評估模型的性能狂男。我們的得到的交叉驗證分數(shù)是81.9%,這對于二分類模型來說還是比較合理的岖食。通常在這種多維特征的數(shù)據(jù)進行擬合分類邏輯回歸都有不錯的效果泡垃。
iimport numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
# 交叉驗證評估模型
scores = cross_val_score(LogisticRegression(),
vect.transform(X_train), y_train, cv=5)
print('平均交叉驗證準確率:{:.3f}'.format(np.mean(scores)))
平均交叉驗證準確率: 0.819
去除停用詞
回過頭看我們會發(fā)現(xiàn)單詞特征中包含很多數(shù)字及其他如“的”、“哦”等單詞唾琼,這些單詞在大多數(shù)情況下對于我們目前的案例沒有提供有效的信息量澎剥,所以需要從原來的詞袋中刪除掉以提高模型的性能哑姚,對于這些特定詞我們稱之為停用詞。這里使用哈工大的停用詞表倡蝙,創(chuàng)建函數(shù)讀取詞表并刪除換行符寺鸥,輸出停用詞列表品山。
def stopwords_list():
with open('哈工大停用詞表.txt') as f:
lines = f.readlines()
result = [i.strip('\n') for i in lines]
return result
stopwords = stopwords_list()
重新構(gòu)建單詞矩陣肘交,max_df
參數(shù)表示舍棄最頻繁的單詞,min_df
參數(shù)表示每個詞必須要在3個評論中出現(xiàn)凉驻,stop_words
參數(shù)對于中文需要指定停用詞列表,最后使用正則表達式去掉所有數(shù)字间狂,處理完后可以看到單詞由7349個減少到1931個。
vect = CountVectorizer(max_df=0.8, min_df=3, stop_words=stopwords,
token_pattern=u'(?u)\\b[^\\d\\W]\\w+\\b')
vect.fit(X_train)
words_matrix = pd.DataFrame(vect.transform(X_train).toarray(),
columns=vect.get_feature_names())
再次評估新的詞袋模型蛛淋,似乎準確率上沒有提升褐荷,這是對于幾千樣本的數(shù)據(jù)叛甫。在幾萬樣本的情況下,通常單詞特征會有幾萬個萌腿,這個時候采用去除停用詞是可以明顯降低數(shù)據(jù)中的噪聲抖苦,提高模型的準確率。不過這里不要擔心我們繼續(xù)優(yōu)化贮庞。
# 訓練模型
lr.fit(vect.transform(X_train), y_train)
print('測試集準確率:{:.3f}'.format(lr.score(vect.transform(X_test), y_test)))
測試集準確率:0.812
用tf-idf縮放數(shù)據(jù)
tf-idf 是一種用于資訊檢索與文本挖掘的常用加權(quán)技術(shù)窗慎。tf-idf 是一種統(tǒng)計方法遮斥,用以評估一字詞對于一個文件集或一個語料庫中的其中一份文件的重要程度扇丛。字詞的重要性隨著它在文件中出現(xiàn)的次數(shù)成正比增加晕拆,但同時會隨著它在語料庫中出現(xiàn)的頻率成反比下降。tf-idf 加權(quán)的各種形式常被搜索引擎應用吝镣,作為文件與用戶查詢之間相關(guān)程度的度量或評級末贾。除了 tf-idf 以外整吆,互聯(lián)網(wǎng)上的搜尋引擎還會使用基于連結(jié)分析的評級方法,以確定文件在搜尋結(jié)果中出現(xiàn)的順序拴测。
scikit-learn 在兩個類中實現(xiàn)了 tf-idf 方法:TfidfTransformer
和 TfidfVectorizer
集索, 前者接受 CountVectorizer
生成的稀疏矩陣并將其變換务荆, 后者接受文本 數(shù)據(jù)并完成詞袋特征提取與 tf-idf 變換。
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import make_pipeline
pipe = make_pipeline(TfidfVectorizer(min_df=3), LogisticRegression())
pipe.fit(X_train, y_train)
scores = cross_val_score(pipe, X_train, y_train, cv=5)
print('平均交叉驗證準確率:{:.3f}'.format(np.mean(scores)))
平均交叉驗證準確率:0.828
我們可以看到使用 tf-idf 代替僅統(tǒng)計詞數(shù)對性能有所提高娱据。我們還可以查看 tf-idf 找到的最重要的單詞中剩。tf-idf較低的詞要么在評論中經(jīng)常出現(xiàn)咽安,要么就是很少出現(xiàn)蓬推,tf-idf較大的詞往往在評論中經(jīng)常出現(xiàn)澡腾。
vectorizer = pipe.named_steps['tfidfvectorizer']
# 找到每個特征中最大值
max_value = vectorizer.transform(X_train).max(axis=0).toarray().ravel()
sorted_by_tfidf = max_value.argsort()
# 獲取特征名稱
feature_names = np.array(vectorizer.get_feature_names())
print("tfidf較低的特征:\n{}".format(feature_names[sorted_by_tfidf[:20]]))
print()
print("tfidf較高的特征:\n{}".format( feature_names[sorted_by_tfidf[-20:]]))
評估模型
最后我們在使用測試集驗證模型的準確率毅糟,可以看到模型最終在測試集上有81.4%的準確率澜公,前面做的交叉驗證僅僅只是在訓練集上坟乾。另外,模型混淆矩陣上看到模型在負類上的準確率為448 / (448 + 99) = 0.81.9
明吩,正類上的準確率為438 / (438 + 103) = 0.81
印荔。
from sklearn import metrics
# 預測值
y_pred = pipe.predict(X_test)
print('測試集準確率:{:.3f}'.format(metrics.accuracy_score(y_test, y_pred)))
print('測試集準確率:{:.3f}'.format(pipe.score(X_test, y_test)))
metrics.confusion_matrix(y_test, y_pred)
測試集準確率:0.814
測試集準確率:0.814
array([[448, 103], [ 99, 438]])
豆瓣評分的思考
前段時間“流浪地球”電影刷分的事件鬧得沸沸揚揚仍律,那些高分改評論的內(nèi)容實際上可以使用機器學習進行修正水泉,即如果發(fā)現(xiàn)評論內(nèi)容判定為正面情感,而評分給出1分或2分則表示該評論與評分很可能不相符躯概,針對這種數(shù)據(jù)可以對該評論的星級評分進行降權(quán)娶靡,權(quán)重可以根據(jù)評分的人數(shù)進行調(diào)整看锉。當然使用簡單的機器學習系統(tǒng)還是要依靠大量的樣本數(shù)據(jù)、合理的模型以及合理的參數(shù)呻此,80%的準確率相當于有可能20%的誤判焚鲜。當然我們還有其他技巧可以再次提高我們模型的準確率放前,例如使用預訓練好的詞嵌入向量和使用更加高級的深度學習模型,期待我們的再次相會吧~