词向量工具---fastText简介、使用教程及源码分析

简介

fastTextFacebook 2016 年开源的一个词向量计算以及文本分类的工具,word2vec 的作者 mikolov 也参与了制作,目前已经引起了广泛的关注。这个工具包的 GitHub链接,该项目是 C++ 写的,和之前的项目相比这个项目更加专业,涉及的内容也比之前的项目多很多。在学术上没有什么创新点,但是好处就是模型简单,性能比肩深度学习而且速度更快,用起来很顺手,做出来的结果也可以达到上线使用的标准。该工具其实是由两部分组成,一部分是高效文本分类,一部分是词向量学习。该工具的理论基础是以下两篇论文:

Bag of Tricks for Efficient Text Classification

这篇论文提出了 fastText 算法,介绍高效文本分类技巧,该算法实际上是将目前用来算 word2vec 的 CBOW 模型架构做了个小修改,原先使用一个词的上下文的所有词向量之和来预测词本身,现在改为用一段短文本的词向量之和来对文本进行分类。在生成文本向量的时候用到了ngram的信息,用这个文档所有单词的词向量的平均预测标签。对这种简单的任务,用简单的模型效果就不错了。具体方法就是把句子每个 word 的 vector 求平均,然后直接用简单的LR分类就行。fastText 的 fast 指的是这个。 这个 知乎答案 总结得挺好的,取平均其实算 Deep Learning 的 average pooling。

fastText文本分类架构图

Enriching Word Vectors with Subword Information

这篇论文使用 subword 信息,也就是连续的字符信息来丰富词汇向量,提出了用 subword 的向量之和来代替简单的词向量的方法,从而使得词的微变形关系也能映射到嵌入空间中,以解决简单 word2vec 无法处理同一词的不同形态的问题。每个词被看做是 n-gram 字母串包。为了区分前后缀情况,”<”, “>” 符号被加到了词的前后端。除了词的子串外,词本身也被包含进了 n-gram 字母串包。以 where 为例,n=3 的情况下,其子串分别为
<wh, whe, her, ere, re>,以及其本身 <where>
注意,这里的 her 与单词 < her> 是不同的。fastText 中提供了 maxn 这个参数来确定 subword 的大小。词向量学习部分可说是一个 word2vec 优化版,用了 subword 的信息,速度是不会提升的,只是效果方面的改进。这个改进能提升模型对 morphology 的效果, 即”字面上”相似的词语 distance 也会小一些. 有人在question-words 数据集上跑过 fastText 和 gensim-word2vec 的对比, 结果在 Jupyter Notebook Viewer .可以看出fastText在 “adjective-to-adverb”, “opposite”之类的数据集上效果还是相当好的. 不过像 “family” 这样的字面上不一样的数据集, fastText效果反而不如 gensim-word2vec.推广到中文上, 结果也类似. “字面上”相似对 vector 的影响非常大. 一个简单的例子是, gensim 训练的模型中与”交易”最相似的是”买卖”, 而 fastText 的结果是”交易法”注:在代码实现中,对于中文是不计算 subword 的。

fastText 用于文本分类

fastText文本分类模型 = word2vec 中 CBOW 模型 + h-softmax 的灵活使用。
模型输入一个词的序列(一段文本或者一句话),输出这个词序列属于不同类别的概率。
序列中的词和词组组成特征向量,特征向量通过线性变换映射到中间层,中间层再映射到标签。fastText 在预测标签时使用了非线性激活函数,但在中间层不使用非线性激活函数。对于有大量类别的数据集,fastText 使用了一个分层分类器(而非扁平式架构)。不同的类别被整合进树形结构中(想象下二叉树而非 列表)。在某些文本分类任务中类别很多,计算线性分类器的复杂度高。为了减少了训练复杂性和测试文本分类器的时间,fastText 利用了类别(class)不均衡这个事实(一些类别出现次数比其他的更多),使用层次 Softmax 技巧。层次 Softmax 通过使用 Huffman 算法建立用于表征类别的树形结构,对标签进行编码,频繁出现的类别的深度要比不频繁出现类别的深度要小,这也使得进一步的计算效率更高。fastText 能在五分钟内将50万个句子分成超过30万个类别。

注:

  1. fastText 只能做多类别分类,从多个类别里预测出一个类。
  2. 文本分类单层网络就够了,非线性的问题用多层的。fasttext只有1层神经网络,属于所谓的 shallow learning,比deep learning 模型的优点是训练和预测速度极快,但是 fasttext 的效果并不差,在工业界这点非常重要。

