理解评估

本教程的这一部分旨在帮助您理解知识图谱嵌入的评估。 特别是解释了在pykeen.evaluation.RankBasedMetricResults中报告的基于排名的评估指标。

知识图谱嵌入通常通过链接预测任务进行评估。为此,提供了一个评估三元组集合 \(\mathcal{T}_{eval} \subset \mathcal{E} \times \mathcal{R} \times \mathcal{E}\),并且对于集合中的每个三元组 \((h, r, t) \in \mathcal{T}_{eval}\),需要解决两个任务:

  • 右侧右侧预测任务中,给定一对头实体和关系,目标是预测尾实体,即\((h, r, ?)\)。为此,使用知识图谱嵌入模型对每个可能的选择\((h, r, e)\)进行评分,其中\(e \in \mathcal{E}\)。分数越高表示可能性越大。

  • 左侧 类似地,在左侧预测任务中,提供了一对关系和尾实体,并旨在预测头实体,即 \((?, r, t)\)。同样,每个可能的选择 \((e, r, t)\) 对于 \(e \in \mathcal{E}\) 根据知识图嵌入模型进行评分。

注意

实际上,许多嵌入模型允许快速计算所有\((e, r, t)\)的分数,而不仅仅是通过模型的评分函数传递三元组。例如,考虑使用评分函数\(score(h,r,t)=\sum_{i=1}^d \mathbf{h}_i \cdot \mathbf{r}_i \cdot \mathbf{t}_i\)的DistMult。在这里,所有实体都可以通过首先计算尾部和关系的元素乘积,然后与所有实体嵌入的矩阵进行矩阵乘法来评分作为给定尾部和关系的候选头部。 # TODO: 链接到解释此概念的部分。

在基于排名的评估协议中,分数用于按分数递减的顺序对可能的选择列表进行排序,并确定真实选择的排名,即在排序列表中的索引。较小的排名表示更好的性能。基于这些针对每个评估三元组和预测的每一侧(左/右)获得的个体排名,存在几种聚合度量来量化模型在单个数字中的性能。

注意

根据索引是基于0-based还是1-based(自然),存在理论上的影响。PyKEEN使用1-based索引以符合相关工作。

例如,考虑我们在国家数据集上训练了一个KGEM,例如,使用

from pykeen.datasets import get_dataset
from pykeen.pipeline import pipeline
dataset = get_dataset(dataset="countries")
result = pipeline(dataset=dataset, model="mure", random_seed=42, training_kwargs=dict(num_epochs=100))

在评估期间,我们现在评估头部和尾部预测,即我们是否可以从三元组的其余部分预测出正确的头部/尾部实体。该数据集测试分割中的第一个三元组是['belgium', 'locatedin', 'europe']。因此,对于尾部预测,我们的目标是回答['belgium', 'locatedin', ?]。我们可以使用预测工作流程查看结果:

from pykeen.predict import predict_target

df = predict_target(
    model=result.model,
    head="belgium",
    relation="locatedin",
    triples_factory=result.training,
)

它返回一个按预测分数排序的所有尾部候选实体的数据框。 在这个排序列表中的索引本质上是正确答案的排名。 以下是该表前5行的样子:

tail_id

分数

tail_label

265

-1.02144

西欧

77

-1.7295

欧洲

69

-2.21642

东欧

216

-2.32269

东南亚

173

-2.39417

北欧

基于排名的指标

给定评估三元组中每个头/尾实体的个体排名分数集合,有多种聚合指标可以将排名集合的不同方面总结为一个单一数字。有关更多详细信息,请参阅其文档

排名类型

虽然上述将排名定义为“排序列表中的索引”是直观的,但它并未指定当存在多个得分完全相同的选择时会发生什么。因此,在之前的工作中,已经实现了不同的变体,这些变体在存在相同得分的情况下会产生不同的结果。

  • 乐观排名假设真实选择在所有得分相同的位置中位于第一位。

  • 悲观排名假设真实选择在所有得分相同的选项中处于最后位置。

  • 实际排名是乐观排名和悲观排名的平均值,并且是所有遵循排序顺序的排列的期望值。

  • 非确定性排名将决策委托给排序算法。因此,结果取决于排序算法实现的内部平局打破机制。

