FastText

FastText是在word2vec的cbow和skip-gram基础上得到模型,其最大的特点是模型简洁,训练速度快且文本分类准确率也令人满意

fastText 训练词向量的时候一般是使用Skip-gram模型的变种。在用作文本分类的时候,一般是使用CBOW的变种

前置知识

N-gram特征

fastText在做词向量的时候引入了subword n-gram的概念来解决词形变化的问题。它将一个单词打散到char级别,并且利用字符级别的n-gram信息来捕捉字符间的顺序关系,希望能够以此丰富单词内部更细微的语义。

而当fastText做文本分类的时候,变为了word n-grams。它将一个短语打散到word级别。

举个例子

  • 做词向量的时候

对于一个词“where“,为了表达单词前后边界,我们加入<>两个字符,即变形为“< where >。假设我们希望抽取3-gram信息,可以得到如下集合:G = { < w h,w h e,h e r,e r e,r e >}。在实践中,我们往往会同时提取单词的多种n-gram信息,如2/3/4/5-gram。这样,原始的一个句子,就被一个char级别的n-gram集合所表达。

对应到中文,其实就是偏旁部首。 阿里曾发布fasttext的一个中文版本,训练的就是偏旁部首。

  • 做文本分类的时候

对于一个短语“我 爱 北京 天安门“,同样加入<>两个字符,变为“< 我 爱 北京 天安门 >”,那么2-gram得到如下集合,G={< 我,我 爱,爱 北京,北京 天安门,天安门 >}。

FastText生成词向量

关于FastText词向量,原论文《Enriching Word Vectors with Subword Information》

