FastText总结,fastText 源码分析

FastText是Facebook开发的一款快速文本分类器,提供简单而高效的文本分类和表征学习的方法,性能比肩深度学习而且速度更快。

在比赛中用了fasttext,发现速度惊人,而且内存优化比较好,用tensorflow搭建3层模型,内存超52g了,但是fasttext训练却非常快,文本分类准确率都还可以,只是为什么loss这么高

2. fastText VS Tagspace

Mikolov 在 fastTetxt 的论文中报告了两个实验,其中一个实验和 Tagspace
模型进行对比。实验是在 YFCC100M 数据集上进行的, YFCC100M 数据集包含将近
1 亿张图片以及摘要、标题和标签。实验使用摘要和标题去预测标签。Tagspace
模型是建立在 Wsabie 模型的基础上的。Wsabie 模型除了利用 CNN
抽取特征之外,还提出了一个带权近似配对排序 (Weighted Approximate-Rank
Pairwise, WARP) 损失函数用于处理预测目标数量巨大的问题。

澳门新葡亰游戏网址 1

上面就是实验结果,从实验结果来看 fastText 能够取得比 Tagspace
好的效果,并拥有无以伦比的训练测试速度。但严格来说,这个实验对 Tagspace
有些不公平。YFCC100M
数据集是关于多标记分类的,即需要模型能从多个类别里预测出多个类。Tagspace
确实是做多标记分类的;但 fastText
只能做多类别分类,从多个类别里预测出一个类。而评价指标 prec@1
只评价一个预测结果,刚好能够评价多类别分类。

其它模块

除了以上两个模块,dictionary
模块也相当重要,它完成了训练文件载入,哈希表构建,word n-gram
计算等功能,但是并没有太多算法在里面。

其它模块例如 Matrix, Vector
也只是封装了简单的矩阵向量操作,这里不再做详细分析。

澳门新葡亰游戏网址,1. fastText 原理

fastText 方法包含三部分:模型架构、层次 Softmax 和 N-gram
特征。下面我们一一介绍。

1.1 模型架构

fastText 模型架构如下图所示。fastText
模型输入一个词的序列(一段文本或者一句话),输出这个词序列属于不同类别的概率。序列中的词和词组组成特征向量,特征向量通过线性变换映射到中间层,中间层再映射到标签。fastText
在预测标签时使用了非线性激活函数,但在中间层不使用非线性激活函数。

澳门新葡亰游戏网址 2

fastText 模型架构和 Word2Vec 中的 CBOW
模型很类似。不同之处在于,fastText 预测标签,而 CBOW 模型预测中间词。

1.2 层次 Softmax

在某些文本分类任务中类别很多,计算线性分类器的复杂度高。为了改善运行时间,fastText
模型使用了层次 Softmax 技巧。层次 Softmax
技巧建立在哈弗曼编码的基础上,对标签进行编码,能够极大地缩小模型预测目标的数量。具体细节参见 文章 。

1.3 N-gram 特征

fastText
可以用于文本分类和句子分类。不管是文本分类还是句子分类,我们常用的特征是词袋模型。但词袋模型不能考虑词之间的顺序,因此
fastText 还加入了 N-gram 特征。“我 爱 她” 这句话中的词袋模型特征是
“我”,“爱”, “她”。这些特征和句子 “她 爱 我” 的特征是一样的。如果加入
2-Ngram,第一句话的特征还有 “我-爱” 和 “爱-她”,这两句话 “我 爱 她” 和
“她 爱 我” 就能区别开来了。当然啦,为了提高效率,我们需要过滤掉低频的
N-gram。

update

update 函数的作用已经在前面介绍过,下面我们看一下它的实现:

