131在线民宿 UGC 数据挖掘实战--深度模型在情感分析中的应用

深度模型在情感分析中的应用

数据准备

本次实验将继续加载两个数据,一个是已经标注好的用户评论数据,另外一个是用户评价主题句,通过标注过的用户评论数据研究不同粒度的用户评价处理对深度情感分析模型的性能的影响,并比较字符级模型在用户评价情感极性推理上的差异。
使用 Pandas 加载已经标注好的在线用户评论情感数据表格,并查看数据维度和前 5 行数据。

import pandas as pd

train_data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/2628/hotel_comment.csv')
print(train_data.shape)
train_data.head()
image.png

加载民宿评论数据,并打印第一行。

comment_source = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/2628/1-1.csv')
print(comment_source.shape)
comment_source.head(1)

数据属性如下表所示

image.png

数据预处理
打印评论情感标注数据集上的标签统计,我们标注好的用户评论情感数据是一个标签平衡的数据。

train_data['label'].groupby(train_data['label']).count()

用户评论分词
jieba 分词器预热,第一次使用需要加载字典和缓存,通过结果看出返回的是分词的列表。

import jieba
' '.join(jieba.lcut(str('使用分词器进行分词')))

批量对用户评价进行分词,并打印第一行数据,处理过程需要一些时间。

%time train_data['text_word'] = train_data['text'].apply(lambda x: " ".join(jieba.lcut(str(x))))
train_data.head(1)

将用户评论处理成字符级别,为字符级模型提供训练集,并打印第一行数据,此预处理速度远远快于分词速度。

%time train_data['text_character'] = train_data['text'].apply(lambda x: " ".join(list(x)))
train_data.head(1)

TextCNN 情感分析模型

TextCNN 使用的卷积神经网络是一个典型的空间上的深度神经网络,基于卷积神经网络的情感分析具有优秀的特征抽取能力,能显著降低情感分类中人工抽取特征的难度。这类方法又根据文本嵌入粒度的不同可以分为字符级嵌入和词嵌入两种,一种是以分词后的词为情感分析粒度,另一种为字符级粒度的情感分析粒度,最后一层将包含全文潜在信息的最终编码送入 Sigmoid 做情感强度计算,即可对用户评论进行情感极性推理,是目前在文本分类经常使用的模型。

