【NLP】自己搭一个多轮对话聊天机器人(附实现源码)
- 01 项目框架
- 02 模型
- 2.1 粗排
- 2.1.1BM25
- 2.1.1 Bool检索
- 2.2 精排
- 意图识别
- 03 API交互
- 04 微信前端
本文的内容如下
本篇介绍一个检索类型的 多轮问答系统/聊天机器人,通过API异步通信,实现在微信上的交互,本项目包括了模型和工程化部署一体化。
包含查天气,知识图谱聊天查询,生成式问答聊天查询,图片识别,多次重复回答等;
涉及到命名实体识别,相似匹配(bm25,bool检索,simbert等),bert+seq2seq生成,neo4j知识图谱查询等技术。
各位可以根据自己的需求部署或修改:
- 问答库如果是任务型的,就是一个任务型聊天机器人,如果闲聊的问答库,那就是闲聊型聊天机器人;
- 后续也可以添加更多意图,用来做意图识别的匹配; 也可以添加个知识图谱的API…
总之可以添加的模块很多,扩展性非常强大。
当前版本支持简单对话,天气查询,表情包和图片识别回答,重复情况识别等。
最终效果如下:
01 项目框架
整体工程如下,主要有三块前端、后端、前后端交互:
- 前端直接用微信展示,比较简单,不需要我们做什么;
- 后端采用检索匹配模型的方式,结合粗排和精排;
- 前后端的交互用sanic实现异步通信。
模型部分实现的主要流程如下,初始时有个问答库:
- 先用BM25和Bool检索把问句query和问答库做字词上的粗排,返回topN;
- 将query和粗排得到的topN用SimBert向量化,再将两者做consine相似度计算,得到最相似的top1。
02 模型
思路:通常情况下,基本的问答库的量相对比较大,至少是上万条以上,如果直接用向量化求相似度的方式,虽然结果相对准确一点,但是速度很慢(需要向量化,还要向量计算);所以一般先用常见的非语义检索的方式,做一层初步筛选,再把筛选过的结果用向量化的方式做精排。
2.1 粗排
2.1.1BM25
BM25是TF-IDF的改进版,计算的原理相同,只是计算的公式做了些改进,具体原理可以自己了解,公式如下:
调用BM25的代码如下:
from bm25_recall import Bm25Recall
sim_pred = Bm25Recall(qa_df)
sim_qa = self.sim_pred.recall(query, topn) # 返回topN
2.1.1 Bool检索
布尔检索的本质就是分字词后,做个排列组合去寻找在问答库里面,尽可能多的出现query问句中字词。做法就是做一个倒排表,一个有字和QA对组合的二维矩阵。想了解更多原理细节的可以直接看源码。
调用bool检索的代码如下:
from Bool.bool_recall import BoolRecall
sim_pred = BoolRecall(qa_df)
sim_qa = sim_pred.recall(query, topn) # 返回topN
2.2 精排
Bert是个非常强大的字词向量化的预训练模型,追一科技的苏剑林在此基础上,训练了可以用来生成或匹配相似句的模型SimBert,个人亲自尝试后深深爱上它,后续只要出现匹配的场景,或者文本训练数据不够需要生成相似句时,我都会用SimBert。更多关于SimBert的介绍见原作者苏剑林的讲解:
https://github.com/ZhuiyiTechnology/pretrained-models
意图识别
在此想特地说下意图识别的方式:
常用方式:通过模型实现意图中domain分类,intent分类,以及slot槽的命名实体识别;
大体思路就是识别了用户想要做什么,然后识别出他要做的事情的对象,比如说用户想要“查天气”,所以意图就是查天气,然后我们就需要了解到用户想查的“城市”和“日期”两个槽,我们需要填充这两个槽。
这里我们最先要弄好的是两个分类模型
作者在研究的时候发现一个更好的实现意图识别的方式:通过匹配相似句达到意图识别
实现方式:
1、在问答库中添加某个意图可能的问法
2、通过SimBert精准匹配意图,匹配上则进入该意图的对话管理
优势:
私认为这种方式具有很高的准确性,以及扩展性
1、准确性:如上文所说,常用的意图需要做分类,而分类模型需要有数据支撑才能有比较好的效果;
用匹配的方式,通过相似句子评分,能够很精确的匹配到意图;
2、扩展性:常用的意图分类方式,在新增意图时需要重新训练分类模型;但是如果是匹配的话,无需重新训练分类模型,只需要在库中新增该意图的问法即可!!!非常方便!!!
以上所有算法均可在网络查到相关原理,个人源代码中都有,不做过多叙述。
03 API交互
异步通信经典常用的有Django等,后来发现了响应速度更快的Sanic,于是就习惯用Sanic,Sanic的官方文档写的很详细,可以根据自己的需求更改。
使用Sanic时,根据自己的实际情况设置ip和host,再通过POST的方式做请求交互,请求时的格式为json,使用代码如下:
@app.route('/QA',methods=['POST'])
async def model_server(request):try:request_json = request.bodyinput_json = json.loads(request_json.decode('utf-8'))start_time = time.time()result = server.get_result(input_json)print('耗时:', time.time() - start_time)except Exception as e:result = {"code": 400, "message": "预测失败", "Error": e}return response.json(result)
04 微信前端
微信前段用的是WXPY,官方文档介绍的很详细:
https://wxpy.readthedocs.io/zh/latest/
接下来就是最最最重要的开源了,关于源码及部署安装方式可以在gitlab上搜索chatbot_simbert,作者是ZJJDJJ,或者点击链接直接跳转!
https://github.com/ZJJDJJ/chatbot_simbert
喜欢的可以star或者fork,后续还会不断持续更新功能。
「卓师叔」
作者:卓师叔,爱书爱金融的NLPer
微信公众号:卓师叔
知乎:学习学习再学习