🧮 第4课:知识表示学习

从符号到向量——TransE/TransH嵌入模型与链接预测

📖 什么是知识表示学习

知识表示学习(Knowledge Representation Learning)是将知识图谱中的实体和关系映射到低维连续向量空间的技术,使得语义相近的实体在向量空间中距离接近,从而支持相似度计算、链接预测和知识推理等下游任务。

🎯 为什么需要表示学习

📐 TransE:翻译模型的开山之作

TransE(Translating Embeddings)是最经典的知识图谱嵌入模型,其核心思想:

如果三元组 (h, r, t) 成立,则 h + r ≈ t

即头实体向量加上关系向量应接近尾实体向量,如同一种"翻译"操作。

TransE训练过程

  1. 随机初始化所有实体和关系的嵌入向量
  2. 对每个正样本三元组 (h, r, t),生成负样本 (h', r, t) 或 (h, r, t')
  3. 最小化正样本距离 d(h+r, t),最大化负样本距离
  4. 使用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 # 初始化嵌入向量(Xavier初始化) 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) # Margin损失 loss = max(0, self.margin + d_pos - d_neg) total_loss += loss # 梯度更新(简化版SGD) 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||关系空间投影,更灵活
DistMulthᵀdiag(r)t双线性模型,简单但无法区分方向
ComplExRe(hᵀdiag(r)t̄)复数空间,处理非对称关系
RotatE||h∘r-t||旋转建模,处理多种关系模式
ConvE⟨σ(conv([h;r]))',t⟩卷积网络,捕捉交互特征
# TransH模型实现 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] # 测试TransH 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