本文目录导读:

Python中实现关系抽取(Relation Extraction, RE)有多种方法,从简单的规则匹配到基于深度学习的模型,下面我会按难度递增的方式介绍几种主流实现方案。
基于规则的关系抽取(入门级)
适合数据规模小、关系类型固定的场景。
import re
import spacy
# 加载模型
nlp = spacy.load("zh_core_web_sm")
def rule_based_re(text):
doc = nlp(text)
relations = []
# 简单规则:找出"生于"、"位于"等关系词
patterns = [
{"relation": "出生地", "pattern": r"(\w+)生于(\w+)"},
{"relation": "位于", "pattern": r"(\w+)位于(\w+)"},
]
for pattern in patterns:
matches = re.findall(pattern["pattern"], text)
for match in matches:
relations.append({
"subject": match[0],
"relation": pattern["relation"],
"object": match[1]
})
return relations
# 测试
text = "爱因斯坦生于德国,普林斯顿位于美国。"
print(rule_based_re(text))
# 输出: [{'subject': '爱因斯坦', 'relation': '出生地', 'object': '德国'},
# {'subject': '普林斯顿', 'relation': '位于', 'object': '美国'}]
基于依存句法分析(进阶级)
利用句法结构提取更复杂的关系。
import spacy
nlp = spacy.load("zh_core_web_sm")
def dependency_based_re(text):
doc = nlp(text)
relations = []
for sent in doc.sents:
# 找出主谓宾结构
for token in sent:
# 识别"是"类型的谓词
if token.dep_ == "ROOT" and token.pos_ == "VERB":
subjects = [child for child in token.children if child.dep_ == "nsubj" or child.dep_ == "nsubjpass"]
objects = [child for child in token.children if child.dep_ == "dobj" or child.dep_ == "pobj"]
for subj in subjects:
for obj in objects:
relations.append({
"subject": subj.text,
"relation": token.text,
"object": obj.text
})
return relations
# 测试
text = "李明是北京大学的教授。"
print(dependency_based_re(text))
基于预训练模型(专业级)
使用BERT等预训练模型进行关系分类。
1 安装依赖
pip install transformers torch
2 基于BERT的关系分类示例
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
class BertRE:
def __init__(self, model_name="bert-base-chinese"):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModelForSequenceClassification.from_pretrained(
model_name,
num_labels=10 # 根据你的关系类型数量设置
)
def prepare_input(self, text, entity1, entity2):
"""将句子和实体对编码为模型输入"""
# 标记实体位置
marked_text = text.replace(
entity1, f"[E1]{entity1}[/E1]"
).replace(
entity2, f"[E2]{entity2}[/E2]"
)
return self.tokenizer(
marked_text,
padding="max_length",
truncation=True,
max_length=128,
return_tensors="pt"
)
def predict(self, text, entity1, entity2):
"""预测关系类型"""
inputs = self.prepare_input(text, entity1, entity2)
with torch.no_grad():
outputs = self.model(**inputs)
predictions = torch.softmax(outputs.logits, dim=-1)
return predictions
# 使用示例
re_model = BertRE()
text = "华为技术有限公司成立于1987年"
result = re_model.predict(text, "华为技术有限公司", "1987年")
print(result)
使用开源工具(快速实现)
1 使用DeepKE
pip install deepke
from deepke.relation_extraction import *
# 加载预训练模型
model = RELoader()
model.load_pretrained("bert-base-chinese")
# 预测关系
predictor = RE.predictor(model)
result = predictor.predict(
sentence="史蒂夫·乔布斯创立了苹果公司",
heads=["史蒂夫·乔布斯"],
tails=["苹果公司"]
)
print(result)
2 使用HanLP
pip install hanlp
import hanlp
# 加载预训练模型
HanLP = hanlp.load(hanlp.pretrained.mtl.CLOSE_TOK_POS_NER_SRL_DEP_SDP_CON_ELECTRA_BASE_ZH)
def extract_relations(text):
doc = HanLP(text)
relations = []
# 提取语义依赖关系
for sent in doc["sentences"]:
for relation in sent.get("srl", []):
relations.append({
"predicate": relation.get("predicate"),
"arguments": relation.get("arguments", [])
})
return relations
# 测试
text = "李明在北京大学学习计算机科学。"
print(extract_relations(text))
完整的项目实现方案
这里是一个结合多种方法的实用方案:
import json
from typing import List, Dict
import spacy
from transformers import pipeline
class RelationExtractor:
def __init__(self, method="hybrid"):
self.method = method
self.nlp = spacy.load("zh_core_web_sm")
if method == "ml":
# 使用HuggingFace的关系抽取pipeline
self.classifier = pipeline(
"text-classification",
model="bert-base-chinese"
)
def extract_entities(self, text: str) -> List[Dict]:
"""实体识别"""
doc = self.nlp(text)
entities = []
for ent in doc.ents:
entities.append({
"text": ent.text,
"label": ent.label_,
"start": ent.start_char,
"end": ent.end_char
})
return entities
def extract_relations_rule(self, text: str) -> List[Dict]:
"""基于规则的简单关系抽取"""
doc = self.nlp(text)
relations = []
# 常见关系模式
relation_patterns = {
"生育": ["生于", "诞生", "出生"],
"属于": ["属于", "隶属", "归属"],
"包含": ["包含", "包括", "含有"],
"位于": ["位于", "坐落", "地处"],
}
for token in doc:
if token.text in [v for vals in relation_patterns.values() for v in vals]:
# 获取主语和宾语
subj = list(token.lefts)[0].text if token.lefts else None
obj = list(token.rights)[0].text if token.rights else None
if subj and obj:
relations.append({
"subject": subj,
"relation": token.text,
"object": obj
})
return relations
def extract(self, text: str) -> Dict:
"""主抽取方法"""
result = {
"text": text,
"entities": self.extract_entities(text),
"relations": []
}
if self.method == "rule":
result["relations"] = self.extract_relations_rule(text)
elif self.method == "ml":
# 这里可以集成机器学习方法
pass
else: # hybrid
# 结合多种方法
rule_relations = self.extract_relations_rule(text)
result["relations"] = rule_relations
return result
# 使用示例
extractor = RelationExtractor(method="rule")
text = "阿里巴巴集团成立于1999年,总部位于杭州。"
result = extractor.extract(text)
print(json.dumps(result, ensure_ascii=False, indent=2))
选择建议
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 规则匹配 | 简单、快速 | 覆盖度低 | 固定格式、小规模 |
| 依存句法 | 可处理复杂句 | 依赖分词质量 | 中等规模、结构清晰 |
| 预训练模型 | 准确率高 | 需要标注数据 | 大规模、通用领域 |
| 开源工具 | 开箱即用 | 定制性差 | 快速原型、标准场景 |
注意事项
- 中文分词:中文关系抽取依赖高质量分词
- 实体识别:先做NER再做RE效果更好
- 长文本处理:需要分段或滑动窗口
- 关系重叠:一个句子可能存在多个关系
- 评估指标:准确率、召回率、F1分数
建议先从规则方法开始,根据效果逐步升级到深度学习方案。