132在线民宿 UGC 数据挖掘实战--部署深度情感推理模型

部署深度情感推理模型

数据准备

本次实验将继续加载两个数据,一个是已经标注好的用户评论数据,另外一个是用户评价数据,将快速实现低代码的深度用户情感分析模型,并将模型进行部署,支持通过网络进行调用。如果我们直接使用 Python 命令行部署模型,我们可以在后台看到数据的请求情况,如下所示。


image.png

使用 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['text_cut'] = train_data['text'].apply(lambda x: " ".join(list(x)))
train_data.head(1)

模型训练
我们本实验采用 litNlp 实现低代码深度情感分析神经网络模型。litNlp 是基于 Tensorflow 2.0 实现的一个轻量级的深度情感极性推理模型库,在内部实现了使用字符级代替词级提升模型准确度和推理速度,并且内部自带数据划分和模型校验,可以实现细粒度的多级别情感极性训练和预测,是此次模型部署实验的比较合适的工具。

!pip install litNlp

直接传入标注好的用户情感数据和对应标签即可。

# 加载训练标签
label = train_data['label']

# 训练数据
train_data = train_data['text_cut']

我们在模型训练之前需要进行参数设置,参数可以按照自己的需求进行改动。

# train_method表示模型训练方式,默认textcnn,可选:bilstm , gru
train_method = 'textcnn'

# 设置单个用户评论的最大句子长度
maxlen = 100

# 字典存储的字符大小
max_words = 1000

# 设置embedding大小
embedding_dim = 300

# 模型的保存位置,后续用于推理
sa_model_path_m = 'model.h5'

# 保存向量字典
tokenize_path = 'tokenizer.pickle'

litNLp 内部实现模型的数据分割和模型校验,并直接将训练好的模型进行保存。可以看出模型在标注好的数据集合上的分类性能为 0.97,从模型架构上看出基本和上一节我们自己实验的字符级 TextCNN 保持一致,只是内部参数有微调。

# 加载模型训练模块
from litNlp.train import SA_Model_Train

# 开始初始化模型训练
model = SA_Model_Train(max_words, embedding_dim, maxlen, tokenize_path,
                       sa_model_path_m, train_method)

# 打印模型架构
model.model.summary()

输入数据集、定义标签类别个数、设置训练批次、开启模型验证,开始模型训练。

model.train(train_data,
            label,
            num_classes=2,
            batch_size=128,
            epochs=2,
            verbose=1,
            evaluate=True)

模型预测
我们使用含有两个完全相同字符的测试用例进行模型测试,但是情感极性却不相同。这个测试用例在之前的 TF-IDF 的处理方式下得出相同的情感极性,我们使用深度模型进行模型推理发现,本次的模型能很好地处理这个问题。

# 加载模型推理模块
from litNlp.predict import SA_Model_Predict

# 添加测试样例
predict_text = ['这个环境不喜欢', '这个环境喜欢不']

# 加载之前保存的模型和字典数据
litnlp_model = SA_Model_Predict(tokenize_path, sa_model_path_m, max_len=maxlen)

# 模型推理
test_score = litnlp_model.predict(predict_text)

# 情感极性概率
print([i[1] for i in test_score])

模型部署

关于深度学习模型部署,最常用的模型部署方式就是将模型部署为 Restful API 的形式进行推理服务,后端部署模型推理服务,前端接收参数传入后端进行模型推理,然后将推理结果按照 JSON 格式返回给前端,我们使用 Python 实现这一模型推理流程,首先安装 我们需要使用的库。

!pip install flask-restful

我们可以直接在 Notebook 内部新建一个叫做 sa_model_service 的 Python 文件,并将此单元格内的代码写入新建的 sa_model_service.py 文件。

%%writefile sa_model_service.py


# coding=utf-8
from flask_restful import Resource, Api, reqparse, request
from litNlp.predict import SA_Model_Predict
from flask import Flask
import json

app = Flask(__name__)
api = Api(app)


def global_para():
    global maxlen, sa_model_path_m, tokenize_path, litnlp_model
    # 设置最大评论长度
    maxlen = 100
    # 推理模型的保存位置
    sa_model_path_m = 'model.h5'
    # 离线保存的向量工具 tokenizer
    tokenize_path = 'tokenizer.pickle'
    # 预先加载模型,提高推理速度
    litnlp_model = SA_Model_Predict(tokenize_path,
                                    sa_model_path_m,
                                    max_len=maxlen)


class sa_model_get_api(Resource):
    def get(self, comment):
        predict_text = [str(comment)]
        sa_score = litnlp_model.predict(predict_text)[0][1]
        # 情感极性概率
        sa_score = round(float(sa_score), 5)
        # 使用阈值划分
        if sa_score < 0.5:
            return {"sa_score": sa_score, "label": '差评'}
        elif sa_score > 0.5:
            return {"sa_score": sa_score, "label": '好评'}
        else:
            return {"sa_score": sa_score, "label": '中评'}


class sa_model_post_api(Resource):
    def post(self):
        # 接收对象
        parser = json.loads(request.get_data())
        # 数据解析
        predict_text = [str(parser['comment'])]
        # 评论情感极性推理
        sa_score = litnlp_model.predict(predict_text)[0][1]
        # 情感极性概率
        sa_score = round(float(sa_score), 5)
        # 使用阈值划分
        if sa_score < 0.5:
            return {"sa_score": sa_score, "label": '差评'}
        elif sa_score > 0.5:
            return {"sa_score": sa_score, "label": '好评'}
        else:
            return {"sa_score": sa_score, "label": '中评'}


