Facebook词向量工具FastText
FastText简介
fastText是Facebook于2016年开源的一个词向量计算和文本分类工具,在文本分类任务中,fastText(浅层网络)往往能取得和深度网络相媲美的精度,却在训练时间上比深度网络快许多数量级。在标准的多核CPU上, 能够训练10亿词级别语料库的词向量在10分钟之内,能够分类有着30万多类别的50多万句子在1分钟之内。
FastText与Word2Vec
fastText的其中的一个作者是Thomas Mikolov。也正是这个人在谷歌的时候,带领团队在2012年提出了 word2vec代替了one-hot编码,将词表示为一个低维连续嵌入,极大促进了NLP的发展。14年她去了脸书,然后提出了word2vec的改进版:fasttext。所以fastText和word2vec在结构上很相似。
相同之处:
- 图模型结构很像,都是采用 embedding 向量的形式,得到 word 的隐向量表达。
- 采用很多相似的优化方法,比如使用 Hierarchical softmax 优化训练和预测中的打分速度。
- 训练词向量时,两者都是无监督算法。输入层是 context window 内的 term。输出层对应的是每一个 term,计算某 term 的概率最大;
- 在使用层次softmax的时候,huffman 树叶子节点处是训练语料里所有词的向量。
不同之处:
- 模型的输出层:word2vec的输出层,对应的是每一个term,计算某term的概率最大;而fasttext的输出层对应的是分类的label。不过不管输出层对应的是什么内容,其对应的vector都不会被保留和使用;
- 模型的输入层:word2vec的输入层,是 context window 内的term;而fasttext 对应的整个sentence的内容,包括term,也包括 n-gram的内容;
- 两者本质的不同,体现在 Hierarchical softmax的使用。
- Wordvec的目的是得到词向量,该词向量 最终是在输入层得到,输出层对应的Hierarchical softmax也会生成一系列的向量,但最终都被抛弃,不会使用。
- fasttext则充分利用了Hierarchical softmax的分类功能,遍历分类树的所有叶节点,找到概率最大的label(一个或者N个)
FastText的优点:
- 适合大型数据+高效的训练速度:能够训练模型“在使用标准多核CPU的情况下10分钟内处理超过10亿个词汇”,特别是与深度模型对比,fastText能将训练时间由数天缩短到几秒钟。使用一个标准多核 CPU,得到了在10分钟内训练完超过10亿词汇量模型的结果。此外, fastText还能在五分钟内将50万个句子分成超过30万个类别。
- 支持多语言表达:利用其语言形态结构,fastText能够被设计用来支持包括英语、德语、西班牙语、法语以及捷克语等多种语言。它还使用了一种简单高效的纳入子字信息的方式,在用于像捷克语这样词态丰富的语言时,这种方式表现得非常好,这也证明了精心设计的字符 n-gram 特征是丰富词汇表征的重要来源。FastText的性能要比时下流行的word2vec工具明显好上不少,也比其他目前最先进的词态词汇表征要好。
- fastText专注于文本分类,在许多标准问题上实现当下最好的表现(例如文本倾向性分析或标签预测)。
- 比word2vec更考虑了相似性,比如 fastText 的词嵌入学习能够考虑 english-born 和 british-born 之间有相同的后缀,但 word2vec 却不能。
模型架构
fastText的架构和word2vec中的CBOW的架构类似,因为它们的作者Tomas Mikolov,而且确实fastText也算是word2vec所衍生出来的。
CBOW的架构
输入的是 的上下文2d个词,经过隐藏层后,输出的是 。word2vec将上下文关系转化为多分类任务,进而训练逻辑回归模型,这里的类别数量是 词库大小。通常的文本数据中,词库少则数万,多则百万,在训练中直接训练多分类逻辑回归并不现实。word2vec中提供了两种针对大规模多分类问题的优化手段,negative sampling 和 hierarchical softmax。在优化中,negative sampling 只更新少量负面类,从而减轻了计算量。hierarchical softmax 将词库表示成前缀树,从树根到叶子的路径可以表示为一系列二分类器,一次多分类计算的复杂度从 降低到了树的高度。
fastText模型架构
其中 表示一个文本中的n-gram向量,每个特征是词向量的平均值。这和前文中提到的cbow相似,cbow用上下文去预测中心词,而此处用全部的n-gram去预测指定类别。注意:此架构图没有展示词向量的训练过程。可以看到,和CBOW一样,fastText模型也只有三层:输入层、隐含层、输出层(Hierarchical Softmax),输入都是多个经向量表示的单词,输出都是一个特定的target,隐含层都是对多个词向量的叠加平均。
fastText与CBOW的不同点:
- CBOW的输入是目标单词的上下文,fastText的输入是多个单词及其n-gram特征,这些特征用来表示单个文档
- CBOW的输入单词被one-hot编码过,fastText的输入特征是被embedding过
- CBOW的输出是目标词汇,fastText的输出是文档对应的类标
值得注意的是,fastText在输入时,将单词的字符级别的n-gram向量作为额外的特征;在输出时,fastText采用了分层Softmax,大大降低了模型训练时间。
Hierarchical softmax
Softmax回归(Softmax Regression)又被称作多项逻辑回归(multinomial logistic regression),它是逻辑回归在处理多类别任务上的推广。
在逻辑回归中,我们有m个被标注的样本: ,其中 。因为类标是二元的,所以我们有 。我们的假设(hypothesis)有如下形式:
代价函数(cost function)如下:
在Softmax回归中,类标是大于2的,因此在我们的训练集 中,。给定一个测试输入x,我们的假设应该输出一个K维的向量,向量内每个元素的值表示x属于当前类别的概率。具体地,假设 形式如下:
代价函数如下:
其中 是指示函数,即
既然我们说Softmax回归是逻辑回归的推广,那我们是否能够在代价函数上推导出它们的一致性呢?当然可以,于是:
可以看到,逻辑回归是softmax回归在K=2时的特例。你可能也发现了,标准的Softmax回归中,要计算y=j时的Softmax概率: ,我们需要对所有的K个概率做归一化,这在 很大时非常耗时。于是,分层Softmax诞生了,它的基本思想是使用树的层级结构替代扁平化的标准Softmax,使得在计算 时,只需计算一条路径上的所有节点的概率值,无需在意其它的节点。
下图是一个分层Softmax示例:
树的结构是根据类标的频数构造的霍夫曼树。K个不同的类标组成所有的叶子节点,K-1个内部节点作为内部参数,从根节点到某个叶子节点经过的节点和边形成一条路径,路径长度被表示为 。于是 就可以被写成:
其中: 表示sigmoid函数; 表示n节点的左孩子; 是一个特殊的函数,被定义为: ; 是中间节点 的参数;X是Softmax层的输入。
上图中,高亮的节点和边是从根节点到 的路径,路径长度 , 可以被表示为:
于是,从根节点走到叶子节点 ,实际上是在做了3次二分类的逻辑回归。通过分层的Softmax,计算复杂度一下从 降低到 。
N-gram
word2vec把语料库中的每个单词当成原子的,它会为每个单词生成一个向量。这忽略了单词内部的形态特征,比如:“book” 和“books”, “apple” 和“apples”,这两个例子中,两个单词都有较多公共字符,即它们的内部形态类似,但是在传统的word2vec中,这种单词内部形态信息因为它们被转换成不同的id丢失了。
为了克服这个问题,fastText使用了字符级别的n-grams来表示一个单词。对于单词“apple”,假设n的取值为3,则它的trigram有
“<ap”, “app”, “ppl”, “ple”, “le>”
其中,<表示前缀,>表示后缀。于是,我们可以用这些trigram来表示“apple”这个单词,进一步,我们可以用这5个trigram的向量叠加来表示“apple”的词向量。
这带来两点好处:
- 对于低频词生成的词向量效果会更好。因为它们的n-gram可以和其它词共享。
- 对于训练词库之外的单词,仍然可以构建它们的词向量。我们可以叠加它们的字符级n-gram向量。
fastText的优化
子空间量化
product quantization是一种保存数据间距离的压缩技术。PQ 用一个码本来近似数据,与传统的 keams 训练码本不同的是, PQ 将数据空间划分为 k 个子空间,并分别用 kmeans 学习子空间码本。数据的近似和重建均在子空间完成,最终拼接成结果。在 fasttext 中,子空间码本大小为 256,可以用 1 byte 表示。子空间的数量在 [2, d/2] 间取值。除了用 PQ 对数据进行量化压缩,fasttext 还提供了对分类系数的 PQ 量化选项。PQ 的优化能够在不影响分类其表现的情况下,将分类模型压缩为原大小的 。
裁剪字典内容
fasttext 提供了一个诱导式裁剪字典的算法,保证裁剪后的字典内容覆盖了所有的文章。具体而言,fasttext 存有一个保留字典,并在线处理文章,如果新的文章没有被保留字典涵盖,则从该文章中提取一个 norm 最大的词和其子串加入字典中。字典裁剪能够有效将模型的数量减少,甚至到原有的 。
FastText的使用
使用FastText训练词向量
# -*- coding: utf-8 -*- import jieba import os import fasttext def get_data(): # 清华大学的新闻分类文本数据集下载:https://thunlp.oss-cn-qingdao.aliyuncs.com/THUCNews.zip data_dir = 'D:\\迅雷下载\\THUCNews\\THUCNews\\财经' with open("finance_news_cut.txt", "w", encoding='utf-8') as f: for file_name in os.listdir(data_dir): print(file_name) file_path = data_dir + os.sep + file_name with open(file_path, 'r', encoding='utf-8') as fr: text = fr.read() seg_text = jieba.cut(text.replace("\t", " ").replace("\n", " ")) outline = " ".join(seg_text) f.write(outline) f.flush() def train_model(): model = fasttext.train_unsupervised('finance_news_cut.txt') model.save_model("news_fasttext.model.bin") def test_model(): model = fasttext.load_model('news_fasttext.model.bin') print(model.words) print(model.get_word_vector("股票")) print(model.get_nearest_neighbors('股票')) if __name__ == "__main__": pass # get_data() # train_model() test_model()
备注:不要使用
pip install FastText进行安装,否则会出现如下报错:
AttributeError: '_FastText' object has no attribute 'get_nearest_neighbors'
安装流程:
git clone https://github.com/facebookresearch/fastText.git cd fastText pip install .
使用FastText进行文本分类
# -*- coding: utf-8 -*- import jieba import os import fasttext def get_data(): # 清华大学的新闻分类文本数据集下载:https://thunlp.oss-cn-qingdao.aliyuncs.com/THUCNews.zip data_dir = 'D:\\迅雷下载\\THUCNews\\THUCNews' # 生成训练数据&测试数据 with open("news_fasttext_train.txt", "w", encoding='utf-8') as train_f, open("news_fasttext_test.txt", "w", encoding='utf-8') as test_f: for category in os.listdir(data_dir): print(category) category_path = data_dir + os.sep + category count = 0 for file_name in os.listdir(category_path): file_path = data_dir + os.sep + category + os.sep + file_name with open(file_path, 'r', encoding='utf-8') as fr: count += 1 text = fr.read() seg_text = jieba.cut(text.replace("\t", " ").replace("\n", " ")) outline = " ".join(seg_text) outline = outline + "\t__label__" + category + "\n" print(count) if count < 10000: train_f.write(outline) train_f.flush() continue elif count < 20000: test_f.write(outline) test_f.flush() continue else: break def train_model(): model = fasttext.train_supervised("news_fasttext_train.txt", label_prefix="__label__") model.save_model("news_fasttext.model.bin") def test_model(): model = fasttext.load_model('news_fasttext.model.bin') result = model.test("news_fasttext_test.txt") print('precision:', result[1]) # fasttext只提供全部结果的p值和r值,想要统计不同分类的结果,就需要自己写代码来实现了。 labels_right = [] texts = [] with open("news_fasttext_test.txt", encoding='utf-8') as fr: for line in fr: line = line.rstrip() labels_right.append(line.split("\t")[1].replace("__label__", "")) texts.append(line.split("\t")[0]) labels_predict = [term[0] for term in model.predict(texts)[0]] # 预测输出结果为二维形式 text_labels = list(set(labels_right)) text_predict_labels = list(set(labels_predict)) A = dict.fromkeys(text_labels, 0) # 预测正确的各个类的数目 B = dict.fromkeys(text_labels, 0) # 测试数据集中各个类的数目 C = dict.fromkeys(text_predict_labels, 0) # 预测结果中各个类的数目 for i in range(0, len(labels_right)): B[labels_right[i]] += 1 C[labels_predict[i]] += 1 if labels_right[i] == labels_predict[i].replace('__label__', ''): A[labels_right[i]] += 1 # 计算准确率,召回率,F值 for key in B: try: r = float(A[key]) / float(B[key]) p = float(A[key]) / float(C['__label__' + key]) f = p * r * 2 / (p + r) print("%s:\t p:%f\t r:%f\t f:%f" % (key, p, r, f)) except: print("error:", key, "right:", A.get(key, 0), "real:", B.get(key, 0), "predict:", C.get(key, 0)) if __name__ == "__main__": pass get_data() # train_model() # test_model()
另外,还可以使用gensim来使用FastText,如:
from gensim.models import FastText
参考链接:
- https://fasttext.cc/
- https://github.com/facebookresearch/fastText
- https://heleifz.github.io/14732610572844.html
The post Facebook词向量工具FastText appeared first on 标点符.
Related posts: