0


基于LSTM电商评论情感分析-多评价指标可视化版(内附源码)【自然语言处理NLP-100例】

  • 🔗 运行环境:python3
  • 🚩 作者:K同学啊
  • 🥇 精选专栏:《深度学习100例》
  • 🔥 推荐专栏:《新手入门深度学习》
  • 📚 选自专栏:《Matplotlib教程》
  • 🧿 优秀专栏:《Python入门100题》

大家好,我是K同学啊!

在上一篇文章中,我使用LSTM对电商评论做了一个较为复杂的情感分析,本文就继续上次的工作做进一部分的分析。**本次主要是在评价指标metrics处增加了

Precision

Recall

AUC

等值,实现了训练模型的同时记录这些指标,是实现方式上与以往也有所不同。与此同时,本次全连接层Dense的输出也被设置为1**,之前很少这样操作的,可以对这块针对性学习一下。

文章目录

一、前期工作

1. 导入数据

#源码内可阅读
df

evaluationlabel0用了一段时间,感觉还不错,可以正面1电视非常好,已经是家里的第二台了。第一天下单,第二天就到本地了,可是物流的人说车坏了,一直催...正面2电视比想象中的大好多,画面也很清晰,系统很智能,更多功能还在摸索中正面3不错正面4用了这么多天了,感觉还不错。夏普的牌子还是比较可靠。希望以后比较耐用,现在是考量质量的时候。正面.........4278一般,差强人意,还弄了点不愉快,投诉了好久才解决负面4279屏幕拐角明显暗,图像不到边。工程师上门尽然说没问题!退货还要收100元的开箱费,帮别人买的,...负面4280一分都不想给,京东这次让我太失望了,买的电视没有声音,说是退货上门取件,规定好的时间不去,一...负面4281新电视买回家不到十多天,底座支架因质量问题断裂,电视从桌子上摔坏,打售后电话,人员一直推脱不...负面4282一般般。这个价位也不会抱太多的期望。比某某TV还是好很多。负面
4283 rows × 2 columns

2. 数据分析

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))
<AxesSubplot:ylabel='label'>

df['length']= df['evaluation'].apply(lambda x:len(x))
df.head()

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

# 源码内可阅读
plt.show()

# 源码内可阅读
plt.show()
分位点为0.9的句子长度:172。

二、数据预处理

1. 打乱数据

将正面文本数据与负面文本数据进行打乱

df = df.sample(frac=1)
df.head()

evaluationlabellength2105电视不错,不过今年的价格比去年贵了……负面19996电视很清晰大品牌值得信赖正面124171电视不错,没有坏点,漏光也基本看不出来,看了下电视剧,有点拖影,网上换个接口就好了,暂时没试...负面1183206喇叭太差劲,有点小卡,界面不是很友好,与泰捷盒子差太远了,但播放效果色彩不错,漏光较多负面431748好,送货速度快,服务好。.3333333正面20

2. 分词处理

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.442 seconds.
Prefix dict has been built successfully.

