468_关系抽取方法¶
一句话说明¶
关系抽取(Relation Extraction)是从文本中识别两个实体之间关系类型的任务,例如"特朗普是美国总统"→(特朗普, 职位, 美国总统)。
核心知识点¶
- 三元组:(Subject实体, Relation关系, Object实体) 是关系抽取的输出单元
- 管道式 vs 联合抽取:
- 管道式:先NER,再对实体对分类(误差传播)
- 联合抽取:同时输出实体和关系(端到端,主流方向)
- 句内关系 vs 跨句关系:大多数方法只处理同句内实体对
- 远程监督:用知识库自动对齐文本生成训练数据(存在噪声)
经典模型/方法¶
| 方法 | 核心思路 | 优点 | 缺点 |
|---|---|---|---|
| BERT + 实体对分类 | 在[CLS]或实体表示上分类 | 简单有效 | 忽略实体对间语境 |
| BERT + Entity Marker | 用特殊token标记实体位置 | 更精确的实体感知 | 需预知实体位置 |
| CASREL(联合抽取) | 主体→关系→客体级联解码 | 联合抽取,处理多关系 | 复杂度高 |
| TPLinker(联合抽取) | Token对链接矩阵 | 无曝光偏差问题 | 矩阵稀疏 |
| 图神经网络 | 依存句法树+GNN | 跨句关系 | 依赖句法解析 |
代码示例¶
# 使用 BERT + Entity Marker 做关系分类
# Entity Marker:在实体前后插入 [E1] [/E1] [E2] [/E2] 特殊token
from transformers import BertTokenizer, BertForSequenceClassification
import torch
# ---- 1. 关系类别定义 ----
rel2id = {'no_relation': 0, 'per:title': 1, 'org:founded_by': 2}
id2rel = {v: k for k, v in rel2id.items()}
# ---- 2. 实体标记处理 ----
def mark_entities(text, e1_span, e2_span):
"""
在文本中插入实体标记token
e1_span, e2_span: (start_char, end_char)
"""
# 确保 e1 在 e2 之前
s1, e1 = e1_span
s2, e2 = e2_span
# 从后往前插入,避免位置偏移
marked = (text[:s1] + '[E1]' + text[s1:e1] + '[/E1]' +
text[e1:s2] + '[E2]' + text[s2:e2] + '[/E2]' +
text[e2:])
return marked
# 示例
text = "马云创立了阿里巴巴公司"
marked_text = mark_entities(text, (0, 2), (4, 9))
# => "[E1]马云[/E1]创立了[E2]阿里巴巴公司[/E2]"
print(marked_text)
# ---- 3. 加载模型(添加特殊token) ----
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
# 添加实体标记特殊token
special_tokens = {'additional_special_tokens': ['[E1]', '[/E1]', '[E2]', '[/E2]']}
tokenizer.add_special_tokens(special_tokens)
model = BertForSequenceClassification.from_pretrained(
'bert-base-chinese',
num_labels=len(rel2id)
)
# 扩展embedding层以适应新token
model.resize_token_embeddings(len(tokenizer))
# ---- 4. 推理 ----
enc = tokenizer(marked_text, return_tensors='pt',
max_length=128, truncation=True, padding='max_length')
model.eval()
with torch.no_grad():
logits = model(**enc).logits # (1, num_relations)
pred_id = logits.argmax(dim=-1).item()
print(f"预测关系: {id2rel[pred_id]}")
# ---- 5. 实体对表示(R-BERT方法)----
# 用实体对应位置的BERT隐状态均值作为实体表示,再拼接[CLS]分类
def get_entity_repr(hidden_states, e_start_id, e_end_id):
"""提取实体span的平均表示"""
entity_tokens = hidden_states[0, e_start_id:e_end_id+1, :] # (span_len, 768)
return entity_tokens.mean(dim=0) # (768,)
面试常问点¶
- 关系抽取和事件抽取的区别?
- 关系抽取:两个实体间的静态关系(属于谁、工作于哪)
事件抽取:动态事件的触发词+参数(谁在何时何地做了什么)
远程监督的噪声怎么处理?
- 多实例学习:一组句子只要有一个真正包含关系即可
注意力机制:加权聚合同一实体对的多个句子
联合抽取相比管道方法的优势?
共享参数、减少误差传播、能处理实体间的依赖
三元组重叠问题(一个实体参与多个关系)怎么处理?
- TPLinker/CasRel专门为此设计,管道方法很难处理
速查表¶
| 任务 | 推荐方案 |
|---|---|
| 快速验证 | BERT + Entity Marker 管道 |
| 联合抽取 | CASREL / TPLinker |
| 跨句关系 | GNN + 句法依存树 |
| 生物医学关系 | BioRED数据集 + BioBERT |
| 大规模弱监督 | 远程监督 + 降噪模块 |