Press "Enter" to skip to content

1 自然语言处理(NLP):20 基于知识图谱的智能问答系统 针对知识图谱的问题系统,重点解决的几个…

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

基于知识图谱的问答系统答即根据一个问题,抽出一条三元组,生成类 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.​

 

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-标准数据可视化分析

句子数量整体分析

句子长度分析

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

 

数据集标签分布

 

 

 

– 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-文本语义相似度模型

 

 

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

 

《机械设计基础》这本书的作者是谁?  作者 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-相同)

数据离散化数据

 

– 4.2-数据可视化分析​

句子标签数量分布

句子对的长度可视化分析

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

 

 

– 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 个三元组。

 

 

 

– 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()

 

导入后的数据效果如下

 

 

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

 

 

    1. query = 高等数学出版社是是哪家?

 

    1. 然后通过 NER 识别: 高等数学

 

    1. 知识库过程 :根据高高等数学的实体名称去查询,获取多条记录

 

 

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 =  谁知道中华人民共和国环境保护部的职能是什幺?

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

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

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

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

 

7-总结

 

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

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

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注