【问题标题】:Why is my Doc2Vec model in gensim not reproducible?为什么我在 gensim 中的 Doc2Vec 模型不可重现?
【发布时间】:2021-07-14 16:22:00
【问题描述】:

我注意到我的 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()

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

提前致谢。

【问题讨论】:

    标签: gensim word2vec random-seed doc2vec


    【解决方案1】:

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

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

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

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

    但是,即使在更正之后:

    正如Gensim FAQ answer #11 中所解释的,考虑到潜在随机性的许多来源以及整个方法的模糊/近似性质,通常不应期望该算法中的确定性。如果您依赖它或测试它,您可能会做出一些毫无根据的假设。

    一般来说,这些算法的测试应该评估“在比较使用中大致等效的有用性”,而不是“相同(甚至相似)的特定向量”。例如,测试appleorange 在彼此的最近邻排名中是否大致处于相同位置比检查它们(有些随意)的精确向量位置甚至余弦相似度更有意义。

    另外:

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

    【讨论】:

    • 感谢您的启发性解释。
    【解决方案2】:

    你检查过差异的大小吗?

    刚刚运行:

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

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

    比较维度:

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

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

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

    【讨论】:

    • 是的,你是对的:这只是来自其他来源的小噪音。您的回答实际上也为我指明了正确的方向,以便在我的应用程序中找到种子之间(大)差异的真正来源。
    • 这是不是数值计算抖动。它甚至可能不是不受控制的随机性——考虑到减少到单个工作人员和常量种子,这可能能够在单个 Python 启动中强制确定性(尽管这样做通常对于这个算法来说是个坏主意)。有关此测试的更深层次问题,请参阅同级答案。
    【解决方案3】:

    改变

    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)
    

    【讨论】:

      猜你喜欢
      • 2021-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多