负采样

对于实体 \(\mathcal{E}\) 和关系 \(\mathcal{R}\),所有可能的三元组 \(\mathcal{T}\) 的集合是通过它们的笛卡尔积 \(\mathcal{T} = \mathcal{E} \times \mathcal{R} \times \mathcal{E}\) 构建的。一个给定的知识图谱 \(\mathcal{K}\) 是所有可能三元组 \(\mathcal{K} \subseteq \mathcal{T}\) 的一个子集。

知识图谱的构建

在封闭世界假设下构建知识图谱\(\mathcal{K}_{\text{closed}}\)时,剩余三元组\((h,r,t) \in \mathcal{T} \setminus \mathcal{K}_{\text{closed}}\)的标签被定义为负。 在开放世界假设下构建知识图谱\(\mathcal{K}_{\text{open}}\)时,剩余三元组\((h,r,t) \in \mathcal{T} \setminus \mathcal{K}_{\text{open}}\)的标签是未知的。

由于大多数知识图谱是在开放世界假设下生成的,因此在知识图谱嵌入模型的训练过程中必须采用负采样技术,以避免过度泛化。

腐败

负采样技术通常通过破坏已知的正三元组来生成负三元组 \((h,r,t) \in \mathcal{K}\),通过替换\(h\)\(r\)\(t\)中的任意一个,使用以下操作之一:

腐败的领导人

