word2vec的ReduceVocab()函数是否存在bug或逻辑遗漏?
word2vec ReduceVocab函数词频裁剪逻辑问题解答
以下是从谷歌官方word2vec.c截取的相关源码片段:
// Reduces the vocabulary by removing infrequent tokens void ReduceVocab() { int a, b = 0; unsigned int hash; for (a = 0; a < vocab_size; a++) if (vocab[a].cn > min_reduce) { vocab[b].cn = vocab[a].cn; vocab[b].word = vocab[a].word; b++; } else free(vocab[a].word); vocab_size = b; for (a = 0; a < vocab_hash_size; a++) vocab_hash[a] = -1; for (a = 0; a < vocab_size; a++) { // Hash will be re-computed, as it is not actual hash = GetWordHash(vocab[a].word); while (vocab_hash[hash] != -1) hash = (hash + 1) % vocab_hash_size; vocab_hash[hash] = a; } fflush(stdout); min_reduce++; }
ReduceVocab()函数会在LearnVocabFromTrainFile函数中被调用,针对你提出的场景疑问,解答如下:
结论:你的分析完全正确
你观察到的词频计数丢失问题真实存在,没有遗漏代码中的相关逻辑:
- ReduceVocab执行裁剪时,词频低于当前
min_reduce阈值的词会被直接free释放内存,历史出现次数会被彻底丢弃,没有任何留存计数的逻辑 - 函数每次执行末尾都会执行
min_reduce++,下一次触发裁剪时的判定阈值会比当前高1 - 被删除的词如果在后续语料读取过程中再次出现,会被识别为未登录词,重新加入词表时词频从0开始重新累计,不会和之前被删除时的历史计数合并
- 你举的"hello"例子完全符合代码实际运行逻辑:第一次裁剪时累计出现4次,达不到初始阈值5被删除;后续新增出现5次时,重新入表的计数仅为5,而第二次裁剪的阈值已经涨到6,因此会再次被删除,哪怕真实累计出现次数已经达到9次,确实会被误删。
该缺陷几乎不会造成实际影响
这个逻辑是原作者做的工程权衡,并非代码疏漏:
- ReduceVocab的触发时机是词表大小超过预设上限时,核心目的是控制训练过程中的内存占用,本身就不是为了实现100%精确的词频统计
- 这类被反复删除又反复加入的词,本身在语料中的分布非常稀疏,绝大多数最终总词频也达不到训练时设置的
min_count(默认值为5),本来就是要被过滤的无效词 - 就算真的存在少量总词频达标的词被误删,占整体语料的比例极低,对最终词向量训练效果的影响可以忽略不计,因此官方原版代码一直没有修复这个逻辑。
内容的提问来源于stack exchange,提问作者prof_FL
相关产品推荐
相关产品推荐

