部署深度情感推理模型
数据准备
本次实验将继续加载两个数据,一个是已经标注好的用户评论数据,另外一个是用户评价数据,将快速实现低代码的深度用户情感分析模型,并将模型进行部署,支持通过网络进行调用。如果我们直接使用 Python 命令行部署模型,我们可以在后台看到数据的请求情况,如下所示。
使用 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()
数据属性如下表所示
加载民宿评论数据,并打印数据行数和第一行数据。
comment_source = pd.read_csv(
'https://labfile.oss.aliyuncs.com/courses/2628/1-1.csv')
print(comment_source.shape)
comment_source.head(1)
数据属性如下表所示
数据预处理
将训练数据进行字符级处理,我们发现用户评论数据已经被切分成字符粒度,打印第一行的数据。
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()