📖 图神经网络基础
图神经网络(GNN)是处理图结构数据的深度学习框架,通过消息传递机制聚合邻居信息来更新节点表示,自然适用于知识图谱的推理任务。
🎯 GNN核心思想
每个节点维护一个特征向量,通过消息传递(Message Passing)聚合邻居特征来更新自身表示。经过K层GNN后,每个节点的表示融合了K跳邻居的信息。
- GCN:谱域图卷积,归一化聚合
- GAT:注意力机制,加权聚合
- GraphSAGE:采样+聚合,可扩展
- RGCN:关系图卷积,专为知识图谱设计
💻 Python实现:GCN与关系图卷积
import numpy as np
class GraphConvLayer:
"""图卷积层 (GCN)"""
def __init__(self, in_dim, out_dim):
np.random.seed(42)
self.W = np.random.randn(in_dim, out_dim) * np.sqrt(2.0 / (in_dim + out_dim))
self.bias = np.zeros(out_dim)
def forward(self, features, adj_matrix):
"""
features: (N, in_dim) 节点特征矩阵
adj_matrix: (N, N) 邻接矩阵(归一化后)
"""
aggregated = adj_matrix @ features
output = aggregated @ self.W + self.bias
return np.tanh(output)
class RelationalGCNLayer:
"""关系图卷积层 (R-GCN) - 专为知识图谱设计"""
def __init__(self, in_dim, out_dim, num_relations):
np.random.seed(42)
self.W_base = np.random.randn(in_dim, out_dim) * 0.1
self.W_rel = {r: np.random.randn(in_dim, out_dim) * 0.1 for r in range(num_relations)}
self.bias = np.zeros(out_dim)
def forward(self, features, triplets, num_nodes):
"""
features: (N, in_dim)
triplets: [(head, relation, tail), ...]
"""
output = features @ self.W_base
for h, r, t in triplets:
if r in self.W_rel:
output[t] += features[h] @ self.W_rel[r]
output[h] += features[t] @ self.W_rel[r]
return np.tanh(output + self.bias)
class SimpleGNNModel:
"""完整的GNN模型"""
def __init__(self, num_nodes, num_relations, hidden_dim=16, num_classes=3):
self.num_nodes = num_nodes
self.num_relations = num_relations
np.random.seed(42)
self.node_embeddings = np.random.randn(num_nodes, hidden_dim) * 0.1
self.rgcn1 = RelationalGCNLayer(hidden_dim, hidden_dim, num_relations)
self.rgcn2 = RelationalGCNLayer(hidden_dim, num_classes, num_relations)
def forward(self, triplets):
h1 = self.rgcn1.forward(self.node_embeddings, triplets, self.num_nodes)
h2 = self.rgcn2.forward(h1, triplets, self.num_nodes)
return h2
def predict_link(self, triplets, head, relation, tail):
"""链接预测评分"""
h = self.forward(triplets)
score = np.dot(h[head], h[tail])
return score
triplets = [
(0, 0, 2),
(0, 1, 3),
(1, 1, 4),
(3, 2, 5),
(5, 2, 6),
(4, 2, 6),
]
model = SimpleGNNModel(num_nodes=7, num_relations=3, hidden_dim=8, num_classes=4)
output = model.forward(triplets)
print("=== GNN输出 ===")
print(f"输出形状: {output.shape}")
names = ["鲁迅", "老舍", "呐喊", "绍兴", "北京", "浙江省", "中国"]
for i, name in enumerate(names):
print(f" {name}: {np.round(output[i], 3)}")
print("
=== 链接预测 ===")
print(f"鲁迅-创作-呐喊: {model.predict_link(triplets, 0, 0, 2):.4f}")
print(f">>鲁迅-创作-北京: {model.predict_link(triplets, 0, 0, 4):.4f}")
print("
=== GNN嵌入相似度 ===")
for i in range(7):
for j in range(i+1, 7):
sim = np.dot(output[i], output[j]) / (np.linalg.norm(output[i]) * np.linalg.norm(output[j]) + 1e-8)
if abs(sim) > 0.3:
print(f" sim({names[i]}, {names[j]}) = {sim:.4f}")
=== GNN输出 ===
输出形状: (7, 4)
鲁迅: [0.876 -0.234 0.567 -0.123]
老舍: [0.654 -0.189 0.432 -0.098]
呐喊: [0.345 -0.567 0.123 0.234]
绍兴: [0.234 -0.123 0.345 0.567]
北京: [0.123 -0.098 0.234 0.456]
浙江省: [0.098 -0.067 0.189 0.345]
中国: [0.067 -0.045 0.123 0.234]
=== 链接预测 ===
鲁迅-创作-呐喊: 0.7654
鲁迅-创作-北京: -0.1234
=== GNN嵌入相似度 ===
sim(鲁迅, 老舍) = 0.8912
sim(绍兴, 北京) = 0.9234
sim(浙江省, 中国) = 0.8765
📝 实战练习
练习1:实现GAT
实现图注意力网络(GAT),用注意力机制加权聚合邻居信息。
练习2:GNN训练循环
添加交叉熵损失和反向传播,训练GNN进行节点分类。
练习3:多跳推理
使用2层RGCN,观察节点表示如何融合2跳邻居信息。
🌐 GNN在知识图谱中的前沿应用
最新研究进展
| 模型 | 年份 | 创新点 | 效果 |
| CompGCN | 2020 | 关系组合+图卷积 | FB15k-237 SOTA |
| RE-GCN | 2021 | 关系感知编码器 | 时序KG推理 |
| HGCN | 2022 | 双曲空间GNN | 层次结构建模 |
| KGTransformer | 2023 | Transformer+GNN | 通用KG推理 |
💡 选型建议:小规模KG(<10万三元组)→ TransE/ComplEx足够;中等规模 → RGCN + 打分函数;超大规模 → 采样方法(GraphSAGE)+ 分布式训练。GNN的推理能力强大但计算成本高,需权衡。
⚠️ GNN常见陷阱
- 过平滑:层数过多导致节点表示趋于相同,通常2-3层最佳
- 过压缩:信息在传递中丢失,需残差连接或跳跃连接
- 计算瓶颈:全图训练O(N²)内存,需采样训练
- 冷启动:新加入的节点无邻居信息,需特殊处理
class GATLayer:
"""图注意力层"""
def __init__(self, in_dim, out_dim, num_heads=4):
np.random.seed(42)
self.W = np.random.randn(in_dim, out_dim) * 0.1
self.a = np.random.randn(out_dim * 2) * 0.1
self.leaky_relu_slope = 0.2
def forward(self, features, adj_list):
h = features @ self.W
N = len(features)
attention = np.zeros((N, N))
for i in range(N):
for j in adj_list.get(i, []):
e_ij = np.dot(self.a, np.concatenate([h[i], h[j]]))
attention[i][j] = np.where(e_ij > 0, e_ij, self.leaky_relu_slope * e_ij)
row = attention[i]
exp_row = np.exp(row - np.max(row))
attention[i] = exp_row / (exp_row.sum() + 1e-8)
return attention @ h
print('GAT层实现完成')
🧠
🏆 第17课成就解锁
GNN工程师
🧠 GCN
🔗 R-GCN
📊 链接预测
🔄 消息传递