PyKEEN 支持前三种:乐观、悲观和现实。当仅使用单一分数时,应报告现实分数。 悲观和乐观的排名,或者更具体地说,两者之间的偏差,可以用来检测模型是否为许多选择预测了完全相同的分数。有几个原因,例如:

  • 有限精度算术与显式使用sigmoid激活函数相结合

  • 分数的截断,例如通过使用ReLU激活函数或类似方法。

排名偏向性

除了不同的排名定义外,PyKEEN 还报告了各个侧面预测的分数。

侧面

解释

头部

仅针对头部/左侧预测评估的基于排名的指标。

尾部

仅针对尾部/右侧预测评估的基于排名的指标。

两者

基于排名的指标在两种预测上进行了评估。

默认情况下,“both”常用于出版物中。然而,特定侧面的分数通常可以提供有趣的见解,例如预测给定其余部分的头/尾的难度差异,或模型无法解决其中一个任务的情况。

排名聚合范围

真实图通常是无标度的,即存在少数节点/实体具有高度,通常称为中心节点,而大多数节点只有少数邻居。这也影响了评估三元组:由于中心节点出现在大量三元组中,它们也更有可能成为评估三元组的一部分。因此,在包含中心实体的三元组上表现良好对整体性能有重要贡献。

例如,我们可以检查pykeen.datasets.WD50KT数据集,其中单个(关系,尾实体)组合, (“instance of”, “human”), 在699个评估三元组中出现。

from pykeen.datasets import get_dataset
ds = get_dataset(dataset="wd50kt")
unique_relation_tail, counts = dataset.testing.mapped_triples[:, 1:].unique(return_counts=True, dim=0)
# c = 699
c = counts.max()
r, t = unique_relation_tail[counts.argmax()]
# https://www.wikidata.org/wiki/Q5 -> "human"
t = dataset.testing.entity_id_to_label[t.item()]
# https://www.wikidata.org/wiki/Property:P31 -> "instance of"
r = dataset.testing.relation_id_to_label[r.item()]

有人认为我们希望这些实体对评估有强烈的影响:因为它们经常出现,它们似乎很重要,因此评估应该反映这一点。然而,有时我们也不希望有这种影响,而是希望均匀地衡量各个节点的性能。类似的现象也存在于类别不平衡的多类分类中,其中频繁的类别可能会主导性能指标。类似于该领域中的宏观\(F_1\)-分数(参见sklearn.metrics.f1_score()),PyKEEN实现了一个pykeen.evaluation.MacroRankBasedEvaluator,它确保三元组被加权,使得每个唯一的排名任务,例如用于尾部预测的(头实体,关系)对,都能均匀地贡献。

从技术上讲,我们通过实现现有基于排名的指标的变体来解决任务,这些指标支持对个别排名进行不同的加权。此外,评估器计算与排名任务的“查询”部分成反比的权重,例如,对于尾部预测的(头,关系)。

过滤