void Model::update(const std::vector<int32_t>& input, int32_t target, real lr) {
  // target 必须在合法范围内
  assert(target >= 0);
  assert(target < osz_);
  if (input.size() == 0) return;
  // 计算前向传播:输入层 -> 隐层
  hidden_.zero();
  for (auto it = input.cbegin(); it != input.cend(); ++it) {
    // hidden_ 向量保存输入词向量的均值,
    // addRow 的作用是将 wi_ 矩阵的第 *it 列加到 hidden_ 上
    hidden_.addRow(*wi_, *it);
  }
  // 求和后除以输入词个数,得到均值向量
  hidden_.mul(1.0 / input.size());

  // 根据输出层的不同结构,调用不同的函数,在各个函数中,
  // 不仅通过前向传播算出了 loss_,还进行了反向传播,计算出了 grad_,后面逐一分析。
  // 1. 负采样
  if (args_->loss == loss_name::ns) {
    loss_ += negativeSampling(target, lr);
  } else if (args_->loss == loss_name::hs) {
  // 2. 层次 softmax
    loss_ += hierarchicalSoftmax(target, lr);
  } else {
  // 3. 普通 softmax
    loss_ += softmax(target, lr);
  }
  nexamples_ += 1;

  // 如果是在训练分类器,就将 grad_ 除以 input_ 的大小
  // 原因不明
  if (args_->model == model_name::sup) {
    grad_.mul(1.0 / input.size());
  }
  // 反向传播,将 hidden_ 上的梯度传播到 wi_ 上的对应行
  for (auto it = input.cbegin(); it != input.cend(); ++it) {
    wi_->addRow(grad_, *it, 1.0);
  }
}

下面我们看看三种输出层对应的更新函数:negativeSampling,hierarchicalSoftmax,softmax

model 模块中最有意思的部分就是将层次 softmax 和负采样统一抽象成多个二元
logistic regression 计算。

如果使用负采样,训练时每次选择一个正样本,随机采样几个负样本,每种输出都对应一个参数向量,保存于 wo_ 的各行。对所有样本的参数更新,都是一次独立的
LR 参数更新。

如果使用层次
softmax,对于每个目标词,都可以在构建好的霍夫曼树上确定一条从根节点到叶节点的路径,路径上的每个非叶节点都是一个
LR,参数保存在 wo_ 的各行上,训练时,这条路径上的 LR
各自独立进行参数更新。

无论是负采样还是层次 softmax,在神经网络的计算图中,所有 LR
都会依赖于 hidden_的值,所以 hidden_ 的梯度 grad_ 是各个 LR
的反向传播的梯度的累加。

LR 的代码如下:

real Model::binaryLogistic(int32_t target, bool label, real lr) {
  // 将 hidden_ 和参数矩阵的第 target 行做内积,并计算 sigmoid
  real score = utils::sigmoid(wo_->dotRow(hidden_, target));
  // 计算梯度时的中间变量
  real alpha = lr * (real(label) - score);
  // Loss 对于 hidden_ 的梯度累加到 grad_ 上
  grad_.addRow(*wo_, target, alpha);
  // Loss 对于 LR 参数的梯度累加到 wo_ 的对应行上
  wo_->addRow(hidden_, target, alpha);
  // LR 的 Loss
  if (label) {
    return -utils::log(score);
  } else {
    return -utils::log(1.0 - score);
  }
}

经过以上的分析,下面三种逻辑就比较容易理解了:

real Model::negativeSampling(int32_t target, real lr) {
  real loss = 0.0;
  grad_.zero();
  for (int32_t n = 0; n <= args_->neg; n++) {
    // 对于正样本和负样本,分别更新 LR
    if (n == 0) {
      loss += binaryLogistic(target, true, lr);
    } else {
      loss += binaryLogistic(getNegative(target), false, lr);
    }
  }
  return loss;
}

real Model::hierarchicalSoftmax(int32_t target, real lr) {
  real loss = 0.0;
  grad_.zero();
  // 先确定霍夫曼树上的路径
  const std::vector<bool>& binaryCode = codes[target];
  const std::vector<int32_t>& pathToRoot = paths[target];
  // 分别对路径上的中间节点做 LR
  for (int32_t i = 0; i < pathToRoot.size(); i++) {
    loss += binaryLogistic(pathToRoot[i], binaryCode[i], lr);
  }
  return loss;
}

