2. 知识图谱嵌入基础:TransE 与 DistMult

在正式啃 ComplEx 之前,咱们得先把地基打牢。知识图谱嵌入(KGE)说白了,就是给图谱里的每个实体和关系找一个“数学位置”。你想想看,药物和蛋白质相互作用这种复杂网络,用文字描述太绕了,不如把它们都扔进一个向量空间里,用算数代替推理。

我最早接触 KGE 是在一个药物重定位项目里。当时数据量大得吓人,光用规则匹配根本跑不动。后来我就明白了:嵌入的本质,就是“用连续向量代替离散符号,用距离表示语义”。

2.1 核心思想:用向量“翻译”三元组

知识图谱里的每一条知识都是一个 三元组 (h, r, t):

  • h(头实体):比如“阿司匹林”
  • t(尾实体):比如“头痛”
  • r(关系):比如“治疗”

嵌入模型的任务,就是给每个 h、r、t 都分配一个低维实数向量(比如 128 维或 256 维),然后定义出一个打分函数 f(h, r, t)。
这个分数越高,说明这个三元组成立的可能性越大。说白了,就是让正确的三元组得分远高于错误的三元组。

我习惯把这个过程叫“结构化压缩”——几千几万个实体,压缩成几百维的向量,还要保留它们之间复杂的语义关系。嗯,这里要记住:得分的幅度本身不重要,重要的是正负样本之间的排序差异。

关键点:嵌入向量的质量,决定了后续所有推荐、预测任务的最终效果。如果向量糊成一团,后面再牛的模型也救不回来。

2.2 TransE:最直觉的翻译模型

TransE 是所有 KGE 模型里最简单的一个,也是我新手时期用的第一个模型。它的思路极度朴素:
h + r ≈ t。

意思是,头实体沿着关系的方向“走”一步,就应该到达尾实体。你想象一下在欧氏空间里:

  • 如果阿司匹林( h) 加上治疗( r),结果应该≈ 头痛( t)。
  • 如果加上之后离头痛很远,那这个三元组就很可疑。

它的打分函数长这样:

f(h, r, t) = -|| h + r - t ||²

对的,就是负的 L2 距离的平方。分数越高,三角形走得越“顺”。

损失函数与训练

TransE 用的是基于边际的排序损失(margin-based ranking loss),我直接贴一段简化实现吧,这是当年我在项目里手写的核心:

import torch

def transE_loss(pos_score, neg_score, margin=1.0):
    # 正样本分数高,负样本分数低
    # 我们希望 pos_score - neg_score 的差距 ≥ margin
    loss = torch.relu(neg_score - pos_score + margin)
    return loss.mean()

训练流程:

  1. 对每个正确三元组,随机替换头或尾,构造一批负样本。
  2. 算正负样本分数。
  3. 利用边际损失更新所有向量。
  4. 每轮训练后归一化实体向量,维持数值稳定。
个人小建议:负采样是 TransE 性能的关键。我试过“均匀采样”和“伯努利采样”,后者效果明显更好——伯努利采样会偏向替换那些“更可能产生假负例”的实体,训练噪声更小。

TransE 的软肋

说白了,TransE 只能处理 1-1 关系。遇到 N-1、1-N 或者对称关系,它就开始犯糊涂了。
我遇到过最典型的问题:“药物A治疗疾病B”和“药物A治疗疾病C”在 TransE 的框架下,B 和 C 的向量会被拉近,不管它们是不是同一个概念。这就是所谓的“自反关系”问题。

我曾经踩过的坑:在药物-靶点数据集上,如果用 TransE 建模“相互抑制”这种对称关系,模型会崩溃。因为它天然假设 h + r ≈ t,但对称关系下 h + r ≈ t 和 t + r ≈ h 会互相矛盾。如果你想保留 TransE,记得先做数据清洗,把对称关系拆成两个方向。

2.3 DistMult:用点积取代距离

DistMult 换了一个思路。它不再用向量加减法,而是用 点积和。打分函数变成:

