【问题标题】:What is the best way to drop old "words" from gensim word2vec model?从 gensim word2vec 模型中删除旧“单词”的最佳方法是什么?
【发布时间】:2020-09-17 03:11:39
【问题描述】:

我有一个从项目-项目图构建的“语料库”,这意味着每个句子都是一个图形步行路径,每个单词都是一个项目。我想在语料库上训练一个 word2vec 模型来获取项目的嵌入向量。该图每天都会更新,因此 word2vec 模型会以更多方式进行训练(使用 Word2Vec.save()Word2Vec.load())以不断更新项目的向量。

与文字不同,我的语料库中的项目有其生命周期,并且每天都会添加新项目。为了防止模型大小的不断增长,我需要丢弃达到其生命周期的项目,同时保持模型可训练。我读过类似的问题 here,但这个问题的答案与增加训练无关,而是基于KeyedVectors。我想出了下面的代码,但我不确定它是否正确和正确:

from gensim.models import Word2Vec
import numpy as np

texts = [["a", "b", "c"], ["a", "h", "b"]]
m = Word2Vec(texts, size=5, window=5, min_count=1, workers=1)

print(m.wv.index2word)
print(m.wv.vectors)

# drop old words
wordsToDrop = ["b", "c"]
for w in wordsToDrop:
    i = m.wv.index2word.index(w)
    m.wv.index2word.pop(i)
    m.wv.vectors = np.delete(m.wv.vectors, i, axis=0)
    del m.wv.vocab[w]

print(m.wv.index2word)
print(m.wv.vectors)
m.save("m.model")
del m

# increased training
new = [["a", "e", "n"], ["r", "s"]]
m = Word2Vec.load("m.model")
m.build_vocab(new, update=True)
m.train(new, total_examples=m.corpus_count, epochs=2)
print(m.wv.index2word)
print(m.wv.vectors)

删除并增加训练后,m.wv.index2wordm.wv.vectors 是否仍然在元素方面对应?上面的代码有副作用吗?如果我的方法不好,有人可以举个例子来说明如何正确删除旧的“单词”并保持模型可训练吗?

【问题讨论】:

    标签: python gensim word2vec


    【解决方案1】:

    没有官方支持从 Gensim Word2Vec 模型中删除单词,一旦它们“成功”被包含。

    即使是添加单词的能力也不是很好,因为该功能不是基于任何经过验证/已发布的更新Word2Vec模型的方法,并且掩盖了困难的权衡更新批次如何影响模型,通过选择学习率或批次是否完全代表现有词汇。最安全的方法是定期从头开始重新训练模型,使用完整的语料库和所有相关单词的足够示例。

    因此,我的主要建议是定期将您的模型替换为使用所有仍然相关数据训练的新模型。这将确保它不再将模型状态浪费在过时的术语上,并且所有仍然有效的术语都接受了同等的交错训练。

    在这样的重置之后,词向量将无法与之前“模型时代”的词向量进行比较。 (同一个词,即使它的有形含义没有改变,也可能是一个任意不同的地方——但与其他向量的相对关系应该保持不变或更好。)但是,同样的偏离比较发生在任何一组小批量更新中,这些更新不会平等地“触及”每个现有单词,只是以某种无法量化的速度。

    OTOH,如果您认为需要继续进行此类增量更新,即使您知道注意事项,您也可以修补模型结构以尽可能多地保留旧模型并继续训练。

    p>

    到目前为止,您的代码是一个合理的开始,但缺少一些正确功能的重要注意事项:

    • 因为删除较早的单词会更改较晚单词的索引位置,所以您需要更新每个幸存单词的vocab[word].index 值,以匹配新的index2word 排序。例如,在完成所有删除后,您可能会这样做:
    for i, word in enumerate(m.wv.index2word):
        m.wv.vocab[word].index = i
    
    • 因为在您的(默认负采样)Word2Vec 模型中,还有另一个与模型的输出层相关的每个单词权重数组,也应该同步更新,所以每个单词都检查正确的输出值。粗略地说,每当您从m.wv.vectors 中删除一行时,您都应该从m.traininables.syn1neg 中删除同一行。

    • 因为幸存的词汇表具有不同的相对词频,负采样和下采样(由sample 参数控制)函数都应该使用不同的预先计算的结构来帮助他们选择。对于负采样使用的累积分布表,这很简单:

    m.make_cum_table(m.wv)
    

    对于下采样,您需要更新.sample_int 值,类似于您可以在https://github.com/RaRe-Technologies/gensim/blob/3.8.3/gensim/models/word2vec.py#L1534 上查看代码的逻辑。 (但是,现在看那个代码,我认为它可能是buggy,因为它只使用新字典中的频率信息更新所有单词,因此可能会破坏真正频繁单词的通常下采样,并且可能错误地下采样单词仅在新更新中经常出现。)

    如果这些内部结构与您现有的操作同步正确更新,则模型可能处于一致状态以进行进一步训练。 (但请注意:这些结构在即将发布的gensim-4.0.0 版本中发生了很大变化,因此升级时需要更新任何自定义篡改。)

    另一个效率注意事项:np.delete() 操作将创建一个新数组,即幸存数组的完整大小,并在每次调用时复制旧值。因此,使用它从一个非常大的原始数组中一次删除许多行可能需要大量冗余分配/复制/垃圾收集。您可以调用它一次,最后列出要删除的所有索引。

    但实际上:更简单、更扎实的方法(也可能产生明显更好的连续可比向量)是尽可能使用所有当前数据重新训练,或者发生大量变化。

    【讨论】:

    • 很好的答案。但是,我们不能确保重新训练的模型能够产生上一段中提到的“可连续比较的向量”,对吧?重新训练模型的主要缺点是,一旦向量被重置,下游任务(下游任务使用嵌入结果很常见)模型也需要重新训练。
    • 是的 - 做一个全新的词向量模型意味着可能与前一个时代的坐标空间没有相关性。因此,应该刷新基于该旧空间的缓存下游计算/模型。但如上所述,即使使用增量训练,未通过增量批次训练的单词与重新训练(或添加)的单词相比,可能任意偏离早期训练学到的有意义的相对排列。它只是更加微妙,并且高度依赖于训练数据和参数选择的精确组合。当心假设增量训练使事情保持兼容/有效。
    猜你喜欢
    • 2018-08-03
    • 2012-03-17
    • 2013-03-16
    • 2016-12-04
    • 2015-08-15
    • 2015-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多