7.5 开源工具实践

优质
小牛编辑
113浏览
2023-12-01

本节将简述基于 Elasticsearch [6] 搭建一个简易实体语义搜索引擎的流程。该搜索引擎可以按照名称搜索实体、实体属性、多跳搜索以及搜索符合多对属性要求的实体。在功能逻辑完成后,可搭建网站将其可视化。本实践的相关工具、实验数据及操作说明由 OpenKG提供,地址为http://openkg.cn。

7.5.1 功能介绍

1.实体搜索

实体搜索即输入实体名称,返回该实体的知识卡片(实体在知识图谱中的所有属性和属性值),如图7-15所示。

图7-15 实体搜索功能示意图

2.实体的属性搜索

输入实体名称和一个属性名称,如果该实体存在该属性值,则返回该属性值,如图7-16所示。

图7-16 实体属性值搜索功能示意图

3.多跳搜索

多跳搜索可以输入多个属性,实现多跳搜索,即形如“姚明的女儿的母亲的身高”,其中“姚明:女儿”查询得到的是实体“姚明”的一个属性,但同时这个属性值也作为一个实体存在于数据集中,那么就可以接着对该实体继续查询其属性和值,如图7-17所示。

图7-17 多跳搜索功能示意图

4.按照多种属性条件检索实体

输入多对 [属性名 opearotr 属性值],它们之间的关系可以是 AND、OR、NOT,同时属性值是等于、大于、小于一个输入值,返回满足这些属性限制的实体。例如,“职业:篮球运动员 or 职业:足球运动员 And Not 国籍:中国 And 身高>=180”,如图7-18所示。

图7-18 根据属性值检索实体示意图

7.5.2 环境搭建及数据准备

1.安装Elasticsearch

在 Elasticsearch 官网下载 Elasticsearch 的安装包。运行安装包目录下的/bin/Elasticsearch.sh(在 Windows 系统中运行/bin/Elasticsearch.bat,本小节的实验内容在Ubuntu系统上完成,后续步骤会涉及一些Linux 指令)。

注意:该命令已经运行了Elasticsearch。可能提示不能在Root账户下运行,此时请切换到非Root账户下运行。如果想让Elasticsearch在后台一直运行,在上述命令最后加参数-d即可。

至此安装完成,可以通过访问本地9200端口 curl 'http://localhost:9200’来访问Elasticsearch。

注意:可通过修改配置文件使Elasticsearch实现远程访问。

7.5.3 数据准备

使用的数据集是一个基于 cnschema 标准的人物属性数据集。该数据集由三元组组成,每个三元组描述一个人物实体的某个属性。在将此数据集导入 Elasticsearch 之前,需要考虑其在 Elasticsearch 中存储的方式。最简单的方式是将每个三元组视作一个文档,其中包含3个字段,分别为三元组的(subject, predicate, object),但本书采取的是另一种方式,即一个实体的所有属性和属性值为一个文档。

1.知识库格式转换(preprocess.py)

数据集的格式如所示。

Elasticsearch要求文档的输入格式为JSON。将实验数据集转化为JSON格式后,每个实体对应一个JSON的 object,也即Elasticsearch中的一个文档。

数据集中"A.J.万德"的所有属性及属性值汇总在一起,存储在一个 JSON 对象中作为一篇文档导入 Elasticsearch,其他的实体类似。

其中,所有属性除了"height"及"weight"两个属性,都存在一个名为"po"的list对象中,每个属性及其属性值作为一个小的object,分别用键"pred"和"obj"标识属性名和属性值。

之所以要将"height"和"weight"单独考虑,而不是和其他属性一样存储在 list 中,是因为这两个属性要支持范围搜索,即"height>200"的搜索。因此,要求它们在存储时的数据类型为 integer,而 list 中的所有属性的属性值的存储类型都为 keyword(不分词的string,只支持全文匹配)。

之所以每一对(属性名,属性值)存储为一个 object,并放入一个 list 中,是因为这是Elasticsearch 定义的一种 nested object 的数据类型。这种数据类型能存储大量拥有相同的key 的对象,并且可以对之进行有效的检索。这样,无论数据集中有多少种不同的属性,都能以相同的格式存储。

之所以不是每一个三元组存储为一篇文档,而是一个实体相关的所有属性及属性值存储为一篇文档,是因为要支持通过多对(属性,属性值)联合检索满足要求的实体,以这种格式存储,能提高检索效率。

另外,数据集中某些属性的属性值不是很规范,例如 height、weight 的属性值存在单位不同、包含无关字符等问题,其他属性的属性值也存在多个值以空格等字符连接作为一个值(例如,"职业:运动员 足球运动员",这个为了检索时匹配方便,应该将其拆成两个)的问题,因此在格式转换的同时也要对属性值做一些清理。

