📖 什么是知识图谱
知识图谱(Knowledge Graph)是一种以图结构组织的知识表示方式,它将现实世界中的实体(Entity)和实体之间的关系(Relation)表示为网络结构,使得机器能够像人类一样"理解"世界的关联性。
🎯 核心定义
知识图谱本质上是一个有向图,其中:
- 节点(Node):代表实体,如人、地点、组织、概念等
- 边(Edge):代表实体间的关系,如"出生于"、"隶属于"、"属于"
- 三元组(Triple):知识图谱的基本单元,形如 (头实体, 关系, 尾实体)
例如:(鲁迅, 出生地, 绍兴)、(北京大学, 位于, 北京)
📅 知识图谱的发展历程
1956 专家系统→
1980 语义网络→
1998 语义Web→
2006 DBpedia→
2012 Google KG→
2020+ 多模态KG
关键里程碑
| 时间 | 事件 | 意义 |
| 1956 | Logic Theorist / 专家系统 | 符号AI的起源,用规则表示知识 |
| 1968 | Quillian 语义网络 | 首次用图结构表示语义关系 |
| 1998 | Berners-Lee 语义Web | 提出Web数据的机器可理解性 |
| 2006 | DBpedia 发布 | 从Wikipedia提取结构化知识 |
| 2012 | Google Knowledge Graph | 工业级知识图谱的标志性产品 |
| 2018 | Wikidata / 百度知心 | 大规模开放知识图谱成熟 |
| 2022+ | 大模型 + 知识图谱 | LLM与KG融合的新范式 |
🏗️ 知识图谱的体系架构
一个完整的知识图谱系统通常包含以下层次:
📐 应用层:智能问答、推荐、搜索增强
↑
🧠 推理层:规则推理、图神经网络、知识补全
↑
💾 存储层:图数据库、RDF存储、索引
↑
🔧 构建层:实体识别、关系抽取、事件抽取
↑
📊 数据源:结构化数据、半结构化数据、非结构化文本
🔬 知识图谱 vs 传统数据库
| 维度 | 关系数据库 | 知识图谱 |
| 数据模型 | 表结构(行/列) | 图结构(节点/边) |
| Schema | 固定Schema优先 | Schema-less或Schema-optional |
| 关联查询 | JOIN操作,多层关联代价高 | 图遍历,天然支持多层关联 |
| 语义表达 | 缺乏语义 | 丰富的语义关系 |
| 推理能力 | 无内置推理 | 支持规则推理和归纳推理 |
| 适用场景 | 事务处理、报表 | 知识发现、智能问答 |
💻 Python实现:构建你的第一个知识图谱
让我们用纯Python实现一个简单的知识图谱,理解其核心数据结构和基本操作。
class KnowledgeGraph:
"""简单知识图谱实现 - 基于三元组存储"""
def __init__(self, name="MyKG"):
self.name = name
self.triples = []
self.entities = set()
self.relations = set()
self.entity_attrs = {}
self.outgoing = {}
self.incoming = {}
def add_triple(self, head, relation, tail):
"""添加三元组 (head, relation, tail)"""
triple = (head, relation, tail)
if triple in self.triples:
return False
self.triples.append(triple)
self.entities.add(head)
self.entities.add(tail)
self.relations.add(relation)
self.outgoing.setdefault(head, set()).add((relation, tail))
self.incoming.setdefault(tail, set()).add((relation, head))
return True
def add_entity_attr(self, entity, attr, value):
"""为实体添加属性"""
self.entities.add(entity)
self.entity_attrs.setdefault(entity, {})[attr] = value
def query_by_entity(self, entity):
"""查询与某实体相关的所有三元组"""
results = []
for h, r, t in self.triples:
if h == entity or t == entity:
results.append((h, r, t))
return results
def query_by_relation(self, relation):
"""查询包含特定关系的所有三元组"""
return [(h, r, t) for h, r, t in self.triples if r == relation]
def get_neighbors(self, entity):
"""获取实体的所有邻居(一步可达)"""
neighbors = set()
for r, t in self.outgoing.get(entity, set()):
neighbors.add(t)
for r, h in self.incoming.get(entity, set()):
neighbors.add(h)
return neighbors
def shortest_path(self, start, end):
"""BFS求两实体间的最短路径(无向)"""
from collections import deque
if start == end:
return [start]
visited = {start}
queue = deque([(start, [start])])
while queue:
current, path = queue.popleft()
for neighbor in self.get_neighbors(current):
if neighbor not in visited:
new_path = path + [neighbor]
if neighbor == end:
return new_path
visited.add(neighbor)
queue.append((neighbor, new_path))
return None
def stats(self):
"""返回图谱统计信息"""
return {
"名称": self.name,
"实体数": len(self.entities),
"关系数": len(self.relations),
"三元组数": len(self.triples),
"平均度": round(sum(len(v) for v in self.outgoing.values()) / max(len(self.entities), 1), 2)
}
def __repr__(self):
s = self.stats()
return f"KnowledgeGraph('{self.name}': {s['实体数']}实体, {s['三元组数']}三元组)"
kg = KnowledgeGraph("中国文学")
kg.add_triple("鲁迅", "出生地", "绍兴")
kg.add_triple("鲁迅", "创作", "呐喊")
kg.add_triple("鲁迅", "创作", "彷徨")
kg.add_triple("鲁迅", "任职", "北京大学")
kg.add_triple("鲁迅", "好友", "许寿裳")
kg.add_triple("许寿裳", "出生地", "绍兴")
kg.add_triple("北京大学", "位于", "北京")
kg.add_triple("北京大学", "创建于", "1898年")
kg.add_triple("老舍", "出生地", "北京")
kg.add_triple("老舍", "创作", "骆驼祥子")
kg.add_triple("老舍", "创作", "茶馆")
kg.add_triple("老舍", "任职", "北京大学")
kg.add_triple("绍兴", "属于", "浙江省")
kg.add_triple("浙江省", "属于", "中国")
kg.add_triple("北京", "属于", "中国")
kg.add_entity_attr("鲁迅", "生卒", "1881-1936")
kg.add_entity_attr("鲁迅", "原名", "周树人")
kg.add_entity_attr("老舍", "生卒", "1899-1966")
kg.add_entity_attr("老舍", "原名", "舒庆春")
kg.add_entity_attr("呐喊", "出版年", "1923")
print("=== 知识图谱统计 ===")
print(kg)
for k, v in kg.stats().items():
print(f" {k}: {v}")
print("\n=== 鲁迅相关三元组 ===")
for h, r, t in kg.query_by_entity("鲁迅"):
print(f" ({h}, {r}, {t})")
print("\n=== '创作' 关系 ===")
for h, r, t in kg.query_by_relation("创作"):
print(f" {h} → {t}")
print("\n=== 路径查询 ===")
path = kg.shortest_path("鲁迅", "中国")
print(f"鲁迅 → 中国: {' → '.join(path) if path else '不可达'}")
path2 = kg.shortest_path("老舍", "呐喊")
print(f"老舍 → 呐喊: {' → '.join(path2) if path2 else '不可达'}")
print("\n=== 北京大学的邻居 ===")
print(kg.get_neighbors("北京大学"))
print("\n=== 鲁迅属性 ===")
print(kg.entity_attrs.get("鲁迅", {}))
=== 知识图谱统计 ===
KnowledgeGraph('中国文学': 11实体, 15三元组)
名称: 中国文学
实体数: 11
关系数: 6
三元组数: 15
平均度: 1.36
=== 鲁迅相关三元组 ===
(鲁迅, 出生地, 绍兴)
(鲁迅, 创作, 呐喊)
(鲁迅, 创作, 彷徨)
(鲁迅, 任职, 北京大学)
(鲁迅, 好友, 许寿裳)
=== '创作' 关系 ===
鲁迅 → 呐喊
鲁迅 → 彷徨
老舍 → 骆驼祥子
老舍 → 茶馆
=== 路径查询 ===
鲁迅 → 中国: 鲁迅 → 绍兴 → 浙江省 → 中国
老舍 → 呐喊: 老舍 → 北京大学 → 鲁迅 → 呐喊
=== 北京大学的邻居 ===
{'鲁迅', '老舍', '北京', '1898年'}
=== 鲁迅属性 ===
{'生卒': '1881-1936', '原名': '周树人'}
🌐 知识图谱可视化
让我们用NetworkX实现一个简单的可视化功能,观察知识图谱的图结构特征。
import networkx as nx
import json
G = nx.DiGraph()
for h, r, t in kg.triples:
G.add_edge(h, t, relation=r)
print("=== 图结构指标 ===")
print(f"节点数: {G.number_of_nodes()}")
print(f"边数: {G.number_of_edges()}")
print(f"密度: {nx.density(G):.4f}")
print(f"是否有向: {G.is_directed()}")
degree_centrality = nx.degree_centrality(G)
print("\n=== 度中心性 Top5 ===")
top5 = sorted(degree_centrality.items(), key=lambda x: -x[1])[:5]
for node, cent in top5:
print(f" {node}: {cent:.4f}")
if nx.is_weakly_connected(G):
print("\n图是弱连通的(所有节点可达)")
else:
components = list(nx.weakly_connected_components(G))
print(f"\n弱连通分量数: {len(components)}")
graph_data = {
"nodes": [{"id": n, "label": n} for n in G.nodes()],
"edges": [{"source": u, "target": v, "label": d["relation"]}
for u, v, d in G.edges(data=True)]
}
print(f"\n导出JSON: {len(graph_data['nodes'])}节点, {len(graph_data['edges'])}边")
print("样例边:", json.dumps(graph_data["edges"][0], ensure_ascii=False))
=== 图结构指标 ===
节点数: 11
边数: 15
密度: 0.1364
是否有向: True
=== 度中心性 Top5 ===
鲁迅: 0.5000
北京大学: 0.3000
老舍: 0.3000
绍兴: 0.2000
北京: 0.2000
图是弱连通的(所有节点可达)
导出JSON: 11节点, 15边
样例边: {"source": "鲁迅", "target": "绍兴", "label": "出生地"}
🌍 知识图谱的主要类型
按覆盖范围分类
| 类型 | 描述 | 典型代表 |
| 通用知识图谱 | 覆盖多领域常识性知识 | Google KG、Wikidata、DBpedia |
| 行业知识图谱 | 面向特定领域的专业知识 | 医疗KG、金融KG、法律KG |
| 企业知识图谱 | 企业内部知识管理 | 微软Academic、阿里商品KG |
按构建方式分类
- 自顶向下:先定义本体Schema,再填充数据。适合领域明确、标准严格的场景(医疗、法律)
- 自底向上:先从数据中提取知识,再归纳Schema。适合开放域、数据驱动的场景(搜索引擎)
- 混合方式:结合两者优势,先有粗粒度Schema引导,再动态扩展
💡 知识图谱的核心价值
1. 搜索增强
Google知识图谱让搜索从"关键词匹配"进化到"语义理解"——搜索"爱因斯坦"不仅返回网页,还直接展示他的生平、成就、相关人物。
2. 智能问答
基于知识图谱的问答系统(KBQA)能直接返回精准答案,而非文档列表。"姚明的妻子是谁?"→ 直接返回"叶莉"。
3. 推荐系统
利用实体间的语义关系做更精准的推荐。用户喜欢"刘慈欣",推荐同属"硬科幻"的"阿瑟·克拉克"。
4. 风控与反欺诈
金融领域利用实体关系网络发现异常关联,识别团伙欺诈。
5. 辅助决策
医疗KG帮助医生关联症状-疾病-药物,提供诊疗辅助。
⚙️ 知识图谱构建流水线
class KGPipeline:
"""知识图谱构建流水线模拟"""
def __init__(self):
self.stages = []
def add_stage(self, name, description, input_type, output_type):
self.stages.append({
"name": name,
"desc": description,
"input": input_type,
"output": output_type
})
def run(self, raw_data):
"""模拟执行流水线"""
print("🚀 知识图谱构建流水线启动\n")
data = raw_data
for i, stage in enumerate(self.stages, 1):
print(f"📌 阶段{i}: {stage['name']}")
print(f" 说明: {stage['desc']}")
print(f" 输入: {stage['input']} → 输出: {stage['output']}")
print()
print("✅ 流水线完成!原始数据 → 结构化知识图谱")
pipeline = KGPipeline()
pipeline.add_stage("数据采集", "从多源获取原始数据", "多源数据", "原始语料")
pipeline.add_stage("实体识别", "识别文本中的命名实体", "原始语料", "实体列表")
pipeline.add_stage("关系抽取", "提取实体间语义关系", "实体+语料", "三元组")
pipeline.add_stage("实体链接", ">将实体指称映射到知识库", "实体列表", "消歧实体")
pipeline.add_stage("知识融合", ">合并多源知识,解决冲突", "多源三元组", ">融合三元组")
pipeline.add_stage("知识存储", ">持久化存储到图数据库", ">融合三元组", ">图数据库")
pipeline.add_stage(">知识推理", ">基于已有知识推导新知识", ">图数据库", ">扩充图谱")
pipeline.run("多源原始数据")
🚀 知识图谱构建流水线启动
📌 阶段1: 数据采集
说明: 从多源获取原始数据
输入: 多源数据 → 输出: 原始语料
📌 阶段2: 实体识别
说明: 识别文本中的命名实体
输入: 原始语料 → 输出: 实体列表
📌 阶段3: 关系抽取
说明: 提取实体间语义关系
输入: 实体+语料 → 输出: 三元组
📌 阶段4: 实体链接
说明: 将实体指称映射到知识库
输入: 实体列表 → 输出: 消歧实体
📌 阶段5: 知识融合
说明: 合并多源知识,解决冲突
输入: 多源三元组 → 输出: 融合三元组
📌 阶段6: 知识存储
说明: 持久化存储到图数据库
输入: 融合三元组 → 输出: 图数据库
📌 阶段7: 知识推理
说明: 基于已有知识推导新知识
输入: 图数据库 → 输出: 扩充图谱
✅ 流水线完成!原始数据 → 结构化知识图谱
📊 知识图谱的规模与挑战
| 知识图谱 | 实体数 | 三元组数 | 领域 |
| Google KG | 5B+ | 500B+ | 通用 |
| Wikidata | 100M+ | 1.5B+ | 通用 |
| DBpedia | 5M+ | 250M+ | 通用 |
| YAGO | 10M+ | 120M+ | 通用 |
| ConceptNet | 8M+ | 21M+ | 常识 |
| CN-DBpedia | 9M+ | 67M+ | 中文通用 |
⚠️ 核心挑战
- 知识覆盖率:长尾实体和关系难以覆盖
- 知识质量:抽取噪声、冲突、不一致
- 时效性:知识动态变化,图谱需持续更新
- 多语言:跨语言知识对齐困难
- 多模态:文本、图像、视频知识的统一表示
📝 实战练习
练习1:扩展知识图谱
在上面代码基础上,添加以下知识并验证:
- 添加"巴金"相关实体和关系(出生地、作品等)
- 添加"浙江省"→"中国"的关系后,验证鲁迅→中国的路径
- 计算添加后图谱的度中心性,找出最核心的实体
练习2:实现反向查询
实现 query_inverse(head, relation) 方法:给定头实体和关系,返回所有尾实体。例如 query_inverse("鲁迅", "创作") 应返回 ["呐喊", "彷徨"]。
练习3:多跳推理
利用已有三元组,实现一个简单的推理规则:如果 (A, 出生地, B) 且 (B, 属于, C),则可以推断 (A, 国籍, C)。验证:鲁迅→绍兴→浙江省→中国,推断出鲁迅国籍为中国。
💡 学习建议:知识图谱的核心思维是万物皆可关联。尝试把日常生活中的知识用三元组表示——你会发现世界比想象中更紧密相连。
🗺️
🏆 第1课成就解锁
知识图谱入门者
你已经理解了知识图谱的基本概念、发展历程、体系架构
掌握了三元组存储、图查询、路径搜索等核心操作
🗺️ 概念理解
💻 三元组实现
🔍 图查询
🛤️ 路径搜索