知识图谱表示学习:从TransE到TransD与Hit@10评价指标详解
1. 知识图谱表示学习到底在解决什么问题第一次接触知识图谱表示学习的人往往会被一堆名词砸晕TransE、TransH、TransR、TransD、Hit10、Mean Rank……每个词看起来都认识拼在一起就不知道在说什么了。我刚开始做知识图谱相关项目的时候也是这样论文翻了好几篇代码跑通了但让我说清楚“为什么Hit10能衡量模型好坏”我讲不出来。后来踩了不少坑才慢慢把这些概念串起来。先把最核心的问题说清楚知识图谱本质上是一张巨大的图图里有两类东西——实体节点和关系边。比如“某公司—成立于—某年”“某人物—毕业于—某学校”这些三元组头实体、关系、尾实体堆在一起就构成了知识图谱。但问题是图结构的数据没法直接喂给机器学习模型做计算你得把它变成向量。表示学习要干的事就一件把实体和关系映射到低维向量空间里同时保留它们之间的语义关系。听起来简单做起来难。因为知识图谱有个特点——它天然是不完整的。你手里有一个三元组A关系B模型要学会判断这个三元组成立的可能性有多大进而预测那些缺失的边。这就引出了两个核心问题第一怎么设计打分函数来衡量一个三元组的合理性第二怎么评价一个模型学出来的向量到底好不好前者对应TransE、TransR这些模型的设计思路后者对应Hit10、Mean Rank这些评价指标。两者是配套的不懂评价指标你就没法判断模型改进到底有没有效果。这篇文章适合谁看如果你正在做知识图谱相关的项目需要选模型、调参数、对比效果那这些内容你绕不开。如果你只是刚入门想搞清楚这些术语到底在说什么我也会尽量用生活化的例子把原理讲透。下面我会从模型设计的底层逻辑讲起再重点拆解评价指标的计算方式和实际使用中的坑。2. 从TransE到TransD翻译模型家族的演进逻辑2.1 TransE最朴素的翻译假设TransE是整个翻译模型家族的起点它的核心思想可以用一句话概括头实体向量加上关系向量应该约等于尾实体向量。用公式写就是h r ≈ t。这个假设非常直观。你可以把它想象成在向量空间里做“平移”从h出发沿着r的方向走一步就能到达t。比如“北京—是首都—中国”如果“北京”的向量加上“是首都”的向量结果应该落在“中国”的向量附近。TransE的打分函数通常用L1或L2距离f(h, r, t) ||h r - t||距离越小说明这个三元组越合理。训练的时候模型会尽量让真实三元组的距离小让随机替换头实体或尾实体构造的负样本距离大。TransE的优点很明显参数少、计算快、在大规模稀疏图谱上表现稳定。但它有个致命缺陷——处理不了一对多、多对一、多对多的关系。举个例子“某导演—执导—电影A”和“某导演—执导—电影B”。按照TransE的逻辑电影A和电影B的向量应该都等于“某导演执导”那这两个电影的向量就会几乎一样。但现实中它们显然是不同的电影。这就是TransE的局限性它假设关系和实体在同一个空间里且每个实体只有一个向量表示没法区分同一个实体在不同关系下的不同角色。2.2 TransH让实体在不同关系下有不同投影TransH的改进思路很直接既然一个实体在不同关系下应该有不同的表现那我就让实体先投影到关系所在的超平面上再在超平面上做翻译。具体来说每个关系r对应一个法向量w_r实体h和t先投影到w_r定义的超平面上得到h⊥和t⊥然后要求h⊥ r ≈ t⊥。这样做的好处是同一个实体在不同关系下投影到不同的超平面上得到的向量表示就不一样了。还是拿导演举例“某导演”在“执导”这个关系下投影一次在“出生于”这个关系下投影一次两次的投影结果不同就能区分开了。但TransH也有问题它假设实体和关系虽然在同一个空间但关系通过超平面来区分。实际上实体和关系的语义空间可能完全不同强行放在一个空间里可能不够灵活。2.3 TransR把实体和关系放到不同空间TransR的思路更进一步实体和关系应该在不同的向量空间里。每个关系r对应一个投影矩阵M_r实体向量通过M_r投影到关系空间然后在这个空间里做翻译。公式变成h_r hM_rt_r tM_r要求 h_r r ≈ t_r。TransR的表达能力比TransH更强因为它允许关系空间和实体空间有不同的维度投影矩阵也能学到更复杂的变换。但代价是参数数量大幅增加——每个关系都要学一个矩阵如果关系数量多参数量会爆炸。而且矩阵运算比向量运算慢很多在大规模图谱上训练时间会明显拉长。2.4 TransD动态投影降低参数量TransD的出发点是解决TransR参数量过大的问题。它的做法是不用完整的投影矩阵而是用两个向量相乘来构造动态投影矩阵。具体来说每个实体和关系都对应一个投影向量。对于头实体h用h和关系r的投影向量构造一个矩阵M_hr然后做投影。这样既保留了TransR“实体和关系在不同空间”的优点又把参数量降下来了。我实测下来的感受是TransD在中等规模的知识图谱上性价比很高效果比TransE和TransH好训练速度又比TransR快不少。但如果你的图谱关系类型特别多TransD的参数量还是会上去需要根据实际情况权衡。2.5 模型选型的实际考量模型核心思想参数量训练速度适用场景TransEh r ≈ t最少最快大规模稀疏图谱关系类型简单TransH超平面投影较少较快需要区分一对多/多对一关系TransR关系空间投影最多最慢关系语义复杂计算资源充足TransD动态投影矩阵中等中等综合性价比优先选模型的时候不能只看论文里的指标得结合你的实际数据特点。我一般会先用TransE跑一个baseline看看Hit10和Mean Rank大概在什么水平然后再试TransH或TransD对比提升幅度。如果提升不明显说明你的图谱可能本身就不需要那么复杂的模型用TransE就够了。3. 评价指标Hit10和Mean Rank到底怎么算3.1 从链接预测任务说起要理解Hit10和Mean Rank得先知道它们评价的是什么任务。知识图谱表示学习最常用的评价任务是链接预测给定一个三元组h, r, t把尾实体t遮住让模型从所有候选实体中预测正确的t应该是谁。具体操作是这样的对于测试集里的每个三元组h, r, t我们把t替换成图谱里的每一个实体构造出一堆候选三元组然后用模型给每个候选打分按分数从高到低排序。如果正确实体t的排名越靠前说明模型越好。这里有个细节替换的时候要过滤掉那些在训练集、验证集、测试集里已经存在的真实三元组。比如h, r, t如果本身就是一个真实存在的事实那它排在前面对模型来说不算错。这个过滤操作叫“Filtered Setting”是标准做法。3.2 Mean Rank平均排名Mean Rank就是所有测试三元组中正确实体排名的平均值。计算方式很简单对每个测试三元组计算正确实体的排名rank_i把所有rank_i加起来除以测试集大小Mean Rank (rank_1 rank_2 ... rank_n) / n这个指标越小越好。如果模型完美每个正确实体的排名都是1Mean Rank就是1。但Mean Rank有个问题它对极端值很敏感。如果有一两个三元组的排名特别靠后比如排到了几千名就会把平均值拉得很高。所以看Mean Rank的时候最好同时看一下排名的分布或者用中位数辅助判断。3.3 Hit10命中率Hit10的意思是正确实体排在前10名的比例。计算方式是Hit10 (排名≤10的测试三元组数量) / 测试集总数这个指标越大越好。Hit10比Mean Rank更直观因为它直接告诉你“有多少比例的情况下模型能把正确答案放进前10个候选里”。实际应用中Hit10往往比Mean Rank更重要。因为很多场景下你只需要模型给出一个候选列表然后人工或者下游系统再筛选。只要正确答案在前10里后续处理就有机会纠正。但如果正确答案排到了几百名开外那基本就没救了。3.4 一个具体的计算示例假设测试集有5个三元组模型给出的正确实体排名分别是1、3、8、15、50。Mean Rank (1381550) / 5 77 / 5 15.4Hit10 3 / 5 0.6因为1、3、8这三个排名≤10Hit3 2 / 5 0.41、3这两个排名≤3Hit1 1 / 5 0.2只有排名1≤1从这个例子可以看出虽然Mean Rank是15.4看起来不算太差但有一个三元组排到了50名拉高了平均值。而Hit10是0.6说明60%的情况下模型能把正确答案放进前10这个信息更实用。3.5 两个指标的配合使用我一般会同时看Hit10、Hit3、Hit1和Mean Rank。如果Hit10很高但Mean Rank也很高说明模型大部分时候能排得靠前但偶尔会犯大错。如果Hit10和Mean Rank都很好那模型就比较稳。另外不同数据集上这些指标的绝对值差异很大。WN18和FB15k这两个常用数据集上好的模型Hit10能到0.8以上Mean Rank能降到100以内。但在更稀疏的数据集上Hit10可能只有0.3到0.5。所以对比的时候一定要在同一数据集、同一划分方式下比不能跨数据集比绝对值。4. 实操中怎么跑出可靠的评价结果4.1 数据划分的坑做链接预测评价第一步是把知识图谱划分成训练集、验证集、测试集。常见的划分比例是8:1:1或者9:0.5:0.5。但这里有个容易忽略的问题划分必须是随机的而且要保证验证集和测试集里的实体在训练集里出现过。如果测试集里出现了训练集里完全没见过的实体那模型根本没法预测指标会非常难看。这种情况叫“冷启动”问题需要单独处理不能混在常规评价里。我踩过的坑是有一次做数据划分的时候按时间顺序切分结果测试集里有很多新出现的实体Hit10直接掉到0.1以下。后来改成随机划分并且过滤掉那些实体不在训练集里的测试样本指标才恢复正常。4.2 负采样策略的影响训练TransE这类模型的时候需要构造负样本。常见做法是随机替换头实体或尾实体。但随机替换有个问题替换后的三元组可能碰巧也是真实的只是没出现在你的数据集里。这种“假阴性”样本会干扰训练。改进方法是采用“伯努利负采样”根据关系的头尾实体分布来决定替换头还是替换尾。比如对于一对多关系更多地替换头实体对于多对一关系更多地替换尾实体。这样构造的负样本质量更高。实测下来伯努利负采样能让Hit10提升2到5个百分点尤其是在关系分布不均衡的数据集上效果更明显。4.3 超参数调优的经验TransE系列模型的超参数主要有这几个向量维度常见50、100、200。维度越高表达能力越强但参数量也越大容易过拟合。我一般从100开始试如果数据集很大可以上200。学习率常用0.001到0.01。太大容易震荡太小收敛慢。用Adam优化器的话0.001是个不错的起点。边际参数γ控制正负样本距离的间隔常用1.0到4.0。这个参数对结果影响很大需要仔细调。负采样数量每个正样本配多少个负样本常用1到10。数量越多训练越充分但计算量也越大。调参的时候不要一次性调所有参数先固定其他参数单独调一个观察Hit10的变化。我一般会先调学习率和维度这两个影响最大然后再微调边际参数。4.4 评价时的过滤操作前面提到过Filtered Setting这里再强调一下具体操作。对于测试三元组h, r, t当我们把t替换成其他实体t时如果h, r, t在训练集、验证集或测试集中存在那这个候选就不应该被算作错误。具体做法是在排序的时候把这些已知真实三元组的分数设为一个很小的值比如负无穷让它们排到最后或者直接从候选列表里去掉。不做过滤的话指标会被严重低估。我见过有人忘了做过滤Hit10只有0.2加上过滤之后直接到0.6。所以这一步绝对不能省。4.5 代码实现的关键片段用PyTorch实现TransE的训练和评价核心逻辑大概是这样import torch import torch.nn as nn class TransE(nn.Module): def __init__(self, num_entities, num_relations, dim100): super().__init__() self.entity_emb nn.Embedding(num_entities, dim) self.relation_emb nn.Embedding(num_relations, dim) # 初始化实体和关系向量归一化 nn.init.uniform_(self.entity_emb.weight, -6/dim**0.5, 6/dim**0.5) nn.init.uniform_(self.relation_emb.weight, -6/dim**0.5, 6/dim**0.5) def forward(self, h, r, t): h_vec self.entity_emb(h) r_vec self.relation_emb(r) t_vec self.entity_emb(t) # L2距离作为打分 score torch.norm(h_vec r_vec - t_vec, p2, dim1) return score评价部分的逻辑def evaluate(model, test_triples, all_entities, filter_set): ranks [] for h, r, t in test_triples: # 构造候选把t替换成所有实体 candidates [] for e in all_entities: if (h, r, e) in filter_set: continue # 过滤已知真实三元组 score model(h, r, e) candidates.append((score, e)) # 按分数升序排列距离越小越好 candidates.sort(keylambda x: x[0]) # 找到正确实体t的排名 for rank, (score, e) in enumerate(candidates, start1): if e t: ranks.append(rank) break mean_rank sum(ranks) / len(ranks) hit_10 sum(1 for r in ranks if r 10) / len(ranks) return mean_rank, hit_10这段代码里有个细节过滤的时候要检查h, r, e是否在filter_set里这个filter_set包含训练集、验证集和测试集的所有三元组。如果不做这个检查指标会偏低。5. 常见问题与排查技巧实录5.1 Hit10很高但Mean Rank也很高怎么办这种情况说明模型大部分时候能把正确答案排进前10但偶尔会排到很后面。可能的原因有两个一是数据里有噪声某些三元组本身就不合理二是模型对某些关系类型学得不好。排查方法把排名最靠后的那些测试三元组挑出来看看它们涉及哪些关系。如果集中在某几种关系上说明模型对这些关系的建模能力不足可以考虑换TransR或TransD试试。如果是分散的那可能是数据噪声需要清洗数据。5.2 训练损失下降但评价指标不提升这是典型的过拟合。模型在训练集上把每个三元组都记得很牢但泛化能力差。解决办法增大负采样数量、降低向量维度、加Dropout、或者提前停止训练。我一般会监控验证集上的Hit10如果连续几轮不提升就停止训练。不要只看训练损失那个指标会骗人。5.3 不同模型对比时指标波动大如果同一个模型跑两次Hit10差了3个点以上说明随机性太大。可能的原因初始化方式不同、负采样随机种子不同、数据划分不同。解决办法固定随机种子多跑几次取平均值。我一般会跑3到5次报告平均值和标准差。如果标准差超过2个点说明模型不稳定需要检查数据划分是否合理。5.4 评价速度太慢怎么优化链接预测评价需要对每个测试三元组遍历所有实体如果实体数量是几万甚至几十万评价一次要很久。优化方法把实体向量矩阵预先算好用矩阵运算批量计算分数而不是逐个循环。具体做法把所有实体向量拼成一个矩阵E对于h, r, ?计算hr得到查询向量q然后计算q和E中每个向量的距离用GPU并行加速。这样能把评价时间从几小时缩短到几分钟。5.5 常见问题速查表问题现象可能原因排查方法解决方案Hit10异常低忘记做Filtered Setting检查评价代码是否有过滤逻辑加上过滤操作Mean Rank波动大随机种子不固定多次运行看方差固定种子多跑取平均训练损失不下降学习率太小或太大尝试不同学习率用Adam从0.001开始调某些关系预测效果差关系类型复杂分析各关系的Hit10换TransR或TransD评价速度慢逐个计算分数检查是否用了批量运算用矩阵运算GPU加速5.6 几个容易被忽略的细节第一实体和关系的ID映射要一致。训练和评价的时候同一个实体必须对应同一个ID否则结果完全乱套。我一般会在数据预处理阶段就把ID映射固定下来存成文件后续都从这个文件读。第二归一化操作要统一。TransE通常会对实体向量做L2归一化但关系向量不一定。如果训练时归一化了评价时也要做同样的操作否则距离计算会不一致。第三测试集的负样本构造要和训练集一致。如果训练时用的是伯努利负采样评价时也应该用同样的策略构造候选否则指标不可比。第四不要用测试集调参。有些人为了刷高Hit10直接在测试集上调超参数这是严重的过拟合。正确的做法是在验证集上调参测试集只用来做最终报告。6. 从指标反推模型改进方向6.1 Hit1和Hit10的差距说明什么如果Hit1很低但Hit10很高说明模型能把正确答案排进前10但很难排到第一。这种情况通常是因为模型对细粒度语义的区分能力不够。比如“某人物—出生于—某城市”和“某人物—居住于—某城市”这两个关系的向量可能学得很接近导致模型分不清。改进方向增加关系的区分度比如用TransR给每个关系单独的空间或者在损失函数里加一个关系分类的辅助任务。6.2 Mean Rank的分布分析不要只看Mean Rank的平均值要看排名的分布。我一般会画一个排名的累积分布图看看有多少比例的测试样本排名在10以内、100以内、1000以内。如果分布很集中说明模型表现稳定如果长尾很长说明模型在某些样本上完全失效。对于长尾部分可以单独分析这些样本的特征。常见原因是实体出现频率太低模型没学好。解决办法对低频实体做特殊处理比如共享参数或者用外部知识增强。6.3 不同关系类型的指标拆解把测试集按关系类型分组分别计算每组的Hit10和Mean Rank。这样能看出模型在哪些关系上表现好哪些关系上表现差。我做过一个实验在一对一关系上TransE的Hit10能到0.9但在一对多关系上直接掉到0.4。换成TransH之后一对多关系的Hit10提升到0.7但一对一关系略有下降。这说明不同模型有各自的擅长领域选型时要根据你的数据特点来定。6.4 实际项目中的指标取舍在实际项目中Hit10和Mean Rank不是唯一的标准。你还要考虑推理速度TransE最快TransR最慢如果线上服务要求低延迟TransE可能是更好的选择。内存占用TransR的投影矩阵很占内存如果部署环境资源有限要慎重。可解释性TransE的翻译假设很直观容易向非技术人员解释TransR的投影矩阵就很难说清楚。我的一般原则是先用TransE快速验证可行性如果指标满足业务需求就直接用如果不满足再逐步尝试更复杂的模型同时监控推理速度和资源消耗。7. 写在最后的一些个人体会做知识图谱表示学习这几年我最大的感受是指标只是手段不是目的。Hit10从0.6提升到0.65在论文里可能是一个显著的改进但在实际业务中这5个百分点能不能带来用户价值的提升需要打一个问号。我见过太多团队花大量时间调模型、刷指标但最后上线效果一般。原因往往不是模型不够好而是评价指标和业务目标脱节。比如你的业务场景是推荐系统用户只关心前3个推荐结果那你应该重点优化Hit3而不是Hit10。如果你的场景是知识问答那Mean Rank可能比Hit10更重要因为你需要模型给出一个可靠的排序。另外数据质量比模型选择重要得多。一个干净、完整、关系定义清晰的知识图谱用TransE就能跑出不错的效果。而一个充满噪声、关系混乱的图谱用再复杂的模型也救不回来。所以在调模型之前先花时间清洗数据、规范关系定义往往能事半功倍。最后分享一个小技巧如果你不确定该选哪个模型可以先用小规模数据跑一遍所有候选模型画出Hit10和训练时间的散点图。通常你会发现TransE在左下角快但效果一般TransR在右上角慢但效果好TransD在中间。根据你的时间和资源预算选一个性价比最高的就行不必追求SOTA。