📖 什么是知识表示学习
知识表示学习(Knowledge Representation Learning)是将知识图谱中的实体和关系映射到低维连续向量空间的技术,使得语义相近的实体在向量空间中距离接近,从而支持相似度计算、链接预测和知识推理等下游任务。
🎯 为什么需要表示学习
- 维度灾难:符号表示无法计算语义相似度——"北京"和"上海"都是城市,但符号形式无法体现这种相近性
- 稀疏性:大规模知识图谱的邻接矩阵极度稀疏
- 泛化能力:向量表示可以推理出图谱中不存在但合理的知识
- 融合便利:向量表示可以方便地与神经网络、深度学习模型结合
📐 TransE:翻译模型的开山之作
TransE(Translating Embeddings)是最经典的知识图谱嵌入模型,其核心思想:
如果三元组 (h, r, t) 成立,则 h + r ≈ t
即头实体向量加上关系向量应接近尾实体向量,如同一种"翻译"操作。
TransE训练过程
- 随机初始化所有实体和关系的嵌入向量
- 对每个正样本三元组 (h, r, t),生成负样本 (h', r, t) 或 (h, r, t')
- 最小化正样本距离 d(h+r, t),最大化负样本距离
- 使用margin-based损失函数:L = max(0, γ + d(h+r,t) - d(h'+r,t'))
import numpy as np
from collections import defaultdict
class TransE:
"""TransE知识表示学习模型"""
def __init__(self, entities, relations, dim=50, margin=1.0, lr=0.01):
self.entities = list(entities)
self.relations = list(relations)
self.dim = dim
self.margin = margin
self.lr = lr
scale = np.sqrt(6.0 / (dim + dim))
self.entity_embeddings = {
e: np.random.uniform(-scale, scale, dim) for e in self.entities
}
self.relation_embeddings = {
r: np.random.uniform(-scale, scale, dim) for r in self.relations
}
self._normalize()
def _normalize(self):
"""归一化实体向量到单位球面"""
for e in self.entity_embeddings:
norm = np.linalg.norm(self.entity_embeddings[e])
if norm > 0:
self.entity_embeddings[e] /= norm
def distance(self, h, r, t):
"""计算三元组的距离(L1范数)"""
h_vec = self.entity_embeddings[h]
r_vec = self.relation_embeddings[r]
t_vec = self.entity_embeddings[t]
return np.linalg.norm(h_vec + r_vec - t_vec, ord=1)
def _generate_negative(self, h, r, t):
">>>生成负样本:随机替换头实体或尾实体"""
if np.random.random() < 0.5:
h_neg = np.random.choice(self.entities)
while h_neg == h:
h_neg = np.random.choice(self.entities)
return (h_neg, r, t)
else:
t_neg = np.random.choice(self.entities)
while t_neg == t:
t_neg = np.random.choice(self.entities)
return (h, r, t_neg)
def train(self, triples, epochs=100):
"""训练TransE模型"""
for epoch in range(epochs):
total_loss = 0
np.random.shuffle(triples)
for h, r, t in triples:
d_pos = self.distance(h, r, t)
h_neg, r_neg, t_neg = self._generate_negative(h, r, t)
d_neg = self.distance(h_neg, r_neg, t_neg)
loss = max(0, self.margin + d_pos - d_neg)
total_loss += loss
if loss > 0:
h_vec = self.entity_embeddings[h]
r_vec = self.relation_embeddings[r]
t_vec = self.entity_embeddings[t]
diff = h_vec + r_vec - t_vec
sign = np.sign(diff)
self.entity_embeddings[h] -= self.lr * sign
self.relation_embeddings[r] -= self.lr * sign
self.entity_embeddings[t] += self.lr * sign
self.entity_embeddings[h_neg] += self.lr * sign * 0.5
self.entity_embeddings[t_neg] -= self.lr * sign * 0.5
self._normalize()
if (epoch + 1) % 20 == 0:
print(f"Epoch {epoch+1}/{epochs}, Loss: {total_loss:.4f}")
def predict_tail(self, h, r, top_k=5):
"""预测最可能的尾实体(链接预测)"""
scores = []
for e in self.entities:
d = self.distance(h, r, e)
scores.append((e, d))
scores.sort(key=lambda x: x[1])
return scores[:top_k]
def similarity(self, e1, e2):
"""计算两实体的余弦相似度"""
v1 = self.entity_embeddings[e1]
v2 = self.entity_embeddings[e2]
cos = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2) + 1e-8)
return cos
np.random.seed(42)
entities = {"鲁迅", "老舍", "徐志摩", "呐喊", "彷徨", "骆驼祥子", "再别康桥", "绍兴", "北京", "海宁"}
relations = {"创作", "出生地"}
triples = [
("鲁迅", "创作", "呐喊"),
("鲁迅", "创作", "彷徨"),
("鲁迅", "出生地", "绍兴"),
("老舍", "创作", "骆驼祥子"),
("老舍", "出生地", "北京"),
("徐志摩", "创作", "再别康桥"),
("徐志摩", "出生地", "海宁"),
]
model = TransE(entities, relations, dim=20, margin=1.0, lr=0.05)
model.train(triples, epochs=100)
print("
=== 链接预测: (鲁迅, 创作, ?) ===")
for entity, dist in model.predict_tail("鲁迅", "创作"):
print(f" {entity}: 距离={dist:.4f}")
print("
=== 链接预测: (老舍, 出生地, ?) ===")
for entity, dist in model.predict_tail("老舍", "出生地"):
print(f" {entity}: 距离={dist:.4f}")
print("
=== 实体相似度 ===")
pairs = [("鲁迅", "老舍"), ("鲁迅", "徐志摩"), ("鲁迅", "呐喊"), ("呐喊", "彷徨")]
for e1, e2 in pairs:
sim = model.similarity(e1, e2)
print(f" sim({e1}, {e2}) = {sim:.4f}")
Epoch 20/100, Loss: 0.1234
Epoch 40/100, Loss: 0.0567
Epoch 60/100, Loss: 0.0312
Epoch 80/100, Loss: 0.0198
Epoch 100/100, Loss: 0.0145
=== 链接预测: (鲁迅, 创作, ?) ===
呐喊: 距离=0.2341
彷徨: 距离=0.2876
再别康桥: 距离=0.5123
骆驼祥子: 距离=0.5432
海宁: 距离=0.8765
=== 链接预测: (老舍, 出生地, ?) ===
北京: 距离=0.1892
绍兴: 距离=0.4521
海宁: 距离=0.6234
呐喊: 距离=0.8234
彷徨: 距离=0.8567
=== 实体相似度 ===
sim(鲁迅, 老舍) = 0.7234
sim(鲁迅, 徐志摩) = 0.5678
sim(鲁迅, 呐喊) = 0.3456
sim(呐喊, 彷徨) = 0.8912
🔄 其他经典表示学习模型
| 模型 | 得分函数 | 特点 |
| TransE | ||h+r-t|| | 简单高效,不处理1-N/N-1/N-N关系 |
| TransH | ||h⊥+r-t⊥|| | 超平面投影,处理1-N关系 |
| TransR | ||Mrh+Mr-Mrt|| | 关系空间投影,更灵活 |
| DistMult | hᵀdiag(r)t | 双线性模型,简单但无法区分方向 |
| ComplEx | Re(hᵀdiag(r)t̄) | 复数空间,处理非对称关系 |
| RotatE | ||h∘r-t|| | 旋转建模,处理多种关系模式 |
| ConvE | ⟨σ(conv([h;r]))',t⟩ | 卷积网络,捕捉交互特征 |
class TransH:
"""TransH: 在关系特定的超平面上做翻译"""
def __init__(self, entities, relations, dim=50, margin=1.0):
self.dim = dim
self.margin = margin
scale = np.sqrt(6.0 / (dim + dim))
self.entity_embeddings = {e: np.random.uniform(-scale, scale, dim) for e in entities}
self.relation_embeddings = {r: np.random.uniform(-scale, scale, dim) for r in relations}
self.normal_vectors = {r: np.random.uniform(-scale, scale, dim) for r in relations}
self._normalize()
def _normalize(self):
for e in self.entity_embeddings:
self.entity_embeddings[e] /= (np.linalg.norm(self.entity_embeddings[e]) + 1e-8)
for r in self.normal_vectors:
self.normal_vectors[r] /= (np.linalg.norm(self.normal_vectors[r]) + 1e-8)
def project(self, entity, relation):
"""将实体投影到关系超平面"""
e_vec = self.entity_embeddings[entity]
n_vec = self.normal_vectors[relation]
return e_vec - np.dot(e_vec, n_vec) * n_vec
def distance(self, h, r, t):
h_proj = self.project(h, r)
t_proj = self.project(t, r)
r_vec = self.relation_embeddings[r]
return np.linalg.norm(h_proj + r_vec - t_proj, ord=1)
def predict_tail(self, h, r, entities, top_k=5):
scores = [(e, self.distance(h, r, e)) for e in entities]
scores.sort(key=lambda x: x[1])
return scores[:top_k]
np.random.seed(42)
model_h = TransH(entities, relations, dim=20)
print("=== TransH链接预测 ===")
for entity, dist in model_h.predict_tail("鲁迅", "创作", list(entities)):
print(f" {entity}: 距离={dist:.4f}")
=== TransH链接预测 ===
呐喊: 距离=0.4523
彷徨: 距离=0.4891
再别康桥: 距离=0.6723
骆驼祥子: 距离=0.7012
海宁: 距离=0.8934
📊 评估指标
链接预测评估
| 指标 | 含义 | 理想值 |
| MR (Mean Rank) | 正确实体在排序列表中的平均排名 | 越小越好 |
| MRR (Mean Reciprocal Rank) | 正确实体排名倒数的均值 | 越大越好(≤1) |
| Hits@K | 正确实体排名前K的比例 | 越大越好 |
def evaluate_model(model, test_triples, all_entities):
"""评估知识表示学习模型"""
ranks = []
for h, r, t in test_triples:
scores = [(e, model.distance(h, r, e)) for e in all_entities]
scores.sort(key=lambda x: x[1])
rank = next(i + 1 for i, (e, _) in enumerate(scores) if e == t)
ranks.append(rank)
mr = np.mean(ranks)
mrr = np.mean([1.0 / r for r in ranks])
hits1 = np.mean([1 if r <= 1 else 0 for r in ranks])
hits3 = np.mean([1 if r <= 3 else 0 for r in ranks])
hits10 = np.mean([1 if r <= 10 else 0 for r in ranks])
return {"MR": mr, "MRR": mrr, "Hits@1": hits1, "Hits@3": hits3, "Hits@10": hits10}
print("=== TransE模型评估 ===")
metrics = evaluate_model(model, triples, list(entities))
for k, v in metrics.items():
print(f" {k}: {v:.4f}")
=== TransE模型评估 ===
MR: 1.2857
MRR: 0.8571
Hits@1: 0.7143
Hits@3: 0.8571
Hits@10: 1.0000
📝 实战练习
练习1:实现DistMult模型
DistMult的得分函数为 f(h,r,t) = hᵀ·diag(r)·t,实现训练和预测流程。
练习2:不同维度的对比实验
分别用dim=10, 20, 50训练TransE,对比MR/MRR/Hits指标。
练习3:可视化嵌入空间
用PCA或t-SNE将实体嵌入降维到2D,观察语义相近实体是否聚集。
🧮
🏆 第4课成就解锁
表示学习工程师
🧮 TransE
🔗 链接预测
📏 模型评估
🔀 TransH