fastText 用于词向量表征

在 fastText 中一个低维度向量与每个单词都相关。隐藏表征在不同类别所有分类器中进行共享,使得文本信息在不同类别中能够共同使用。常用的特征是词袋模型( Bag-of-Words ),但词袋模型忽略了词序信息,因此 fastText 还加入了 n-gram ,n-gram 来将局部词序考虑在内,特征这对很多文本分类问题来说十分重要。
“我 爱 你” 这句话中的词袋模型特征是 “我”,“爱”, “你”。这些特征和句子 “你 爱 我” 的特征是一样的。如果加入 2-gram,第一句话的特征还有 “我-爱” 和 “爱-你”,这两句话 “我 爱 你” 和 “你 爱 我” 就能区别开来了。当然,为了提高效率,我们需要过滤掉低频的 n-gram。

举例来说:fastText 能够学会“男孩”、“女孩”、“男人”、“女人”指代的是特定的性别,并且能够将这些数值存在相关文档中。然后,当某个程序在提出一个用户请求(假设是“我女友现在在儿?”),它能够马上在 fastText 生成的文档中进行查找并且理解用户想要问的是有关女性的问题。

facebook 公开了90种语言的 Pre-trained word vectors(词向量维度为300维),点击了解更多详情

fastText词向量优势

  1. 适合大型数据+高效的训练速度:能够训练模型“在使用标准多核CPU的情况下10分钟内处理超过10亿个词汇”,特别是与深度模型对比,fastText 能将训练时间由数天缩短到几秒钟。使用一个标准多核 CPU,得到了在10分钟内训练完超过10亿词汇量模型的结果。此外,fastText 还能在五分钟内将50万个句子分成超过30万个类别。
  2. 支持多语言表达:利用其语言形态结构,fastText能够被设计用来支持包括英语、德语、西班牙语、法语以及捷克语等多种语言。它还使用了一种简单高效的纳入子字信息的方式,在用于像捷克语这样词态丰富的语言时,这种方式表现得非常好,这也证明了精心设计的字符 n-gram 特征是丰富词汇表征的重要来源。fastText的性能要比时下流行的word2vec工具明显好上不少,也比其他目前最先进的词态词汇表征要好。
  3. fastText 专注于文本分类,在许多标准问题上实现当下最好的表现(例如文本倾向性分析或标签预测)。fastText 与基于深度学习方法的 Char-CNN 以及 VDCNN 对比:
    fastText与其他模型的实验结果对比图
  4. 比word2vec更考虑了同一词的不同形态的问题,比如 fastText 的词向量学习能够考虑 english-born 和 british-born 之间有相同的后缀,但 word2vec 却不能。

fastText vs word2vec

相同之处

  1. 图模型结构很像,都是采用 embedding 向量的形式,得到 word 的隐向量表达。
  2. 采用很多相似的优化方法,比如使用 Hierarchical softmax 优化训练和预测中的打分速度。
  3. 训练词向量时,两者都是无监督算法。输入层是 context window 内的 term。输出层对应的是每一个 term,计算某 term 的概率最大;
  4. 在使用层次softmax的时候,huffman 树叶子节点处是训练语料里所有词的向量。

不同之处

  1. fastText 用于学习词向量时,加入了词的 subword 信息和 n-gram 信息。
  2. fastText 用于文本分类时,是有监督算法。输入层对应整个 sentence 的内容,包括term,也包括 n-gram 的内容。输出层对应的是分类的 label。huffmax树叶子节点处是每一个类别标签的词向量
  3. fastText 用于文本分类时,遍历分类树的所有叶节点,找到概率最大的 label(一个或者 N 个)。

参数说明

  • loss function选用hs(hierarchical softmax)要比ns(negative sampling) 训练速度要快很多倍,并且准确率也更高。
  • wordNgrams 默认为1,设置为2以上可以明显提高准确率。
  • 如果词数不是很多,可以把bucket设置的小一点,否则预留会预留太多bucket使模型太大。

因为facebook提供的只是C++版本的代码,上github已经有封装的python接口。用起来特别方便,觉得还不能满足自己的使用要求,修改源码也非常方便。
所以对于文本分类,先用fasttext做一个简单的baseline是很适合的。

fastText 分类示例

