第2章 知识图谱基础

什么是知识图谱

先跑个题。2012年谷歌提这个概念的时候,很多人以为只是又一种数据库。

但说白了,知识图谱就是一种用图结构来组织知识的方法。把现实世界中的「事物」和它们之间的「关系」用图的方式存起来。

我个人特别喜欢的一个定义是:知识图谱 = 实体 + 关系 + 语义。

举个例子:
「阿司匹林」和「环氧合酶-1」之间有什么关系?
——阿司匹林通过抑制环氧合酶-1来发挥镇痛作用。

这其实就是一条知识。如果把成千上万条这样的知识连起来,就构成了一张网络。这张网络就是知识图谱。

我在项目中遇到过不少同学把知识图谱和传统的分类目录混为一谈。两者有本质区别:分类目录是树状的,知识图谱是网状的。树状结构只能上下归类,网状结构才能支持复杂的推理。

核心记忆点:知识图谱的核心不是「知识」,而是「关联」。没有关联的孤立实体,只是数据,不是知识。

三元组:知识图谱的基本单元

你想想看,一张大网是怎么织起来的?

最小单元就是「三元组」(Triple)。

三元组的结构极其简单:
主体(Subject)— 谓词(Predicate)— 客体(Object)

用自然语言描述就是:
「谁 — 做了什么 — 对谁」或者「什么事物 — 有什么属性 — 值是多少」

主体 谓词 客体
阿司匹林 抑制 环氧合酶-1
环氧合酶-1 属于 环氧合酶家族
布洛芬 治疗 头痛
药物靶点 定义 药物作用的生物分子

我刚开始做知识图谱时,总觉得三元组太简单,不以为然。但真的动手建图才发现,把自然语言中的关系拆成标准化的三元组,才是整个工程中最容易出bug的环节。

我建议:在构建三元组之前,先定义好谓词的命名规范。不要出现同义不同形的谓词,比如「抑制」和「抑制作用」同时存在,会导致查询时漏掉大量数据。

存储三元组的方式有很多种。最基础的是用CSV文件或者JSON:

{
  "subject": "阿司匹林",
  "predicate": "抑制",
  "object": "环氧合酶-1"
},
{
  "subject": "环氧合酶-1",
  "predicate": "属于",
  "object": "环氧合酶家族"
}

我曾经踩过的坑:在一个项目中,因为主体和客体的标识符不统一,同一个概念在A文件中叫做「COX-1」,在B文件中叫做「环氧化酶-1」,导致生成的图谱里同样一个靶点出现了两个节点,连边连不上。后来不得不花了两周时间做实体对齐。切记:统一实体ID。

实体关系抽取

有了三元组的概念,那问题就来了:这些三元组从哪里来?

答案就是:实体关系抽取。

这其实包含两个子任务:

  1. 命名实体识别(NER):从文本中找出实体,比如「阿司匹林」「环氧合酶-1」
  2. 关系分类(RC):判断实体之间存在什么关系,比如「抑制」「激活」「结合」

说白了,实体关系抽取就是把非结构化的文本,变成结构化三元组的过程。

常用的方法有这几种:

方法 原理 适用场景
规则模板 人工编写正则/模式串匹配 少量数据、特定格式文本
传统机器学习 特征工程 + CRF/SVM 标注数据充足、特征清晰
深度学习 BERT/GPT + 序列标注 大规模数据、跨领域泛化

我个人习惯先用规则模板快速生成一批种子三元组。比如药物的说明书里,经常会出现「适应症」「不良反应」「作用机制」这些固定字段,直接用正则抽取效率很高。

看一个最简单的规则模板例子:

import re

text = "阿司匹林通过抑制环氧合酶-1来发挥镇痛作用。"
pattern = r"(.+?)通过(.+?)([激活|抑制|结合|促进])(.+?)来"

match = re.search(pattern, text)
if match:
    subject = match.group(1)  # 阿司匹林
    predicate = match.group(3) # 抑制
    obj = match.group(4)      # 环氧合酶-1

避坑指南:我曾经以为直接用预训练模型就能搞定所有关系,结果发现药物-靶点关系里有很多长尾关系,比如「调节」「修饰」「共价结合」,模型根本识别不出来。最后老老实实把高频关系单独建了规则。

实体关系抽取的质量,直接决定知识图谱的可用性。如果这一步跑偏,后面建出来的图谱就是一坨垃圾。嗯,这句话有点重,但你做一次就知道了。

图数据库简介

好了,三元组有了,怎么存?怎么查?

传统的关系数据库(比如MySQL)不是不能存,但你想想看,如果我想查「阿司匹林通过什么机制影响哪些靶点」,用SQL写关联查询,可能要JOIN四五张表,效率很低。

图数据库就是专门干这个的。

对比维度 关系数据库 图数据库
数据模型 表+行+外键 节点+边+属性
多跳查询性能 差(需要多次JOIN) 好(索引邻接关系)
典型操作 SELECT … JOIN MATCH … WHERE
适用场景 事务性OLTP 关联分析、路径搜索

目前最常用的图数据库是Neo4j。它的查询语言Cypher,写起来就像在画图。

举个例子。我要查阿司匹林抑制的所有靶点:

MATCH (drug:药物 {名称: '阿司匹林'})-[r:抑制]->(target:靶点)
RETURN drug, r, target

你看,跟自然语言的描述几乎一样。这也是图数据库受欢迎的原因之一——查询意图和代码结构高度一致。

我记得第一次用Neo4j时,被Cypher的简洁程度惊艳到了。以前写SQL查三度关系,要递归CTE写半屏代码。现在直接:MATCH (a)-[*1..3]-(b) 搞定。

不过图数据库也不是万能神药。它不适合频繁更新单节点属性的场景,也不适合做聚合统计分析(那种事还是交给关系数据库)。

在药物靶点知识图谱这个领域,图数据库是我们构建和检索知识的主要阵地。后面几章我会带你手把手搭一个Neo4j实例,然后把药物和靶点的三元组数据灌进去。

注意:不要一开始就追求完美的图数据库设计。先用简单模型跑通流程,再根据查询需求做优化。我见过太多人花了一个月设计图模型,最后发现查询性能还不如邻接表——白费功夫。

为了帮你建立起本章的知识体系,我把核心概念和它们之间的关系画成了一张框架图:

第2章 知识图谱核心逻辑 知识图谱 三元组 (S-P-O) 实体关系抽取 图数据库 命名实体识别(NER) 关系分类(RC) Neo4j / Cypher 构建流程:非结构化文本 → 实体关系抽取 → 三元组 → 入库(图数据库) → 知识图谱 核心基石:标准化三元组 + 高质量实体关系抽取

这张图把本章的核心知识点串起来了:知识图谱的基础单元是三元组,三元组靠实体关系抽取从文本中获得,最后存到图数据库里供查询和推理。每一步都是下一环节的前提,缺一不可。