基于知识图谱的问答系统答即根据一个问题,抽出一条三元组,生成类 sql 语句,知识图谱查询返回答案。本文是基于知识图谱的问答系统,通过 BERT+CRF 做命名实体识别和句子相似度比较,最后实现线上的部署。

作者:走在前方

博客:https://wenjie.blog.csdn.net/

技术交流群: 访问博客首页,加入 "NLP技术交流群" ,祝共同进步。

0-学什么

针对知识图谱的问题系统,重点解决的几个重要问题。通过本文学习,让你掌握知识图谱涉及的主要内容,通过本文分享,大家搭建快速搭建一个知识图谱的智能问答应该没什么问题了。

  • 知识库创建( 一般情况下,这个工作工作非常大,也非常重要。针对数据一般来自公司、比赛、以及通过爬虫来构建数据知识库)
  • 命名实体识别(一般通过bert_crf,bilstm_crf 等模型来完成,实际工作一般也需要工作增加规则来处理)
  • 实体链接到知识库进行检索(我们识别实体后,如何正确去对应到我们的知识库,也是我们的重点,这块很多思路大家可以提前在nlp 很多比赛都有提及)
  • 基于知识库的问答(一般我们都是基于图数据库来完成的,根据数据量的大小来选择,例如:neo4j、JanusGraph等。如果是简单的知识库关系可以mysql,redis,mongo存储即可)
  • 知识图谱一般垂直领域图谱或者开放领域图谱,持续优化的过程关注在知识库创建上。创建完成,可以直接应用到搜索,推荐,智能客服等对话系统。。。

本文主要研究内容如下

* 问答 QA 系统简单介绍

 * 数据集介绍(实体间歧义问题、检索模型效果)

 * KBQA整理流程介绍

 * 评价标准回顾

* 命名实体识别数据准备

 * 数据预处理

 * 数据集准备(切分、ner数据、文本相似度数据、知识库数据)

* BERT-CRF命名实体识别训练

 * BERT原理

 * BERT-CRF 模型实现NER

 * 在线预测服务

* 文本语义相似度模型训练

 * 数据可视化

 * BERT分类模型

 * 文本相似度在线服务

* 知识库构建

 * 知识库介绍

 * 知识库构建

 * 基于知识检索方案

* 基于知识库的QA系统检索

 * 搭建QA检索服务(依赖NER模型、文本相似度模型、知识库)

 * QA检索服务案例验证

* 扩展以及回顾

 * 不足以及未来的发展

 * 扩展解决方案思路

1-项目介绍

- 1.1-数据集介绍

NLPCC 全称自然语言处理与中文计算会议(The Conference on Natural Language Processing and Chinese Computing),它是由中国计算机学会(CCF)主办的 CCF 中文信息技术专业委员会年度学术会议,专注于自然语言处理及中文计算领域的学术和应用创新。

 

数据集来自 NLPCC ICCPOL 2016 KBQA 任务集,其包含 14609 个问答对的训练集和包含 9870 个问答对的测试集。

提供的知识库

  • 6502738 个实体
  • 587875 个属性
  • 43063796 个 实体-实体关系

 

知识库文件中每行存储即三元组 ( 实体、属性、属性值)

各文件统计如下:

训练集:14609
开发集:9870
知识库:43063796

知识库样例如下:

"希望之星"英语风采大赛|||中文名|||“希望之星”英语风采大赛
"希望之星"英语风采大赛|||主办方|||中央电视台科教节目中心
"希望之星"英语风采大赛|||别名|||"希望之星"英语风采大赛
"希望之星"英语风采大赛|||开始时间|||1998
"希望之星"英语风采大赛|||比赛形式|||全国选拔
"希望之星"英语风采大赛|||节目类型|||英语比赛

原数据中本只有问答对(question-answer)无标注三元组(triple),如下所示

<question id=1> 《机械设计基础》这本书的作者是谁?
<answer id=1> 杨可桢,程光蕴,李仲生
==================================================
<question id=2> 《高等数学》是哪个出版社出版的?
<answer id=2> 武汉大学出版社
==================================================
<question id=3> 《线性代数》这本书的出版时间是什么?
<answer id=3> 2013-12-30

 

本人所用问答对数据来自该比赛第一名的预处理 ​​https://github.com/huangxiangzhou/NLPCC2016KBQA​​ 。构造 Triple 的方法为从知识库中反向查找答案,根据问题过滤实体,最终筛选得到,也会存在少量噪音数据。该 Triple 之后用于构建实体识别和属性选择等任务的数据集。

 

问答对样例如下所示(重点增加 triple,这里实体抽取和属性抽取的基本数据-知识抽取问题)