FastText生成词向量是在skip-gram模型基础上提出来的,需要回顾一下word2ve中的skip-gram模型(参考上一篇文章

如上图为Word2vec原Skip-Gram模型。

FastText构建词向量与word2vec中Skip-Gram模型唯一不同的就是在输入层

  • FastText取n-gram求和作为词向量,后者取One-Hot词向量;
  • 输入向量矩阵 WV×N\ W_{V×N}中,FastText的N为所有语料的n-grams总数,而Word2vec则为语料word总数。

其余过程就和skip-Gram过程相同了,乘上一个矩阵w,获得向量,然后乘上另一个矩阵w*,进行softmax最后输出属于每个标签的概率,再进行反向传播。最后的w矩阵和最初的1*V向量相乘作为表示的词向量。

FastText的输入

例如

比如对于词汇表D中的 where 词:
原词:< w h e r e > ,分别获得其3,4,5,6-gram

  • 3-grams: < w h , w h e , h e r , e r e , r e >
  • 4-grams: < w h e, w h e r , h e r e, e r e >
  • 5-grams: < w h e r , w h e r e , h e r e >
  • 6-grams: < w h e r e , w h e r e >

然后将原词的和字符级n-grams的one-hot编码进行累加得到输入向量 xwhere\ x_{where},

即将< where>和3-grams,4-grams,5-grams,6-grams的one-hot向量相加得到 xwhere\ x_{where}

如果遇到了OOV怎么办?只使用n-grams的向量和来表示就可以

void Dictionary::computeSubwords(const std::string& word,std::vector<int32_t>& ngrams,std::vector<std::string>* substrings)const {
  for (size_t i = 0; i < word.size(); i++) {
    std::string ngram;
    if ((word[i] & 0xC0) == 0x80) {
      continue; // 遇到10开头字节跳过,保证中文从第一个字节开始读
    }
    for (size_t j = i, n = 1; j < word.size() && n <= args_->maxn; n++) {
      ngram.push_back(word[j++]);
      while (j < word.size() && (word[j] & 0xC0) == 0x80) {
        ngram.push_back(word[j++]); // 如果是中文,读取该字符的所有字节
      }
      if (n >= args_->minn && !(n == 1 && (i == 0 || j == word.size()))) {// subword满足长度,
        int32_t h = hash(ngram) % args_->bucket;//将字符通过hash,得到的hash值加入到ngrams里面
        pushHash(ngrams, h);
        if (substrings) {
          substrings->push_back(ngram);
        }
      }
    }
  }
}

fastText源代码中,将n-gram进行hash,hash到同一位置的多个n-gram会共享一个embedding。这样降低了学习的embedding规模,够极大的节省了空间。

FastText进行文本分类

关于FastText文本分类,原论文《[Enriching Word Vectors with Subword Information]》

fasttext文本分类的模型,该模型类似word2vec的cbow模型.

与Word2vec的CBOW仅有两个区别:

  • 输出是预测类别标签而不是预测中心词。

  • 使用句子中所有单词及其n-gram作为输入,进行embedding,而不再是单单的针对滑动窗口中的单词进行one-hot。

    其中输入的n-gram向量和fastText进行词向量过程是一致的,不过此时n-gram变为了词语级别而不是字符级别

其余过程就和cbow过程相同了,乘上一个矩阵w,对向量进行加权平均,然后乘上另一个矩阵w*,进行softmax最后输出属于每个标签的概率,再进行反向传播。

例如

对于一个短语“我 爱 北京 天安门“,同样加入<>两个字符,变为“< 我 爱 北京 天安门 >”

  • 2-gram得到如下集合,{< 我,我 爱,爱 北京,北京 天安门,天安门 >}。
  • 3-gram得到如下集合,{<我爱,我 爱 北京,爱 北京 天安门,北京 天安门 >}
  • 4-gram得到如下集合,{< 我 爱 北京,我 爱 北京 天安门,爱 北京 天安门 >}
  • 5-gram得到如下集合,{< 我 爱 北京 天安门,我 爱 北京 天安门 >}

然后将原句的和词语级n-grams的one-hot编码进行累加得到输入向量 xwhere\ x_{where},

将< 我 爱 北京 天安门 >和2-gram,3-grams,4-grams,5-grams的one-hot向量相加求平均得到 hdoc\ h_{doc}

优化

与word2vec一样,可以通过层次softmax进行优化。

与word2vec不同的是,fastText层次优化的叶子结点为文档标签的类别而不是中心词概率,以训练集中标签的频率进行排序,频率越高则越靠近根节点。

Gensim实现

FastText生成词向量在gensim中调用如下为:

class gensim.models.fasttext.FastText(sentences=None, corpus_file=None, sg=0, hs=0, size=100, alpha=0.025, window=5, min_count=5, max_vocab_size=None, word_ngrams=1, sample=0.001, seed=1, workers=3, min_alpha=0.0001, negative=5, ns_exponent=0.75, cbow_mean=1, hashfxn=<built-in function hash>, iter=5, null_word=0, min_n=3, max_n=6, sorted_vocab=1, bucket=2000000, trim_rule=None, batch_words=10000, callbacks=())

数与word2vec遵循相同的模式。FastText支持原始word2vec中的下列参数:

  • model: 训练架构. (允许的值: cbow, skipgram (默认 cbow)
  • vector_size: 需要学习的嵌入的大小 (默认 100)
  • alpha: 初始学习率 (默认 0.025)
  • window: 上下文窗口大小 (默认 5)
  • min_count: 忽略出现次数小于某个值的单词 (默认 5)
  • loss: 训练目标. (允许的值: ns, hs, softmax (默认 ns)
  • sample: 高频词的下采样阈值 (默认 0.001)
  • negative: 要抽样的否定词数, for ns (默认 5)
  • iter: epochs的数量 (默认 5)
  • sorted_vocab: 按下降频率排序的单词 (默认 1)

threads: 使用的线程数量 (默认 12)
此外, FastText 还有3个额外参数:

  • min_n: char ngrams的最小长度 (默认 3)
  • max_n: char ngrams的最大长度 (默认 6)
  • bucket: 用于散列ngrams的buckets数量 (默认 200000)
from pprint import pprint as print
from gensim.models.fasttext import FastText as Fasttext
from gensim.test.utils import datapath
if __name__ == '__main__':
    corpus_file = datapath('lee_background.cor')
    model = Fasttext(vector_size=60)
    # build the vocabulary
    model.build_vocab(corpus_file=corpus_file)
    # train the model
    model.train(
        corpus_file=corpus_file, epochs=model.epochs,
        total_examples=model.corpus_count, total_words=model.corpus_total_words
    )
    print(model.wv['爱丽丝']) 
E:\python310\python.exe E:\fastText.py 
array([ 0.00829479,  0.00071116,  0.00169409, -0.0025965 ,  0.00328374,
       -0.00180369,  0.00734048,  0.00590759,  0.00469353,  0.00143918,
       -0.00479525, -0.00138552,  0.00741526, -0.00677875, -0.0017609 ,
       -0.00018423,  0.00386455,  0.0009411 ,  0.00489743,  0.00577713,
       -0.00434757, -0.00546971,  0.00332852,  0.00614409, -0.00440957,
       -0.00459056, -0.00591491,  0.00492364, -0.00173003,  0.00622221,
        0.00049063,  0.00308326,  0.00337223, -0.00502754, -0.00046228,
        0.00731711, -0.00710381, -0.00627137,  0.00071484,  0.00328238,
       -0.00096842, -0.00138118, -0.00020508, -0.00524326, -0.00524897,
        0.00445769, -0.00016085, -0.00373317, -0.00042375,  0.00661727,
        0.00350993,  0.00502089, -0.00400781, -0.00213982, -0.00387585,
       -0.00850575,  0.00332142,  0.00020067, -0.00368993,  0.00553988],
      dtype=float32)

进程已结束,退出代码0

总结

fastText是word2vec的优化模型

相比于Bert等方法,fastText具有如下优点:

  • 训练速度快,精度相当;
  • 不需要预训练的词向量;
  • N-gram特征关注了词序信息;
  • 层次化Softmax优化了时间效率。