classification-example.sh 示例说明,包含训练、测试和预测示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/env bash
#
# Copyright (c) 2016-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
#
# 对文本进行预处理并且打乱
myshuf() {
perl -MList::Util=shuffle -e 'print shuffle(<>);' "$@";
}
normalize_text() {
tr '[:upper:]' '[:lower:]' | sed -e 's/^/__label__/g' | \
sed -e "s/'/ ' /g" -e 's/"//g' -e 's/\./ \. /g' -e 's/<br \/>/ /g' \
-e 's/,/ , /g' -e 's/(/ ( /g' -e 's/)/ ) /g' -e 's/\!/ \! /g' \
-e 's/\?/ \? /g' -e 's/\;/ /g' -e 's/\:/ /g' | tr -s " " | myshuf
}
# 然后就是下载数据集,normalize数据集,normalize_text()包括了myshuf。训练集是dbpedia.train,测试集是dbpedia.test
RESULTDIR=result
DATADIR=data
mkdir -p "${RESULTDIR}"
mkdir -p "${DATADIR}"
if [ ! -f "${DATADIR}/dbpedia.train" ]
then
wget -c "https://github.com/le-scientifique/torchDatasets/raw/master/dbpedia_csv.tar.gz" -O "${DATADIR}/dbpedia_csv.tar.gz"
tar -xzvf "${DATADIR}/dbpedia_csv.tar.gz" -C "${DATADIR}"
cat "${DATADIR}/dbpedia_csv/train.csv" | normalize_text > "${DATADIR}/dbpedia.train"
cat "${DATADIR}/dbpedia_csv/test.csv" | normalize_text > "${DATADIR}/dbpedia.test"
fi
# 编译C++代码
make
# 根据训练集训练fasttext,输出训练的结果
./fasttext supervised -input "${DATADIR}/dbpedia.train" -output "${RESULTDIR}/dbpedia" -dim 10 -lr 0.1 -wordNgrams 2 -minCount 1 -bucket 10000000 -epoch 5 -thread 4
# 根据训练的结果以及测试集进行测试,得到准确率
./fasttext test "${RESULTDIR}/dbpedia.bin" "${DATADIR}/dbpedia.test"
# 根据训练的结果以及测试集,输出测试集的结果
./fasttext predict "${RESULTDIR}/dbpedia.bin" "${DATADIR}/dbpedia.test" > "${RESULTDIR}/dbpedia.test.predict"

fastText 词向量示例

word-vector-example.sh 示例说明,包括用skip-gram模式训练和评估词向量的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env bash
#
# Copyright (c) 2016-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
#
RESULTDIR=result
DATADIR=data
mkdir -p "${RESULTDIR}"
mkdir -p "${DATADIR}"
# 下载语料
if [ ! -f "${DATADIR}/fil9" ]
then
wget -c http://mattmahoney.net/dc/enwik9.zip -P "${DATADIR}"
unzip "${DATADIR}/enwik9.zip" -d "${DATADIR}"
perl wikifil.pl "${DATADIR}/enwik9" > "${DATADIR}"/fil9
fi
# 下载测试集
if [ ! -f "${DATADIR}/rw/rw.txt" ]
then
wget -c https://nlp.stanford.edu/~lmthang/morphoNLM/rw.zip -P "${DATADIR}"
unzip "${DATADIR}/rw.zip" -d "${DATADIR}"
fi
# 编译源代码
make
# 用skipgram模式进行训练
./fasttext skipgram -input "${DATADIR}"/fil9 -output "${RESULTDIR}"/fil9 -lr 0.025 -dim 100 \
-ws 5 -epoch 1 -minCount 5 -neg 5 -loss ns -bucket 2000000 \
-minn 3 -maxn 6 -thread 4 -t 1e-4 -lrUpdateRate 100
cut -f 1,2 "${DATADIR}"/rw/rw.txt | awk '{print tolower($0)}' | tr '\t' '\n' > "${DATADIR}"/queries.txt
cat "${DATADIR}"/queries.txt | ./fasttext print-word-vectors "${RESULTDIR}"/fil9.bin > "${RESULTDIR}"/vectors.txt
# 评估训练的词向量
python eval.py -m "${RESULTDIR}"/vectors.txt -d "${DATADIR}"/rw/rw.txt

-------------本文结束感谢您的阅读-------------
坚持整理学习笔记,您的支持将鼓励我继续整理下去!