📚 第21课:百科知识图谱

实战项目1——从维基百科构建知识图谱

📖 项目概述

本课将综合运用前20课所学,从维基百科条目中构建一个完整的百科知识图谱。涵盖数据采集、信息抽取、知识存储和查询的全流程。

🎯 项目目标

💻 Python实现:百科知识图谱构建流水线

import re import json from collections import defaultdict class WikiKnowledgeGraphBuilder: ">>>百科知识图谱构建器""" def __init__(self, domain_name): self.domain = domain_name self.entities = {} # {name: {type, attrs}} self.triples = set() # {(h, r, t)} self.texts = [] # 原始文本 def ingest_text(self, title, text): """导入百科条目文本""" self.texts.append({"title": title, "text": text}) # 自动创建实体 self.entities[title] = {"type": "条目", "attrs": {}} def extract_infobox(self, text): """从百科信息框提取结构化数据""" infobox = {} # 模拟信息框解析 patterns = { "出生日期": r'出生[日期]*[::]\s*(\d{4}年\d{1,2}月\d{1,2}日)', "出生地": r'出生地[::]\s*([一-鿿]{2,8})', "国籍": r'国籍[::]\s*([一-鿿]{2,4})', ">职业": r'职业[::]\s*([一-鿿]{2,6})', ">毕业院校": r'毕业[于院校]*[::]\s*([一-鿿]{2,8})', } for attr, pattern in patterns.items(): match = re.search(pattern, text) if match: infobox[attr] = match.group(1) return infobox def extract_relations(self, text, title): """从文本中提取关系三元组""" triples = [] # 基于模板的关系抽取 rel_patterns = [ (r'([一-鿿]{2,4})出生于([一-鿿]{2,6})', "出生地"), (r'([一-鿿]{2,4})创作了([一-鿿《》]{2,10})', "创作"), (r'([一-鿿]{2,4})毕业于([一-鿿]{2,8})', ">>毕业院校"), (r'([一-鿿]{2,6})位于([一-鿿]{2,6})', ">>位于"), (r'([一-鿿]{2,4})与([一-鿿]{2,4})是好友', ">>好友"), ] for pattern, rel_type in rel_patterns: for match in re.finditer(pattern, text): subj, obj = match.group(1), match.group(2) triples.append((subj, rel_type, obj)) # 自动注册实体 if subj not in self.entities: self.entities[subj] = {"type": "未知", ">>attrs": {}} if obj not in self.entities: self.entities[obj] = {"type": ">>未知", ">>attrs": {}} return triples def build(self): """执行完整的构建流程""" print(f"🏗️ 开始构建【{self.domain}】知识图谱...") print(f" 导入条目数: {len(self.texts)}") # 步骤1: 信息框提取 for entry in self.texts: infobox = self.extract_infobox(entry["text"]) if infobox: self.entities[entry["title"]]["attrs"].update(infobox) # 步骤2: 关系抽取 for entry in self.texts: new_triples = self.extract_relations(entry["text"], entry["title"]) for t in new_triples: self.triples.add(t) print(f" 实体数: {len(self.entities)}") print(f" 三元组数: {len(self.triples)}") def query_entity(self, name): """查询实体信息""" entity = self.entities.get(name) if not entity: return None related = [(h, r, t) for h, r, t in self.triples if h == name or t == name] return {"entity": entity, "related_triples": related} def export_json(self): """导出为JSON""" return json.dumps({ "domain": self.domain, "entities": self.entities, "triples": list(self.triples) }, ensure_ascii=False, indent=2) # ========== 构建中国现代文学百科知识图谱 ========== builder = WikiKnowledgeGraphBuilder("中国现代文学") # 导入百科条目(模拟) builder.ingest_text("鲁迅", """ 鲁迅,原名周树人,1881年9月25日出生于浙江省绍兴。 职业:作家、思想家。毕业于南京矿路学堂。 鲁迅创作了呐喊,鲁迅创作了彷徨。鲁迅与许寿裳是好友。 """) builder.ingest_text("老舍", """ 老舍,原名舒庆春,1899年2月3日出生于北京。 职业:作家、剧作家。毕业于北京师范大学。 老舍创作了骆驼祥子,老舍创作了茶馆。 """) builder.ingest_text("徐志摩", """ 徐志摩,1897年1月15日出生于浙江省海宁。 职业:诗人。毕业于北京大学。 徐志摩创作了再别康桥。 """) # 构建 builder.build() # 查询 print(" === 查询:鲁迅 ===") result = builder.query_entity("鲁迅") if result: print(f" 类型: {result['entity']['type']}") print(f" 属性: {result['entity']['attrs']}") print(" 关系:") for h, r, t in result["related_triples"]: if h == "鲁迅": print(f" → {r} → {t}") else: print(f" ← {r} ← {h}") # 导出 print(f" === 导出JSON(前300字符) ===") print(builder.export_json()[:300])
🏗️ 开始构建【中国现代文学】知识图谱... 导入条目数: 3 实体数: 10 三元组数: 7 === 查询:鲁迅 === 类型: 条目 属性: {'出生日期': '1881年9月25日', '出生地': '浙江省绍兴', '职业': '作家、思想家', '毕业院校': '南京矿路学堂'} 关系: → 出生地 → 浙江省绍兴 → 创作 → 呐喊 → 创作 → 彷徨 → 毕业院校 → 南京矿路学堂 → 好友 → 许寿裳 === 导出JSON(前300字符) === { "domain": "中国现代文学", "entities": { "鲁迅": {"type": "条目", "attrs": {"出生日期": "1881年9月25日", ...}} }, "triples": [...] }

📝 实战练习

练习1:扩展到更多领域

选择历史、地理或科学领域,构建该领域的百科知识图谱。

练习2:实现Web界面

用Flask/FastAPI构建一个简单的Web界面,支持实体搜索和图谱可视化。

练习3:评估知识图谱质量

人工标注50个三元组,计算抽取的P/R/F1。

🌐 百科KG的工程实践

大规模构建的最佳实践

💡 规模参考:中文维基百科约120万条目,DBpedia从中提取了约500万实体和2亿三元组。Wikidata则超过1亿实体。构建大规模百科KG通常需要集群环境(10+节点)和数周时间。
📚

🏆 第21课成就解锁

百科KG构建者

📚 数据采集
🔧 信息抽取
💾 图谱存储
🔍 实体查询