// 普通 softmax 的参数更新
real Model::softmax(int32_t target, real lr) {
  grad_.zero();
  computeOutputSoftmax();
  for (int32_t i = 0; i < osz_; i++) {
    real label = (i == target) ? 1.0 : 0.0;
    real alpha = lr * (label - output_[i]);
    grad_.addRow(*wo_, i, alpha);
    wo_->addRow(hidden_, i, alpha);
  }
  return -utils::log(output_[target]);
}

4. 总结

Facebook Research 已经在 Github 上公布了 fastText
的 项目代码 。不过这个项目其实是有两部分组成的,一部分是这篇文章介绍的
fastText 文本分类,另一部分是词嵌入学习。按论文来说只有文本分类部分才是
fastText,但也有人把这两部分合在一起称为
fastText,比如这篇文章 Comparison of FastText and
Word2Vec 。fastText
的词嵌入学习比 word2vec 考虑了词组成的相似性。比如 fastText
的词嵌入学习能够考虑 english-born 和 british-born 之间有相同的后缀,但
word2vec 却不能。fastText
的词嵌入学习的具体原理可以参照 论文 。


部分内容出自:科技控

两者本质的不同,体现在 h-softmax的使用。
Wordvec的目的是得到词向量,该词向量 最终是在输入层得到,输出层对应的
h-softmax 也会生成一系列的向量,但最终都被抛弃,不会使用。
fasttext则充分利用了h-softmax的分类功能,遍历分类树的所有叶节点,找到概率最大的label(一个或者N个)

最近在一个项目里使用了fasttext[1],
这是facebook今年开源的一个词向量与文本分类工具,在学术上没有什么创新点,但是好处就是模型简单,训练速度又非常快。我在最近的一个项目里尝试了一下,发现用起来真的很顺手,做出来的结果也可以达到上线使用的标准。

  • Forever-守望 – 博客频道 –
    CSDN.NET要是数据量很大的话,可以参考word2vec的思路,使用更复杂的分类器,我用卷积神经网络实现了一个基于大规模短文本的分类问题CNN在中文文本分类的应用
  • Forever-守望 – 博客频道 – CSDN.NET

 

fastText 方法包含三部分:模型架构、Softmax 和 N-gram
特征。下面我们一一介绍。
fastText 模型架构和 Word2Vec 中的 CBOW
模型很类似。不同之处在于,fastText 预测标签,而 CBOW 模型预测中间词。
Softmax建立在哈弗曼编码的基础上,对标签进行编码,能够极大地缩小模型预测目标的数量。
常用的特征是词袋模型。但词袋模型不能考虑词之间的顺序,因此 fastText
还加入了 N-gram 特征。“我 爱 她” 这句话中的词袋模型特征是 “我”,“爱”,
“她”。这些特征和句子 “她 爱 我” 的特征是一样的。如果加入
2-Ngram,第一句话的特征还有 “我-爱” 和 “爱-她”,这两句话 “我 爱 她” 和
“她 爱 我” 就能区别开来了。当然啦,为了提高效率,我们需要过滤掉低频的
N-gram。

 

而fasttext在进行文本分类时,huffmax树叶子节点处是每一个类别标签的词向量,在训练的过程中,训练语料的每一个词也会得到对应的词向量,输入为一个window内的词对应的词向量,hidden
layer为这几个词的线性相加,相加的结果作为该文档的向量,再通过层次softmax得到预测标签,结合文档的真实标签计算loss,梯度与迭代更新词向量。

Please cite 1 if using this code for learning word representations or 2
if using for text classification.

分完词,使用facebook开源工具fasttext试试,效果超赞。如果你自己做的话,tfidf其实对于两三句话的短评可效果还是可以的。

