基於LDA對電商商品評論進行情感分析
1、專案背景: 現在大眾在進行網購之前都會先看下相關商品的評論,包括好評與差評,再綜合衡量,最後才決定是否會購買相關的物品。甚至有的消費者已經不看商品的詳情秒數頁而是直接看評論,然後決定是否下單。商品評論已經是使用者決策最為核心的考量因素了。 在本專案中要根據商品的好評與差評,提取出關鍵詞,快速瞭解一件商品的好與不好的地方。
原始評論需要提煉: 商品雖然希望買家給好評,但是從資料探勘的角度看,單純的好拼價值不大,尤其是一些空洞的評價,並未能給未來潛在消費者提供有價值的參考資訊。比如大部分消費者會這麼說“質量不錯”、“好評,下次買還是你家”等,但對於一個關心該產品安全性的消費者來說顯然是沒用的,當消費者沒了解到他想了解的問題,很可能在頭腦發熱期過了之後就不會再考慮購買了。
2、專案需求: 對於一個指定的商品,生產商、賣家或消費者需要了解使用者認同該商品的哪些優點,不認同該商品的哪些弱點或缺點。
3、專案輸出: (1)商品好評的若干個topic中,其關鍵詞各是什麼,以及每個關鍵詞的權重。 (2)商品差評的若干個topic中,其關鍵詞各是什麼,以及每個關鍵詞的權重。
4、需要安裝的庫: snownlp – 用來處理情感分析 jieba – 分詞 gensim – 構建詞向量
5、LDA主題模型: 如果一篇文章有一箇中心思想或主題,那麼一些特定詞語會更頻繁地出現。一篇文章通常可以包含多種主題,每個主題所佔比例各不相同。對於不同主題的文章而言,其不同的主題實際上構成了一個分佈。 一篇文章有主題,那麼對於一個文件集,我們也可以分析其主題分佈。藉此甚至可以看出我的閱讀愛好是什麼。如果按每五年來分析,可以回顧下過去我的閱讀興趣的變化。當然現實中對主題分佈進行分析,更多是出於商業目的。
本質上主題模型是一個詞袋模型,不考慮詞的順序。 (1)已知條件:給定一個文件,可以看到的每篇文件都是由哪些詞構成。 (2)假設條件:LDA模型假設每篇文件都是在講述若干個主題,至於具體幾個主題,可以認為預先設定k值。 (3)求解:通過LDA模型,可以求出這k個主題(latent)中,每個主題到底長什麼樣子。
Documents–Topics–Words(Tokens)
LDA能做什麼: (1)能做:求解出k個主題長什麼樣子。 (2)不能做:不能認為設定是什麼主題,比如不能認為設定為財經、體育的主題。因為LDA是無監督演算法,模型訓練結束後,每個主題長什麼樣子就是什麼樣子,和整個語料中的每篇文件的內容有關係。無法人為強求某個主題表示財經,某個主題表示體育等。
LDA優點: (1)無監督:不用對訓練資料打標籤 (2)預處理簡單:分詞,去除停用詞 (3)引數少:一般只需要設定主題數k
6、程式碼實現: (1)讀取資料
import pandas as pd
inputfile = 'huizong.csv'
outputfile = 'meidi_jd.txt'
data = pd.read_csv(inputfile,encoding='utf8')
#只提取與美的品牌產品有關的評論
data = data[data['品牌']=='美的']['評論']
(2)資料預處理
#除去NAN
data = data.dropna()
import numpy as np
#去重,比如差不多的評論考慮其中一條就可以
l1 = len(data)
data = pd.DataFrame(data.unique())
l2 = len(data)
print(u'刪除了%s條評論.'%(l1-l2))
'''
壓縮詞,也就是對句子內冗長的部分進行壓縮
'''
def cutword(strs,reverse=False):
s1 = []
s2 = []
s = []
if reverse:
strs = strs[::-1]
s1.append(strs[0])
for i in strs[1:]:
if i == s1[0]:
if len(s2)==0:
s2.append(i)
else:
if s1 == s2:
s2 = []
s2.append(i)
else:
s = s+s1+s2
s1 = []
s2 = []
s1.append(i)
else:
if s1 == s2 and len(s1)>=2 and len(s2)>=2:
s = s+s1
s1 = []
s2 = []
s1.append(i)
else:
if len(s2)==0:
s1.append(i)
else:
s2.append(i)
if s1 == s2:
s = s+s1
else:
s = s+s1+s2
if reverse :
return ''.join(s[::-1])
else:
return ''.join(s)
data2 = data.iloc[:,0].apply(cutword)
#短句過濾
data3 = data2[data2.apply(len)>=4]
(3)LDA模型訓練 使用snownlp庫中的SnowNLP類進行情感分析。 該類接收一個句子文字,然後使用該類的sentiments屬性產生一個概率值,概率表示該句子是積極的概率,越接近於1表示越積極,越接近於0表示越負面。 舉個例子:
from snownlp import SnowNLP
s = SnowNLP(u'這個東西真心很贊')
s.words # [u'這個', u'東西', u'真心',
# u'很', u'贊']
s.tags # [(u'這個', u'r'), (u'東西', u'n'),
# (u'真心', u'd'), (u'很', u'd'),
# (u'贊', u'Vg')]
s.sentiments # 0.9769663402895832 positive的概率
回到資料集,由於資料集是一個DataFrame格式,直接使用apply()函式進行對所有行句子的情感分析。得到的coms是一個Series型別資料,只有一列,並且都是概率值。
from snownlp import SnowNLP
coms = data3.apply(lambda x:SnowNLP(x).sentiments)
coms
輸出如下:
0 9.827906e-01
1 8.398297e-01
2 9.995530e-01
...
53043 8.272506e-01
53044 4.341055e-01
53045 1.220829e-01
53046 7.864625e-01
53047 9.859058e-01
Name: 0, Length: 52426, dtype: float64
對積極評論與負面評論進行分類分析。
#coms的值越接近1,表示越positive,越接近0表示越negative
data_positive = data3[coms>=0.6]
data_negative = data3[coms<0.4]
接著進行分詞:
import jieba
#進行分詞
data_positive = data_positive.apply(lambda x:' '.join(jieba.cut(x)))
data_negative = data_negative.apply(lambda x:' '.join(jieba.cut(x)))
with open('stoplist.txt',encoding='utf8') as file:
stop = file.readlines()
stop = [x.strip('\n') for x in stop]
stop.append('')
data_positive_split = data_positive.apply(lambda x:x.split(' '))
data_positive = data_positive_split.apply(lambda x:[i for i in x if i not in stop])
data_negative_split = data_negative.apply(lambda x:x.split(' '))
data_negative = data_negative_split.apply(lambda x:[i for i in x if i not in stop])
在處理完以上資料格式之後,還不能講資料餵給lda模型,必須要再進一步處理。因為list中有些詞是重複的,而且缺少每個詞的詞頻資訊,為了讓模型可以執行,還需要先用doc2bow這個函式將資料轉換成恰當的格式。
如何轉換? 使用gensim庫中corpora.Dictionary()類進行儲存,然後通過呼叫它的函式doc2bow()進行轉換。
from gensim import corpora,models
neg_dict = corpora.Dictionary(data_negative)
neg_dict.id2token
輸出示例:
{0: '再',
1: '早上',
2: '晚上',
3: '水方',
4: '洗',
5: '滿意',
6: '熱',
7: '50',
8: '不到',
9: '買',
...}
接著讓文件中的所有句子中的詞都對映稱為(id,frequency)元組來表示,frequency表示的是詞在句子中出現的頻率。其中如果出現一個新句子,且存在新詞,那麼新詞是沒有元組替代表示的。 這裡使用corpora.Dictionary類的方法doc2bow()來轉換。 注意這裡是對整個文件進行轉換,進而整個文件變成了一個語料庫,作為Ida模型的corpus的輸入。
neg_corpus = [neg_dict.doc2bow(i) for i in data_negative]
此外模型還需要詞與id的對映關係作為輸出的轉換,也就是剛剛的neg_dict,當然還要人工選擇一個主題的數量。這裡選擇3。
#放進gensim.models中的lda模型進行訓練
neg_lda = models.LdaModel(corpus=neg_corpus,num_topics=3,id2word=neg_dict)
print(neg_lda.print_topics(3))
輸出如下:
[(0, '0.031*"安裝" + 0.023*"好" + 0.020*"加熱" + 0.013*"熱水器" + 0.011*"不錯" + 0.011*"京東" + 0.009*"問題" + 0.009*"送貨" + 0.009*"師傅" + 0.008*"使用"'), (1, '0.081*"安裝" + 0.027*"師傅" + 0.017*"好" + 0.016*"熱水器" + 0.015*"不錯" + 0.013*"美的" + 0.011*"售後" + 0.011*"東西" + 0.011*"安裝費" + 0.010*"收費"'), (2, '0.030*"買" + 0.015*"熱水器" + 0.014*"安裝" + 0.012*"美的" + 0.009*"遙控" + 0.009*"一個" + 0.009*"n" + 0.008*"好" + 0.006*"價格" + 0.006*"60"')]
同理對積極評論的內容也進行同樣的轉換以便分析:
pos_dict = corpora.Dictionary(data_positive)
pos_corpus = [pos_dict.doc2bow(i) for i in data_positive]
pos_lda = models.LdaModel(corpus=pos_corpus,num_topics=3,id2word=pos_dict)
print(pos_lda.print_topics(3))
輸出如下:
[(0, '0.073*"好" + 0.040*"不錯" + 0.028*"很快" + 0.028*"價格" + 0.027*"質量" + 0.025*"速度" + 0.023*"送貨" + 0.022*"實惠" + 0.021*"安裝" + 0.020*"加熱"'), (1, '0.095*"不錯" + 0.062*"安裝" + 0.028*"使用" + 0.019*"感覺" + 0.019*"外觀" + 0.018*"送貨" + 0.014*"師傅" + 0.013*"熱水器" + 0.011*"漂亮" + 0.010*"美的"'), (2, '0.108*"好" + 0.062*"不錯" + 0.049*"買" + 0.029*"京東" + 0.028*"東西" + 0.025*"熱水器" + 0.022*"美的" + 0.022*"挺" + 0.021*"滿意" + 0.020*"值得"')]
註釋:以上輸出的結果是對應的三個主題的積極評論以及負面評論,在每個主題裡面按照主題的相關性排序,越靠前的表示該主題越突出。
負面評論分析: 會發現在負面評價當中出現了’好’,'不錯’等這些正面的詞,這並不奇怪,原因是京東的客戶評價還是以好評為主, 當然這也與電商傾向於保留正面評價有關. 在第0個主題當中,安裝的比重最大,說明該主題與安裝相關,而’有點’是傾向於負面的評論,又出現了師傅的字眼, 可以反應出可能安裝中出現師傅服務態度不好等一些負面問題,還有加熱等問題. 在第1個主題當中,安裝的比重也是最大,但是出現安裝費主題,可以從這裡看出一點端倪,可能安裝費太貴了 可能會發現大多數都是好評,. 在第2個主題中,不僅有安裝,還有送貨字眼.綜上可能大部分差評與師傅服務態度,配送費貴,安裝費貴,送貨快慢等原因有關.
正面評論分析: 好評當中第一個主題出現了送貨,安裝,很快,滿意等字眼,那說明有人認可該送貨速度和安裝速度或服務的. 第二個主題出現了值得,信賴,品牌,滿意等字眼,那可能就有人比較信賴牌子,認可美的這個品牌 第三個主題特別提到了京東,可能有人比較信任不僅美的,同時發現出現價格,高,價效比,實惠等主題出現, 又因為這是好評,所以可能是與價格實惠有關,價效比高.