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

Posted K同学啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于LSTM电商评论情感分析-多评价指标可视化版(内附源码)自然语言处理NLP-100例相关的知识,希望对你有一定的参考价值。


大家好,我是K同学啊!

上一篇文章中,我使用LSTM对电商评论做了一个较为复杂的情感分析,本文就继续上次的工作做进一部分的分析。本次主要是在评价指标metrics处增加了PrecisionRecallAUC等值,实现了训练模型的同时记录这些指标,是实现方式上与以往也有所不同。与此同时,本次全连接层Dense的输出也被设置为1,之前很少这样操作的,可以对这块针对性学习一下。

文章目录

一、前期工作

1. 导入数据

#源码内可阅读
df
evaluationlabel
0用了一段时间,感觉还不错,可以正面
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()
evaluationlabellength
0用了一段时间,感觉还不错,可以正面15
1电视非常好,已经是家里的第二台了。第一天下单,第二天就到本地了,可是物流的人说车坏了,一直催...正面97
2电视比想象中的大好多,画面也很清晰,系统很智能,更多功能还在摸索中正面33
3不错正面2
4用了这么多天了,感觉还不错。夏普的牌子还是比较可靠。希望以后比较耐用,现在是考量质量的时候。正面46
# 源码内可阅读
plt.show()

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

二、数据预处理

1. 打乱数据

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

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

3. 去除停用词

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["y"] = np.array([1 if i=="正面" else 0 for i in df['label']])
df.head()
evaluationlabellengthwords去除停用词后的数据y
2105电视不错,不过今年的价格比去年贵了……负面19[电视, 不错, ,, 不过, 今年, 的, 价格比, 去年, 贵, 了, …, …][电视, 不错, 今年, 价格比, 去年, 贵]0
996电视很清晰大品牌值得信赖正面12[电视, 很, 清晰, 大, 品牌, 值得, 信赖][电视, 很, 清晰, 大, 品牌, 值得, 信赖]1
4171电视不错,没有坏点,漏光也基本看不出来,看了下电视剧,有点拖影,网上换个接口就好了,暂时没试...负面118[电视, 不错, ,, 没有, 坏点, ,, 漏光, 也, 基本, 看不出来, ,, 看, ...[电视, 不错, 没有, 坏点, 漏光, 基本, 看不出来, 看, 下, 电视剧, 有点, ...0
3206喇叭太差劲,有点小卡,界面不是很友好,与泰捷盒子差太远了,但播放效果色彩不错,漏光较多负面43[喇叭, 太, 差劲, ,, 有点, 小卡, ,, 界面, 不是, 很, 友好, ,, 与,...[喇叭, 太, 差劲, 有点, 小卡, 界面, 不是, 很, 友好, 泰捷, 盒子, 差太远...0
1748好,送货速度快,服务好。.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')

# 将文本转化为向量
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])
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']

def plot_metrics(history):
    metrics = ['accuracy','loss', 'prc', 'precision', 'recall']
    for n, metric in enumerate(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 并对新输入进行词向量计算
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 "消极"

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.


以上是关于基于LSTM电商评论情感分析-多评价指标可视化版(内附源码)自然语言处理NLP-100例的主要内容,如果未能解决你的问题,请参考以下文章

python实现电商评论的情感分析

数据可视化基于自然语言情感分析的网店评价

基于LSTM的文本情感分析(Keras版)

基于LSTM的文本情感分析(Keras版)

基于双向LSTM的影评情感分析算法设计学习记录

基于双向LSTM的影评情感分析算法设计学习记录