介绍

fastText 是 facebook
近期开源的一个词向量计算以及文本分类工具,该工具的理论基础是以下两篇论文:

Enriching Word Vectors with Subword
Information

这篇论文提出了用 word n-gram
的向量之和来代替简单的词向量的方法,以解决简单 word2vec
无法处理同一词的不同形态的问题。fastText 中提供了 maxn 这个参数来确定
word n-gram 的 n 的大小。

Bag of Tricks for Efficient Text
Classification

这篇论文提出了 fastText 算法,该算法实际上是将目前用来算 word2vec
的网络架构做了个小修改,原先使用一个词的上下文的所有词向量之和来预测词本身(CBOW
模型),现在改为用一段短文本的词向量之和来对文本进行分类。

在我看来,fastText 的价值是提供了一个 更具可读性,模块化程度较好 的
word2vec 的实现,附带一些新的分类功能,本文详细分析它的源码。

 

不久前为某咨询公司针对某行业做过一个在twitter上的情感分析项目。题主的数据比较好的一点是评论已经按维度划分好,免去了自建分类器来划分维度的步骤,而这一点对为客户创造价值往往相当重要。情感分析一般是个分类或者预测问题,首先需要定义情感的scale,通常的做法是polarity,直接可以使用把问题简化为分类模型,如果题主的数据不是简单的两极,而是类似于1-5分的评分模式,则可以考虑把问题建模成预测模型以保存不同level之间的逻辑关系。分类模型需一定量的标注数据进行训练,如果题主数据量比较小的话,像肖凯提到的,可以去寻找类似的标注好的文本数据,当然最好是酒店和汽车行业的。如果没有现成标注,在预算之内可以使用像AMT这样的服务进行标注。接着是特征的抽取,对于短文本特征确实比较少,可以参考像微博这种短文本的分析,用什么方法提取特征比较好呢?

fastText 的词嵌入学习能够考虑 english-born 和 british-born
之间有相同的后缀,但 word2vec 却不能。

model 模块

model
模块对外提供的服务可以分为 update 和 predict 两类,下面我们分别对它们进行分析。由于这里的参数较多,我们先以图示标明各个参数在模型中所处的位置,以免各位混淆。

澳门新葡亰游戏网址 3

图中所有变量的名字全部与 model
模块中的名字保持一致,注意到 wo_ 矩阵在不同的输出层结构中扮演着不同的角色。

其实fasttext使用的模型与word2vec的模型在结构上是一样的,拿cbow来说,不同的只是在于word2vec
cbow的目标是通过当前词的前后N个词来预测当前词,在使用层次softmax的时候,huffman树叶子节点处是训练语料里所有词的向量。

支持多语言表达:利用其语言形态结构,fastText能够被设计用来支持包括英语、德语、西班牙语、法语以及捷克语等多种语言。
FastText的性能要比时下流行的word2vec工具明显好上不少,也比其他目前最先进的词态词汇表征要好。

训练数据格式

训练数据格式为一行一个句子,每个词用空格分割,如果一个词带有前缀“__label__”,那么它就作为一个类标签,在文本分类时使用,这个前缀可以通过-label参数自定义。训练文件支持
UTF-8 格式。

  1. Enriching Word Vectors with Subword Information
  2. Bag of Tricks for Efficient Text Classification
    FastText其实包含两部分。一个是word2vec优化版,用了Subword的信息,速度是不会提升的,只是效果方面的改进,对于中文貌似完全没用。另外一块是文本分类的Trick,结论就是对这种简单的任务,用简单的模型效果就不错了。具体方法就是把句子每个word的vec求平均,然后直接用简单的LR分类就行。FastText的Fast指的是这个。
    这个知乎答案总结得挺好的,取平均其实算DL的average pooling,呵呵。

要是数据量不够的话
可以直接嵌入一些规则来做,这里是我总结的一篇基于规则的情感分析;短文本情感分析

 

网站地图xml地图