📖 什么是共指消解
共指消解(Coreference Resolution)是识别文本中指向同一实体的不同表述,并将它们关联起来的技术。在"鲁迅创作了呐喊。他当时32岁。"中,"他"指代"鲁迅"——这就是共指。
🎯 共指的类型
| 类型 | 示例 | 说明 |
| 代词共指 | 鲁迅→他 | 代词指代实体 |
| 名称变体 | 周树人→鲁迅 | 同一实体的不同名称 |
| 别名/昵称 | 老舍→舒先生 | 别名、字号等 |
| 定指名词 | 鲁迅→那位作家 | 定指描述指代实体 |
| 同位语 | 鲁迅,那位伟大的作家 | 同位语修饰 |
💻 Python实现:共指消解系统
import re
from collections import defaultdict
class CoreferenceResolver:
"""共指消解系统"""
def __init__(self):
self.alias_db = defaultdict(set)
self.pronoun_map = {"他": "MASC", "她": "FEM", "它": "NEUT", "其": "ANY"}
self.entity_mentions = []
def add_aliases(self, canonical, aliases):
"""添加实体别名"""
for alias in aliases:
self.alias_db[canonical].add(alias)
self.alias_db[alias].add(canonical)
def detect_mentions(self, text, entities):
"""检测文本中的实体提及"""
mentions = []
for entity, etype, start, end in entities:
mentions.append({
"text": entity,
"type": etype,
"start": start,
"end": end,
"gender": None
})
for match in re.finditer(r'[他她它其]', text):
pronoun = match.group()
mentions.append({
"text": pronoun,
"type": "PRON",
"start": match.start(),
"end": match.end(),
"gender": self.pronoun_map.get(pronoun)
})
mentions.sort(key=lambda x: x["start"])
return mentions
def resolve(self, mentions, context_entities=None):
"""执行共指消解,返回共指链"""
clusters = []
entity_map = {}
for i, mention in enumerate(mentions):
if mention["type"] == "PRON":
resolved = False
for j in range(i - 1, -1, -1):
prev = mentions[j]
if prev["type"] != "PRON":
if mention["gender"] == "ANY" or prev.get("gender") is None:
if j in entity_map:
clusters[entity_map[j]].append(mention)
entity_map[i] = entity_map[j]
else:
clusters.append([prev, mention])
entity_map[j] = len(clusters) - 1
entity_map[i] = entity_map[j]
resolved = True
break
if not resolved:
clusters.append([mention])
entity_map[i] = len(clusters) - 1
else:
found = False
for ci, cluster in enumerate(clusters):
for cm in cluster:
if cm["type"] != "PRON":
if (mention["text"] in self.alias_db and
cm["text"] in self.alias_db[mention["text"]]):
cluster.append(mention)
entity_map[i] = ci
found = True
break
if found:
break
if not found:
clusters.append([mention])
entity_map[i] = len(clusters) - 1
return clusters
resolver = CoreferenceResolver()
resolver.add_aliases("鲁迅", ["周树人", "鲁迅先生"])
resolver.add_aliases("老舍", ["舒庆春", "舒先生"])
text1 = "鲁迅创作了呐喊。他当时32岁。周树人也是著名的翻译家。"
entities1 = [("鲁迅", "PER", 0, 2), ("呐喊", "WORK", 5, 7), ("周树人", "PER", 18, 21)]
mentions1 = resolver.detect_mentions(text1, entities1)
print("=== 文本1提及检测 ===")
for m in mentions1:
print(f" [{m['type']}] {m['text']} ({m['start']}-{m['end']})")
clusters1 = resolver.resolve(mentions1)
print("
=== 共指链 ===")
for i, cluster in enumerate(clusters1):
names = [m["text"] for m in cluster]
print(f" 链{i+1}: {' = '.join(names)}")
=== 文本1提及检测 ===
[PER] 鲁迅 (0-2)
[WORK] 呐喊 (5-7)
[PRON] 他 (8-9)
[PER] 周树人 (18-21)
=== 共指链 ===
链1: 鲁迅 = 他
链2: 呐喊
链3: 周树人
📊 共指消解评估
def evaluate_coref(gold_clusters, pred_clusters):
"""MUC评估指标"""
def get_links(clusters):
links = set()
for cluster in clusters:
items = frozenset(m["text"] for m in cluster)
for a in items:
for b in items:
if a < b:
links.add((a, b))
return links
gold_links = get_links(gold_clusters)
pred_links = get_links(pred_clusters)
tp = len(gold_links & pred_links)
fp = len(pred_links - gold_links)
fn = len(gold_links - pred_links)
p = tp / (tp + fp) if tp + fp > 0 else 0
r = tp / (tp + fn) if tp + fn > 0 else 0
f1 = 2 * p * r / (p + r) if p + r > 0 else 0
return {"P": f"{p:.4f}", "R": f"{r:.4f}", "F1": f"{f1:.4f}"}
print("=== 共指消解评估 ===")
gold = [[{"text": "鲁迅"}, {"text": "他"}, {"text": "周树人"}]]
pred = [[{"text": "鲁迅"}, {"text": "他"}], [{"text": "周树人"}]]
print(evaluate_coref(gold, pred))
=== 共指消解评估 ===
{'P': '0.5000', 'R': '0.3333', 'F1': '0.4000'}
📝 实战练习
练习1:别名扩展
添加更多别名对(如字号、笔名),验证共指消解效果提升。
练习2:性别一致性
为实体添加性别信息,实现基于性别一致性的代词消解。
练习3:跨句共指
处理跨句子的共指问题:第一句提到"鲁迅",第三句的"他"仍能正确消解。
🔄
🏆 第9课成就解锁
共指消解工程师
🔄 共指检测
🔗 代词消解
📋 别名匹配
📊 MUC评估