基于排名的评估允许使用“过滤设置”,该设置由[bordes2013]提出,默认情况下是启用的。 在评估三元组\((h, r, t)\)的尾部预测时,即对所有三元组\((h, r, e)\)进行评分时, 可能存在其他已知的三元组\((h, r, t')\),其中\(t \neq t'\)。如果模型为\((h, r, t')\)预测了更高的分数, 排名将会增加,因此测量的模型性能将会下降。然而,为\((h, r, t')\)赋予高分(从而低排名)是可取的,因为它也是一个真实的三元组。因此, 过滤评估设置会忽略给定三元组\((h, r, t)\)的所有其他已知真实三元组\((h, r, t')\)的分数。

下面,我们展示了来自[bordes2013]的理念以及它在PyKEEN中的实现方式:

HPO 场景

在使用pykeen.hpo.hpo_pipeline()进行训练/优化时,已知的正三元组集合包括训练集和验证集。优化完成后进行最终评估时,已知的正三元组集合包括训练集、验证集和测试集。PyKEEN明确表示在HPO期间不使用测试三元组进行过滤,以避免任何测试泄漏。

早停场景

在训练过程中使用早停时,它会定期使用验证集来计算损失和评估指标。在此评估过程中,已知的正三元组集合包括训练集和验证集。当使用测试集进行最终评估时,已知的正三元组集合包括训练集、验证集和测试集。PyKEEN明确地不使用测试三元组进行过滤,以避免任何测试泄漏。

管道场景

在使用没有优化、没有提前停止、也没有使用验证集进行任何事后选择的pykeen.pipeline.pipeline()进行普通训练时,已知的正三元组包括训练集和测试集。这种情况非常不典型,无论如何,应该增加验证三元组,以使其与其他不考虑这种情况的已发表结果更具可比性。

自定义训练循环

如果在评估测试数据集时应过滤验证三元组,可以将参数 filter_validation_when_testing=False传递给pykeen.hpo.hpo_pipeline()pykeen.pipeline.pipeline()

如果你正在构建自己的管道,你应该记住以下几点:在过滤设置中,当filtered=True时,pykeen.evaluation.Evaluator将始终使用评估集(无论它是测试集还是验证集)进行过滤。任何其他应该被过滤的三元组应该传递给pykeen.evaluation.Evaluator.evaluate()中的additional_filter_triples。通常,这至少包括训练三元组。在使用[bordes2013]技术时,测试集用于评估,additional_filter_triples应该包括训练三元组和验证三元组,如下例所示:

from pykeen.datasets import FB15k237
from pykeen.evaluation import RankBasedEvaluator
from pykeen.models import TransE

# Get FB15k-237 dataset
dataset = FB15k237()

# Define model
model = TransE(
    triples_factory=dataset.training,
)

# Train your model (code is omitted for brevity)
...

# Define evaluator
evaluator = RankBasedEvaluator(
    filtered=True,  # Note: this is True by default; we're just being explicit
)

# Evaluate your model with not only testing triples,
# but also filter on validation triples
results = evaluator.evaluate(
    model=model,
    mapped_triples=dataset.testing.mapped_triples,
    additional_filter_triples=[
        dataset.training.mapped_triples,
        dataset.validation.mapped_triples,
    ],
)

实体和关系限制

有时,我们只对某一组实体和/或关系感兴趣, \(\mathcal{E}_I \subset \mathcal{E}\)\(\mathcal{R}_I \subset \mathcal{R}\) 分别表示, 但还有其他实体/关系的三元组形式的额外信息可用。 例如,我们想预测一个演员是否出演了一部电影。因此,我们只对演员/电影之间的 stars_in 关系感兴趣。然而,我们可能有其他信息可用,例如 谁导演了这部电影,或者电影的语言,这些信息可能有助于预测任务。因此,我们希望 在包含所有可用关系和实体的完整数据集上训练模型,但将评估限制在我们 所针对的任务上。

为了限制评估,我们按以下步骤进行:

  1. 我们过滤评估三元组 \(\mathcal{T}_{eval}\),使其仅包含我们感兴趣的三元组,即 \(\mathcal{T}_{eval}' = \{(h, r, t) \in \mathcal{T}_{eval} \mid h, t \in \mathcal{E}_I, r \in \mathcal{R}_I\}\)

  2. 在对三元组\((h, r, t)\)进行尾部预测/评估时,我们将候选尾部实体\(t'\)限制为\(t' \in \mathcal{E}_{eval}\)。同样地,对于头部预测/评估,我们将候选头部实体\(h'\)限制为\(h' \in \mathcal{E}_{eval}\)

示例

pykeen.datasets.Hetionet 是一个包含药物、基因、疾病、其他生物实体及其相互关系的生物医学知识图谱。它由 Himmelstein 等人Systematic integration of biomedical knowledge prioritizes drugs for repurposing 中描述,用于支持药物重新定位,这转化为药物和疾病节点之间的链接预测任务。

图中的边列在这里,但在评估期间,我们将仅关注化合物治疗疾病(CtD)和化合物缓解疾病(CpD)关系。这可以通过以下方式完成:

from pykeen.pipeline import pipeline

evaluation_relation_whitelist = {'CtD', 'CpD'}
pipeline_result = pipeline(
    dataset='Hetionet',
    model='RotatE',
    evaluation_relation_whitelist=evaluation_relation_whitelist,
)

通过将评估限制在感兴趣的边缘,可以在超参数优化期间识别更适合药物重新定位的模型,而不是那些擅长预测所有类型关系的模型。HPO管道接受相同的参数:

from pykeen.hpo import hpo_pipeline

evaluation_relation_whitelist = {'CtD', 'CpD'}
hpo_pipeline_result = hpo_pipeline(
    n_trials=30,
    dataset='Hetionet',
    model='RotatE',
    evaluation_relation_whitelist=evaluation_relation_whitelist,
)