📖 什么是实体链接
实体链接(Entity Linking)是将文本中的实体指称(Mention)映射到知识库中唯一实体的过程。核心挑战是消歧——同一个名字可能指不同的实体(多义性),不同的名字可能指同一实体(多样性)。
🎯 核心问题
- 消歧:"李白"是唐代诗人还是现代导演?
- nil实体:文本中提到的实体不在知识库中
- 嵌套实体:"北京大学信息学院"中的实体层次
💻 Python实现:实体链接系统
import re
from collections import defaultdict
from typing import List, Dict, Tuple, Optional
class KnowledgeBase:
">>>简易知识库(模拟)"""
def __init__(self):
self.entities = {}
self.name_index = defaultdict(list)
def add_entity(self, entity_id, name, etype, aliases=None, desc="", facts=None):
self.entities[entity_id] = {
"name": name, "type": etype,
"aliases": aliases or [], "desc": desc,
"facts": facts or {}
}
self.name_index[name].append(entity_id)
for alias in (aliases or []):
self.name_index[alias].append(entity_id)
def search(self, mention):
"""根据指称搜索候选实体"""
return self.name_index.get(mention, [])
def get_entity(self, entity_id):
return self.entities.get(entity_id)
class EntityLinker:
"""实体链接系统"""
def __init__(self, kb):
self.kb = kb
self.popularity = {}
self.type_weights = {"PER": 1.0, "LOC": 0.8, "ORG": 0.9, "WORK": 0.7}
def set_popularity(self, entity_id, score):
self.popularity[entity_id] = score
def _context_similarity(self, mention_context, entity_desc):
"""计算上下文与实体描述的相似度(词重叠)"""
ctx_words = set(mention_context)
desc_words = set(entity_desc)
if not ctx_words or not desc_words:
return 0.0
overlap = ctx_words & desc_words
return len(overlap) / len(ctx_words | desc_words)
def link(self, mention, mention_type=None, context=""):
"""将指称链接到知识库实体"""
candidates = self.kb.search(mention)
if not candidates:
return {"mention": mention, "entity": None, "status": "NIL", "confidence": 0.0}
if len(candidates) == 1:
return {"mention": mention, "entity": candidates[0], "status": "UNAMBIG", "confidence": 1.0}
scores = {}
for eid in candidates:
entity = self.kb.get_entity(eid)
score = 0.0
score += self.popularity.get(eid, 0.5) * 0.3
if mention_type and entity["type"] == mention_type:
score += 0.3
if context:
sim = self._context_similarity(context, entity["desc"])
score += sim * 0.4
scores[eid] = score
best_eid = max(scores, key=scores.get)
best_score = scores[best_eid]
return {
"mention": mention,
"entity": best_eid,
"status": "DISAMBIG",
"confidence": best_score,
"candidates": len(candidates)
}
def batch_link(self, mentions_with_context):
"""批量链接"""
results = []
for mention, mtype, ctx in mentions_with_context:
result = self.link(mention, mtype, ctx)
results.append(result)
return results
kb = KnowledgeBase()
kb.add_entity("libai_poet", "李白", "PER",
aliases=["李太白", "诗仙", "青莲居士"],
desc="唐代伟大的浪漫主义诗人 被誉为诗仙 与杜甫并称李杜",
facts={"朝代": "唐", "风格": "浪漫主义"})
kb.add_entity("libai_director", "李白", "PER",
aliases=[],
desc="现代导演 电影制作人",
facts={"职业": "导演"})
kb.add_entity("pku_university", "北京大学", "ORG",
aliases=["北大", "燕园"],
desc="中国顶尖综合性大学 位于北京海淀区 创建于1898年")
kb.add_entity("pku_press", "北京大学出版社", "ORG",
aliases=["北大出版社"],
desc="北京大学下属出版社 学术出版")
kb.add_entity("luxun", "鲁迅", "PER",
aliases=["周树人", "鲁迅先生"],
desc="中国现代文学的奠基人 作家 文学革命")
kb.add_entity("beijing_city", "北京", "LOC",
desc="中国首都 政治中心 文化中心")
linker = EntityLinker(kb)
linker.set_popularity("libai_poet", 0.95)
linker.set_popularity("libai_director", 0.15)
linker.set_popularity("pku_university", 0.9)
test_cases = [
("李白", "PER", "唐代诗人杜甫与李白并称李杜"),
("李白", "PER", "导演李白的新片即将上映"),
("北大", "ORG", "他考上了北大 位于海淀区"),
("鲁迅", "PER", "文学革命中的鲁迅"),
("王阳明", "PER", "心学大师"),
]
print("=== 实体链接消歧 ==="
for mention, mtype, ctx in test_cases:
result = linker.link(mention, mtype, ctx)
entity = kb.get_entity(result["entity"]) if result["entity"] else None
if entity:
print(f" {mention} → {entity['name']}({result['entity']}) [{result['status']}] conf={result['confidence']:.2f}")
else:
print(f" {mention} → NIL (不在知识库中)")
=== 实体链接消歧 ===
李白 → 李白(libai_poet) [DISAMBIG] conf=0.79
李白 → 李白(libai_director) [DISAMBIG] conf=0.31
北大 → 北京大学(pku_university) [DISAMBIG] conf=0.97
鲁迅 → 鲁迅(luxun) [UNAMBIG] conf=1.00
王阳明 → NIL (不在知识库中)
📝 实战练习
练习1:扩充知识库
添加更多同名实体(如两个"华盛顿":城市vs人物),测试消歧效果。
练习2:实现局部协同消歧
同一文档中多个实体的链接应该相互一致(如提到"唐代诗人"时,所有"李白"都应链接到诗人)。
练习3:NIL实体处理
实现NIL实体聚类:将不在KB中的新实体根据上下文相似度聚类。
🎯
🏆 第10课成就解锁
实体链接工程师
🎯 候选生成
⚖️ 消歧打分
📊 上下文相似度
🔗 批量链接