evaluationlabellengthwords2105电视不错,不过今年的价格比去年贵了……负面19[电视, 不错, ,, 不过, 今年, 的, 价格比, 去年, 贵, 了, …, …]996电视很清晰大品牌值得信赖正面12[电视, 很, 清晰, 大, 品牌, 值得, 信赖]4171电视不错,没有坏点,漏光也基本看不出来,看了下电视剧,有点拖影,网上换个接口就好了,暂时没试...负面118[电视, 不错, ,, 没有, 坏点, ,, 漏光, 也, 基本, 看不出来, ,, 看, ...3206喇叭太差劲,有点小卡,界面不是很友好,与泰捷盒子差太远了,但播放效果色彩不错,漏光较多负面43[喇叭, 太, 差劲, ,, 有点, 小卡, ,, 界面, 不是, 很, 友好, ,, 与,...1748好,送货速度快,服务好。.3333333正面20[好, ,, 送货, 速度, 快, ,, 服务, 好, 。, ., 3333333]

3. 去除停用词

withopen("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 +=["…","去","还","西","一件","月","年",".","都"]defremove_stopwords(ls):# 去除停用词return[word for word in ls if word notin stopwords_list]

df['去除停用词后的数据']=df["words"].apply(lambda x: remove_stopwords(x))
df["y"]= np.array([1if i=="正面"else0for i in df['label']])
df.head()

evaluationlabellengthwords去除停用词后的数据y2105电视不错,不过今年的价格比去年贵了……负面19[电视, 不错, ,, 不过, 今年, 的, 价格比, 去年, 贵, 了, …, …][电视, 不错, 今年, 价格比, 去年, 贵]0996电视很清晰大品牌值得信赖正面12[电视, 很, 清晰, 大, 品牌, 值得, 信赖][电视, 很, 清晰, 大, 品牌, 值得, 信赖]14171电视不错,没有坏点,漏光也基本看不出来,看了下电视剧,有点拖影,网上换个接口就好了,暂时没试...负面118[电视, 不错, ,, 没有, 坏点, ,, 漏光, 也, 基本, 看不出来, ,, 看, ...[电视, 不错, 没有, 坏点, 漏光, 基本, 看不出来, 看, 下, 电视剧, 有点, ...03206喇叭太差劲,有点小卡,界面不是很友好,与泰捷盒子差太远了,但播放效果色彩不错,漏光较多负面43[喇叭, 太, 差劲, ,, 有点, 小卡, ,, 界面, 不是, 很, 友好, ,, 与,...[喇叭, 太, 差劲, 有点, 小卡, 界面, 不是, 很, 友好, 泰捷, 盒子, 差太远...01748好,送货速度快,服务好。.3333333正面20[好, ,, 送货, 速度, 快, ,, 服务, 好, 。, ., 3333333][好, 送货, 速度, 快, 服务, 好, 3333333]1

4. Word2vec处理

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

from gensim.models.word2vec  import Word2Vec

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')# 将文本转化为向量defaverage_vec(text):
    vec = np.zeros(300).reshape((1,300))for word in text:try:
            vec += w2v.wv[word].reshape((1,300))except KeyError:continuereturn vec

# 将词向量保存为 Ndarray
x_vec = np.concatenate([average_vec(z)for z in x])
y     = df['y']

5. 划分训练集与测试集

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
import tensorflow as tf

#定义模型
model = Sequential()
model.add(Embedding(100000,100))
model.add(LSTM(100, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(1, activation='sigmoid'))

METRICS =[
    tf.keras.metrics.TruePositives(name='tp'),
    tf.keras.metrics.FalsePositives(name='fp'),
    tf.keras.metrics.TrueNegatives(name='tn'),
    tf.keras.metrics.FalseNegatives(name='fn'), 
    tf.keras.metrics.BinaryAccuracy(name='accuracy'),# 注意需要根据loss改变
    tf.keras.metrics.Precision(name='precision'),
    tf.keras.metrics.Recall(name='recall'),
    tf.keras.metrics.AUC(name='auc'),
    tf.keras.metrics.AUC(name='prc', curve='PR'),# precision-recall curve]

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=METRICS)

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, 1)                 101       
=================================================================
Total params: 10,080,501
Trainable params: 10,080,501
Non-trainable params: 0
_________________________________________________________________
epochs =30
batch_size =64

history = model.fit(X_train, 
                    y_train,
                    epochs=epochs,
                    batch_size=batch_size,
                    validation_split=0.2)
Epoch 1/30
43/43 [==============================] - 40s 879ms/step - loss: 0.6343 - tp: 691.0000 - fp: 397.0000 - tn: 1132.0000 - fn: 520.0000 - accuracy: 0.6653 - precision: 0.6351 - recall: 0.5706 - auc: 0.6972 - prc: 0.6335 - val_loss: 0.5641 - val_tp: 194.0000 - val_fp: 76.0000 - val_tn: 304.0000 - val_fn: 112.0000 - val_accuracy: 0.7259 - val_precision: 0.7185 - val_recall: 0.6340 - val_auc: 0.7814 - val_prc: 0.7354
......
Epoch 29/30
43/43 [==============================] - 37s 869ms/step - loss: 0.4196 - tp: 985.0000 - fp: 279.0000 - tn: 1250.0000 - fn: 226.0000 - accuracy: 0.8157 - precision: 0.7793 - recall: 0.8134 - auc: 0.8859 - prc: 0.8316 - val_loss: 0.4754 - val_tp: 244.0000 - val_fp: 74.0000 - val_tn: 306.0000 - val_fn: 62.0000 - val_accuracy: 0.8017 - val_precision: 0.7673 - val_recall: 0.7974 - val_auc: 0.8592 - val_prc: 0.8097
Epoch 30/30
43/43 [==============================] - 37s 855ms/step - loss: 0.4189 - tp: 992.0000 - fp: 301.0000 - tn: 1228.0000 - fn: 219.0000 - accuracy: 0.8102 - precision: 0.7672 - recall: 0.8192 - auc: 0.8852 - prc: 0.8336 - val_loss: 0.4716 - val_tp: 247.0000 - val_fp: 73.0000 - val_tn: 307.0000 - val_fn: 59.0000 - val_accuracy: 0.8076 - val_precision: 0.7719 - val_recall: 0.8072 - val_auc: 0.8589 - val_prc: 0.8040

三、结果分析

import matplotlib as mpl
mpl.rcParams['figure.figsize']=(16,8)
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']defplot_metrics(history):
    metrics =['accuracy','loss','prc','precision','recall']for n, metric inenumerate(metrics):
        name = metric.replace("_"," ").capitalize()
        plt.subplot(2,3,n+1)
        plt.plot(history.epoch, history.history[metric], color=colors[2], label='Train')
        plt.plot(history.epoch, history.history['val_'+metric],color=colors[1], linestyle="--", label='Val')
        
        plt.xlabel('Epoch',fontsize=14)
        plt.ylabel(name,fontsize=14)
        
        plt.legend()

plot_metrics(history)

四、情感预测

# 读取 Word2Vec 并对新输入进行词向量计算defaverage_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:continuereturn vec

# 对电影评论进行情感判断defmodel_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))# 实时返回积极或消极结果ifint(result)==1:# print(string, '[积极]')return"积极"else:# print(string, '[消极]')return"消极"

comment_sentiment =[]# 用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.')
电视不错,不过今年的价格比去年贵了…… | 消极
电视很清晰大品牌值得信赖 | 消极
电视不错,没有坏点,漏光也基本看不出来,看了下电视剧,有点拖影,网上换个接口就好了,暂时没试。京东的预约客服真的要给差评,没经过我同意擅自把送货时间改到星期五,害送货大哥白跑趟。这里要给送货大哥好评,30几度的天气,大中午把电视扛到5楼 | 消极
喇叭太差劲,有点小卡,界面不是很友好,与泰捷盒子差太远了,但播放效果色彩不错,漏光较多 | 消极
好,送货速度快,服务好。.3333333 | 消极
买的第一台微鲸电视 感觉很不错 系统很流畅 清晰度也可以 就是个人感觉遥控的时候会有短暂的延迟 外观各方面还是感觉挺好的 这个价位性价比还可以 | 消极
6.18买的,活动力度大,画面感有待提高,目前没有质量问题,待观察 | 消极
挺好的,看久了下面底部很热,售后安装220元,被兜售一个HIDMI高清线99元,还有58元的有线电视线,一共花了快400块钱,有点被售后忽悠了,后来网上一看两根线最多40快!线上给你优惠,线下想法搞你! | 消极
挺好的 就是开发票太慢了 催了好久到现在还没到呢 | 消极
图电视剧的尺寸大性价比较高,最主要的就是搞活动的时候价格也不是太高啦 | 消极
done.

本文转载自: https://blog.csdn.net/qq_38251616/article/details/123776439
版权归原作者 K同学啊 所有, 如有侵权,请联系我们删除。

“基于LSTM电商评论情感分析-多评价指标可视化版(内附源码)【自然语言处理NLP-100例】”的评论:

还没有评论