基于LSTM的电商评论情感分析-TensorFlow2实现(内附源码)【自然语言处理NLP-100例】

  • 📍本文选自专栏:《自然语言处理NLP-实例教程》
  • 📍推荐精品专栏:《深度学习100例》

一句话介绍LSTM,它是RNN的进阶版,如果说RNN的最大限度是理解一句话,那么LSTM的最大限度则是理解一段话,详细介绍如下:

LSTM,全称为长短期记忆网络(Long Short Term Memory networks),是一种特殊的RNN,能够学习到长期依赖关系。LSTM由Hochreiter & Schmidhuber (1997)提出,许多研究者进行了一系列的工作对其改进并使之发扬光大。LSTM在许多问题上效果非常好,现在被广泛使用。

所有的循环神经网络都有着重复的神经网络模块形成链的形式。在普通的RNN中,重复模块结构非常简单,其结构如下:

基于LSTM的电商评论情感分析-TensorFlow2实现(内附源码)【自然语言处理NLP-100例】

LSTM避免了长期依赖的问题。可以记住长期信息!LSTM内部有较为复杂的结构。能通过门控状态来选择调整传输的信息,记住需要长时间记忆的信息,忘记不重要的信息,其结构如下:

基于LSTM的电商评论情感分析-TensorFlow2实现(内附源码)【自然语言处理NLP-100例】
神经网络程序的基本流程如下
基于LSTM的电商评论情感分析-TensorFlow2实现(内附源码)【自然语言处理NLP-100例】

前期工作

导入数据

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import font_manager
from itertools import accumulate
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

df = pd.read_csv('data_single.csv')

df.head()
evaluation label
0 用了一段时间,感觉还不错,可以 正面
1 电视非常好,已经是家里的第二台了。第一天下单,第二天就到本地了,可是物流的人说车坏了,一直催… 正面
2 电视比想象中的大好多,画面也很清晰,系统很智能,更多功能还在摸索中 正面
3 不错 正面
4 用了这么多天了,感觉还不错。夏普的牌子还是比较可靠。希望以后比较耐用,现在是考量质量的时候。 正面

数据分析

df.groupby('label')["evaluation"].count()
label
正面    1908
负面    2375
Name: evaluation, dtype: int64
df.label.value_counts().plot(kind='pie', autopct='%0.05f%%', colors=['lightblue', 'lightgreen'], explode=(0.01, 0.01))

基于LSTM的电商评论情感分析-TensorFlow2实现(内附源码)【自然语言处理NLP-100例】

df['length'] = df['evaluation'].apply(lambda x: len(x))
df.head()
evaluation label length
0 用了一段时间,感觉还不错,可以 正面 15
1 电视非常好,已经是家里的第二台了。第一天下单,第二天就到本地了,可是物流的人说车坏了,一直催… 正面 97
2 电视比想象中的大好多,画面也很清晰,系统很智能,更多功能还在摸索中 正面 33
3 不错 正面 2
4 用了这么多天了,感觉还不错。夏普的牌子还是比较可靠。希望以后比较耐用,现在是考量质量的时候。 正面 46
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("句子长度及出现频数统计图")
plt.xlabel("句子长度")
plt.ylabel("句子长度出现的频数")
plt.show()

基于LSTM的电商评论情感分析-TensorFlow2实现(内附源码)【自然语言处理NLP-100例】

# 绘制句子长度累积分布函数(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.9
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="g", linestyles="--")
plt.vlines(index, 0, quantile, colors="g", linestyles="--")
plt.text(0, quantile, str(quantile))
plt.text(index, 0, str(index))
plt.title("句子长度累积分布函数图")
plt.xlabel("句子长度")
plt.ylabel("句子长度累积频率")
plt.show()
分位点为0.9的句子长度:172。

基于LSTM的电商评论情感分析-TensorFlow2实现(内附源码)【自然语言处理NLP-100例】

数据预处理

随机播放数据