<question id=1> 《机械设计基础》这本书的作者是谁?
<triple id=1> 机械设计基础 ||| 作者 ||| 杨可桢,程光蕴,李仲生
<answer id=1> 杨可桢,程光蕴,李仲生
==================================================
<question id=2> 《高等数学》是哪个出版社出版的?
<triple id=2> 高等数学 ||| 出版社 ||| 武汉大学出版社
<answer id=2> 武汉大学出版社
==================================================
<question id=3> 《线性代数》这本书的出版时间是什么?
<triple id=3> 线性代数 ||| 出版时间 ||| 2013-12-30
<answer id=3> 2013-12-30
==================================================

- 1.1.1-知识库实体间的歧义

以“贝拉克·奥巴马”为例,涉及该实体的问答对如下:

<question id=9687>  谁是贝拉克·奥巴马的妻子?
<triple id=9687> 贝拉克·奥巴马 ||| 妻子 ||| 米歇尔·奥巴马
<answer id=9687> 米歇尔·奥巴马

 

在知识库中查询包含该实体的三元组,结果如下(部分):

贝拉克·奥巴马(美国现任总统) ||| 别名 ||| 贝拉克·奥巴马
贝拉克·奥巴马(美国现任总统) ||| 姓名 ||| 贝拉克·侯赛因·奥巴马
贝拉克·奥巴马(美国现任总统) ||| 妻子 ||| 米歇尔·奥巴马
......
贝拉克·奥巴马 ||| 主要成就 ||| 1996 年伊利诺伊州参议员 美国第 56 届、57 届总统 2009 年诺贝尔和平奖获得者 时代周刊年度风云人物 2008、2011 任期内清除本·拉登
贝拉克·奥巴马 ||| 代表作品 ||| 《我相信变革》《我父亲的梦想》《无畏的希望》
贝拉克·奥巴马 ||| 妻子 ||| 米歇尔·拉沃恩·奥巴马
......
贝拉克·奥巴马(美国第 44 任总统) ||| 血型 ||| ab
贝拉克·奥巴马(美国第 44 任总统) ||| 学院 ||| 西方学院
贝拉克·奥巴马(美国第 44 任总统) ||| 妻子 ||| 米歇尔·拉沃恩·奥巴马

首先,知识库中存在“贝拉克·奥巴马”的多条实体,有可能是多数据来源的融合或其他原因,从而并不能完全保证信息的对齐。我们查看“妻子”这一属性,发现有的是“米歇尔·拉沃恩·奥巴马”有的是“米歇尔·奥巴马”,而我们问答对中给出的答案是“米歇尔·奥巴马”。因此当我们的模型检索到正确三元组时:

贝拉克·奥巴马(美国第 44 任总统) ||| 妻子 ||| 米歇尔·拉沃恩·奥巴马

虽然在实体和属性都映射正确的情况下,最终答案仍可能被判定为错误。

 

- 1.1.2-问题实体歧义

 

以“博士来拜”为例,涉及该实体的问答对如下:

<question id=249> 博士来拜是什么年代的作品?
<triple id=249> 博士来拜 ||| 年代 ||| 1461 年
<answer id=249> 1461 年

在知识库中查询包含该实体的三元组,结果如下(部分):

博士来拜(曼特尼亚画作) ||| 别名 ||| 博士来拜
博士来拜(曼特尼亚画作) ||| 中文名 ||| 博士来拜
博士来拜(曼特尼亚画作) ||| 类别 ||| 油画,壁画
博士来拜(曼特尼亚画作) ||| 年代 ||| 1461 年
博士来拜(曼特尼亚画作) ||| 作者 ||| 曼特尼亚
......
博士来拜(维登画作) ||| 别名 ||| 博士来拜
博士来拜(维登画作) ||| 中文名 ||| 博士来拜
博士来拜(维登画作) ||| 类别 ||| 油画
博士来拜(维登画作) ||| 年代 ||| 1455 年
博士来拜(维登画作) ||| 作者 ||| 维登
博士来拜(维登画作) ||| 属地 ||| 慕尼黑画廊藏
......
博士来拜(达·芬奇画作) ||| 别名 ||| 博士来拜
博士来拜(达·芬奇画作) ||| 中文名 ||| 博士来拜
博士来拜(达·芬奇画作) ||| 类别 ||| 油画
博士来拜(达·芬奇画作) ||| 年代 ||| 1481-1482
博士来拜(达·芬奇画作) ||| 作者 ||| 达芬奇
博士来拜(达·芬奇画作) ||| 现藏 ||| 佛罗伦萨乌菲兹美术馆
博士来拜(达·芬奇画作) ||| 规格 ||| 246 x 243 厘米

问句中的问题是:“博士来拜是什么年代的作品?“,涉及到”年代“这个属性,而这幅作品被不同时期的很多人创作过,我们无法从当前问句下得到要询问的是哪位艺术家的创作年代。因此该问题的涉及的实体具有歧义性,同样的,当模型检索到我们认为的正确实体和正确属性后,依然有可能会被判定为错误答案。

- 1.1.3-检索模型的效果影响

在知识库中相关实体三元组数量过多的情况下,对检索模型的效果、效率也是个挑战

