一、整体结构
fastText的代码整体结构如下图所示:
1、fastText简介
- fastText是一个快速文本分类算法,与基于神经网络的分类算法相比有两大优点:
1、fastText在保持高精度的情况下加快了训练速度和测试速度
2、fastText不需要预训练好的词向量,fastText会自己训练词向量 3、fastText两个重要的优化:Hierarchical
Softmax、N-gram
2、fastText模型架构
-
fastText模型架构和word2vec中的CBOW很相似,不同之处是fastText预测标签而CBOW预测的是中间词,即模型架构类似但是模型的任务不同。下面我们先看一下CBOW的架构:
-
word2vec将上下文关系转化为多分类任务,进而训练逻辑回归模型,这里的类别数量|V|词库大小。通常的文本数据中,词库少则数万,多则百万,在训练中直接训练多分类逻辑回归并不现实。word2vec中提供了两种针对大规模多分类问题的优化手段,negative sampling 和hierarchical softmax。在优化中,negative sampling只更新少量负面类,从而减轻了计算量。hierarchical softmax将词库表示成前缀树,从树根到叶子的路径可以表示为一系列二分类器,一次多分类计算的复杂度从|V|降低到了树的高度
-
fastText模型架构:其中x1,x2,…,xN−1,xN表示一个文本中的n-gram向量,每个特征是词向量的平均值。这和前文中提到的cbow相似,cbow用上下文去预测中心词,而此处用全部的n-gram去预测指定类别
3、层次softmax
- softmax函数常在神经网络输出层充当激活函数,目的就是将输出层的值归一化到0-1区间,将神经元输出构造成概率分布,主要就是起到将神经元输出值进行归一化的作用
- 在标准的softmax中,计算一个类别的softmax概率时,我们需要对所有类别概率做归一化,在这类别很大情况下非常耗时,因此提出了分层softmax(Hierarchical Softmax),思想是根据类别的频率构造霍夫曼树来代替标准softmax,通过分层softmax可以将复杂度从N降低到logN,下图给出分层softmax示例:
- 在层次softmax模型中,叶子结点的词没有直接输出的向量,而非叶子节点都有响应的输在在模型的训练过程中,通过Huffman编码,构造了一颗庞大 Huffman树,同时会给非叶子结点赋予向量。我们要计算的是目标词w的概率,这个概率的具体含义,是指从root结点开始随机走,走到目标词w的概率。因此在途中路过非叶子结点(包括root)时,需要分别知道往左走和往右走的概率。
4、N-gram特征
- n-gram是基于语言模型的算法,基本思想是将文本内容按照子节顺序进行大小为N的窗口滑动操作,最终形成窗口为N的字节片段序列。而且需要额外注意一点是n-gram可以根据粒度不同有不同的含义,有字粒度的n-gram和词粒度的n-gram。
二、fastText实战篇
1、fastText环境要求
- fastText需要运行在MacOS或Linux上,因为fastText使用了C++11,因此需要很好支持C++11的编译器,支持的编译器包括:
(1) gcc-4.6.3 或者更新版本
(2) clang-3.3 或者更新版本 - 编译是使用Makefile执行的,因此你需要有一个工作的make,对于单词相似度评估脚本则需要如下环境:
(1) python2.6 或者更新 (2) numpy 和 spicy
2、在本地快速搭建fastText
为了搭建fastText,打开命令窗口依次执行以下命令
$ git clone https://github.com/facebookresearch/fastText.git
$ cd fastText
$ make
上述命令将为所有类和主二进制fastText生成目标文件,如果你不打算使用默认的系统范围编译器,可以更新Makefile(CC和include)开头定义的两个宏
3、fastText-文本分类
- 首先我们需要做的便是安装搭建fastText,需要系统支持c++
11的c++编译器,先从GitHub上下载fastText到本地(版本在更新,可以到GitHub上查看最近版本进行下载):
$ wget https://github.com/facebookresearch/fastText/archive/v0.1.0.zip
- 然后将下载的zip文件夹进行解压,解压后进入目录对fastText项目执行make命令进行编译(因此这里便需要你的系统有支持c++11的编译器)
$ unzip v0.1.0.zip
$ cd fastText-0.1.0
$ make
- 在根目录下运行名为fasttext的二进制文件,便会打印出fastText支持的各种不同的命令,如:supervised进行模型训练,quantize量化模型以减少内存使用,test进行模型测试,predict预测最可能的标签等,运行结果如下所示:
>> ./fasttext
usage: fasttext <command> <args>
The commands supported by fasttext are:
supervised train a supervised classifier
quantize quantize a model to reduce the memory usage
test evaluate a supervised classifier
predict predict most likely labels
predict-prob predict most likely labels with probabilities
skipgram train a skipgram model
cbow train a cbow model
print-word-vectors print word vectors given a trained model
print-sentence-vectors print sentence vectors given a trained model
nn query for nearest neighbors
analogies query for analogies
每个命令分别为:
supervised: 训练一个监督分类器
quantize:量化模型以减少内存使用量
test:评估一个监督分类器
predict:预测最有可能的标签
predict-prob:用概率预测最可能的标签
skipgram:训练一个 skipgram 模型
cbow:训练一个 cbow 模型
print-word-vectors:给定一个训练好的模型,打印出所有的单词向量
print-sentence-vectors:给定一个训练好的模型,打印出所有的句子向量
nn:查询最近邻居
analogies:查找所有同类词
4、获取数据及数据预处理
- 我们需要带有标签的数据去训练我们的监督学习的分类器,本教程中,我们使用cooking相关数据构建我们的分类器,因此首先我们下载数据,数据网址为stackexchange,进行如下命令操作:
wget https://dl.fbaipublicfiles.com/fasttext/data/cooking.stackexchange.tar.gz
tar xvzf cooking.stackexchange.tar.gz
head cooking.stackexchange.txt
- 通过head命令便可看到文档形式,文档的每一行都包含一个标签,标签后面跟着相应的单词短语,所有的标签都以__label__前缀开始,这事fastText便是标签和单词短语的方式,训练的模型便是预测文档中给定单词短语预测其对应的标签。
- 在训练分类器之前,我们需要将数据分割成训练集和验证集,我们将使用验证集来评估学习到的分类器对新数据的性能好坏,先通过下面命令来查看文档中总共含有多少数据:
>> wc cooking.stackexchange.txt
15404 169582 1401900 cooking.stackexchange.txt
可以看到我们数据中总共包含了15404个示例,我们把文档分成一个包含12404个示例的训练集和一个包含3000个示例的验证集,执行如下命令:
head -n 12404 cooking.stackexchange.txt > cooking.train
tail -n 3000 cooking.stackexchange.txt > cooking.valid
5、使用fastText快速搭建分类器
- 接下来我们便开始训练我们的模型,首先执行如下命令进行模型的训练:
>> ./fasttext supervised -input cooking.train -output model_cooking
Read 0M words
Number of words: 14598
Number of labels: 734
Progress: 100.0% words/sec/thread: 75109 lr: 0.000000 loss: 5.708354 eta: 0h0m
- input命令选项指示训练数据,-output选项指示的是保存的模型的位置,在训练结束后,文件model_cooking.bin是在当前目录中创建的,model_cooking.bin便是我们保存训练模型的文件。
- 模型训练好之后,我们可以交互式测试我们的分类器,即单独测试某一个句子所属的类别,可以通过以下命令进行交互式测试:
>> ./fasttext predict model_cooking.bin -
输入以上命令后,命令行会提示你输入句子,然后我们可以进行如下句子测试:
Which baking dish is best to bake a banana bread ?
上面句子可以得到预测的标签是baking,显然这个预测结果是正确的,我们再进行尝试
Why not put knives in the dishwasher?
上面句子预测的标签是food-safety,可以看出是不相关的,显然预测的不正确,为了验证学习到的分类模型的好坏,我们在验证集上对它进行测试,观察模型的精准率precision和召回率recall:
>> ./fasttext test model_cooking.bin cooking.valid
N 3000
P@1 0.124
R@1 0.0541
Number of examples: 3000
6、模型优化
- 上面通过使用默认参数运行fastText训练得到的模型在分类新问题上效果很差,接下来我们通过更改默认参数来提高性能。
数据预处理: - 查看数据,我们发现有些单词包含大写字母和标点符号,因此改善模型性能的第一步就是应用一些简单的预处理,预处理可以使用命令行工具例如sed、tr来对文本进行简单的标准化操作,执行命令如下:
>> cat cooking.stackexchange.txt | sed -e "s/\([.\!?,'/()]\)/ \1 /g" | tr "[:upper:]" "[:lower:]" > cooking.preprocessed.txt
>> head -n 12404 cooking.preprocessed.txt > cooking.train
>> tail -n 3000 cooking.preprocessed.txt > cooking.valid
接下来我们在预处理的数据集上进行模型训练并进行测试,命令如下:
>> ./fasttext supervised -input cooking.train -output model_cooking
Read 0M words
Number of words: 9012
Number of labels: 734
Progress: 100.0% words/sec/thread: 82041 lr: 0.000000 loss: 5.671649 eta: 0h0m h-14m
>> ./fasttext test model_cooking.bin cooking.valid
N 3000
P@1 0.164
R@1 0.0717
Number of examples: 3000
观察上面的结果,由于对数据预处理,词典变小了,由原来的14K个单词变成了9K,精准率也上升了4%,因此数据预处理起到了一定的效果。
更多的训练次数和更大的学习率:
- 在默认情况下,fastText在训练期间对每个训练用例仅重复使用五次,这太小,因为我们的训练集只有12k训练样例,因此我们可以通过-epoch选项增加每个样例的使用次数,命令如下:
>> ./fasttext supervised -input cooking.train -output model_cooking -epoch 25
Read 0M words
Number of words: 9012
Number of labels: 734
Progress: 100.0% words/sec/thread: 77633 lr: 0.000000 loss: 7.147976 eta: 0h0m
然后测试模型查看效果:
>> ./fasttext test model_cooking.bin cooking.valid
N 3000
P@1 0.501
R@1 0.218
Number of examples: 3000
- 从上面测试效果可以看出,精准率和召回率都有了大幅度提升,可见增加每个样例的使用次数对于数据集少的情况下效果提升明显。另一个增强算法能力是改变模型的学习速度即学习速率,这对应于处理每个示例后模型的更改程度,当学习率为0时意味着模型根本不会发生改变,因此不会学到任何东西,良好的学习率值在0.1-1.0的范围内,下面我们通过设置算法学习率为learning rate = 1.0进行模型训练:
>> ./fasttext supervised -input cooking.train -output model_cooking -lr 1.0
Read 0M words
Number of words: 9012
Number of labels: 734
Progress: 100.0% words/sec/thread: 81469 lr: 0.000000 loss: 6.405640 eta: 0h0m
>> ./fasttext test model_cooking.bin cooking.valid
N 3000
P@1 0.563
R@1 0.245
Number of examples: 3000
可以看到效果比上面增加epoch还要好,下面我们来将二者结合起来:
>> ./fasttext supervised -input cooking.train -output model_cooking -lr 1.0 -epoch 25
Read 0M words
Number of words: 9012
Number of labels: 734
Progress: 100.0% words/sec/thread: 76394 lr: 0.000000 loss: 4.350277 eta: 0h0m
>> ./fasttext test model_cooking.bin cooking.valid
N 3000
P@1 0.585
R@1 0.255
Number of examples: 3000
下面我们来增加一些新的方式来进一步提升模型的性能。
word n-grams:
- 此方案中,我们使用单词bigrams而不是仅仅是unigrams来提高模型的性能,这对于词序很重要的分类问题尤其重要,例如情感分析。n-gram是基于语言模型的算法,基本思想是将文本内容按照子节顺序进行大小为N的窗口滑动操作,最终形成窗口为N的字节片段序列。训练模型命令如下:
>> ./fasttext supervised -input cooking.train -output model_cooking -lr 1.0 -epoch 25 -wordNgrams 2
Read 0M words
Number of words: 9012
Number of labels: 734
Progress: 100.0% words/sec/thread: 75366 lr: 0.000000 loss: 3.226064 eta: 0h0m
>> ./fasttext test model_cooking.bin cooking.valid
N 3000
P@1 0.599
R@1 0.261
Number of examples: 3000
通过几个步骤,可以看出我们将模型精准率从12.4%提升到了59.9%。