为什么我在 gensim 中的 Doc2Vec 模型不可重现?

Why is my Doc2Vec model in gensim not reproducible?

我注意到我的 gensim Doc2Vec (DBOW) 模型对文档标签很敏感。我的理解是这些标签是装饰性的,因此它们不应影响学习到的嵌入。我误会了什么吗?这是一个最小的例子:

from gensim.test.utils import common_texts
from gensim.models.doc2vec import Doc2Vec, TaggedDocument
import numpy as np
import os
    
os.environ['PYTHONHASHSEED'] = '0'
    
reps = []
for a in [0,500]:
    documents = [TaggedDocument(doc, [i + a]) 
                 for i, doc in enumerate(common_texts)]
    model = Doc2Vec(documents, vector_size=100, window=2, min_count=0,
                    workers=1, epochs=10, dm=0, seed=0)
    reps.append(np.array([model.docvecs[k] for k in range(len(common_texts))])
    
reps[0].sum() == reps[1].sum()

最后一行 returns False。我正在使用 gensim 3.8.3 和 Python 3.5.2。更一般地说,标签的值是否发挥了[=1​​8=]任何的作用(假设它们是唯一的)?我问是因为我发现在分类任务中对文档使用不同的标签会导致性能差异很大。

提前致谢。

你检查过差异的大小了吗?

就运行宁:

delta = reps[0].sum() - reps[1].sum()

当我 运行 时,与 -1.2598932e-05 的总差异结果。

尺寸比较:

 eps = 10**-4
 over = (np.abs(diff) <= eps).all()

Returns True 在绝大多数 运行 上,这意味着考虑到计算的复杂性,您将获得可重复的结果。

我会责怪 numerical stability 的计算或不受控制的随机性。即使您确实尝试控制随机种子,NumPy 和 random 标准库中也有不同的随机种子,因此您无法控制所有随机源。这也会对结果产生影响,但我没有检查 gensim 中的实际实现及其依赖项。

首先,您的测试甚至没有比较对应于相同文本的向量!

在 运行 #1 中,model.docvecs[0] 中第一个文本的向量。在 运行 #2 中,第一个文本的向量在 model.docvecs[1].

而且,在 运行 #2 中,model.docvecs[0] 处的向量只是一个随机初始化但从未训练过的向量——因为 none 的训练文本有一个(int) 0 的文档 tag。 (如果使用纯整数作为文档标签,Doc2Vec 将它们用作文字索引 - 可能会留下任何未使用的槽,少于分配和初始化但从未训练过的最高标签。)

由于 common_texts 只有 11 个条目,当您到达 运行 #12 时,所有 您的 reps 数组中的向量前 11 个矢量中的垃圾与您的任何文本都不相关/

然而,即使在更正之后:

Gensim FAQ answer #11 中所述,鉴于潜在随机性的许多来源以及整个方法的 fuzzy/approximate 性质,通常不应期望此算法中的确定性。如果您依赖它或对其进行测试,您可能会做出一些没有根据的假设。

通常,对这些算法的测试应该评估“在比较用途中大致等效的有用性”,而不是“相同(或什至相似)的特定向量”。例如,测试 appleorange 是否在彼此的最近邻排名中大致相同的位置比检查它们(有点随意)的确切向量位置甚至余弦相似性更有意义。

另外:

  • common_texts 这样的小玩具数据集不会显示算法通常的 behavior/benefits
  • PYTHONHASHSEED 仅在启动时由 Python 解释器查询;从 Python 设置它不会有任何效果。而且,它引入的那种不确定性只会在单独的解释器启动时出现:在单个解释器中的紧密循环 运行 在任何情况下都不会受到影响。

改变

import os
    
os.environ['PYTHONHASHSEED'] = '0'

import os
import sys
hashseed = os.getenv('PYTHONHASHSEED')
if not hashseed:
    os.environ['PYTHONHASHSEED'] = '0'
    os.execv(sys.executable, [sys.executable] + sys.argv)