# 加载全局变量
global_para()

# 定义 GET 接口的请求信息
api.add_resource(sa_model_get_api, '/sa_get_api/<string:comment>')

# 定义 POST 接口的请求信息
api.add_resource(sa_model_post_api, '/sa_post_api')

if __name__ == '__main__':
    app.run()

从 Notebook 中启动情感极性推理服务。

import subprocess
import time
# 先确认关闭原有的 flask 程序
subprocess.run("pkill -9  flask", shell=True)
# 启动子进程执行 Flask app
service = subprocess.Popen("FLASK_APP=sa_model_service.py flask run",
                           shell=True)

# 等待 5 s 进行服务开启
time.sleep(5)
print('Flask API 服务已开启')

模型接口使用

使用 GET 方式进行请求,直接将请求的数据添加至接口之后即可。

import requests
import json


# 测试样例
data_get = '这家环境真的好喜欢'

# 开始请求接口数据
req_get = requests.get(url="http://127.0.0.1:5000/sa_get_api/{}".format(data_get))

# 对请求之后的数据进行解析
req_get.json()

使用 POST 方式进行请求,需要将请求的数据构造成 JSON 数据进行数据提交,然后服务器按照对应字段进行解析,处理之后返回处理后的 JSON 数据。

# 测试样例
data_post = {"comment":"这家环境真的好喜欢"}

# 使用 POST 方式进行请求
req_post = requests.post(url="http://127.0.0.1:5000/sa_post_api",  data=json.dumps(data_post))

# 对请求之后的数据进行解析
req_post.json()  

加载可视化进程处理模块和定义测试样例。

from tqdm.notebook import tqdm

# 测试样例
api_test_comment = [
    '这家环境我推荐',
    '这家环境有家的感觉',
    '这家环境我不喜欢',
    '这家环境垃圾',
    '这家环境真的不行', ]

# 预测结果收集
sa_model_score = []

批量预测,我们编造一些测试样例对接口情感分析接口进行测试。

# 开始使用接口进行预测
for comment in tqdm(api_test_comment):
    data_post = {"comment": comment}
    # 使用 POST 的方式进行测试样例提交
    req_post = requests.post(url="http://127.0.0.1:5000/sa_post_api",
                             data=json.dumps(data_post)).json()
    # 对返回结果进行解析
    sa_model_score.append(req_post['sa_score'])

将数据写入 DataFrame 进行展示,test_comment 表示测试样例内容,sa_model_score 表示模型预测的情感极性,从结果上看,模型能够准确地识别用户评价在情感上的差异。

data_predict = pd.DataFrame({
    'test_comment':api_test_comment,
    'sa_model_score': sa_model_score
})

# 打印前 5 数据
data_predict.head()

关闭情感分析接口服务。

# 关闭后台接口进程
service.terminate()
subprocess.run("pkill -9 flask", shell=True)
print('Flask API 服务已关闭')

用户情感极性可视化

模型批量预测
我们使用 litNlp 进行快速的全量预测。

# 批量对语料进行处理
%time batch_sa_score = litnlp_model.predict(comment_source['content'].tolist())

# 解析用户情感极性
model_batch_predict = [i[1] for i in batch_sa_score]

将预测好的数据写入用户评论中。

comment_source['model_batch_predict'] = model_batch_predict

# 打印第一行数据
comment_source.head(1)

数据聚合
对原始的用户打分进行数据清洗和聚合,并打印处理好的前 1 行数据。

# 清洗字符数据
comment_source_clean = comment_source[
    comment_source['user_score'] != '信息不存在'].copy()

# 将用户打分极性归一化
comment_source_clean['user_score_normal'] = comment_source_clean[
    'user_score'].apply(lambda x: float(x) / 5)

# 打印第 1 行数据
comment_source_clean.head(1)

打分数据聚合。

# 提取模型打分数据
model_score_normal = comment_source_clean['model_batch_predict'].tolist()

# 提取用户打分数据
user_score_normal = comment_source_clean['user_score_normal'].tolist()

情感极性对比
对比分析用户真实打分和情感极性推理分,我们发现情感极性模型推理出来的用户评论情感极性在分布上更加均匀和详细,用户打分出现断层现象,并且主观的用户打分会出现用户打分和用户评论不一致的现象,模型作为中立的情感极性推理,数据量越大,模型性能越高,越能挖掘到用户细粒度的情感极性。

from matplotlib import pyplot as plt
import numpy  as np

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

# 使用红色代表模型的推理结果
plt.hist(model_score_normal,
         bins=np.arange(0, 1, 0.01),
         color='red',
         label='model_score')

# 使用蓝色代表真实用户打分的结果
plt.hist(user_score_normal,
         bins=np.arange(0, 1, 0.01),
         color='blue',
         label='user_score')

plt.legend()
# 定义横轴为评论统计量
plt.xlabel('count')

# 定义纵轴为模型打分
plt.ylabel('sa_score')

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

推荐阅读更多精彩内容