为了方便阅读,本文分成三篇文章进行发布,本文先介绍技术背景、统一数据格式设计和规则设计;第二篇介绍抽取程序设计;第三篇介绍规则配置交互设计
1. 背景
知识抽取是从数据中提炼、萃取知识信息的过程。按照数据的结构化程度,分为结构化数据知识抽取、半结构化数据知识抽取和非结构化数据知识抽取。
最常见的结构化数据是表格式数据,在传统的信息化系统中存在的大量关系数据库库表数据都是表格式数据,尽管其中一些字段可能是文本、二进制数据(即非结构化的),但总体来都认为是结构化数据。另外常见的Excel表单、Word/PDF文件中的表格,也可以看成是结构化数据,但那些格式特别自由随意的数据不在此列。熟悉Excel操作的人都知道,日常在使用表格软件时,要尽量统一列的格式、避免单元格合并、尽量使用数字等,这其实就是因为结构化的数据计算较为方便、便于计算机自动化处理,而那些一开始随意编辑的表格,后续很难利用。
那么既然结构化数据本身已经具有较好的结构,为什么还需要做知识抽取呢?这当然不是因为要构建知识图谱就理所当然应该有这个过程,主要的动机是在于数据的结构与知识的结构不一致。建设知识图谱系统的必要性及其主要优点是海量数据的汇聚、关联、融合,而数据存在多种来源(文件、数据库、网络、IO设备等)、多种格式(如txt、word、pdf等)、多种模态(文本、图片、语音、视频)、多种结构(具有不同的数据元定义、组成等),知识抽取就是解决这个关键问题的第一步,之后是知识融合。
举个简单例子,需要融合来自两套系统的数据,其中系统A包含“人物表”(包括字段:id、name、position),B系统包含“人员表”(包括字段:身份证号、姓名、性别、年龄),两张表的数据存在一些重复。很显然,我们需要把两张表的数据汇聚融合,都汇聚形成“人物”类型的实例。但两个表的结构不一样,数据库系统无法解决这个问题,必须通过更高层的技术和智慧来解决。
从一方面,由于结构化数据本身相对结构化,数据内容也较为规范(如果不规范怎么办?通过数据治理系统进行预处理),数据内容的语义是明确的,所以进行知识抽取最常用的方法是基于规则的。这套规则定义如何从输入的结构化数据,映射或转换到知识图谱的目标结构(即本体或知识Schema)。这个过程本质上是浅层的数据转换,本质上就是ETL技术中的“T”。而针对非结构化数据的知识抽取,可以认为是“E”+“T”,相关内容后续介绍。
2. 统一数据格式设计
首先对结构化数据设计一种统一格式,便于后续统一处理。使用表Table表示一张二维表,每张表具有表头和表行,表头包括表的各个字段的名称、类型、备注等信息,表行是表中的具体数据,表行的数据单元与对应的字段定义相匹配。
为了满足应用需要,数据格式包括三个方面:
- 业务项:实际业务数据
- 标识项:基于业务或单纯技术目的而设计的数据标识,一般采用一个,例如id或_id,有些情况会采用多个标识项,业务ID和物理ID。物理ID是数据处理方面便于区分的目的,往往是自增ID或UUID,通过随机、哈希化等方法,使得ID较为整齐。
- 元数据项:除了标识以外的元信息,例如数据来源(类型、数据库地址等)、数据类型、文件名等
看下ElasticSearch的文档结构:
{
"_id":"xxx",//文档ID
"_index":"index-name",//索引名
"_type":"type-name", //类型名,ES6以后统一成doc
"source": { } //业务数据
}
可以看出,ES采用了source表示业务数据,在对象顶层用_id、_index、_type是数据的标识。扩展这个数据结构,加入meta字段表示元信息,设计数据结构如下:
{
"_id":"xxx",//文档ID
"data": { } //业务数据
"meta": { "source": { "type":"file", "path": "/home/chenbo/data/1.csv"}}
}
meta根据具体需要可以进行扩展,示例中是包含了数据源(source)信息,指明数据源类型是文件,文件路径是/home/chenbo/data/1.csv。
上述格式在逻辑上较为丰富,但是在应用中由于大量业务数据的元数据相同导致一定程度的冗余,因此设计更加紧凑的格式:
{
"meta": { "source": { "type":"db", "dbtype":"mysql", "host":"10.0.0.1", "port":3306, "database": "buz1", "table": "person"}},
"rows": [
{"_id": "xxx", "data": {} } //一条数据
]
}
通过items表示若干表行。每个item格式与前面格式一致。
为了方便使用,业务数据字段data支持两种格式:
- 普通JSON对象,即K-V格式
- 单元格列表形式:{“_id”, “header”: [“name”], values: [“陈波”]}
支持将header提升以及到顶层,格式如下:
{
"meta": { "source": { "type":"db", "dbtype":"mysql", "host":"10.0.0.1", "port":3306, "database": "buz1", "table": "person"}},
"header": [], //表头数据
"rows": [
{"_id": "xxx", "values": [] } //一条数据
]
}
此外,支持对header进行扩展,header中每项表一个字段,如果是字符串,则表示了字段名称,字段类型程序自动推动;如果是JSON对象,则可以显式声明字段的名称、类型、默认值、描述等,如下所示:
{
"header": [
{"name": "名称","type":"string","default":null,"comment":"人员姓名"
]
}
上述数据格式具备丰富的表达能力,同时也兼顾了简洁和效率,可根据业务进行检索、扩展。
3. 抽取规则设计
基于JSON格式进行规则设计:
{
"rules": { // 抽取规则
"idPrefix": "taskABC", // 统一指定的ID前缀 可以标识一批数据
"nodes": [//点规则,用于抽取实体、事件、文档
{
"_nodeId": "0", //顺序编号 方便边规则引用
"tableId": "table1", //节点来源表,一种节点仅支持一张来源表
"id": "_", //节点ID配置 支持2种配置方式,可空
"keyFields": ["@name"], //标识字段 用于冲突检测
"name": "@name", //节点名称规则 支持2种配置方式
"mustFields": ["@Entity_name"], //必填字段
"type": {"id": "/entity/human", name: "human"}, //实体类型 支持3种配置
"mappings": [//列与属性的映射
{"name": {"id": "/p/name", "name": "Entity_name"}, "value": "@转账人姓名"} //name支持3种方式 value支持2种
]
}],
"edges": [{//边规则,用于抽取任意对象之间的关系
"tableId": "table1", //边来源表,一种边仅对应一个表
"id": "_", //ID支持2种配置方式,可空
"type": {"id": "/relation/work", name: "work"}, //关系类型 支持3种配置
"fromId": "@人员ID", //头结点标识,支持数组
"toId": "@公司ID", //尾节点标识,支持数组
"mappings": [], //与node的mapping相同
"fromNode": "0", //头结点ID,引用自 nodes._nodeId
"toNode": "1", //尾节点ID,引用自nodes._nodeId
"directed": true //是否为有向边
}]
}
}
说明
- 映射规则分为点规则和边规则两种
- 点和边都支持ID配置、类型配置和任意数量的属性配置,其中ID和类型是必填项;对于边,还需要配置fromId和toId
- ID配置支持2种:自动生成;数据字段值,支持多字段连接
- 实体名称支持2种:数据字段值(理论上也可以支持多字段连接);字面值
- 类型配置支持3种:Schema选择;数据字段值;字面值
- 属性名配置支持2种:Schema选择;字面值(一般就是字段名)
- 属性值配置支持2种:数据字段值;字面值
- fromId 和 toId,是边的头尾节点标识,一般采用数据字段值,与节点的ID对应,支持多字段连接
- keyFields 标识字段,用于冲突检测;ID为空时,自动将keyFields作为ID
- mustFields 必填字段,如果对应字段为空,则产生必填冲突
- idPrefix 用于标识同一批数据;便于对数据维护管理,前端可以使用图谱ID或用户输入的标识符。
规则配置方式
- _:自动生成
- @field-name:数据字段值
- xxx:字面值,直接使用xxx作为相应的数据
- {id, name} Schema选择,传递id和name信息 方便使用