\(\mathcal{H}(h, r, t) = \{(h', r, t) \mid h' \in \mathcal{E} \land h' \neq h\}\)

腐败关系

\(\mathcal{R}(h, r, t) = \{(h, r', t) \mid r' \in \mathcal{E} \land r' \neq r\}\)

损坏的尾部

\(\mathcal{T}(h, r, t) = \{(h, r, t') \mid t' \in \mathcal{E} \land t' \neq t\}\)

通常,腐败关系操作 \(\mathcal{R}(h, r, t)\) 被省略,因为在链接预测任务中评估知识图嵌入模型仅考虑头部预测和尾部预测的优劣,而不考虑关系预测。因此,对于给定的已知正三元组 \((h,r,t) \in \mathcal{K}\),候选负三元组 \(\mathcal{N}(h, r, t)\) 的集合由以下公式给出:

\[\mathcal{N}(h, r, t) = \mathcal{T}(h, r, t) \cup \mathcal{H}(h, r, t)\]

通常,所有正三元组 \((h,r,t) \in \mathcal{K}\) 的潜在负三元组集合 \(\mathcal{N}\) 定义为:

\[\mathcal{N} = \bigcup_{(h,r,t) \in \mathcal{K}} \mathcal{N}(h, r, t)\]

均匀负采样

默认的负采样器 pykeen.sampling.BasicNegativeSampler 通过均匀随机地使用损坏头部操作或损坏尾部操作,从已知的正三元组 \((h,r,t) \in \mathcal{K}\) 生成损坏的三元组。默认的负采样器在以下代码中自动使用:

from pykeen.pipeline import pipeline

results = pipeline(
    dataset='YAGO3-10',
    model='PairRE',
    training_loop='sLCWA',
)

可以明确设置:

from pykeen.pipeline import pipeline

results = pipeline(
    dataset='YAGO3-10',
    model='PairRE',
    training_loop='sLCWA',
    negative_sampler='basic',
)

通常,在使用pykeen.pipeline.pipeline()时,可以通过传递negative_sampler_kwargs参数来修改负采样器的行为。为了明确指定使用哪种头、关系和尾的破坏方法,可以使用corruption_schema参数。例如,要使用所有三种方法,可以传递集合('h', 'r', 't'),如下所示:

from pykeen.pipeline import pipeline

results = pipeline(
    dataset='YAGO3-10',
    model='PairRE',
    training_loop='sLCWA',
    negative_sampler='basic',
    negative_sampler_kwargs=dict(
        corruption_scheme=('h', 'r', 't'),
    ),
)

伯努利负采样

伯努利负采样器 pykeen.sampling.BernoulliNegativeSampler 从已知的正三元组 \((h,r,t) \in \mathcal{K}\) 生成损坏的三元组,与均匀负采样器类似,但它预先计算了每个关系 \(r\) 的概率 \(p_r\),以加权是否以概率 \(p_r\) 使用头部损坏,或以概率 \(1 - p_r\) 使用尾部损坏。

from pykeen.pipeline import pipeline

results = pipeline(
    dataset='YAGO3-10',
    model='PairRE',
    training_loop='sLCWA',
    negative_sampler='bernoulli',
)

NegativeSampler(*, mapped_triples[, ...])

一个负采样器。

BasicNegativeSampler(*[, corruption_scheme])

一个基本的负采样器。

BernoulliNegativeSampler(*, mapped_triples, ...)

[wang2014]提出的伯努利负采样方法的实现。

PseudoTypedNegativeSampler(*, ...)

一个采样器,用于考虑哪些实体与关系共同出现。

变量

negative_sampler_resolver

负采样器的解析器

类继承图

Inheritance diagram of pykeen.sampling.negative_sampler.NegativeSampler, pykeen.sampling.basic_negative_sampler.BasicNegativeSampler, pykeen.sampling.bernoulli_negative_sampler.BernoulliNegativeSampler, pykeen.sampling.pseudo_type.PseudoTypedNegativeSampler

过滤

考虑关系 \(r\) 的以下属性。由于腐败操作(参见 Corruption)是独立于三元组应用的,生成的候选腐败三元组可能与已知的正三元组在 \(\mathcal{K}\) 中重叠。

\(r\)的属性

三元组的示例对

影响

一对多

\((h,r,t_1), (h,r,t_2) \in \mathcal{K}\)

\((h,r,t_2) \in T(h,r,t_1) \cup (h,r,t_1) \in T(h,r,t_2)\)

多个

\((h,r_1,t), (h,r_2,t) \in \mathcal{K}\)

\((h,r_2,t) \in R(h,r_1,t) \cup (h,r_1,t) \in R(h,r_2,t)\)

多对一

\((h_1,r,t), (h_2,r,t) \in \mathcal{K}\)

\((h_2,r,t) \in H(h_1,r,t) \cup (h_1,r,t) \in H(h_2,r,t)\)

如果在\(\mathcal{K}\)中没有关系满足在负采样中选择的腐败模式的任何相关属性,那么保证\(\mathcal{N}\)\(\mathcal{K}\)之间不会有重叠,使得\(\mathcal{N} \cap \mathcal{K} \neq \emptyset\)。然而,这种情况在现实世界的知识图谱中非常不可能发生。

\(\mathcal{N}\)中出现的已知正三元组是已知的假阴性。因此,我们知道这些是不正确的(阴性)训练示例,可能希望排除它们以减少训练噪声。

警告

应该考虑到,即使是不属于知识图谱的损坏三元组也可能代表真实的事实。这些“未知”的假阴性在过滤设置中无法先验地去除。该方法的理念再次依赖于未知假阴性的数量较少,以便学习能够进行。

然而,在实践中,\(|\mathcal{N}| \gg |\mathcal{K}|\),因此生成假阴性的可能性相当低。 因此,为了降低计算成本,通常会省略额外的过滤步骤。这种一般观察可能并不适用于所有实体;例如,对于一个连接到许多其他实体的中心实体,如果没有过滤,可能会有相当数量的假阴性。

在训练期间识别假阴性

默认情况下,PyKEEN在训练期间不会\(\mathcal{N}\)中过滤假阴性。要在训练期间启用负例过滤,可以向negative_sampler_kwargs提供filtered关键字,如下所示:

results = pipeline(
    dataset='YAGO3-10',
    model='PairRE',
    training_loop='sLCWA',
    negative_sampler='basic',
    negative_sampler_kwargs=dict(
        filtered=True,
    ),
)

PyKEEN 实现了多种具有不同特性的过滤算法,可以通过在 negative_sampler_kwargs 中使用 filterer 关键字参数来选择。默认情况下,使用了一种快速且近似的算法,该算法基于 pykeen.sampling.filtering.BloomFilterer,该算法基于 布隆过滤器。布隆过滤器还具有可配置的期望错误率,可以通过增加内存和计算成本来进一步降低该错误率。

from pykeen.pipeline import pipeline

results = pipeline(
    dataset='YAGO3-10',
    model='PairRE',
    training_loop='sLCWA',
    negative_sampler='basic',
    negative_sampler_kwargs=dict(
        filtered=True,
        filterer='bloom',
        filterer_kwargs=dict(
            error_rate=0.0001,
        ),
    ),
)

如果你想确保所有已知的假阴性都被过滤掉,你可以使用基于Python内置集合的较慢实现,即pykeen.sampling.filtering.PythonSetFilterer。它可以通过以下方式激活:

from pykeen.pipeline import pipeline

results = pipeline(
    dataset='YAGO3-10',
    model='PairRE',
    training_loop='sLCWA',
    negative_sampler='basic',
    negative_sampler_kwargs=dict(
        filtered=True,
        filterer='python-set',
    ),
)

评估期间识别假阴性

与训练相比,PyKEEN 在评估期间默认会从 \(\mathcal{N}\) 中过滤掉假阴性。 要在评估期间禁用“过滤设置”,可以将 filtered 关键字传递给 evaluator_kwargs,如下所示:

from pykeen.pipeline import pipeline

results = pipeline(
    dataset='YAGO3-10',
    model='PairRE',
    evaluator_kwargs=dict(
        filtered=False,
    ),
)

在评估期间的过滤实现方式与负采样不同:

首先,通过pykeen.sampling.filtering.Filterer在精确算法或近似算法之间没有选择。相反,评估过滤可以就地修改分数,而不是仅选择未过滤的条目。原因主要是评估总是在1:n评分中进行,因此,我们通过保持张量的“密集”形状(batch_size, num_entities)在这里获得了一些效率。

其次,评估期间的过滤必须正确,这对于在过滤设置中重现结果至关重要。对于评估,使用我们拥有的所有信息以获得尽可能可靠的评估结果是有意义的。

Filterer(*args, **kwargs)

用于过滤负三元组方法的接口。

BloomFilterer(mapped_triples[, error_rate])

基于布隆过滤器的负三元组过滤器。

PythonSetFilterer(mapped_triples)

使用Python集合进行过滤的过滤器。

变量

filterer_resolver

用于映射过滤器的解析器

类继承图

Inheritance diagram of pykeen.sampling.filtering.Filterer, pykeen.sampling.filtering.BloomFilterer, pykeen.sampling.filtering.PythonSetFilterer