跳转至

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,)

面试常问点

  1. 关系抽取和事件抽取的区别?
  2. 关系抽取:两个实体间的静态关系(属于谁、工作于哪)
  3. 事件抽取:动态事件的触发词+参数(谁在何时何地做了什么)

  4. 远程监督的噪声怎么处理?

  5. 多实例学习:一组句子只要有一个真正包含关系即可
  6. 注意力机制:加权聚合同一实体对的多个句子

  7. 联合抽取相比管道方法的优势?

  8. 共享参数、减少误差传播、能处理实体间的依赖

  9. 三元组重叠问题(一个实体参与多个关系)怎么处理?

  10. TPLinker/CasRel专门为此设计,管道方法很难处理

速查表

任务推荐方案
快速验证BERT + Entity Marker 管道
联合抽取CASREL / TPLinker
跨句关系GNN + 句法依存树
生物医学关系BioRED数据集 + BioBERT
大规模弱监督远程监督 + 降噪模块