当前位置:   article > 正文

自然语言处理(NLP)之使用LSTM进行文本情感分析_lstm情感分析

lstm情感分析

情感分析简介

文本情感分析(Sentiment Analysis)是自然语言处理(NLP)方法中常见的应用,也是一个有趣的基本任务,尤其是以提炼文本情绪内容为目的的分类。它是对带有情感色彩的主观性文本进行分析、处理、归纳和推理的过程。
  本文将介绍情感分析中的情感极性(倾向)分析。所谓情感极性分析,指的是对文本进行褒义、贬义、中性的判断。在大多应用场景下,只分为两类。例如对于“喜爱”和“厌恶”这两个词,就属于不同的情感倾向。
  本文将详细介绍如何使用深度学习模型中的LSTM模型来实现文本的情感分析。

文本介绍及语料分析  

以某电商网站中某个商品的评论作为语料(corpus.csv),该数据集的下载网址为:https://github.com/renjunxiang/Text-Classification/blob/master/TextClassification/data/data_single.csv ,该数据集一共有4310条评论数据,文本的情感分为两类:“正面”和“反面”,该数据集的前几行如下:

  1. evaluation,label
  2. 用了一段时间,感觉还不错,可以,正面
  3. 电视非常好,已经是家里的第二台了。第一天下单,第二天就到本地了,可是物流的人说车坏了,一直催,客服也帮着催,到第三天下午5点才送过来。父母年纪大了,买个大电视画面清晰,趁着耳朵还好使,享受几年。,正面
  4. 电视比想象中的大好多,画面也很清晰,系统很智能,更多功能还在摸索中,正面
  5. 不错,正面
  6. 用了这么多天了,感觉还不错。夏普的牌子还是比较可靠。希望以后比较耐用,现在是考量质量的时候。,正面
  7. 物流速度很快,非常棒,今天就看了电视,非常清晰,非常流畅,一次非常完美的购物体验,正面
  8. 非常好,客服还特意打电话做回访,正面
  9. 物流小哥不错,辛苦了,东西还没用,正面
  10. 送货速度快,质量有保障,活动价格挺好的。希望用的久,不出问题。,正面

接着我们需要对语料做一个简单的分析:

  • 数据集中的情感分布;
  • 数据集中的评论句子长度分布。

使用以下Python脚本,我们可以统计出数据集中的情感分布以及评论句子长度分布。

  1. import pandas as pd
  2. import matplotlib.pyplot as plt
  3. from matplotlib import font_manager
  4. from itertools import accumulate
  5. # 设置matplotlib绘图时的字体
  6. my_font = font_manager.FontProperties(fname='C:\Windows\Fonts\simfang.ttf')
  7. # 统计句子长度及出现次数的频数
  8. df = pd.read_csv('./data_single.csv')
  9. print(df.groupby('label')['label'].count())
  10. df['length'] = df['evaluation'].apply(lambda x: len(x))
  11. # print(df)
  12. len_df = df.groupby('length').count()
  13. sent_length = len_df.index.tolist()
  14. sent_freq = len_df['evaluation'].tolist()
  15. # 绘制句子长度及出现频数统计图
  16. plt.bar(sent_length, sent_freq)
  17. plt.title("句子长度及出现频数统计图", fontproperties=my_font)
  18. plt.xlabel("句子长度", fontproperties=my_font)
  19. plt.ylabel("句子长度出现的频数", fontproperties=my_font)
  20. plt.savefig("./句子长度及出现频数统计图.png")
  21. plt.close()
  22. # 绘制句子长度累计分布函数(CDF)
  23. sent_pentage_list = [(count / sum(sent_freq)) for count in accumulate(sent_freq)]
  24. # 绘制CDF
  25. plt.plot(sent_length, sent_pentage_list)
  26. # 寻找分位点为quantile的句子长度
  27. quantile = 0.91
  28. # print(list(sent_pentage_list))
  29. for length, per in zip(sent_length, sent_pentage_list):
  30. if round(per, 2) == quantile:
  31. index = length
  32. break
  33. print('\n分位点为%s的句子长度:%d' % (quantile, index))
  34. # 绘制句子长度累积分布函数图
  35. plt.plot(sent_length, sent_pentage_list)
  36. plt.hlines(quantile, 0, index, colors="c", linestyles="dashed")
  37. plt.vlines(index, 0, quantile, colors="c", linestyles="dashed")
  38. plt.text(0, quantile, str(quantile))
  39. plt.text(index, 0, str(index))
  40. plt.title("句子长度累积分布函数图", fontproperties=my_font)
  41. plt.xlabel("句子长度", fontproperties=my_font)
  42. plt.ylabel("句子长度累积频率", fontproperties=my_font)
  43. plt.savefig("./句子长度累积分布函数图.png")
  44. plt.close()

运行结果:

  1. label
  2. 正面 1908
  3. 负面 2375
  4. Name: label, dtype: int64
  5. 分位点为0.91的句子长度:183

