作者:LogM
本文原载于 https://segmentfault.com/u/logm/articles ,不允许转载~
1. 源码来源
FastText 源码:https://github.com/facebookresearch/fastText
本文对应的源码版本:Commits on Jun 27 2019, 979d8a9ac99c731d653843890c2364ade0f7d9d3
FastText 论文:
[1] P. Bojanowski, E. Grave, A. Joulin, T. Mikolov, Enriching Word Vectors with Subword Information
[2] A. Joulin, E. Grave, P. Bojanowski, T. Mikolov, Bag of Tricks for Efficient Text Classification
2. 概述
之前的博客介绍了"分类器的预测"的源码,里面有一个重点没有详细展开,就是"基于字母的 Ngram 是怎么实现的"。这块论文里面关于"字母Ngram的生成"讲的比较清楚,但是对于"字母Ngram"如何加入到模型中,讲的不太清楚,所以就求助于源码,源码里面把这块叫做 Subwords
。
看懂了源码其实会发现 Subwords
加入到模型很简单,就是把它和"词语"一样对待,一起求和取平均。
另外,我自己再看源码的过程中还有个收获,就是关于"中文词怎么算subwords",之前我一直觉得 Subwords
对中文无效,看了源码才知道是有影响的。
最后是词向量中怎么把 Subwords
加到模型。这部分我估计大家也不怎么关心,所以我就相当于写给我自己看的,解答自己看论文的疑惑。以skipgram
为例,输入的 vector 和所要预测的 vector 都是单个词语
与subwords
相加求和的结果。
3. 怎么计算 Subwords
之前的博客有提到,Dictionary::getLine
这个函数的作用是从输入文件中读取一行,并将所有的Id(包括词语的Id,SubWords的Id,WordNgram的Id)存入到数组 words
中。
首先我们要来看看 Subwords
的 Id 是怎么生成的,对应的函数是 Dictionary::addSubwords
。
// 文件:src/dictionary.cc
// 行数:378
int32_t Dictionary::getLine(
std::istream& in, // `in`是输入的文件
std::vector<int32_t>& words, // `words`是所有的Id组成的数组(包括词语的id,SubWords的Id,WordNgram的Id)
std::vector<int32_t>& labels) const { // 因为FastText支持多标签,所以这里的`labels`也是数组
std::vector<int32_t> word_hashes;
std::string token;
int32_t ntokens = 0;
reset(in);
words.clear();
labels.clear();
while (readWord(in, token)) { // `token`