文本情感分析(Sentiment Analysis)是天然語言處理(NLP)方法中常見的應用,也是一個有趣的基本任務,尤爲是以提煉文本情緒內容爲目的的分類。它是對帶有情感色彩的主觀性文本進行分析、處理、概括和推理的過程。
本文將介紹情感分析中的情感極性(傾向)分析。所謂情感極性分析,指的是對文本進行褒義、貶義、中性的判斷。在大多應用場景下,只分爲兩類。例如對於「喜好」和「厭惡」這兩個詞,就屬於不一樣的情感傾向。
本文將詳細介紹如何使用深度學習模型中的LSTM模型來實現文本的情感分析。python
咱們以某電商網站中某個商品的評論做爲語料(corpus.csv),該數據集的下載網址爲:https://github.com/renjunxiang/Text-Classification/blob/master/TextClassification/data/data_single.csv ,該數據集一共有4310條評論數據,文本的情感分爲兩類:「正面」和「反面」,該數據集的前幾行以下:git
evaluation,label
用了一段時間,感受還不錯,能夠,正面
電視很是好,已是家裏的第二臺了。第一天下單,次日就到本地了,但是物流的人說車壞了,一直催,客服也幫着催,到第三天下午5點才送過來。父母年紀大了,買個大電視畫面清晰,趁着耳朵還好使,享受幾年。,正面
電視比想象中的大好多,畫面也很清晰,系統很智能,更多功能還在摸索中,正面
不錯,正面
用了這麼多天了,感受還不錯。夏普的牌子仍是比較可靠。但願之後比較耐用,如今是考量質量的時候。,正面
物流速度很快,很是棒,今天就看了電視,很是清晰,很是流暢,一次很是完美的購物體驗,正面
很是好,客服還特地打電話作回訪,正面
物流小哥不錯,辛苦了,東西還沒用,正面
送貨速度快,質量有保障,活動價格挺好的。但願用的久,不出問題。,正面
接着咱們須要對語料作一個簡單的分析:github
使用如下Python腳本,咱們能夠統計出數據集中的情感分佈以及評論句子長度分佈。web
import pandas as pd import matplotlib.pyplot as plt from matplotlib import font_manager from itertools import accumulate # 設置matplotlib繪圖時的字體 my_font = font_manager.FontProperties(fname="/Library/Fonts/Songti.ttc") # 統計句子長度及長度出現的頻數 df = pd.read_csv('./corpus.csv') print(df.groupby('label')['label'].count()) df['length'] = df['evaluation'].apply(lambda x: len(x)) len_df = df.groupby('length').count() sent_length = len_df.index.tolist() sent_freq = len_df['evaluation'].tolist() # 繪製句子長度及出現頻數統計圖 plt.bar(sent_length, sent_freq) plt.title("句子長度及出現頻數統計圖", fontproperties=my_font) plt.xlabel("句子長度", fontproperties=my_font) plt.ylabel("句子長度出現的頻數", fontproperties=my_font) plt.savefig("./句子長度及出現頻數統計圖.png") plt.close() # 繪製句子長度累積分佈函數(CDF) sent_pentage_list = [(count/sum(sent_freq)) for count in accumulate(sent_freq)] # 繪製CDF plt.plot(sent_length, sent_pentage_list) # 尋找分位點爲quantile的句子長度 quantile = 0.91 #print(list(sent_pentage_list)) for length, per in zip(sent_length, sent_pentage_list): if round(per, 2) == quantile: index = length break print("\n分位點爲%s的句子長度:%d." % (quantile, index)) # 繪製句子長度累積分佈函數圖 plt.plot(sent_length, sent_pentage_list) plt.hlines(quantile, 0, index, colors="c", linestyles="dashed") plt.vlines(index, 0, quantile, colors="c", linestyles="dashed") plt.text(0, quantile, str(quantile)) plt.text(index, 0, str(index)) plt.title("句子長度累積分佈函數圖", fontproperties=my_font) plt.xlabel("句子長度", fontproperties=my_font) plt.ylabel("句子長度累積頻率", fontproperties=my_font) plt.savefig("./句子長度累積分佈函數圖.png") plt.close()
輸出的結果以下:算法
label 正面 1908 負面 2375 Name: label, dtype: int64 分位點爲0.91的句子長度:183.
能夠看到,正反面兩類情感的比例差很少。句子長度及出現頻數統計圖以下:微信
句子長度累積分佈函數圖以下:app
能夠看到,大多數樣本的句子長度集中在1-200之間,句子長度累計頻率取0.91分位點,則長度爲183左右。框架
接着咱們使用深度學習中的LSTM模型來對上述數據集作情感分析,筆者實現的模型框架以下:dom
完整的Python代碼以下:機器學習
# -*- coding: utf-8 -*- import pickle import numpy as np import pandas as pd from keras.utils import np_utils, plot_model from keras.models import Sequential from keras.preprocessing.sequence import pad_sequences from keras.layers import LSTM, Dense, Embedding, Dropout from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # 導入數據 # 文件的數據中,特徵爲evaluation, 類別爲label. def load_data(filepath, input_shape=20): df = pd.read_csv(filepath) # 標籤及詞彙表 labels, vocabulary = list(df['label'].unique()), list(df['evaluation'].unique()) # 構造字符級別的特徵 string = '' for word in vocabulary: string += word vocabulary = set(string) # 字典列表 word_dictionary = {word: i+1 for i, word in enumerate(vocabulary)} with open('word_dict.pk', 'wb') as f: pickle.dump(word_dictionary, f) inverse_word_dictionary = {i+1: word for i, word in enumerate(vocabulary)} label_dictionary = {label: i for i, label in enumerate(labels)} with open('label_dict.pk', 'wb') as f: pickle.dump(label_dictionary, f) output_dictionary = {i: labels for i, labels in enumerate(labels)} vocab_size = len(word_dictionary.keys()) # 詞彙表大小 label_size = len(label_dictionary.keys()) # 標籤類別數量 # 序列填充,按input_shape填充,長度不足的按0補充 x = [[word_dictionary[word] for word in sent] for sent in df['evaluation']] x = pad_sequences(maxlen=input_shape, sequences=x, padding='post', value=0) y = [[label_dictionary[sent]] for sent in df['label']] y = [np_utils.to_categorical(label, num_classes=label_size) for label in y] y = np.array([list(_[0]) for _ in y]) return x, y, output_dictionary, vocab_size, label_size, inverse_word_dictionary # 建立深度學習模型, Embedding + LSTM + Softmax. def create_LSTM(n_units, input_shape, output_dim, filepath): x, y, output_dictionary, vocab_size, label_size, inverse_word_dictionary = load_data(filepath) model = Sequential() model.add(Embedding(input_dim=vocab_size + 1, output_dim=output_dim, input_length=input_shape, mask_zero=True)) model.add(LSTM(n_units, input_shape=(x.shape[0], x.shape[1]))) model.add(Dropout(0.2)) model.add(Dense(label_size, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) plot_model(model, to_file='./model_lstm.png', show_shapes=True) model.summary() return model # 模型訓練 def model_train(input_shape, filepath, model_save_path): # 將數據集分爲訓練集和測試集,佔比爲9:1 # input_shape = 100 x, y, output_dictionary, vocab_size, label_size, inverse_word_dictionary = load_data(filepath, input_shape) train_x, test_x, train_y, test_y = train_test_split(x, y, test_size = 0.1, random_state = 42) # 模型輸入參數,須要本身根據須要調整 n_units = 100 batch_size = 32 epochs = 5 output_dim = 20 # 模型訓練 lstm_model = create_LSTM(n_units, input_shape, output_dim, filepath) lstm_model.fit(train_x, train_y, epochs=epochs, batch_size=batch_size, verbose=1) # 模型保存 lstm_model.save(model_save_path) N = test_x.shape[0] # 測試的條數 predict = [] label = [] for start, end in zip(range(0, N, 1), range(1, N+1, 1)): sentence = [inverse_word_dictionary[i] for i in test_x[start] if i != 0] y_predict = lstm_model.predict(test_x[start:end]) label_predict = output_dictionary[np.argmax(y_predict[0])] label_true = output_dictionary[np.argmax(test_y[start:end])] print(''.join(sentence), label_true, label_predict) # 輸出預測結果 predict.append(label_predict) label.append(label_true) acc = accuracy_score(predict, label) # 預測準確率 print('模型在測試集上的準確率爲: %s.' % acc) if __name__ == '__main__': filepath = './corpus.csv' input_shape = 180 model_save_path = './corpus_model.h5' model_train(input_shape, filepath, model_save_path)
對上述模型,共訓練5次,訓練集和測試集比例爲9:1,輸出的結果爲:
...... Epoch 5/5 ...... 3424/3854 [=========================>....] - ETA: 2s - loss: 0.1280 - acc: 0.9565 3456/3854 [=========================>....] - ETA: 1s - loss: 0.1274 - acc: 0.9569 3488/3854 [==========================>...] - ETA: 1s - loss: 0.1274 - acc: 0.9570 3520/3854 [==========================>...] - ETA: 1s - loss: 0.1287 - acc: 0.9568 3552/3854 [==========================>...] - ETA: 1s - loss: 0.1290 - acc: 0.9564 3584/3854 [==========================>...] - ETA: 1s - loss: 0.1284 - acc: 0.9568 3616/3854 [===========================>..] - ETA: 1s - loss: 0.1284 - acc: 0.9569 3648/3854 [===========================>..] - ETA: 0s - loss: 0.1278 - acc: 0.9572 3680/3854 [===========================>..] - ETA: 0s - loss: 0.1271 - acc: 0.9576 3712/3854 [===========================>..] - ETA: 0s - loss: 0.1268 - acc: 0.9580 3744/3854 [============================>.] - ETA: 0s - loss: 0.1279 - acc: 0.9575 3776/3854 [============================>.] - ETA: 0s - loss: 0.1272 - acc: 0.9579 3808/3854 [============================>.] - ETA: 0s - loss: 0.1279 - acc: 0.9580 3840/3854 [============================>.] - ETA: 0s - loss: 0.1281 - acc: 0.9581 3854/3854 [==============================] - 18s 5ms/step - loss: 0.1298 - acc: 0.9577 ...... 給父母買的,特地用了一段時間再來評價,電視很是好,沒有壞點和損壞,界面也很簡潔,便於操做,稍微不足就是開機會比普通電視慢一些,這應該是智能電視的通病吧,若是能夠但願微鯨大大能夠更新系統優化下開機時間~電視真的很棒,性價比爆棚,值得你們考慮購買。 客服很細心,快遞小哥很耐心的等我通電驗貨,態度很是好。 負面 正面 長鬚鯨和海獅回答都很及時,雖然物流不夠快可是服務不錯電視不錯,對比了樂視小米和微鯨論性價比仍是微鯨好點 負面 負面 因此看不到4k效果,可是應該能夠。 自帶音響,中規中矩吧,好像沒有別人說的好。並且,到如今沒鏈接上個人漫步者,這個很是不滿意,由於看到網上說好像普通3.5mm的連不上或者連上了聲音小。但願廠家接下來開發的電視有改進。不知道我要不要換個音響。其餘的用用再說。 放在地上的是跟我混了兩年的tcl,天氣受潮,修了一次,下崗了。 最後,我也以爲底座不算太穩,湊合着用。 負面 負面 電視機通常,低端機不要求那麼高咯。 負面 負面 很好,兩點下單上午就到了,服務很好。 正面 正面 幫朋友買的,好好好好好好好好 正面 正面 ...... 模型在測試集上的準確率爲: 0.9020979020979021.
能夠看到,該模型在訓練集上的準確率爲95%以上,在測試集上的準確率爲90%以上,效果仍是至關不錯的。
接着,咱們利用剛剛訓練好的模型,對新的數據進行測試。筆者隨機改造上述樣本的評論,而後預測其情感傾向。情感預測的Python代碼以下:
# -*- coding: utf-8 -*- # Import the necessary modules import pickle import numpy as np from keras.models import load_model from keras.preprocessing.sequence import pad_sequences # 導入字典 with open('word_dict.pk', 'rb') as f: word_dictionary = pickle.load(f) with open('label_dict.pk', 'rb') as f: output_dictionary = pickle.load(f) try: # 數據預處理 input_shape = 180 sent = "電視剛安裝好,說實話,畫質不怎麼樣,不好!" x = [[word_dictionary[word] for word in sent]] x = pad_sequences(maxlen=input_shape, sequences=x, padding='post', value=0) # 載入模型 model_save_path = './sentiment_analysis.h5' lstm_model = load_model(model_save_path) # 模型預測 y_predict = lstm_model.predict(x) label_dict = {v:k for k,v in output_dictionary.items()} print('輸入語句: %s' % sent) print('情感預測結果: %s' % label_dict[np.argmax(y_predict)]) except KeyError as err: print("您輸入的句子有漢字不在詞彙表中,請從新輸入!") print("不在詞彙表中的單詞爲:%s." % err)
輸出結果以下:
輸入語句: 電視剛安裝好,說實話,畫質不怎麼樣,不好! 情感預測結果: 負面
讓咱們再嘗試着測試一些其餘的評論:
輸入語句: 物超所值,真心不錯 情感預測結果: 正面 輸入語句: 很大很好,方便安裝! 情感預測結果: 正面 輸入語句: 卡,慢,死機,閃退。 情感預測結果: 負面 輸入語句: 這種貨色就這樣吧,別期待怎樣。 情感預測結果: 負面 輸入語句: 啥服務態度碼,出了事情一個推一個,送貨安裝還收我50 情感預測結果: 負面 輸入語句: 京東服務很好!但我買的這款電視兩天後就出現這樣的問題,很後悔買了這樣的電視 情感預測結果: 負面 輸入語句: 產品質量不錯,就是這位客服的態度十分惡劣,對相關服務不予解釋說明,缺少耐心, 情感預測結果: 負面 輸入語句: 很滿意,電視很是好。護眼模式,很好,也很清晰。 情感預測結果: 負面
固然,該模型並非對一切該商品的評論都會有好的效果,仍是應該針對特定的語料去訓練,去預測。
本文主要介紹了LSTM模型在文本情感分析方面的應用,該項目已上傳Github,地址爲: https://github.com/percent4/Sentiment_Analysis 。
注意:不妨瞭解下筆者的微信公衆號: Python爬蟲與算法(微信號爲:easy_web_scrape), 歡迎你們關注~