第4章 知识抽取之关系抽取:管道式与联合抽取、远程监督、基于预训练模型的关系分类
关系抽取,说白了就是让机器读懂实体之间的「关系」。
比如「苹果公司发布了iPhone 14」这句话,实体是「苹果公司」和「iPhone 14」,关系是「发布」。机器得学会把这种隐含的结构拎出来。
我个人习惯把关系抽取分为三大流派:管道式、联合抽取、以及基于预训练模型的分类方法。今天咱们一个一个拆开看。
4.1 管道式抽取:先抽实体,再判关系
这是最朴素的做法。先在句子里把实体标注出来,然后针对每一对实体做关系分类。
- 实体识别(NER)——找到句子中的主语和宾语。
- 关系分类——对每一对候选实体,判断它们之间是否有关系,以及是什么关系。
举个例子,句子「特斯拉收购了太阳城公司」:
- NER 阶段标注出「特斯拉」和「太阳城公司」
- 关系分类阶段判断这两者之间的关系标签——「收购」
这种方法直观,但有一个大问题:错误传播。NER 漏了一个实体,关系就跟着丢了。我在项目中遇到过这样的情况:NER 把「北京」识别成了人名,结果整个关系链全乱套。
# 伪代码示意:管道式关系抽取
from ner_model import extract_entities
from relation_model import classify_relation
def pipeline_extraction(sentence):
entities = extract_entities(sentence)
# 枚举所有实体对
for i in range(len(entities)):
for j in range(i+1, len(entities)):
rel = classify_relation(sentence, entities[i], entities[j])
if rel is not None:
print(f"{entities[i]} --{rel}--> {entities[j]}")
4.2 联合抽取:端到端解决问题
管道式的缺点很明显——任务分裂。联合抽取想干一件事:同时输出实体和关系,不再分两步走。
说白了,它把序列标注和关系分类合并到一个模型里。典型做法是在解码阶段设计一个特殊的标注体系,或者使用多任务学习。
| 对比维度 | 管道式 | 联合抽取 |
|---|---|---|
| 错误传播 | 严重 | 较轻 |
| 解耦难度 | 容易 | 困难 |
| 模型复杂度 | 低 | 高 |
| 训练数据需求 | 分开标注 | 需要完整的实体-关系三元组 |
联合抽取的代表工作包括CasRel和TPLinker。我记得第一次复现 CasRel 时,那个主题标注的思路让我眼前一亮——它用「头实体标注 + 尾实体与关系联合标注」的方式,一步到位。
# CasRel 的输入输出示意
# 输入:句子
sentence = "乔布斯在1976年创立了苹果公司"
# 输出:三元组列表
{
"head": "乔布斯",
"relation": "创立",
"tail": "苹果公司"
},
{
"head": "苹果公司",
"relation": "成立时间",
"tail": "1976年"
}
但联合抽取也有痛处。它对解码的要求很高,一旦关系数很多(比如几千种),解码空间暴涨,训练非常不稳定。我建议刚入门的朋友先从管道式入手,把流程跑通再说。
4.3 远程监督:自动打标,数据不用愁
标签数据永远不够,怎么办?远程监督来救场。
它的思想很粗暴:把知识图谱中的三元组拿来对齐到文本。如果一个句子中同时出现了图谱中的两个实体,就认为这个句子表达了图谱中对应的关系。
举个例子,知识图谱里有一条三元组 (比尔·盖茨,创办,微软),那么任何同时提到「比尔·盖茨」和「微软」的句子,都被自动打上「创办」标签。哪怕句子说的是「比尔·盖茨曾经在微软担任CEO」——实际上不是创办关系,但也算。
这种做法会引入大量噪声。我在一个生物医学知识图谱项目中用过远程监督,结果发现60%的自动标注样本都是错的。后来我们加了一层句子级别的注意力机制,才把有效样本比例提到75%。
- 多实例学习(MIL)
- 句子级别注意力(PCNN-ATT等)
- 对比学习筛选
4.4 基于预训练模型的关系分类
现在的大语言模型给关系抽取带来了新玩法。你可以把关系分类任务看成一个文本分类问题或问答问题。
典型做法有两种:
- 序列分类:把句子+实体标记拼成输入序列,让BERT或GPT直接输出关系标签
- 提示学习(Prompt-tuning):把关系定义写入提示模板,让模型生成答案
# 基于预训练模型的关系分类(提示学习方式)
prompt = f"{head_entity} 和 {tail_entity} 之间的关系是 [Z]"
# 模型输出候选关系
model_output = "收购" # 假设模型推理得出
# 你也可以做成多选题:
prompt = f"请问 '{head_entity}' 和 '{tail_entity}' 之间的关系是?"
options = ["收购", "投资", "合作", "无关系"]
# 模型从中选一个
我建议你关注一下当前的主流实践:
- Noah's Ark Lab 的 KnowPrompt——专门为关系抽取设计的提示方法
- GPT-3/4 的零样本能力——不需要任何训练样本,直接扔上下文就能做
- 微调策略——如果数据量少,用 LoRA 微调;数据量大,做全参数微调
使用预训练模型做关系分类,最大的坑是实体标记的有效性。如果你直接输入纯文本,模型可能搞不清到底是哪两个实体之间的关系。所以我们通常会在实体前后加
最后给你一个总结表,方便对比:
| 方法 | 代表技术 | 适用场景 | 核心风险 |
|---|---|---|---|
| 管道式 | NER + 分类器 | 数据量小、流程透明 | 错误传播 |
| 联合抽取 | CasRel, TPLinker | 关系类型固定、精度要求高 | 解码复杂度高 |
| 远程监督 | PCNN-ATT, MIL | 大冷启动、无标注数据 | 噪声极其严重 |
| 预训练模型 | BERT, GPT, KnowPrompt | 零样本、少样本、高泛化 | 实体标记、推理成本 |