在具有 4300W 条三元组的知识库中,同一个实体会检索出大量(几十、几百条)的相关三元组,而且在存在上述两个歧义性问题的情况下,识别的效果和效率都是很大的问题。以上的两个问题在实体识别实验部分和属性抽取部分的影响较小,但在实体链接知识库检索最终答案三元组的部分会有较大的影响。

- 1.2-KBQA 整体流程

query-> 实体识别模型+属性抽取模型-> 知识库检索

 

KBQA 实现业务流程:

  • 输入问句 query
  • 通过实体识别模型检测问句中的实体,得到 mention(采用 BERT+CRF 进行实体抽取,可以看成中文命名实体识别问题
  • 通过检索模型在知识库中检索 mention,得到候选集(K 个候选实体的所有三元组,通过类 sql 去知识库检索
  • 通过属性抽取模型在候选集中挑选最合适的属性,得到唯一三元组
  • 规则匹配:如果前 n 个三元组有某个三元组的属性是 query 问题字符串的子集(相当于字符串匹配),将这个三元组的答案作为正确答案返回
  • 语义匹配:采用 BERT,多个结果与 query 进行相似度匹配进行打分
  • 输出答案

 

- 1.3-评价标准

  • 命名实体识别和属性映射评价标准

召回率 (Recall),精确率 (Precision) ,F1-Score。

  • 对话系统的评价指标可以分为客观评价指标和主观评价指标。

其中客观评价指标又可以分为:

  • 词重叠评价指标:(包括 BLEU、ROUGE、METEOR)
  • 词向量评价指标:(包括 Greedy matching、Embedding Average、Vector Extrema、perplexity 困惑度)

 

2-命名实体识别数据准备

- 2.1-数据预处理

清洗训练数据、测试数据、知识库过滤属性,去除‘-’,‘•’,空格等噪音符号;同时把每一行lower()转成小写。

​https://github.com/huangxiangzhou/NLPCC2016KBQA​

 

- 2.2-数据集准备

- 2.2.1-数据集切分

'nlpcc-iccpol-2016.kbqa.testing-data','nlpcc-iccpol-2016.kbqa.training-data'

-> 合并-> 按照比例切分 train,dev、test

 

  • 输入数据 'nlpcc-iccpol-2016.kbqa.testing-data','nlpcc-iccpol-2016.kbqa.training-data' 如下

<question id=6> 《高等数学一(微积分)》是哪一门课的通用教材?
<triple id=6> 高等数学 ||| 书名 ||| 高等数学一(微积分)
<answer id=6> 高等数学一(微积分)
==================================================
<question id=7> 有谁知道万达广场英文怎么说?
<triple id=7> 万达广场 ||| 外文名 ||| amoy wanda plaza
<answer id=7> amoy wanda plaza

 

 

  • 比例切分 train,dev、test

<question id=1> 《机械设计基础》这本书的作者是谁?
<triple id=1> 机械设计基础 ||| 作者 ||| 杨可桢,程光蕴,李仲生
<answer id=1> 杨可桢,程光蕴,李仲生
==================================================
<question id=2> 《高等数学》是哪个出版社出版的?
<triple id=2> 高等数学 ||| 出版社 ||| 武汉大学出版社
<answer id=2> 武汉大学出版社

 

- 2.2.2-构建实体识别训练数据: 实体识别

通过 NLPCC2016KBQA 中的原始数据,构建用来训练 NER 的样本集合。构造 NER 训练集,实体序列标注,训练 BERT+CRF。其中:LOC 表示其他类型,最终我们可是识别 BIO 标签即可

《O
线 B-LOC
性 I-LOC
代 I-LOC
数 I-LOC
》O
的 O
i O
s O
b O
n O
码 O
是 O
什 O
么 O
?O

最终我们的数据保存 train.txt,dev.txt,test.txt 三个文件

 

另外,我们需要文本长度分析

最要考虑 bert 模型,需要我们指定 seq_length,即句子的最大长度序列。

train.txt,dev.txt,test.txt -> 统计句子的长度-> seq_length = 64 合适。

 

- 2.2.3-构建属性识别训练数据: 属性识别

query-> 实体识别模型-> 获取实体名称-> 知识库检索-> 返回多个三元组

注:三元组组成(实体,属性名称,属性数值) 

 

问题:究竟哪个三元组是我们需要的呢? 

解决方案

  • query 去掉识别的实体后的内容,通过字符串匹配三元组属性名称
  • query 去掉识别的实体后的内容,和 三元组列表中的每个属性进行一个相似度打分,然后进行排序,最终得到一个最合适的答案。可以转换成一个二分类的问题。

那么接下来我们面临的问题。

  • 正样本: 当前 query 就是我们的正样本
  • 负验证:我们可以随机选择一部分属性名称,然后凭接 query 后构成我们的负样本

 

一个 sample 由“问题+属性+Label”构成,原始数据中的属性值置为 1。

 

原始的输入样本

<question id=1> 《机械设计基础》这本书的作者是谁?
<triple id=1> 机械设计基础 ||| 作者 ||| 杨可桢,程光蕴,李仲生
<answer id=1> 杨可桢,程光蕴,李仲生

通过采样构建的 属性分类样本数据

《机械设计基础》这本书的作者是谁?  作者 1
《机械设计基础》这本书的作者是谁?imdb 链接 0
《机械设计基础》这本书的作者是谁? 周点击 0
《机械设计基础》这本书的作者是谁? 建校时间 0
《机械设计基础》这本书的作者是谁? 主讲 0
《机械设计基础》这本书的作者是谁? 注意 0

当前我们完成了实体识别+属性识别-> 就可以直接去知识库中锁定唯一的答案了。

 

- 2.2.4-知识库搭建: 实体-属性名称-属性数值

entity,attribute,answer
机械设计基础,作者,杨可桢,程光蕴,李仲生
高等数学,出版社,武汉大学出版社
线性代数,出版时间,2013-12-30

例如:

query = 《机械设计基础》这本书的作者是谁?

query -> 实体识别模型-> 机械设计基础

query-> 属性识别模型-> 分类问题?文本相似比较( # TODO)

 

3-BERT-CRF 命名实体识别

- 3.1-BERT 原理介绍

​BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding​

​BERT上下文表示和其他预训练语言模型​

​he Illustrated BERT, ELMo, and co.​

 

自然语言处理(NLP):20 基于知识图谱的智能问答系统_json自然语言处理(NLP):20 基于知识图谱的智能问答系统_数据_02

 

 

  • MRPC(Microsoft Research Paraphrase Corpus,也有的成其为 MSRP)是一些句子对,有的是同义的,有的是不同义的
  • CoNLL 2003 是进行 NER 经典的一个数据集

 

- 3.2-BERT+CRF 模型

​pytorch BiLSTM+CRF代码详解​

​pytorch中bilstm-crf部分code解析​

​最通俗易懂的BiLSTM-CRF模型中的CRF层介绍​

​Bidirectional LSTM-CRF Models for Sequence Tagging(论文翻译)​

​pytorch lstm crf 代码理解​

 

- 3.3-实体识别模型训练

标签: ["O", "B-LOC", "I-LOC"] 。

另外,我们把 BIO 编码格式数据转换为平台的统一标准,然后直接借助平台统一进行模型的训练,看看效果如何。

 

- 3.3.1-标准数据转换

上述数据处理生成三个文件

  • seq.in 每行表示一个完整的句子,然后以空格分割

《 机 械 设 计 基 础 》 这 本 书 的 作 者 是 谁 ?
《 高 等 数 学 》 是 哪 个 出 版 社 出 版 的 ?

  • seq.out 对应每个单词的 TAG,空格分割

O B-LOC I-LOC I-LOC I-LOC O O O O O O O O O O O O O
B-LOC I-LOC I-LOC O O O O O O O O O

  • slot_label.txt NER 标签的类型,这里增加 PAD 和 UNK 两个特殊的标签

PAD
UNK
O
B-LOC
I-LOC

 

- 3.3.2-标准数据可视化分析

  • 句子数量整体分析

自然语言处理(NLP):20 基于知识图谱的智能问答系统_json_03

  • 句子长度分析

我们可以设置 seq_length = 64 做为 bert 模型的 max_seq_length 超参数

自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_04自然语言处理(NLP):20 基于知识图谱的智能问答系统_数据_05

 

 

  • 数据集标签分布 

 

自然语言处理(NLP):20 基于知识图谱的智能问答系统_数据_06

 

自然语言处理(NLP):20 基于知识图谱的智能问答系统_json_07

 

自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_08

 

 

- 3.3.3-模型训练

python3 main.py  \
--model_name_or_path ../pretrained_models/bert-base-chinese/ \
--task NLPCC2016KBQA \
--model_type bert \
--model_dir kbqa_bert_zh_model \
--do_train --do_eval --use_crf \
--num_train_epochs 3 \
--train_batch_size 128 \
--eval_batch_size 64 \
--max_seq_len 60 \
--dropout_rate 0.9

 

模型的效果如下所示,整个感觉训练效果还不错

dev:

06/05/2020 21:04:54 - INFO - train -     loss = 0.3480187522040473

06/05/2020 21:04:54 - INFO - train -     sementic_frame_acc = 0.9718463553033609

06/05/2020 21:04:54 - INFO - train -     slot_f1 = 0.9731967748964916

06/05/2020 21:04:54 - INFO - train -     slot_precision = 0.9717145343777197

06/05/2020 21:04:54 - INFO - train -     slot_recall = 0.9746835443037974

 

test:

06/05/2020 21:05:09 - INFO - train -     loss = 0.36994590636376024

06/05/2020 21:05:09 - INFO - train -     sementic_frame_acc = 0.9704555705908886

06/05/2020 21:05:09 - INFO - train -     slot_f1 = 0.970283656010806

06/05/2020 21:05:09 - INFO - train -     slot_precision = 0.9685393258426966

06/05/2020 21:05:09 - INFO - train -     slot_recall = 0.9720342805593144

- 3.4-预测

- 3.4.1-在线服务启动

我们通过 gpu 下训练的模型,也可以直接运行在 cpu 下。

 

python3 api.py \
--model_name_or_path ../pretrained_models/bert-base-chinese/ \
--task NLPCC2016KBQA \
--model_type bert \
--model_dir kbqa_bert_zh_model \
--use_crf

 

注意:model_name_or_path 和 model_dir 换成自己预训练模型路径和模型文件路径

预测效率对比:gpu 单条记录预测 13ms 左右,cpu 大概 60ms(和服务器配置有关系)。对于 cpu 下推理速度慢的问题我们也可以针对模型进行优化(比如:albert-tiny....)

 

在线预测命令行如下:

 

我们抽查的原始数据

<question id=8> 王平的出生日期是哪一年呀?
<triple id=8> 王平 ||| 出生日期 ||| 1954 年
<answer id=8> 1954 年

 

在线服务预测,希望 query = xxx. 模型可以识别出" 王平" 这个实体

curl http://127.0.0.1:8000/predict \
-H "Content-Type:application/json" \
-X POST \
--data '{"text": "王平的出生日期是哪一年呀?","lang":"zh"}'

 

返回的结果 

$ curl http://127.0.0.1:8000/predict -H "Content-Type:application/json" -X POST --data '{"text": "王平的出生日期是哪一年呀?","lang":"zh"}'  
{
"errno": 0,
"errmsg": "success",
"time": 13,
"data": [
{
"word": "王",
"tag": "B-LOC"
},
{
"word": "平",
"tag": "I-LOC"
},
{
"word": "的",
"tag": "O"
},
{
"word": "出",
"tag": "O"
},
{
"word": "生",
"tag": "O"
},
{
"word": "日",
"tag": "O"
},
{
"word": "期",
"tag": "O"
},
{
"word": "是",
"tag": "O"
},
{
"word": "哪",
"tag": "O"
},
{
"word": "一",
"tag": "O"
},
{
"word": "年",
"tag": "O"
},
{
"word": "呀",
"tag": "O"
},
{
"word": "?",
"tag": "O"
}
]
}

 

后台日期服务器的数据(是我们正确识别出来的实体)

127.0.0.1 - - [05/Jun/2020 21:09:19] "POST /predict HTTP/1.1" 200 -

text =  王平的出生日期是哪一年呀?

seq_len =  13

0       王      B-LOC

1       平      I-LOC

2       的      O

3       出      O

4       生      O

5       日      O

6       期      O

7       是      O

8       哪      O

9       一      O

10      年      O

11      呀      O

12?O

- 3.4.2-批量数据核查

 

提供一批 问题列表,调用在线 api.py 中提供的服务,查看效果怎么样? 

我们的数据,一定不能参与模型训练的数据,然后拿过来进行测试。

 

4-文本语义相似度模型

自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_09

 

关于属性映射我们看下提供的数据:

《机械设计基础》这本书的作者是谁?  作者 1
《机械设计基础》这本书的作者是谁?imdb 链接 0
《机械设计基础》这本书的作者是谁? 周点击 0
《机械设计基础》这本书的作者是谁? 建校时间 0
《机械设计基础》这本书的作者是谁? 主讲 0
《机械设计基础》这本书的作者是谁? 注意 0

- 4.1-数据集处理

 

  • 原始预料数据

0《机械设计基础》这本书的作者是谁? 作者 1
1《机械设计基础》这本书的作者是谁?imdb 链接 0
2《机械设计基础》这本书的作者是谁? 周点击 0
3《机械设计基础》这本书的作者是谁? 建校时间 0
4《机械设计基础》这本书的作者是谁? 主讲 0
5《机械设计基础》这本书的作者是谁? 注意 0

针对每行的句子的含义说明如下: 

  • id: 表示句子唯一标识
  • text_a:句子1
  • text_b:句子2
  • label:句子1和句子是否相关 (0-不同;1-相同)

 

  • 数据离散化数据

自然语言处理(NLP):20 基于知识图谱的智能问答系统_数据_10

 

 

- 4.2-数据可视化分析​

 

 

  • 句子标签数量分布

自然语言处理(NLP):20 基于知识图谱的智能问答系统_json_11

  • 句子对的长度可视化分析 

句子的最大的长度我们可以设置 64

自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_12自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_13

 

 

- 4.3-BERT 分类模型训练

 

python sim_main.py --data_dir ./data/ \
--task_name NLPCCKBQA2016 \
--num_train_epochs 2 \
--pre_train_model ../pretrained_models/bert-base-chinese \
--max_seq_length 64 \
--do_train \
--train_batch_size 64 \
--eval_batch_size 64 \
--gradient_accumulation_steps 4 \
--output ./NLPCCKBQA2016_bert_zh_model

 

  • data_dir:训练预料数据
  • pre_train_model : BERT 中文预训练的模型
  • output:训练完成模型保持路径
  • max_seq_length:表示 bert 模型输入句子最大的长度 (通过数据分析获得)
  • train_batch_size、eval_batch_size: 批量进行数据的处理
  • task_name:表示我们计算任务的名称,对应数据集加载方法
  • num_train_epochs 训练的迭代次数

 

 

模型训练完成后,我们看看在验证集上的效果(当前数据好坏影响非常大。)

15:10:17 - INFO - __main__ -     acc = 0.981087
15:10:17 - INFO - __main__ - Precision = 0.920916
15:10:17 - INFO - __main__ - Recall = 0.969807
15:10:17 - INFO - __main__ - f1_score = 0.944730
15:10:17 - INFO - __main__ - Evaluating EPOCH = [2/2] global_step = 684 eval_loss = 0.082648 eval_acc = 0.981087


best_acc = 0.9810874704491725

 

这里仅仅进行2次迭代,我们发现效果非常不错,f1_score = 94%

 

- 4.4-文本相似度在线服务

 

在线服务预测,希望 query = 丽江子馨小院客栈离车站多远啊?. 模型可以识别出" 丽江子馨" 这个实体。 那么这个时候会检索出该实体名称相关的列表(一个三元组结构),然后我们需要检索出 query 对应具体的那条记录(可以看成文本相似度问题-> 转为二分类问题求相似)

 

  • 启动在线服务应用服务

python3 api.py  \
--model_dir ./NLPCCKBQA2016_bert_zh_model/best_sim.bin \
--bert_model ../pretrained_models/bert-base-chinese/ \
--max_seq_length 64

  • model_dir  训练好文本相似度的模型
  • bert_model 预训练模型的路径
  • max_seq_length  模型文本最大的句子长度(数据分析获取)

 

  • 测试案例1:API服务文本相似度调用(同一个含义案例)

通过curl 命令行模式执行

curl http://127.0.0.1:8000/v1/predict  \
> -H "Content-Type:application/json" \
> -X POST \
> --data '{"text_a": "丽江子馨小院客栈离车站多远啊?","text_b":"汽车站"}'

返回的结果

{


"errno": 0,


"errmsg": "success",


"time": 50,


"data": {


"text_a": "丽江子馨小院客栈离车站多远啊?",


"text_b": "汽车站",


"result": "相同",


"prob": "95.13%"


}
}

 

  • 测试案例2:API服务文本相似度调用(不是同一个含义的案例)

 

通过curl执行

curl http://127.0.0.1:8000/v1/predict  \
> -H "Content-Type:application/json" \
> -X POST \
> --data '{"text_a": "丽江子馨小院客栈离车站多远啊?","text_b":"组织门派"}'

返回结果如下

{


"errno": 0,
"errmsg": "success",
"time": 28,
"data": {
"text_a": "丽江子馨小院客栈离车站多远啊?",
"text_b": "组织门派",
"result": "不同",
"prob": "96.12%"
}
}

 

5-知识库导入数据库,并抽取三元组

- 5.1-知识库导入数据库

- 5.1.1-三元组数据格式

本篇知识问答实战来源 NLPCC2016 的任务:Open Domain Question Answering;其包含 14,609 个问答对的训练集和包含 9870 个问答对的测试集。并提供一个知识库,包含 6,502,738 个实体、587,875 个属性以及 43,063,796 个三元组。

自然语言处理(NLP):20 基于知识图谱的智能问答系统_json_14

自然语言处理(NLP):20 基于知识图谱的智能问答系统_json_15自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_16

 

- 5.1.2-MySQL 安装以及数据表设计

  • MySQL 安装以及使用

​https://www.jianshu.com/p/2906abd8fd47​

​https://downloads.mysql.com/archives/community/​

​https://www.jianshu.com/p/2a23264b63d8​

​https://blog.csdn.net/memory6364/article/details/82426052​

​https://blog.csdn.net/skh2015java/article/details/80156278​

 

我们这里设置:root/12345678

 

  • 知识库表设计

create databases KB_QA;
use KB_QA;
CREATE TABLE `nlpcc_qa` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`entity` text CHARACTER SET utf8,
`attr_name` text CHARACTER SET utf8,
`attr_value` text CHARACTER SET utf8,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

字段说明:

entity : 实体

attr_name: 属性名称

attr_value: 属性数值

- 5.1.3-数据导入知识库

我们直接把知识库: 实体-属性名称-属性数值 (三元组) 倒入 mysql 数据库中。

注意: 数据比较大,考虑 4000 万+ 条数据比较大,在进行 mysql 插入数据操作可以进行批量进行插入 。

 

我们看下知识库的数据:kbqa.kb

 

空气干燥 ||| 别名 ||| 空气干燥
空气干燥 ||| 中文名 ||| 空气干燥
空气干燥 ||| 外文名 ||| air drying
空气干燥 ||| 形式 ||| 两个
空气干燥 ||| 作用 ||| 将空气中的水份去除
罗育德 ||| 别名 ||| 罗育德
罗育德 ||| 中文名 ||| 罗育德

 

  • 通过命令行导入数据(注:如果数据出现不支持的数据特殊字符会出现错误)

load data local infile '/data01/tt/kbqa.kb' \
into table nlpcc_qa \
CHARACTER SET 'utf8' \
fields terminated by '|||' enclosed by '"' \
ignore 0 lines \
(entity,attr_name,attr_value);

 

  • 通过程序批量数据导入

'''
知识库数据-批量导入
'''
import pymysql
api_server_host = 'localhost'
def connect():
conn = pymysql.connect(
user="root",
password="12345678",
host=api_server_host,
port=3306,
db="KB_QA",
charset="utf8"
)
return conn


def execute_insert():
conn = connect()
cursor = conn.cursor()
# 打开文件,读取所有文件存成列表
with open("kbqa.kb", "r", encoding='utf-8') as file:
"""
kbqa.kb 知识库格式如下(4000万+数据大概15分钟内就可以完成):
空气干燥 ||| 别名 ||| 空气干燥
空气干燥 ||| 中文名 ||| 空气干燥
空气干燥 ||| 外文名 ||| air drying
空气干燥 ||| 形式 ||| 两个
空气干燥 ||| 作用 ||| 将空气中的水份去除
"""
# 可以选择readline或者read的方式,但下面的代码要有所变化
data_list = file.readlines()
# 遍历列表
count = 0
for t in data_list:
t = t.strip()
text = t.split("|||")
# sql语句
sql = "insert into nlpcc_qa(entity,attr_name,attr_value) values (%s,%s,%s)"
# 参数化方式传参
try:
cursor.execute(sql, [text[0], text[1], text[2]])
if count % 100000 == 0:
print('commit......count={}'.format(count))
conn.commit()
except Exception as e:
# 这里错误数据直接忽略-考虑考虑错误的数据放入一个文本,然后check
# print("Error: unable to fecth data: %s ,%s" % (repr(e), sql))
pass
# 显示操作结果
count = count + 1




# 统一提交
conn.commit()
# 关闭游标 
cursor.close()
# 关闭连接
conn.close()
if __name__ == '__main__':
execute_insert()

 

导入后的数据效果如下

自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_17

 

 

 

- 5.2-返回分数最高的三元组

  1. query = 高等数学出版社是是哪家?
  2. 然后通过 NER 识别: 高等数学
  3. 知识库过程 :根据高高等数学的实体名称去查询,获取多条记录

sql = "select * from nlpcc_qa where entity = '高等数学'

 

结果如新:

高等数学 出版社 武汉大学出版社

高等数学 书名 高等数学一(微积分)

 

我们都指导,根据 query,我们的答案是 "武汉大学出版社" ,那么技术解决方案:

query 和 出版社

query 和书名

 

上述对应的 <question,attr_name> 进行相似度比较,然后获取正确的答案。

 

- 5.3-预测

NER 模型和文本相似度模型

NER 模型: kbqa_bert_zh_model/pytorch_model.bin

文本相似度模型: NLPCCKBQA2016_bert_zh_model/best_sim.bin

 

训练好上述两个模型后,就可以直接使用了,这里提供对应的服务供调用(也可以单独提供一个api的web 服务,同时加载两个模型,实际这样效率会高点)

 

  • 服务对应:8080 端口

python3 api.py \
--model_name_or_path ../pretrained_models/bert-base-chinese/ \
--task NLPCC2016KBQA \
--model_type bert \
--model_dir kbqa_bert_zh_model \
--use_crf

 

curl http://127.0.0.1:8000/predict \
-H "Content-Type:application/json" \
-X POST \
--data '{"text": "王平的出生日期是哪一年呀?","lang":"zh"}'

 

  • 服务对应:8081 端口

python3 api.py  \
--model_dir ./NLPCCKBQA2016_bert_zh_model/best_sim.bin \
--bert_model ../pretrained_models/bert-base-chinese/ \
--max_seq_length 64

 

curl http://127.0.0.1:8001/v1/predict  \
-H "Content-Type:application/json" \
-X POST \
--data '{"text_a": "王平的出生日期是哪一年呀","text_b":"出生日期"}'

 

6-基于知识库QA检索

 

通过前面内容,已经提供了满足QA 检索的服务,下面我们按照下面的过程就可以提供服务了.

 

  • query = xxxxx
  • query->调用NER 模型,返回多个元组列表
  • 多个元组列表-> 依次通过文本相似度模型进行打分,提取相关度最高的三元组为符合条件的内容,然后抽取predicate.
  • 根据实体名称subject + 关系predicate-> 知识库检索答案

- 6.1-搭建QA检索服务

 

下面是上述步骤的完整代码过程如下

 

import requests
import json
import pymysql
aheaders = {'Content-Type': 'application/json'}
api_server_host = 'localhost'
def connect():
conn = pymysql.connect(
user="root",
password="12345678",
host=api_server_host,
port=3306,
db="KB_QA",
charset="utf8"
)
return conn
def close(conn):
if conn:
conn.close()
def execute_sql(conn, sql):
cursor = conn.cursor()
try:
cursor.execute(sql)
results = cursor.fetchall()
except Exception as e:
print("Error: unable to fecth data: %s ,%s" % (repr(e), sql))
finally:
cursor.close()
return results


def get_ner_list(json_data):
'''
curl http://127.0.0.1:8000/predict \
-H "Content-Type:application/json" \
-X POST \
--data '{"text": "王平的出生日期是哪一年呀?","lang":"zh"}'
'''
url = "http://{}:8000/predict".format(api_server_host)
response = requests.post(url, headers=aheaders, data=json.dumps(json_data))
json_ner_result = json.loads(response.text)
entities = [] # 实体列表
entity = [] # 每个实体临时变量
for data in json_ner_result['data']:
word = data['word']
tag = data['tag']
if tag != 'O':
entity.append(word)
else:
if len(entity) > 0:
entities.append("".join(entity))
entity = []
if len(entity) > 0:
entities.append("".join(entity))
return entities


def get_similarity(text_a, text_b):
"""
curl http://127.0.0.1:8001/v1/predict \
-H "Content-Type:application/json" \
-X POST \
--data '{"text_a": "王平的出生日期是哪一年呀","text_b":"出生日期"}'
"""
url = "http://{}:8001/v1/predict".format(api_server_host)
input_json = {'text_a': text_a, 'text_b': text_b}
response = requests.post(url, headers=aheaders, data=json.dumps(input_json))
json_sim_result = json.loads(response.text)
if json_sim_result['data']['result'] == '相同':
return json_sim_result
else:
return None
def search(query):
print('query = ',query)
json_ner = {
"text": query,
"lang": "zh"
}
# 1. ner模型获取实体名称
entities = get_ner_list(json_ner)
# 2. 实体名称-> 知识库检索 得到三元组
conn = connect()
#
for entity in entities:
print('entity = ',entity)
sql = "select * from nlpcc_qa where entity = '{}'".format(entity)
data_list = execute_sql(conn, sql)
print('*********** 识别实体名称: [{}] 检索的三元组列表 文本相似度比较***********'.format(entity))
max_sim_text = ''
max_prob = 0.
for index, spo in enumerate(data_list):
text_a = json_ner['text']
text_b = spo[2]
json_sim_result = get_similarity(text_a, text_b)
if json_sim_result:
print(json_sim_result['data'])
prob = json_sim_result['data']['prob']
prob = float(prob.split('%')[0])
if prob > max_prob:
max_prob = prob
max_sim_text = json_sim_result
# 最终的结果
print('*********** 识别实体名称: [{}] 检索的三元组列表 文本相似度比较 最终结果***********'.format(entity))
predicate = max_sim_text['data']['text_b']
print('predicate = ',predicate)
# 3。 entitiy + predicate -> 知识库检索给出最终的答案
print('*********** 识别实体名称: [{}] , 关系: [{} ] 知识库检索答案 ***********'.format(entity,predicate))
sql = "select * from nlpcc_qa where entity = '{}' and attr_name = '{}' ".format(entity,predicate)
data_list = execute_sql(conn, sql)
print('*********** 知识库检索答案 结果如下 ')
for data in data_list:
entity = data[1]
attr_name = data[2]
attr_value = data[3]
print('search result: {} - {} - {}' .format(entity,attr_name,attr_value))
#
conn.close()




if __name__ == '__main__':
#query = "谁知道中华人民共和国环境保护部的职能是什么?"
query = "奥巴马毕业哪个大学"
search(query)

 

- 6.2-案例分析


 

  • 案例测试1: query =  谁知道中华人民共和国环境保护部的职能是什么?

自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_18


 

  • 案例测试2: query = 北京大学的地址在哪里?

自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_19


 

  • 案例测试3:query =  杨幂有多重?

自然语言处理(NLP):20 基于知识图谱的智能问答系统_三元组_20


 

  • 案例测试4:query =  中国的国土面积是多少

自然语言处理(NLP):20 基于知识图谱的智能问答系统_json_21


 

  • 案例测试5:query =  奥巴马毕业哪个大学

 

自然语言处理(NLP):20 基于知识图谱的智能问答系统_json_22

 

7-总结

我们在上述内容基础上,提出一些可以优化的知识点,供参考。

 

  • 知识库需要不断更新,知识库可以导入 图数据库,快速检索
  • 关于命名实体识别部分可以增加规则,提升效果(基于知识库过滤所有的实体,检索过程通过AC自动机快速检索)
  • 文本相似度文本,可以把识别的实体进行隐藏,重新训练模型查看效果
  • 根据query 识别出的实体,在知识库检索目前是精确匹配,也可以考虑基于语义进行匹配,提供知识库检索的召回内容