f(h, r, t) = h^T · Diag(r) · t = Σ_i h_i * r_i * t_i

说白了,就是三个向量对应元素相乘再求和。这里 r 是一个对角矩阵,只在对角线上有值。

为什么这么做?因为点积天然适合衡量相似度——如果 h、r、t 的对应维度趋势一致,得分就高。这很符合药物关系中的一些场景,比如“多个靶点通过同一个机制被同一个药物激活”。

DistMult 的参数比 TransE 少了一截:关系只用 d 维的对角矩阵( d 就是向量长度),而不是满矩阵 d×d。训练速度更快,内存占用也更友好。

DistMult 的天生局限

然而,DistMult 有一个致命弱点:它只能处理对称关系。因为点积本身是对称的:h^T · Diag(r) · t = t^T · Diag(r) · h。

这就意味着,如果数据中存在方向性关系(比如“药物A靶向蛋白B”≠“蛋白B靶向药物A”),DistMult 就完全无能为力了。你的模型会把逆关系也学成高得分,导致预测严重走偏。

真实项目踩点:我在做“药物-副作用”预测时,因为副作用关系是方向性的——药物导致副作用,而非副作用导致药物。DistMult 在该任务上直接翻车,准确率比 TransE 还低 15%。你说气不气人?

2.4 TransE vs DistMult 核心对比

对比维度 TransE DistMult
关系建模方式 h + r ≈ t (向量平移) h^T · Diag(r) · t (点积)
参数数量 每个关系一个向量 (d维) 每个关系一个对角矩阵 (d个参数)
对称关系 ❌ 无法建模 ✅ 天然支持
方向性关系 ✅ 天然支持 ❌ 无法建模
计算复杂度 O(d) O(d)
适合场景 1-1关系、方向性强 对称关系、相似性建模

这两者就像左右手——各有特长,也各有软肋。那有没有一个模型能同时处理这两种关系?这就要引到 ComplEx 了。不过在那之前,我们先看看它们各自的知识图谱嵌入流程,这样你心里就有个全景图。

下面这张图展示了我自己在项目中总结的 KGE 通用流程,以及 TransE 与 DistMult 分别卡在哪一阶段:

知识图谱嵌入基础流程(TransE vs DistMult 对比) 输入三元组 (h, r, t) 随机初始化 实体/关系向量 TransE 计算 h + r - t → L2距离 方向关系 ✅ DistMult 计算 Σ h_i * r_i * t_i 对称关系 ✅ 损失计算(边际损失) relu(neg_score - pos_score + margin) 反向传播 & 参数更新 实体向量归一化每轮执行 📌 模型选路原则 • 方向性强的关系 → 选 TransE 例:药物A → 靶向 → 蛋白B • 对称/相似关系 → 选 DistMult 例:药物A → 相互作用 → 药物B ⚠️ 共同约束: - 嵌入维度 d 统一(一般 100~200) - 边际 margin 选 1.0~5.0 - 学习率不宜超过 0.01 - 负采样比例:1:10~1:50
作为过来人的建议:如果你想快速验证某个数据集是否适合 KGE,我建议你把 TransE 和 DistMult 都跑一遍,比较一下在验证集上的 Hits@10 和 MRR。如果 TransE 明显好,说明数据里的方向性关系更重要;如果 DistMult 好,说明对称关系占主导。这个对比结果,往往能帮你判断该不该上更复杂的模型(比如 ComplEx,它就是一种巧妙的“取长补短”)。

2.5 小结

我们把这一章的内容串一下:

  • 知识图谱嵌入的本质是把符号化的三元组映射到连续向量空间,用距离或相似度进行推理。
  • TransE 靠的是向量平移,适合方向性强的 1-1 关系,但在对称/多关系上表现不佳。
  • DistMult 靠的是三向点积,天然支持对称关系,但完全无法处理方向性。
  • 两者都是参数少、训练快的轻量级模型,适合作为基线模型。