Word2Vec子采样-实现

我正在Pytorch和Tensorflow2中实现Skipgram模型。我对常用字的二次采样的实施方式有疑问。根据本文的逐字记录,二次采样单词wi的概率计算为

Word2Vec子采样-实现

其中t是自定义阈值(通常是一个很小的值,例如 0.0001 ),而f是文档中单词的出现频率。尽管作者以不同但几乎等效的方式实现了它,但让我们坚持这个定义。

在计算P(wi)时,我们最终会得到负值。例如,假设我们有100个单词,并且其中一个单词的出现频率比其他单词高得多(因为我的数据集就是这种情况)。

import numpy as np
import seaborn as sns

np.random.seed(12345)

# generate counts in [1,20]
counts = np.random.randint(low=1,high=20,size=99)

# add an extremely bigger count
counts = np.insert(counts,100000)

# compute frequencies
f = counts/counts.sum()

# define threshold as in paper
t = 0.0001

# compute probabilities as in paper
probs = 1 - np.sqrt(t/f)
sns.distplot(probs);

问:使用这种“概率”实现子采样的正确方法是什么?

作为附加信息,我已经看到在keras函数keras.preprocessing.sequence.make_sampling_table中采用了不同的方法:

def make_sampling_table(size,sampling_factor=1e-5):
    """Generates a word rank-based probabilistic sampling table.
    Used for generating the `sampling_table` argument for `skipgrams`.
    `sampling_table[i]` is the probability of sampling
    the i-th most common word in a dataset
    (more common words should be sampled less frequently,for balance).
    The sampling probabilities are generated according
    to the sampling distribution used in word2vec:
    ```
    p(word) = (min(1,sqrt(word_frequency / sampling_factor) /
        (word_frequency / sampling_factor)))
    ```
    We assume that the word frequencies follow Zipf's law (s=1) to derive
    a numerical approximation of frequency(rank):
    `frequency(rank) ~ 1/(rank * (log(rank) + gamma) + 1/2 - 1/(12*rank))`
    where `gamma` is the Euler-Mascheroni constant.
    # Arguments
        size: Int,number of possible words to sample.
        sampling_factor: The sampling factor in the word2vec formula.
    # Returns
        A 1D Numpy array of length `size` where the ith entry
        is the probability that a word of rank i should be sampled.
    """
    gamma = 0.577
    rank = np.arange(size)
    rank[0] = 1
    inv_fq = rank * (np.log(rank) + gamma) + 0.5 - 1. / (12. * rank)
    f = sampling_factor * inv_fq

    return np.minimum(1.,f / np.sqrt(f))
fancykee 回答:Word2Vec子采样-实现

我倾向于更信任部署的代码,而不是纸上的文章,尤其是在word2vec这样的情况下,论文作者发布的原始作者的word2vec.c code已被广泛使用并用作其他实现的模板。如果我们看看它的二次采样机制...

        if (sample > 0) {
          real ran = (sqrt(vocab[word].cn / (sample * train_words)) + 1) * (sample * train_words) / vocab[word].cn;
          next_random = next_random * (unsigned long long)25214903917 + 11;
          if (ran < (next_random & 0xFFFF) / (real)65536) continue;
        }

...我们看到那些计数极小(.cn)的单词可能在原始公式中给出负值,而在此处给出的值大于1.0,因此永远不能小于long的随机掩码和缩放比例永远不会超过1.0(next_random & 0xFFFF) / (real)65536)。因此,似乎作者的意图是使原始公式的所有负值都表示“永不丢弃”。

根据 keras make_sampling_table()的评论和实施,他们完全不咨询实际的词频。取而代之的是,他们假设基于词序的Zipf类分布来合成模拟的词频。

如果他们的假设成立-相关词来自具有类似Zipf频率分布的自然语言语料库-那么我希望他们的抽样概率接近于本来可以计算出的下抽样概率从真实的频率信息。在大多数情况下,这可能“足够接近”。

我不确定他们为什么选择这种近似值。也许他们通常的过程的其他方面直到这一步都没有保持真正的频率,他们期望始终使用自然语言文本,因为假定的频率通常是正确的。

(很幸运,而且因为人们经常想将频率推算到单词向量的公共集合上,这些词向量已经减少了真实的计数,但仍然从最频繁到最不频繁地排序,我几天前才写过an answer about simulating a fake-but-plausible distribution using Zipf's law –类似于此keras代码正在执行的操作。)

但是,如果您使用的数据不匹配(与您的综合数据集或描述的数据集一样),其抽样概率将与您自己计算出的概率完全不同,以及使用真实词频的任何原始公式形式。

特别是,假设一个令牌具有一百万次的分布,然后一百个令牌都只出现10次。 “排名”列表中的那一百个代币的顺序是任意的-确实,它们的频率都捆绑在一起。但是,通过基于该顺序拟合Zipfian分布的基于仿真的方法实际上将对每个样本进行非常不同的采样。幸运的是,出现在第二名位置的10个单词的采样率将大大降低,就好像它出现的频率更高。并且,通过使真实频率*低于* *近似,排名第一的“高头”值将比其他情况下减少下采样。这些效果似乎都没有好处,也没有体现出“频繁单词下采样”选项的精神-该选项仅应“稀疏”非常频繁的单词,并且在所有情况下,在原始语料库中彼此保留相同频率的单词大致在缩减采样的语料库中彼此等效存在。

因此,对于您的情况,我将采用原始公式(需要负值的特殊处理的丢弃概率)或word2vec.c实际/反向实现(概率-保持饱和度为1.0),而不是采用keras样式的近似值。

(作为一个完全分开的注释,如果您使用负采样,则可能与您的数据集/用途有关:还有另一个参数控制负示例的相对采样,通常固定在0.75中较早的实现,即one paper has suggested can usefully vary for non-natural-language token distributions & recommendation-related end-uses。在Python ns_exponent实现中,此参数名为gensim,但简称为a fixed power value internal to a sampling-table pre-calculation in the original word2vec.c code。)

本文链接:https://www.f2er.com/3135480.html

大家都在问