2.属性同义词扩展 (attr_mapping.txt)

因为实验的数据集较小,包含的属性种类不多,因此可以人工增加一些同义的属性词。下面的文件中每一行第一个词为数据中存在的属性,后面的是后添加的同义属性词。在解析查询语句的时候,如遇到同义属性词,可将其映射到数据集中存在的属性上。

7.5.4 导入Elasticsearch

1.Elasticsearch的index和type简介

Elasticsearch用index和type管理导入的文档。其中index可以类比为一个单独的数据库,存放的是结构相似的文档;type是index的一个子结构,可以存放不同部分的数据,可以类比为一张表,而每一篇文档都存储在一个type中,类似于一条记录存储在一张表中。

2.在Elasticsearch中新建index和type

为实验数据集新建index('demo')和type('person')。Elasticsearch使用RESTful API可以方便地交互,通过Elasticsearch的mapping文件可以创建index和type,并指定每个字段在Elasticsearch中存储的类型。

下述示例用 curl 命令在命令行中与 Elasticsearch 交互。其中,height、weight 存储为integer 数据类型,而实体名 subj 和其他属性存储为 keyword 类型。所有其他属性存储在一个nested object对象中。打开命令行,运行如下代码。

3.导入数据 (insert.py)

向新建的 type 中导入实验数据集,导入时同样使用 RESTful API,可以使用Elasticsearch提供的insert方法。

到这一步,已经可以检索该知识库了,解析查询中的实体名和属性名,以实体名为keyword 检索实体,并解析出答案中属性名对应的属性值。例如,查询“姚明”,构造类似如下查询来检索实体。查询“姚明:身高”,先检索实体“姚明”,再获取结果中的“身高”属性的值。

注意:Elasticsearch的查询除了常见的get斱式,即将参数和参数值作为链接的一部分提交,也支持如上所示将查询参数写入一个JSON结构体,用该请求体查询的斱式。这种斱式由于表达斱式更加灵活,因此可以表达较为复杂的查询。

7.5.5 功能实现

1.按名称检索实体

按名称检索实体,并返回该实体的所有属性和属性值。这种检索的查询在第3步其实已经实现,只需要将查询的结果解析一下,写入一个 python dict 对象返回即可。代码如下:

2.检索实体的属性以及多跳查询

检索一个实体的某个属性的值,也是先检索该实体,然后判断返回的结果中是否包含检索的属性,如果包含,则返回对应的值。因此,这种检索的查询语句同上。如果是多跳查询,则在检索出一个属性对应的属性值后,需要再判断知识库中是否存在以该属性值为名称的实体,如果存在,则以该属性值为实体名称检索对应的实体,再判断结果是否包含检索的第2个属性。如此循环,直到得到最终结果,如下所示。

3.根据多对(属性名,属性值)检索实体

该功能首先需要进行查询构建,在构建出查询语句后执行该查询,解析查询结果即可。

在查询构建部分要稍微复杂一些。假设已经解析好了查询语句的组成部分,即每对属性值对,它们之间的and或or关系、not操作以及每个属性值对的操作是等于还是范围检索,可以构造出一个查询直接返回满足这些要求的实体。这里注意部分属性,例如height、weight支持范围搜索,例如“身高 >= 200”对应的部分查询为:

检索除 height、weight 外的其他存储在 nested object 中的属性,例如“Not 国籍:中国”。

如果关系是or,那么对应Elasticsearch的should关键字;如果在属性值对前加了否定not,那么对应的Elasticsearch关键字是must_not,如下所示。

7.5.6 执行查询

以上实现了几种不同种类的查询,对于用户的查询输入,先进行分类,判断属于哪一种查询,再调用对应的查询函数:

●首先,由于定义的查询格式比较简单,可以在将查询按照操作符分割后,判断第一个词是否是属性名,操作符为And、Or、Not、=、<、>等。

●如果是,代表当前查询是依据属性条件检索实体。

●否则代表当前的查询数据实体或实体的属性进行检索。

上述过程中判断一个词是否是属性词可以通过查询 Elasticsearch 完成。在对用户查询分类后,基于每种查询问题的模板,填充解析时识别出来的实体名和属性名,生成Elasticsearch查询。构造并执行用户查询对应的Elasticsearch查询后,解析查询结果。

上述只是基于 Elasticsearch 实现了基本的查询功能,感兴趣的读者还可以在此基础上可通过一些扩展使其支持更复杂的查询,如别名检索、反向检索、路径检索等。