知识图谱自动化构建:从非结构化文本到三元组

FreeGuideOnline 最新 2026-06-26

(艾伦·图灵,国籍,英国) (艾伦·图灵,提出,图灵测试)


实体可以是人物、地点、概念、事件等,关系描述实体之间的语义联系。从文本中自动抽取三元组的过程被称为信息抽取,主要包含命名实体识别(NER)和关系抽取(RE)两个子任务。

### 自动构建的核心流程

非结构化文本 → 预处理 → 实体识别 → 实体对齐 → 关系抽取 → 三元组生成 → 知识图谱


本教程将重点介绍如何利用现有工具和开源模型,搭建成一个实用的自动化流水线,并以处理一篇科技新闻为例进行演示。

## 工具选型与安装

我们将使用 Python 结合以下主流库:

- **spaCy**:高性能自然语言处理库,内置命名实体识别和依存句法分析。
- **Hugging Face Transformers**:提供预训练的关系抽取模型,无需从零训练。
- **NetworkX**:用于存储和简单可视化图结构。

### 环境快速配置

```bash
pip install spacy transformers torch networkx matplotlib
python -m spacy download zh_core_web_trf  # 中文Transformer模型,也可选择en_core_web_trf

以上命令安装了中文的大模型管道,其NER准确率高于轻量版,且支持基于上下文的词向量,利于后续关系抽取。

第一步:文本预处理与分句

原始文本需要切分为句子,因为关系通常存在于单句或相邻句范围内。

import spacy

nlp = spacy.load("zh_core_web_trf")
text = """
艾伦·图灵1912年出生于英国伦敦。他提出了著名的图灵测试,对计算机科学影响深远。
图灵还在第二次世界大战期间为盟军破译密码,其工作地点位于布莱切利园。
"""

doc = nlp(text)
sentences = [sent.text for sent in doc.sents]
print(sentences)

输出两个句子,后续在每个句子上独立进行实体识别和关系抽取。

第二步:命名实体识别与实体对齐

实体识别

利用 spaCy 的预训练NER组件直接提取实体。

entities = []
for sent in doc.sents:
    for ent in sent.ents:
        entities.append((ent.text, ent.label_, sent.text))
        # 打印方式
print(entities)

示例输出实体:艾伦·图灵(PERSON)、1912年(DATE)、英国伦敦(GPE)、第二次世界大战(EVENT)等。

实体对齐(共指消解简化处理)

同一个实体可能在上下文中有多种指代,例如“图灵”和“他”都指向艾伦·图灵。可以使用简单的规则或引入共指消解工具(如neuralcoref,或基于Hugging Face的coref模型)。此处为简化,我们假定文本中实体指代已经清晰,若需要生产级系统,可使用fastcoref等库。

第三步:关系抽取方法选型

关系抽取可分为有监督、远程监督和少样本三种范式。对于快速搭建,推荐使用预训练的关系抽取模型,直接从句子中推理出关系类型。

使用 Hugging Face 零样本关系抽取管道

zero-shot-classification管道的理念是:给定一个句子、一个头实体和一个尾实体,模型判断它们之间属于哪种预定义关系(即使模型未见过)。

首先定义候选关系列表,例如:国籍出生于提出工作于位于

from transformers import pipeline

classifier = pipeline("zero-shot-classification", model="MoritzLaurer/DeBERTa-v3-base-mnli-fever-anli")
# 也可使用支持中文的多语言模型,如 "joeddav/xlm-roberta-large-xnli"

对每一对实体组合和句子,构建推理模板:

def extract_relations(doc, candidate_relations):
    triples = []
    for sent in doc.sents:
        entities = list(sent.ents)
        for i in range(len(entities)):
            for j in range(i+1, len(entities)):
                e1 = entities[i]
                e2 = entities[j]
                # 构建假设模板:“e1 的 关系 是 e2”
                sequence = f"{e1.text}{e2.text}"
                result = classifier(sequence, candidate_relations)
                # 取置信度最高的关系
                top_relation = result['labels'][0]
                score = result['scores'][0]
                if score > 0.7:   # 阈值可调
                    triples.append((e1.text, top_relation, e2.text))
    return triples

注意:零样本方法对模板设计敏感,需要根据语种和关系类型调整。更精确的做法是使用专门的RE模型,如通过huggingface"Babelscape/rebel-large"(英文)或中文RE模型(如"wangfan/bert-chinese-relation-extraction"),本教程以零样本为例便于理解。

基于依存句法的规则抽取(辅助方法)

对于特定关系,可以使用依存路径进行抽取。例如,“出生于”关系常出现在“出生”动词连接出生地和人物。spaCy提供依存分析:

for token in doc:
    if token.pos_ == "VERB" and token.lemma_ in ["出生", "提出", "工作"]:
        # 查找主语和宾语作为实体
        subj = [child for child in token.children if child.dep_ == "nsubj"]
        obj = [child for child in token.children if child.dep_ == "obl"]
        if subj and obj:
            print(f"({subj[0].text}, {token.lemma_}, {obj[0].text})")

这种方法适用于抽取模式明确的关系,但泛化能力弱。实际系统中通常结合两者。

第四步:三元组生成与质量过滤

从句子中得到多个候选三元组后,需要确保主体和客体确实是实体,且关系有效。过滤策略:

  • 实体类型检查:主体和客体应为特定类型(PERSON、ORG、GPE等)。
  • 关系置信度阈值:保留零样本分数高于0.7的抽取结果。
  • 去除过短或无效实体(如“他”、“这”)。
  • 实体标准化:利用实体链接将名称归一化(如“图灵”和“艾伦·图灵”统一为知识库ID)。可使用Wikidata API或OpenTapioca等工具,本教程不做深入展开。
valid_triples = []
for triple in extracted:
    if len(triple[0]) > 1 and len(triple[2]) > 1:
        valid_triples.append(triple)

将三元组存储为列表或 CSV 文件,方便导入图数据库。

第五步:构建并可视化知识图谱

使用 NetworkX 快速构建图并可视化。

import networkx as nx
import matplotlib.pyplot as plt

G = nx.DiGraph()
for subj, rel, obj in valid_triples:
    G.add_edge(subj, obj, label=rel)

plt.figure(figsize=(8,6))
pos = nx.spring_layout(G, seed=42)
nx.draw(G, pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=2000, font_size=10)
edge_labels = {(u,v): d['label'] for u,v,d in G.edges(data=True)}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.show()

执行后将看到实体之间的连线及其关系标签,形成一个小型知识图谱的雏形。

进阶技巧与完整流水线示例

使用更先进的 RE 模型

Hugging Face 上有专门的关系抽取模型,例如 "Babelscape/rebel-large" 可以一次性输出所有三元组,无需指定关系类型,并且支持同时抽取多个三元组。更改为:

from transformers import AutoModelForSeq2SeqLM, AutoTokenizer

model = AutoModelForSeq2SeqLM.from_pretrained("Babelscape/rebel-large")
tokenizer = AutoTokenizer.from_pretrained("Babelscape/rebel-large")
gen_kwargs = {"max_length": 256, "length_penalty": 1.5, "num_beams": 5, "num_return_sequences": 1}

def extract_triplets(text):
    model_inputs = tokenizer(text, max_length=256, padding=True, truncation=True, return_tensors='pt')
    generated_tokens = model.generate(**model_inputs, **gen_kwargs)
    decoded_preds = tokenizer.batch_decode(generated_tokens, skip_special_tokens=False)
    # 解析模型输出为三元组(输出格式为 "head <triplet> tail <triplet> ...")
    # 省略具体解析代码,可参考huggingface博客 "End-to-end relation extraction with REBEL"