可以看到,正反面两类情感的比例差不多。句子长度及出现频数统计图如下:

句子长度累积分布函数图如下:

可以看到,大多数样本的句子长度集中在1-200之间,句子长度累计频率取0.91分位点,则长度为183左右。

使用LSTM模型

  接着我们使用深度学习中的LSTM模型来对上述数据集做情感分析,笔者实现的模型框架如下:

完整的Python代码如下:

  1. import pickle
  2. import numpy as np
  3. import pandas as pd
  4. from keras.utils import np_utils, plot_model
  5. from keras.models import Sequential
  6. from keras.preprocessing.sequence import pad_sequences
  7. from keras.layers import LSTM, Dense, Embedding, Dropout
  8. from sklearn.model_selection import train_test_split
  9. from sklearn.metrics import accuracy_score
  10. # 导入数据
  11. # 文件的数据中,特征为evaluation, 类别为label.
  12. def load_data(filepath, input_shape=20):
  13. df = pd.read_csv(filepath)
  14. # 标签及词汇表
  15. labels, vocabulary = list(df['label'].unique()), list(df['evaluation'].unique())
  16. # print(len(labels))
  17. # print(len(vocabulary))
  18. # 构造字符级别的特征
  19. string = ''
  20. for word in vocabulary:
  21. string += word
  22. # print(string)
  23. vocabulary = set(string)
  24. # print(vocabulary)
  25. # 字典列表
  26. word_dictionary = {word: i + 1 for i, word in enumerate(vocabulary)}
  27. with open('word_dict.pk', 'wb') as f:
  28. pickle.dump(word_dictionary, f)
  29. inverse_word_dictionary = {i + 1: word for i, word in enumerate(vocabulary)}
  30. label_dictionary = {label: i for i, label in enumerate(labels)}
  31. with open('label_dict.pk', 'wb') as f:
  32. pickle.dump(label_dictionary, f)
  33. output_dictionary = {i: labels for i, labels in enumerate(labels)}
  34. vocab_size = len(word_dictionary.keys()) # 词汇表大小
  35. label_size = len(label_dictionary.keys()) # 标签类别数量
  36. # print(vocab_size, labels)
  37. # 序列填充,按input_shape填充,长度不足的按0补充
  38. x = [[word_dictionary[word] for word in sent] for sent in df['evaluation']]
  39. x = pad_sequences(maxlen=input_shape, sequences=x, padding='post', value=0)
  40. y = [[label_dictionary[sent]] for sent in df['label']]
  41. y = [np_utils.to_categorical(label, num_classes=label_size) for label in y]
  42. y = np.array([list(_[0]) for _ in y])
  43. return x, y, output_dictionary, vocab_size, label_size, inverse_word_dictionary
  44. # 创建深度学习模型, Embedding + LSTM + Softmax.
  45. def create_LSTM(n_units, input_shape, output_dim, filepath):
  46. x, y, output_dictionary, vocab_size, label_size, inverse_word_dictionary = load_data(filepath)
  47. model = Sequential()
  48. model.add(Embedding(input_dim=vocab_size + 1, output_dim=output_dim,
  49. input_length=input_shape, mask_zero=True))
  50. model.add(LSTM(n_units, input_shape=(x.shape[0], x.shape[1])))
  51. model.add(Dropout(0.2))
  52. model.add(Dense(label_size, activation='softmax'))
  53. model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
  54. plot_model(model, to_file='./model_lstm.png', show_shapes=True)
  55. model.summary()
  56. return model
  57. # 模型训练
  58. def model_train(input_shape, filepath, model_save_path):
  59. # 将数据集分为训练集和测试集,占比为9:1
  60. # input_shape = 100
  61. x, y, output_dictionary, vocab_size, label_size, inverse_word_dictionary = load_data(filepath, input_shape)
  62. train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.1, random_state=42)
  63. # 模型输入参数,需要自己根据需要调整
  64. n_units = 100
  65. batch_size = 32
  66. epochs = 5
  67. output_dim = 20
  68. # 模型训练
  69. lstm_model = create_LSTM(n_units, input_shape, output_dim, filepath)
  70. lstm_model.fit(train_x, train_y, epochs=epochs, batch_size=batch_size, verbose=1)
  71. # 模型保存
  72. lstm_model.save(model_save_path)
  73. N = test_x.shape[0] # 测试的条数
  74. predict = []
  75. label = []
  76. for start, end in zip(range(0, N, 1), range(1, N + 1, 1)):
  77. sentence = [inverse_word_dictionary[i] for i in test_x[start] if i != 0]
  78. y_predict = lstm_model.predict(test_x[start:end])
  79. label_predict = output_dictionary[np.argmax(y_predict[0])]
  80. label_true = output_dictionary[np.argmax(test_y[start:end])]
  81. print(''.join(sentence), label_true, label_predict) # 输出预测结果
  82. predict.append(label_predict)
  83. label.append(label_true)
  84. acc = accuracy_score(predict, label) # 预测准确率
  85. print('模型在测试集上的准确率为: %s.' % acc)
  86. if __name__ == '__main__':
  87. filepath = './data_single.csv'
  88. input_shape = 180
  89. # load_data(filepath, input_shape)
  90. model_save_path = './corpus_model.h5'
  91. model_train(input_shape, filepath, model_save_path)