将正文本数据与负文本数据混洗

df = df.sample(frac=1)
df.head()
evaluation label length
1595 电视蛮好的!没想到很快就收到了!赞一个! 晒单: 收起 向左转 向右转 正面 35
2305 根本不用预约呀,1198直接买的,非传统品牌,心情坎坷不安,咬牙下单试试,收货验过屏幕没事就… 负面 134
3831 屏幕不错,但是声音一般啊,看片还得配音响 负面 20
2119 价格略比去年高,但画面效果不错,独角鲸服务到位 负面 23
3687 这个电视毫无智能语音体验,只有一些简单的操作,不该贪几百块的便宜,买小米就好了,开关机不灵敏… 负面 166

分词

import jieba

word_cut = lambda x: jieba.lcut(x)
df['words'] = df["evaluation"].apply(word_cut)
df.head()
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\ADMINI~1\AppData\Local\Temp\jieba.cache
Loading model cost 0.434 seconds.
Prefix dict has been built successfully.
evaluation label length words
1595 电视蛮好的!没想到很快就收到了!赞一个! 晒单: 收起 向左转 向右转 正面 35 [电视, 蛮, 好, 的, !, 没想到, 很快, 就, 收到, 了, !, 赞, 一个, …
2305 根本不用预约呀,1198直接买的,非传统品牌,心情坎坷不安,咬牙下单试试,收货验过屏幕没事就… 负面 134 [根本, 不用, 预约, 呀, ,, 1198, 直接, 买, 的, ,, 非传统, 品牌,…
3831 屏幕不错,但是声音一般啊,看片还得配音响 负面 20 [屏幕, 不错, ,, 但是, 声音, 一般, 啊, ,, 看片, 还, 得配, 音响]
2119 价格略比去年高,但画面效果不错,独角鲸服务到位 负面 23 [价格, 略, 比, 去年, 高, ,, 但, 画面, 效果, 不错, ,, 独角, 鲸, …
3687 这个电视毫无智能语音体验,只有一些简单的操作,不该贪几百块的便宜,买小米就好了,开关机不灵敏… 负面 166 [这个, 电视, 毫无, 智能, 语音, 体验, ,, 只有, 一些, 简单, 的, 操作,…

删除停用词

with open("hit_stopwords.txt", "r", encoding='utf-8') as f:
    stopwords = f.readlines()
    
stopwords_list = []
for each in stopwords:
    stopwords_list.append(each.strip('\n'))

# 添加自定义停用词
stopwords_list += ["…","去","还","西","一件","月","年",".","都"]

def remove_stopwords(ls):  # 去除停用词
    return [word for word in ls if word not in stopwords_list]

df['去除停用词后的数据']=df["words"].apply(lambda x: remove_stopwords(x))
df.head()
evaluation label length words 去除停用词后的数据
1595 电视蛮好的!没想到很快就收到了!赞一个! 晒单: 收起 向左转 向右转 正面 35 [电视, 蛮, 好, 的, !, 没想到, 很快, 就, 收到, 了, !, 赞, 一个, … [电视, 蛮, 好, 没想到, 很快, 收到, !, 赞, !, , 晒单, , 收起,…
2305 根本不用预约呀,1198直接买的,非传统品牌,心情坎坷不安,咬牙下单试试,收货验过屏幕没事就… 负面 134 [根本, 不用, 预约, 呀, ,, 1198, 直接, 买, 的, ,, 非传统, 品牌,… [根本, 不用, 预约, 1198, 直接, 买, 非传统, 品牌, 心情, 坎坷, 不安,…
3831 屏幕不错,但是声音一般啊,看片还得配音响 负面 20 [屏幕, 不错, ,, 但是, 声音, 一般, 啊, ,, 看片, 还, 得配, 音响] [屏幕, 不错, 声音, 看片, 得配, 音响]
2119 价格略比去年高,但画面效果不错,独角鲸服务到位 负面 23 [价格, 略, 比, 去年, 高, ,, 但, 画面, 效果, 不错, ,, 独角, 鲸, … [价格, 略, 去年, 高, 画面, 效果, 不错, 独角, 鲸, 服务到位]
3687 这个电视毫无智能语音体验,只有一些简单的操作,不该贪几百块的便宜,买小米就好了,开关机不灵敏… 负面 166 [这个, 电视, 毫无, 智能, 语音, 体验, ,, 只有, 一些, 简单, 的, 操作,… [电视, 毫无, 智能, 语音, 体验, 简单, 操作, 不该, 贪, 几百块, 便宜, 买…

Word2vec处理

Word2vec是一个用来产生词向量的模型。是一个将单词转换成向量形式的工具。 通过转换,可以把对文本内容的处理简化为向量空间中的向量运算,计算出向量空间上的相似度,来表示文本语义上的相似度。

from gensim.models.word2vec  import Word2Vec
import numpy as np

x = df["去除停用词后的数据"]

# 训练 Word2Vec 浅层神经网络模型
w2v = Word2Vec(vector_size=300,  #是指特征向量的维度,默认为100。
               min_count=10)     #可以对字典做截断. 词频少于min_count次数的单词会被丢弃掉, 默认值为5。
w2v.build_vocab(x)
w2v.train(x,                         
          total_examples=w2v.corpus_count, 
          epochs=20)
# 保存 Word2Vec 模型及词向量
w2v.save('w2v_model.pkl')

# 将文本转化为向量
def average_vec(text):
    vec = np.zeros(300).reshape((1, 300))
    for word in text:
        try:
            vec += w2v.wv[word].reshape((1, 300))
        except KeyError:
            continue
    return vec

# 将词向量保存为 Ndarray
x_vec = np.concatenate([average_vec(z) for z in x])
#多类标签的one-hot展开
y = pd.get_dummies(df['label']).values
y[:5]
array([[1, 0],
       [0, 1],
       [0, 1],
       [0, 1],
       [0, 1]], dtype=uint8)

划分训练集和测试集

from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test = train_test_split(x_vec,y,test_size=0.2)
from keras.models          import Sequential
from keras.layers          import Dense,LSTM,Bidirectional,Embedding

#定义模型
model = Sequential()
model.add(Embedding(100000, 100))
model.add(LSTM(100, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(2, activation='softmax'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

model.summary()
WARNING:tensorflow:Layer lstm will not use cuDNN kernels since it doesn't meet the criteria. It will use a generic GPU kernel as fallback when running on GPU.
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 100)         10000000  
_________________________________________________________________
lstm (LSTM)                  (None, 100)               80400     
_________________________________________________________________
dense (Dense)                (None, 2)                 202       
=================================================================
Total params: 10,080,602
Trainable params: 10,080,602
Non-trainable params: 0
_________________________________________________________________
epochs = 100
batch_size = 64

history = model.fit(X_train, 
                    y_train, 
                    epochs=epochs, 
                    batch_size=batch_size,
                    validation_split=0.2)
Epoch 1/100
43/43 [==============================] - 39s 868ms/step - loss: 0.6140 - accuracy: 0.7051 - val_loss: 0.5903 - val_accuracy: 0.6910
Epoch 2/100
43/43 [==============================] - 37s 860ms/step - loss: 0.5555 - accuracy: 0.7288 - val_loss: 0.5735 - val_accuracy: 0.7172
Epoch 3/100
43/43 [==============================] - 37s 858ms/step - loss: 0.5421 - accuracy: 0.7336 - val_loss: 0.5713 - val_accuracy: 0.7099
  ......
43/43 [==============================] - 36s 848ms/step - loss: 0.1602 - accuracy: 0.9409 - val_loss: 0.5864 - val_accuracy: 0.8338
Epoch 99/100
43/43 [==============================] - 36s 849ms/step - loss: 0.1757 - accuracy: 0.9292 - val_loss: 0.5823 - val_accuracy: 0.8338
Epoch 100/100
43/43 [==============================] - 36s 847ms/step - loss: 0.1410 - accuracy: 0.9467 - val_loss: 0.6176 - val_accuracy: 0.8222

情绪预测

# 读取 Word2Vec 并对新输入进行词向量计算
def average_vec(words):
    # 读取 Word2Vec 模型
    w2v = Word2Vec.load('w2v_model.pkl')
    vec = np.zeros(300).reshape((1, 300))
    for word in words:
        try:
            vec += w2v.wv[word].reshape((1, 300))
        except KeyError:
            continue
    return vec

# 对电影评论进行情感判断
def model_predict(string):

    # 对评论分词
    words = jieba.lcut(str(string))
    words_vec = average_vec(words)
    # 读取支持向量机模型
    # model = joblib.load('svm_model.pkl')

    result = np.argmax(model.predict(words_vec))
    # 实时返回积极或消极结果
    if int(result) == 1:
        # print(string, '[积极]')
        return "积极"
    else:
        # print(string, '[消极]')
        return "消极"

# 用10条数据做测试
for index, row in df.iloc[:10].iterrows():
    print(row["evaluation"],end=" | ")
    result = model_predict(row["去除停用词后的数据"])
    comment_sentiment.append(result)
    print(result)

#将情绪结果与原数据合并为新数据
merged = pd.concat([df, pd.Series(comment_sentiment, name='用户情绪')], axis=1)
# 储存文件
pd.DataFrame.to_csv(merged,'comment_sentiment.csv',encoding="utf-8-sig")
print('done.')

我们的最终预测如下:

电视蛮好的!没想到很快就收到了!赞一个! 晒单: 收起 向左转 向右转 | 积极
根本不用预约呀,1198直接买的,非传统品牌,心情坎坷不安,咬牙下单试试,收货验过屏幕没事就自己装,4个螺丝而已。头三次开机好慢啊,最长的几乎5分钟,赶紧拨打客服电话,可能是系统更新,后来就好了,待机开机飞快,音画满意。总算安心了。连接移动硬盘居然连原盘也能放,强啊! | 积极
屏幕不错,但是声音一般啊,看片还得配音响 | 消极
价格略比去年高,但画面效果不错,独角鲸服务到位 | 消极
这个电视毫无智能语音体验,只有一些简单的操作,不该贪几百块的便宜,买小米就好了,开关机不灵敏,边框不是金属的,底座是我见到的最简陋的 ,屏幕背景暗淡,根本看不清,也不知道设计软件的怎么想的 ,大半夜起来12点整抢的,被告知没有挂架送,已送完,其他赠品也没兑现,建议大家考虑下别的品牌吧 优点是 便宜、配置高 买电视还是要买听话的电视 | 积极
价格比之前买的高了些 送货速度和服务值得表扬 机器色彩操作界面部分发白的厉害 设置了也一样 看片子尚可 勉强接受 | 积极
电视内容丰富,画质清晰。语音搜索功能不够完善,反应慢1-2秒。希望后续更新能够再加强。 | 积极
电视很不错,价格也实惠! | 积极
速度快还包邮,已经看上了,一年会员也已经领到(需要把系统升级到最新版本才能领取哟);速度刷刷的,并没有说什么开机5分钟这一说儿;模拟信号接口有些鸡肋,谁用这种电视看模拟信号啊,usb3.0也是棒棒哒;内存16g以电视来说足够用啦;屏幕完美,就是左右有些许漏光的情况(在电视机正面看比较明显稍微一歪头就纯黑了,我不专业不知道说的对不对),总之还是物有所值的! | 消极
之前的电视坏了,今天刚换的货!希望不要有问题! | 消极
done.

版权声明:本文为博主K同学啊原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/qq_38251616/article/details/123138717

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2022年3月1日 下午7:25
下一篇 2022年3月1日 下午7:40

相关推荐