from tensorflow.keras.layers import Dense, Embedding, Flatten, Dropout, Convolution1D, Activation, MaxPooling1D
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.callbacks import EarlyStopping
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
import numpy as np
def build_cnn_model():
    # 序列模型初始化
    model = Sequential()
    #  嵌入层
    model.add(Embedding(max_words, embedding_dim, input_length=maxlen))
    # CNN 结构
    model.add(Convolution1D(64, 3, input_shape=(-1, embedding_dim)))
    # 激活层
    model.add(Activation('relu'))
    # 特征降维层
    model.add(MaxPooling1D(2, 2))
    # 特征拉平
    model.add(Flatten())
    # 全连接层
    model.add(Dense(128, activation='relu'))
    # 参数随机丢弃层
    model.add(Dropout(0.5))
    # 输出层
    model.add(Dense(1, activation='sigmoid'))
    # 使用二分类交叉熵计算损失,并使用 adam 进行损失调节,调节指标是准确度
    model.compile(loss='binary_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])
    return model

词级别模型训练

# 设置将用户评论映射到空间的维度
embedding_dim = 300

# 设置用户评论的最大长度
maxlen = 100

# 设置词频字典大小
max_words = 1000

# 加载 TextCNN 结构
model_w_cnn = build_cnn_model()

# 打印网络结构
model_w_cnn.summary()
# 标签数据转换为 np.array
label_data = train_data['label'].to_numpy()

# 创建分词器 Tokenizer 对象
tokenizer = Tokenizer(num_words=max_words)

# 将Tokenizer拟合语料,生成字典,形成新的tokenizer
tokenizer.fit_on_texts(train_data['text_word'].tolist())

# 文本数据转换和填充
train_data_word = pad_sequences(
    tokenizer.texts_to_sequences(train_data['text_word'].tolist()), maxlen)

按照训练集 8 成和测试集 2 成的比例对数据集进行划分。

# 设置固定划分数据集
x_train_word, y_train_word, x_test, y_test = train_test_split(train_data_word,
                                                              label_data,
                                                              test_size=0.2,
                                                              random_state=1)

词级 TextCNN 模型训练,设置 128 条数据为一个批次,2 轮模型训练,训练集中的 20% 作为验证集,并加入早停设置。

# 加载记时模块
import time
# 记录开始时间
st = time.time()

# 模型训练
model_w_cnn.fit(
    x_train_word,
    x_test,
    batch_size=128,
    epochs=2,
    validation_split=0.2,
    callbacks=[EarlyStopping(monitor='val_loss', min_delta=0.0001)])

# 计算模型耗时
time_used = time.time() - st

通过传入原始的标签和预测的标签可以直接将分类器性能进行度量,并对指标收集,包含:模型的训练时间、accuracy_score 表示被正确预测的样本占总样本的比例、f1_score 值表示精确率与召回率的调和平均数和模型标签。

# 使用列表的形式收集数据指标
from sklearn import metrics
method, acc_list, f1_list, time_use = list(), list(), list(), list()

# 开始对测试集合进行批量计算
model_w_cnn_result = model_w_cnn.predict_classes(y_train_word,
                                                 batch_size=128,
                                                 verbose=0)

# 收集训练标签
method.append('W_TextCNN')

# 收集 accuracy_score 分数
acc_list.append(metrics.accuracy_score(y_test, model_w_cnn_result))

# 收集 f1 分数
f1_list.append(metrics.f1_score(y_test, model_w_cnn_result))

# 收集模型训练耗时
time_use.append(time_used)

对训练的模型进行加载,并打印网络结构。

# 加载 TextCNN 结构
model_c_cnn = build_cnn_model()

# 打印网络结构
model_c_cnn.summary()
# 创建分词器 Tokenizer 对象
tokenizer = Tokenizer(num_words=max_words)

# 将Tokenizer拟合语料,生成字典,形成新的tokenizer
tokenizer.fit_on_texts(train_data['text_character'].tolist())

# 文本数据转换和填充
train_data_character = pad_sequences(
    tokenizer.texts_to_sequences(train_data['text_character'].tolist()),
    maxlen)

设置固定划分数据集,划分比例为 0.2 即训练集是测试集的 4 倍量。

# 设置固定划分数据集
x_train_character, y_train_character, x_test, y_test = train_test_split(
    train_data_character, label_data, test_size=0.2, random_state=1)

字符级别模型训练
字符级 TextCNN 模型训练,设置 128 条数据为一个批次,2 轮模型训练,训练集中的 20% 作为验证集,并加入早停设置。

# 记录开始时间
st = time.time()

# 模型训练
model_c_cnn.fit(
    x_train_character,
    x_test,
    batch_size=128,
    epochs=2,
    validation_split=0.2,
    callbacks=[EarlyStopping(monitor='val_loss',
                             min_delta=0.0001)]  # 当val-loss不再提升时停止训练
)

# 计算模型耗时
time_used = time.time() - st

对字符级 TextCNN 的预测结果进行收集。

# 开始对测试集合进行批量计算
model_c_cnn_result = model_c_cnn.predict_classes(y_train_character,
                                                 batch_size=128,
                                                 verbose=0)

# 收集训练标签
method.append('C_TextCNN')

# 收集 accuracy_score 分数
acc_list.append(metrics.accuracy_score(y_test, model_c_cnn_result))

# 收集 f1 分数
f1_list.append(metrics.f1_score(y_test, model_c_cnn_result))

# 收集模型训练耗时
time_use.append(time_used)

GRU 情感分析模型

GRU 属于 RNN(recurrent neural networks,循环神经网络),是 LSTM 最流行的一个变体,比 LSTM 模型要简单,GRU 的门控单元减少了一个,GRU 与 LSTM 一样都是旨在解决标准 RNN 中出现的梯度消失问题,GRU 比 LSTM 在减少了计算量的条件下,做到了精度与 LSTM 持平,是目前在文本分类经常使用的模型。
我们使用函数定义的方式进行 GRU 模型的初始化。

from tensorflow.keras.layers import GRU


def build_gru_model():
    # 序列模型初始化
    model = Sequential()
    # 嵌入层
    model.add(Embedding(max_words, embedding_dim, input_length=maxlen))
    # GRU 结构
    model.add(GRU(units=64, return_sequences=False))
    # 全连接层
    model.add(Dense(128, activation='relu'))
    # 参数随机丢弃层
    model.add(Dropout(0.5))
    # 输出层
    model.add(Dense(1, activation='sigmoid'))
    # 使用二分类交叉熵计算损失,并使用 adam 进行损失调节,调节指标是准确度
    model.compile(loss='binary_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])
    return model

对训练的模型进行加载,并打印网络结构。

# 加载 GRU 结构
model_w_gru = build_gru_model()

# 打印结构
model_w_gru.summary()

词级别模型训练
词级 GRU 模型训练,设置 128 条数据为一个批次,2 轮模型训练,训练集中的 20% 作为验证集,并加入早停设置。

# 记录模型训练开始时间
st = time.time()

# 模型训练
model_w_gru.fit(
    x_train_word,
    x_test,
    batch_size=128,
    epochs=2,
    validation_split=0.2,
    # 当 val_loss 不再提升时停止训练
    callbacks=[EarlyStopping(monitor='val_loss', min_delta=0.0001)])

# 计算模型耗时
time_used = time.time() - st
# 开始对测试集合进行批量计算
model_w_gru_result = model_w_gru.predict_classes(y_train_word,
                                                 batch_size=128,
                                                 verbose=0)

# 收集训练标签
method.append('W_GRU')

# 收集 accuracy_score 分数
acc_list.append(metrics.accuracy_score(y_test, model_w_gru_result))

# 收集 f1 分数
f1_list.append(metrics.f1_score(y_test, model_w_gru_result))

# 收集模型训练耗时
time_use.append(time_used)

对训练的模型进行加载,并打印网络结构。

# 加载字符级 GRU 结构
model_c_gru = build_gru_model()

# 打印结构
model_c_gru.summary()

字符级别模型训练
将处理好的用户评论数据进行字符级处理即可输入字符级 GRU 模型训练,设置 128 条数据为一个批次,2 轮模型训练,训练集中的 20% 作为验证集,并加入早停设置。

# 记录开始时间
st = time.time()

# 模型训练
model_c_gru.fit(
    x_train_character,
    x_test,
    batch_size=128,
    epochs=2,
    validation_split=0.2,
    # 当val-loss不再提升时停止训练
    callbacks=[EarlyStopping(monitor='val_loss', min_delta=0.0001)])

# 计算模型耗时
time_used = time.time() - st

对字符级 GRU 的测试集预测性能进行记录。

# 开始对测试集合进行批量计算
model_c_gru_result = model_c_gru.predict_classes(y_train_character,
                                                 batch_size=128,
                                                 verbose=0)

# 收集训练标签
method.append('C_GRU')

# 收集 accuracy_score 分数
acc_list.append(metrics.accuracy_score(y_test, model_c_gru_result))

# 收集 f1 分数
f1_list.append(metrics.f1_score(y_test, model_c_gru_result))

# 收集模型训练耗时
time_use.append(time_used)

模型性能分析
通过控制参数变量的方式进行,并使用同样的数据集合观察性能指数测试结果。字符级能使用较小的字符级词典对语料的覆盖度更高,字符级预处理在测试集上的表现基本接近词级模型,并从耗时来看字符级都是最少的。TextCNN 架构总体高于 GRU 的准确度和综合值,并且训练时间相对较短。字符级语言建模的思想来自于信号处理,使用语言最小的文字单元去模拟复杂的语义关系,因为我们相信模型可以捕捉到这些语法和单词语义信息,在后续我们继续使用这种方式。

result_analysis = pd.DataFrame({
    'method': method,
    'time_use': time_use,
    'acc_list': acc_list,
    'f1_list': f1_list,
})

result_analysis.head()

字符级深度模型对比

对用户评论数据预处理

# 批量将用用户评论进行字符级处理,并打印第一行处理后的结果。
%time comment_source['text_character'] = comment_source['content'].apply(lambda x: " ".join(list(x)))
comment_source.head(1)

对用户评论进行字符向量化。

# 创建字符位置的字典
tokenizer = Tokenizer(num_words=max_words)

# 利用 Tokenizer 的字典对字符化的用户评论进行转化
tokenizer.fit_on_texts(train_data['text_character'].tolist())

# 找不到的和字符数不够的进行填补
test_text = pad_sequences(
    tokenizer.texts_to_sequences(comment_source['text_character'].tolist()),
    maxlen)

情感极性推理
使用训练好的字符级 TextCNN 对用户评论进行情感预测,需要一些时间,请耐心等待。

# 批量用户情感极性预测,设置 128 条数据为一个处理批次
%time model_c_textcnn_result = model_c_cnn.predict_proba(test_text, batch_size=128)

# 输出用户评论对应的积极情感对应的概率值作为用户情感极性映射
comment_source['model_c_textcnn_result'] = [i[0]
                                            for i in model_c_textcnn_result]

使用训练好的字符级 GRU 对用户评论进行情感预测,需要一些时间,请耐心等待。

# 批量用户情感极性预测,设置 128 条数据为一个处理批次
%time model_c_gru_result = model_c_gru.predict_proba(test_text, batch_size=128)

# 输出用户评论对应的积极情感对应的概率值作为用户情感极性映射
comment_source['model_c_gru_result'] = [i[0] for i in model_c_gru_result]

情感极性推理结果可视化
将两种字符级神经网络情感极性推理模型的结果取出来。

# 取出字符级 TextCNN 的推理结果
model_textcnn_result = comment_source['model_c_textcnn_result'].tolist()

# 取出字符级 GRU 的推理结果
model_gru_result = comment_source['model_c_gru_result'].tolist()

对全量的用户评论分别使用两个模型进行情感极性预测,并进行可视化,我们发现两种模型在全量的用户评论上的表现基本一致,字符级 TextCNN 在用户两极情感极性上表现更好。

from matplotlib import pyplot as plt
%matplotlib inline
# 定义画布大小
plt.rcParams['figure.figsize'] = (8.0, 8.0)

# 使用红色代表字符级 TextCNN 模型的推理结果
plt.hist(model_textcnn_result,
         bins=np.arange(0, 1, 0.01),
         color='red',
         label='model_c_textcnn_result')

# 使用蓝色代表字符级 GRU 模型的推理结果
plt.hist(model_gru_result,
         bins=np.arange(0, 1, 0.01),
         color='blue',
         label='model_c_gru_result')

plt.legend()
# 定义横轴为评论统计量
plt.xlabel('count')
# 定义纵轴为模型打分
plt.ylabel('model_sa_score')
plt.title('Diff_Character_Model_Customer_Satisfaction_Analysis')
plt.show()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容