对上述模型,共训练5次,训练集和测试集比例为9:1,输出的结果为:

  1. Model: "sequential"
  2. _________________________________________________________________
  3. Layer (type) Output Shape Param #
  4. =================================================================
  5. embedding (Embedding) (None, 180, 20) 43100
  6. _________________________________________________________________
  7. lstm (LSTM) (None, 100) 48400
  8. _________________________________________________________________
  9. dropout (Dropout) (None, 100) 0
  10. _________________________________________________________________
  11. dense (Dense) (None, 2) 202
  12. =================================================================
  13. Total params: 91,702
  14. Trainable params: 91,702
  15. Non-trainable params: 0
  16. _________________________________________________________________
  17. Epoch 1/5
  18. 121/121 [==============================] - 15s 94ms/step - loss: 0.5719 - accuracy: 0.6683
  19. Epoch 2/5
  20. 121/121 [==============================] - 8s 65ms/step - loss: 0.2164 - accuracy: 0.9286
  21. Epoch 3/5
  22. 121/121 [==============================] - 7s 58ms/step - loss: 0.1884 - accuracy: 0.9385
  23. Epoch 4/5
  24. 121/121 [==============================] - 7s 57ms/step - loss: 0.1435 - accuracy: 0.9590
  25. Epoch 5/5
  26. 121/121 [==============================] - 7s 57ms/step - loss: 0.1161 - accuracy: 0.9646
  27. 硬件一般,但是软件很棒, 负面 负面
  28. 客服态度好。电视还没有开始用,还不知道效果。用了再评价 正面 负面
  29. 对的起这样的价钱 支持京东 想要下单的亲可以放心下单 加油 语音遥控器没有 希望京东送一个 谢谢 负面 正面
  30. 非常差,881元预购的电视礼包,说不发就不发了,真是非常差劲,真后悔在这家店买东西,大家不要再来了。 负面 负面
  31. 京东物流慢了些,本来应该昨天送到的,结果今天才送到。电视还可以,稍微有点延迟,性价比很高。 负面 负面
  32. 后还选择了这个创维一些国产的品牌,但是仔细参考参数之后还是做出了一个大胆的选择,选择的微鲸,使用了,之后的感觉非常的不错,老婆也非常的喜欢,感觉比那个乐视的话还是有一定的优势。可惜的话就是会员不够多,然后的话价格稍微贵了一点,现在可能那个平板液晶平板这一块也涨价了,所以说这个也情有可原。喜欢的可以大胆下手了,不会失望的,至少比创维,海信要好多了…性价比更高! 负面 负面
  33. 卧室用的,画面挺清晰,但是不能离近看,否则颜色会很诡异,安装师傅挺好的 正面 正面
  34. ......
  35. ......
  36. 电视机一般,低端机不要求那么高咯。 负面 负面
  37. 很好,两点下单上午就到了,服务很好。 正面 正面
  38. 帮朋友买的,好好好好好好好好 正面 正面
  39. 模型在测试集上的准确率为: 0.9230769230769231.

模型预测

  接着,我们利用刚刚训练好的模型,对新的数据进行测试。在这儿随机改造上述样本的评论,然后预测其情感倾向。情感预测的Python代码如下:

  1. # Import the necessary modules
  2. import pickle
  3. import numpy as np
  4. from keras.models import load_model
  5. from keras.preprocessing.sequence import pad_sequences
  6. # 导入字典
  7. with open('word_dict.pk', 'rb') as f:
  8. word_dictionary = pickle.load(f)
  9. with open('label_dict.pk', 'rb') as f:
  10. output_dictionary = pickle.load(f)
  11. try:
  12. # 数据预处理
  13. input_shape = 180
  14. sent = "很满意,电视非常好。护眼模式,很好,也很清晰。"
  15. x = [[word_dictionary[word] for word in sent]]
  16. x = pad_sequences(maxlen=input_shape, sequences=x, padding='post', value=0)
  17. # 载入模型
  18. model_save_path = './corpus_model.h5'
  19. lstm_model = load_model(model_save_path)
  20. # 模型预测
  21. y_predict = lstm_model.predict(x)
  22. label_dict = {v: k for k, v in output_dictionary.items()}
  23. print('输入语句: %s' % sent)
  24. print('情感预测结果: %s' % label_dict[np.argmax(y_predict)])
  25. except KeyError as err:
  26. print("您输入的句子有汉字不在词汇表中,请重新输入!")
  27. print("不在词汇表中的单词为:%s." % err)

运行结果:

  1. 输入语句: 很满意,电视非常好。护眼模式,很好,也很清晰。
  2. 情感预测结果: 正面
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/344930?site
推荐阅读
相关标签
  

闽ICP备14008679号