首先,我们得搞清楚,FastText 是什么?有的地方说是分类模型,有的地方又将其用于词向量,那么,FastText究竟指的是什么?我搜集资料时发现很多视频的up主都没弄清楚,其实,FastText 的指向有两个模型,一个就是指向的文本分类模型,首先在论文《Bag of Tricks for Efficient Text Classification》中提出,另一个自然就是词向量模型,首先在文章《Enriching Word Vectors with Subword Information》中提出,接下来我们将会介绍一下两种FastText模型,并将其复现。
FastText的分类模型具有速度快、精度高的优点,其分类的准确率甚至不输于大型的深度学习模型,但是由于其模型简单,其训练的速度则要比后者快上好几个数量级。
FastText在模型结构上采用了
C
B
O
W
CBOW
CBOW 模型的结构,结构如下:
其中这里的
x
1
,
x
2
,
⋯
,
x
N
x_1,x_2,\cdots,x_N
x1,x2,⋯,xN 是输入的词,整个网络与
C
B
O
W
CBOW
CBOW 都一样,不同之处主要有以下方面
首先要声明,在原论文中,n-gram并不是FastText必要的步骤,仅仅是一个锦上添花的步骤而已,没有n-gram它还是FastText。
引入n-gram首先是为了解决word2vec中的词序问题,比如两个句子“你礼貌吗”和“礼貌你吗”这两个句子仅仅词序不同,但是意思却天差地别,这种情况word2vec是检测不到词序的不同的,由此提出了n-gram。
注意,词分类模型的n-gram的是word级别的,并不是字符级别的,比如,有如下的句子
I
h
a
v
e
a
n
a
p
p
l
e
I \hspace{0.5em}have\hspace{0.5em} an \hspace{0.5em}apple
Ihaveanapple如果n-gram中的
n
=
2
n=2
n=2 时,那么输入其中的句子经过n-gram后被分为以下部分
I
h
a
v
e
,
h
a
v
e
a
n
,
a
n
a
p
p
l
e
I \hspace{0.5em}have,have\hspace{0.5em} an,an \hspace{0.5em}apple
Ihave,havean,anapple三个部分,输入也是这三个部分。
尽管如此,还是需要注意一下几个方面:
FastText 模型中也引入了n-gram,n-gram的引入其实是为了解决word2vec忽略词型的问题。比如单词 e a t , e a t e n eat,eaten eat,eaten 其实就是一个单词的不同时态,但是,在不同语境下,其词向量可能会相差特别大,而引入n-gram就是为了能够很好的解决词的形态学方面的问题。
例如一个单词
n
o
r
m
a
l
normal
normal,如果使用n-gram,当
n
=
3
n=3
n=3 时,可以将其分为
n
o
r
,
o
r
m
,
r
m
a
,
m
a
l
nor,orm,rma,mal
nor,orm,rma,mal 四个部分,在使用n-gram时一般会用一个尖括号将单词括起来,表示这个单词的开始和结束,如
n
o
r
m
a
l
normal
normal 变为
<
n
o
r
m
a
l
>
引入了n-gram后,接下来需要做的就是将n-gram后的部分输入模型,这里因为是根据word2vec改进的,所以依旧使用的是 S k i p − g r a m Skip-gram Skip−gram 模型,而这里输入的时候,因为我们有子词以及词,就又会有不同的选择了。
对于word2vec而言,其训练的有中心词以及周围词矩阵,设 a a a 为中心词矩阵, b b b 为周围词矩阵,那么搭配就有很多种,这里FastText选择的是 a 3 + b 1 a3+b1 a3+b1,即对中心词使用子词+词的输入来预测周围词。
2017 ACL 的 FastText展示提问环节,有人问过为什么不是 a 2 a2 a2,作者的回答是他们试过 a 2 a2 a2,发现效果并不好。
比如一个单词
n
o
r
m
a
l
normal
normal,我们将其输入到网络中时,输入的是
<
n
o
,
n
o
r
,
o
r
m
,
r
m
a
,
m
a
l
,
a
l
>
,
n
o
r
m
a
l
对于未登录词 ( O u t − o f − V o c a b u l a r y ) (Out-of-Vocabulary) (Out−of−Vocabulary),这里采用的方法是将 O O V OOV OOV 词按照n-gram进行拆解,然后将每一部分的词向量进行相加并求平均值,则得到 O O V OOV OOV 词的词向量。
当然,对于进行 n-gram 后没有的部分也是无法进行词向量的计算的,而且,这样做的前提是基于词的拼写、子词在形态学上是有意义的。因此,不同语言,不同效果,作者发现这种方法对阿拉伯语、德语和俄语就比对英语、法语和西班牙语效果好。
实现FastText的过程与word2vec相差不大,唯一的差距就是word2vec使用的是一个单词来预测上下文,而FastText使用的是word+gram后的结果来进行上下文的预测,由于二者的相似性,这里就不再一步步的给大家进行实现了,如果要训练FastText模型,可以安装 fasttext
库或者使用 gensim.models
中的Fasttext模型,gensim
中的函数是生词词向量, fasttext
中既含有词向量的生成,也含有文本分类,具体使用方法如下。
训练模型
训练模型直接调用构造函数即可,该构造函数的参数如下:
参数 | 描述 | 可选值 |
---|---|---|
vector_size | 词向量维度 | 整数 |
window | 词向量窗口大小 | 整数 |
epochs | 训练轮次 | 整数 |
negative | 负采样个数 | 整数 |
sg | 是否使用skipgram ,1是使用,0是用cbow | 0,1 |
hs | 是否使用层化softmax,1是使用,0是不使用 | 0,1 |
alpha | 学习率 | 0-1 |
使用如下所示:
from gensim.models import FastText
from gensim.models.word2vec import LineSentence
model=FastText(LineSentence(open(r'../GloVe/data/test.txt', 'r', encoding='utf8')),
vector_size=150, window=5, min_count=5, epochs=10, min_n=3, max_n=6, word_ngrams=1,workers=4)
常用API
API | 描述 |
---|---|
model.wv[word] | 得到 word 的词向量 |
"nights" in model.wv.vocab | 判断词是否在词向量中 |
model.similarity("night", "nights") | 计算两个词的相似度 |
model.most_similar(word, topn=n) | 查找最相关的两个词 |
model.similarity("night", "nights") | 计算两个词的相似度 |
保存模型
API | 描述 |
---|---|
model.wv.save_word2vec_format('fasttext.vector', binary=False) | 保存词向量 |
model.save('fasttext_test.model') | 保存模型 |
model = FastText.load('fasttext_test.model') | 载入模型 |
训练模型
这里我们以前面的预处理的文本做示例,文本处理如下。
训练词向量使用的函数为 train_unsupervised
,其常用参数如下:
参数 | 描述 | 可选参数 | 默认值 |
---|---|---|---|
input | 输入的文件 | ||
lr | 学习率 | 0-1 | 0.1 |
dim | 学习后词向量维度 | 整数 | 100 |
ws | 背景词 | 整数 | 5 |
neg | 负采样个数 | 整数 | 5 |
epoch | 训练轮次 | 整数 | 5 |
model | 训练的方式 | skipgram,cbow | skipgram |
wordNgrams | n-gram的值 | 整数 | 1 |
loss | 损失函数 | 可选ns, hs, softmax, ova ,hs 为层化softmax,ova 用于多标签分类 | softmax |
model=fasttext.train_unsupervised(input='../GloVe/data/test.txt', model='skipgram')
常用API
API | 描述 |
---|---|
model.get_word_vector(word) | 得到 word 的词向量 |
model.get_nearest_neighbors(word) | 得到距离 word 最近的10个词向量 |
model.get_analogies(a,b,c) | 类比,类比 a 和 b 的关系,得到 c 对应该关系的词 |
保存和加载模型
使用 model.save_model(filename)
以及 model.load_model(filename)
即可。
训练模型
Fasttext用于分类使用的函数是 train_unsupervised
,该函数对传入的文本数据有一定的要求,其要求传入的数据标签在前,语句在后,且标签前加上 __label__
前缀,格式示例如下:
__label__sauce __label__cheese How much does potato starch affect a cheese sauce recipe?
__label__food-safety __label__acidity Dangerous pathogens capable of growing in acidic environments
__label__cast-iron __label__stove How do I cover up the white spots on my cast iron stove?
__label__restaurant Michelin Three Star Restaurant; but if the chef is not there
__label__knife-skills __label__dicing Without knife skills, how can I quickly and accurately dice vegetables?
__label__storage-method __label__equipment __label__bread What's the purpose of a bread box?
__label__baking __label__food-safety __label__substitutions __label__peanuts how to seperate peanut oil from roasted peanuts at home?
__label__chocolate American equivalent for British chocolate terms
比如上述的第一行,其标签就为 sauce, cheese
,句子为 How much does potato starch affect a cheese sauce recipe?
,该函数的API如下:
参数 | 描述 | 可选参数 | 默认值 |
---|---|---|---|
input | 输入的文件 | ||
lr | 学习率 | 0-1 | 0.1 |
dim | 学习后词向量维度 | 整数 | 100 |
ws | 背景词 | 整数 | 5 |
neg | 负采样个数 | 整数 | 5 |
epoch | 训练轮次 | 整数 | 5 |
model | 训练的方式 | skipgram,cbow | skipgram |
wordNgrams | n-gram的值 | 整数 | 1 |
loss | 损失函数 | 可选ns, hs, softmax, ova ,hs 为层化softmax,ova 用于多标签分类 | softmax |
label | 标签的前缀 | 字符串 | __label__ |
常用API
API | 描述 |
---|---|
model.test(file) | 对文件进行测试集的测试,返回三个参数,分别是 样本数,准确率,召回率 |
model.pridict(sentence,k=-1) | 预测句子所属的类别,返回各个标签及概率,k=-1 表示返回全部标签及其概率 |
保存和加载模型
使用 model.save_model(filename)
以及 model.load_model(filename)
即可。