文本分类的方法很多,传统方法有朴素贝叶斯、LDA和SVM等,可以说基本上所有用于分类任务的模型算法都可以用于文本分类,差别只是特征工程不同而已,介绍这些算法的书其实非常多,如李航的《机器学习方法》和周志华的《机器学习》等,有兴趣的读者可以阅读一下。我们的主题与深度学习相关,所以跳过了对传统方法的介绍。
工业行业解决业务问题经常受限于性能原因,因此不得不继续使用传统方法,所以传统方法并未过时,并且依然有很大的作用。具体到文本分类任务,其实词向量相关的方法表现已经比传统方法好,如fastText的性能表现就非常好。
下面先介绍词向量的训练,然后基于词向量进行文本分类。
在介绍Doc2vec和fastText之前先介绍Word2vec,因为这两种技术与Word2vec有非常密切的关系。
第一种Word2vec模型是CBOW,为什么叫这个名字?把前面的C去掉,就是BOW(Bag Of Words,词袋模型),这里加上的C就是Continuous,CBOW表示连续的词袋模型。
词袋模型就是把一篇文章或一句话的所有词拿出来,直接作为NLP任务的特征。例如,TF-IDF、LDA和SVM等模型在文本分类场景下使用的特征都是词袋,也就是没有先后顺序关系的词特征。当然也可以使用特征工程的方法划分先后顺序,如前面讲的在分词里加入bi-gram双字信息,其实就是一种包含两个字的先后顺序的信息;后面介绍的fastText在分类时附加的n-gram信息也是这个原理。不过即使这样也只能加入少量的局部顺序信息,所以我们依然认为它们是词袋模型。
连续的词袋模型其实就是词向量,以前,词特征要么使用one-hot形式表示,要么直接使用词本身,如LDA和全文检索等,都是直接基于词的。这样的词是离散的,各个词之间无论语义关系远近,但在特征表征上其距离都是相同的。例如,one-hot就是一个高维向量,只有一个值是1,其他值都是0,那么两个one-hot向量无论是哪两个词,其距离都是相等的。
使用词向量表征词意,打破了前面的离散状态,语义相近的词在向量空间里的距离就相近,而语义没什么相关性的词在向量空间的距离就比较远。这就是CBOW,如图2.1所示。
CBOW模型可以说没有隐藏层,每个词进行嵌入(Embedding)之后就直接求平均,然后计算结果。
训练方法就是用每个词的前后各 c 个词作为输入,训练的目标就是这个词本身。具体 c 的值是可以设置的,属于超参数。
数学公式就是:
图2.1 CBOW模型
CBOW为了优化softmax,减少计算量,把最后一层改为了层级结构,也就是树型的softmax。但这只是改变了计算量,对最终的效果影响不大,后面介绍fastText时再讲。
损失函数就是交叉熵。虽然训练是有监督训练,但是全部的样本都是基于语料自动化构建的,所以也称为无监督或自监督学习。这样最终训练出来的词向量就会包含一些词的特性,至于包含多少词本身的语义则不好判断。虽然不一定真的包含词的语义,但是这些词的位置特征对处理其他NLP任务有很大的帮助。例如,NMT翻译问题,即使是位置信息,也能通过前面出现的上下文而推测出当前位置哪些词出现的概率更大。
Skip-gram模型的逻辑与CBOW比较相似,二者都可以认为是没有隐藏层的超浅层网络。二者的区别是训练方式,Skip-gram是以某一个词作为输入,预测的是这个词的上下文,同样是前后 c 个词。这个 c 的值也是超参数,如图2.2所示。
CBOW的projection是求平均,这里的projection其实什么操作都没有,直接使用输入作为输出。
Skip-gram同样也面临softmax计算量过大的问题,但它没有使用CBOW的层级方案,而是使用了负采样,就是通过采样估计出softmax值,而不是计算准确值。
图2.2 Skip-gram网络结构
简单理解一下,从前面计算softmax的公式中可以看出,当词表数量比较大时,分母是求和运算,就是要把所有的词的贡献分都求一遍,这样的计算量非常大。
层级softmax的逻辑大体可以理解为基于树形结构,直接计算某个范围的概率贡献,而不是一个一个地计算后再相加,然后将这个范围概率逐层相乘,就能得出最终的概率贡献。例如,一个箱子里有a、b、c、d、e、f、g、h共8个小球,问随机选一次,选到b的概率是多少?如果直接计算节点的概率,8个球中一个球的概率就是1/8。如果按层级式计算,先选中a、b、c、d的概率是1/2,然后在a、b、c、d中选中a、b的概率是1/2,接着在a、b中选中b的概率是1/2,最终选中b的概率就是
。
至于对负采样的理解,可以认为,如果能计算代表一半概率贡献值的那部分值,然后将其乘以2,就可以得到最终值,如果能计算代表1/10甚至1/100的那部分值,则最终只需要将其乘一个值即可。
下面给出softmax的负采样逻辑性推导,这部分内容需要具备微积分和概率期望值的相关知识。
这一步统一softmax的公式应该没什么问题,关于 f(x) ,其实很多场景就是当前权重与输入 h 相乘,这里的 h 就是 x 。不过这里 f(x) 不展开,所以认为是任一函数(网络层)。
两边求对数,取对数有很多好处,如把连乘转化为连加,把指数转为正常函数等。交叉熵公式也是包含对数的。
log P ( x )的softmax的导数可以分为两部分,右边对分母求导,得出:
然后两边求导数。为什么求导?因为要求梯度来反向传播。
这里最后一项的每个分子与分母配对,其实都是一个 P(x′) 化简为:
因为 P(x′) 是一个概率分布,最后这一项就相当于基于 P ( x′ )分布的期望等于 E p ∇ f ( x )。
就因为不想计算 P ,准确说是 P 的分母,才进行采样。用 P 的近似分布 Q 代替 P :
但这里还是需要计算 P 的分母,并没有减少计算量。继续观察,其实分母也是几项之和,可以认为是一个均匀分布概率的期望:
也可以用 Q 函数分布去代替这里的均匀分布,形成基于 Q ( x )的期望:
这里的 N 是哪里来的?就是基于 Q 分布想要采样的数量,我们这样做就是为了降低计算数量,所以求和项的数量一定要比原来的项数 M 少。此外,这个数量是可选的,最终必然要平衡权重,让采样不同数量的目标值偏向均衡,所以就得除以这个项数 N 。
这里与 Z ′一样,采样需要增加一个1/ N 的平衡项,这样与分母 Z ′的 N 就可以约掉:
最后就得出一个可以基于 Q 分布采样计算出来的期望值,这样就可以基于 Q 函数采样来近似计算softmax了。极限情况下 N 为1也是可以工作的。
以上就是softmax负采样的逻辑推导。
有了对Word2vec的理解,下面就可以学习fastText和Doc2vec了。
这里先提个问题:有了Word2vec,能否可以用于文本分类呢?
答案肯定是可以的,具体如何使用呢?其实有了词向量就相当于把一篇文本全部转化为向量集,应用分类就变得比较简单了。例如,最简单的方法就是把所有词向量相加,然后进行线性变换,最终输出一个值后接sigmoid就成为一个二分类概率。fastText的实现也是类似的。
fastText是Facebook于2016年开源的一个词向量计算和文本分类模型工具,在学术上并没有太大创新。但是它的优点也非常明显,在文本分类任务中,fastText往往能取得和深度网络相媲美的精度,但在训练时间上比深度网络快许多数量级。在标准的多核CPU上,fastText能够在10分钟之内训练10亿词级别语料库的词向量,能够在1分钟之内分类超过30万个类别的50多万个句子。
看着是不是跟XGBoost很像?fastText模型其实没有太大的创新,主要工作是克服了一些工程问题和实际困难,且有较好的效果。
至于fastText的内容实现和原理,很多人都会混淆,其实它不是一个模型而是两个。n-gram特征也是两种用法:一是Word2vec的优化,使用所谓n-gram信息也就是subword来解决低频词和OOV词对词向量的影响;二是文本分类,这里使用了一些技巧来加速模型的训练和推断。
用fastText来训练词向量其实就是指第一个模型,当然这里Skip-gram和CBOW是可选的。
这里的n-gram其实是基于字符而言的,与我们前面讲分词的时候使用的bi-gram特征不太一样。
例如,如果 n 取3,apple单词就被拆为下面这样5个subword:
<ap, app ppl, ple, le>
每个subword对应一个向量。
subword向量如何参与到模型里进行训练呢?
先看不包含n-gram信息的词向量是如何训练的,我们以Skip-gram为例:
w t 就是用来预测目标上下文词的词向量; w c 就是前后几个词的词向量之和。
s 就是打分函数,也就是两个向量的内积。
这里引入subword向量并不是全部词都用subword向量来代替,也不是像常见模型那样将正常的词向量与subword向量进行拼接,而是 w t 部分使用subword向量, w c 部分使用词向量。
z g 就是subword向量,一共有 G w 个,对于apple来说就是5个。
简单说就是输入词的subword向量之和,预测输入词前后几个词的词向量。对于CBOW模型,就是上下文前后几个词的subword向量之和,预测目标词的词向量。注意,这里输入的上下文没有加入词向量,全部都是subword向量。
这样就可以把词向量与subword向量一起训练出来。
对于中文,fastText使用的依然是分词的词向量,不是字向量,所以n-gram就是字的组合。不过与英文不同,英文只有26个字母,组合种类也很少,所以基于正常的语料训练,基本上都能覆盖大部分的组合情况。而中文因为汉字太多,即使是2-gram的组合,一般语料也不一定能完全覆盖,所以中文OOV方面就别期望太高了。当然也可以直接使用字向量而跳过n-gram特征,但效果要重新评估。
fastText的词向量也只做了n-gram优化。而分类模型使用的n-gram特征与这个n-gram是不同的,下面会讲。
下面我们再来介绍fastText分类模型。
fastText分类模型跟CBOW是一样的,都是在输入层把词向量输入网络,然后通过加权平均得出 h ,然后基于 h 求目标结果。区别是CBOW输入的是目标词的前后几个词,fastText输入的是文本全量的词。CBOW的输出是预测中间那个词的概率,而fastText的输出是预测文本是哪个类别。
再就是fastText增加了n-gram信息,这里的n-gram信息有点像分词使用的bi-gram信息。例如,“he is a car engineer”,如果要附带bi-gram信息,那么就是“he/he is/is/is a/a/a car…”,如果要附带tri-gram信息,那么就是“he/he is/he is a/is/…”。
这里的n-gram信息对中文就有效了,跟分词的bi-gram一样,也是双字向量,对于词也行,就是双词向量。但这样Embedding的空间就会非常大,即使是英文,双词的组合也是爆炸式地增大。fastText使用了一个小技巧,使用Hash进行向量的索引,就是有些组合词的向量可能是共用一个Embedding,这样会不会产生词意冲突?显然有一些影响,但是,如果Hash的桶设置得合理,那么对最终结果的影响可以降到很小。
其实对特征空间进行Hash存储不是fastText独有的技巧,在很多场景中,如果特征空间过于庞大,而特征的覆盖度又不均衡,如fastText的组合词频率不同,那么可以使用Hash操作来压缩空间,这其实相当于把低频向量合并了,让其训练成几个低频向量的平均值,这样既有一定的语义保存,又节省了大量的空间。
不过基于Hash的先验就是高频特征比例不高,所以随机Hash大概率是低频特征碰撞,如果感觉不保险则可以手动统计一下各个特征的频率,然后只让低频特征进行Hash操作,这样会进一步降低对模型指标的影响。
其实CBOW进行语言模型训练的时候还是分类模型,因为预测的是某个位置是哪个词,词就是词表里几万或几十万个类别之一。
我们知道,进行分类预测一般最后一层都会使用softmax:
这样模拟出一个不同类别的概率分布,但现在类别有几万或几十万个,那么在计算这个softmax时会非常消耗时间,那么怎么解决呢?
现在主流的方法有两种,其一就是分层softmax,其二就是随机负采样softmax。
这里使用的是分层softmax,其结构如图2.3所示。
图2.3 分层softmax结构
分层softmax的逻辑与树查找一样,如果是正常的softmax计算,就像遍历一样从头算到尾,而层次计算就像二分查找,只计算路径上的节点,直接取对数,计算量减少了很多。
L
(
y
j
)表示节点对应的层级;
σ
是sigmoid函数;
n
(
y
j
,
l
)表示第l级的路径节点;
表示下一级节点是否为当前节点的左节点,如果是则为1,如果不是则为-1;
表示第l级节点的参数。
上面的公式看着很复杂,其实拆开仔细看看可分为三部分,第一部分是判断子树为左节点还是右节点,如果是左就为1,如果是右就为-1,第二部分是正常的权重与输入相乘,第三部分是sigmoid。
如图2.3所示的加粗路径,
在训练阶段,就用这个分层计算的softmax代替原来的softmax即可。而在推断的时候依然是基于这个分层结构进行计算,每次都选择sigmoid值最大的那个分支,直到概率最大的那个叶子节点。
或许有人会有疑问,这个树形结构是如何生成的?
其实树形结构可以是任意生成的,例如CBOW训练时softmax的树结构是基于词频构建的,而fastText则是基于定义的类别的频率构建的,其实与词频的原理是一样的。虽然树可以任意生成,但是,如果没有逻辑性,那么最终的效果肯定会比较差。
在这里CBOW和fastText都是基于节点出现频率生成的Huffman树,CBOW是词频,fastText是类别频率。虽然这种树的生成法在性能上有先验逻辑存在,也就是频率越高的节点层数越少,计算量越少,从而降低总的计算量,但是最终效果在逻辑上却没有什么贡献,计算量少并不能保证最终预测的节点是最精确的,所以笔者认为可以通过调整树形结构来提升最新效果。例如,可以基于词意进行聚类,然后将其作为树的结合点一级级地合并为一棵树,这样在计算父节点的左右概率时就有了一定的可解释性,如左子树节点全部都是活的生物,而右子树节点全部都是物品,这样理论上预测的效率应该会有一定的提升。
另外,fastText如果在文本分类任务中没有使用预先训练好的词向量,那么在训练分类的过程中会训练出新的词向量。至于n-gram多词向量,全部都是在分类任务时训练的。如果分类任务的类别比较少,也没有层级(如一级、二级类别)等增加任务难度,那么这个词向量被训练后可能包含的信息太少,不足以作为预训练特征迁移到其他任务里。
相反,如果分类的类别较多,或者有层级分类任务,又或者有多目标分类任务等,最终词向量包含的信息也许又可以作为一种词向量用于迁移学习。例如,在CV领域,基于imageNet数据集训练出来的网络,就可以直接作为高阶特征迁移到其他CV任务中。
NLP至今还没有类似CV领域基于分类预训练出来的词向量的另一个原因是,分类的类别需要客观,猫就是猫,狗就是狗,不能你认为是猫,我认为是狗,否则样本标签的客观一致性就达不到要求,训练的结果肯定不具有客观通用性。这个问题在图像领域相对容易,但在NLP领域就不那么容易了,例如,你认为这是一篇养生类文章,我认为是医学类的,甚至有人认为是低俗类的,这同样也导致了NLP任务经常与业务绑定较深,也就是没有共识,只能基于不同的业务定一个业务标准。
Doc2vec是2014年由Tomas Mikolow和Quoc Le在论文Distributed Representation of Sentences and Documents中提出的概念,Tomas Mikolow是Word2vec的作者之一。
分布记忆的段落向量(Distributed Memory Model of Paragraph Vectors,PV-DM)的模型结构基本上与Word2vec的CBOW模式相同,如图2.4所示。
图2.4 Doc2vec的PV-DM模式
读者有没有发现Doc2vec的PV-DM模式与CBOW几乎是相同的,训练方法其实也是相同的,都是无监督模式,唯一的区别就是在前面多了一个Paragraph ID向量(其实就是doc ID),Doc2vec的核心就是它。该向量及另外三个语境向量的拼接或者平均结果被用于预测第四个词,其实可以跟CBOW完全相同,用某个词的前后几个上下文词预测当前词,效果都差不多。这样Paragraph ID向量训练结束后表示上下文缺失的信息同时也充当了关于该段落话题的一份记忆,相当于这个向量包含全文所有词的信息,所以被用作表征文档的向量。
在模型中,上下文长度是固定的,如图2.4中的3个词。段落向量是被所有由同一段落生成的训练样本共享的,而不是所有段落都实现共享。而词向量矩阵 W 是跨段落分享的,就是每个段落或文章都有一个独有的段落向量,其他的词向量与CBOW一样,是全局一致的。
分布词袋版本的段落向量(Distributed Bag of Words version of Paragraph Vector,PV-DBOW)与Word2vec的Skip-gram类似,如图2.5所示。它直接使用段落向量去预测一个窗口内的几个词,通过每篇文章或段落的向量,预测整篇文章所有窗口的词。段落向量与每个窗口内的几个词作为一个训练样本。
图2.5 PV-DBOW模式
这种训练方式是直接让所有的词向量进行一个加权求和从而得到段落向量。但段落向量在训练的过程中会保留一些对全文来说更重要的信息,类似于注意力,而加权平均容易被大量的无意义词降低信息量。有人认为段落向量也会包含顺序信息,但基于训练的方式看,难以佐证这种观点。基于PV-DM训练方式用前面的词预测后面的词,或用前后的词预测中间的词,这个训练过程或许会附带一些顺序信息,但PV-DBOW则完全不会。
下面看一下Doc2vec训练出来的段落向量的效果。
图2.6为Doc2vec应用于情感分析场景时与其他技术的数据对比。数据集为斯坦福情感分析树库(Stanford Sentiment Treebank Dataset),论文是2014年发表的,当时并没有更深层的模型,如BERT等,但Doc2vec继承了Word2vec和fastText的优点,就是训练容易,运行效率也非常高,如果将其与BERT对比,其实并不公平。
如图2.6所示,朴素贝叶斯和SVM使用bi-gram信息的朴素贝叶斯、词向量平均和RNN等,无论是粗粒度级别二分类的情感分析,还是精细级别五分类的指标,都比Doc2vec弱。另外,虽然PV-DBOW的训练好像是把词向量加权平均而得到的段落向量,但是效果却比对全部词向量直接求和取平均好得多,读者可以慢慢体会其中的区别。
图2.6 情感分析技术对比
Doc2vec除了进行文本分类之外还有其他应用吗?其实还是有不少应用的,如文本相似度、文本打标签等。打标签也是一个文本分类任务,如果用Doc2vec打标签就不同。怎么不同呢?
就是在训练段落向量的同时把当前段落的标签也加入输入序列中配合窗口中的前三个词,用于预测后一个词。但当前段落的这个标签是从哪里来的呢?还是要打标签,也就是虽然训练方式感觉像是无监督训练,但是内部的标签信息已经预先打好标签,其实还是有监督训练。这样最终每个标签,例如一共预先定义了20种标签,那么每个标签就训练成一个向量。当要打标签时就用段落向量与标签向量做距离计算,距离最近的那几个就是当前段落的标签。
这个方法源自某公司内部的实践,结果证明用Doc2vec比直接用Word2vec等高性能模型做文本分类任务时打标签的准确率高一些。
打标签这种任务经常都是大批量的执行,例如今日头条每天的新闻量可能在几十万,即使是其他二、三线新闻App的新闻量也都能达到几万的量,这样如果选择BERT之类的重模型或许指标会提升,但容易导致线上任务队列的积压,如果积压时间太久那可能就是事故了。
所以笔者也反复强调,学习机器学习的各种模型技术,不要单看最终的评估效果指标,还要看包括计算量消耗、推断速度和手工特征依赖度等其他标准。
当然,有些大公司的算法工程师可能只需要负责提升指标,无论他使用了多大、多“笨重”的模型,都有工程团队的人负责去压缩和优化模型。但问题是,一个完整的BERT即使再优化,在相同配置的计算节点上其推断速度也不可能比fastText快。当然,模型优化和压缩也是非常重要的手段,可以显著增加重模型的适用范围,某些线上场景可能对推断性能没有那么高的要求,模型压缩则能实现既提升指标又不降低计算效率的目标。
介绍了两个快速、高效的模型,下面就要介绍加重模型了。
这一节算是对前文CBOW和Skip-gram的softmax技术的一个展开讲解,涉及一些公式的推导。
softmax为什么需要加速?
下面是softmax的公式,对应于LM的输出层为:
这里的 h 是最终用于计算softmax的向量,所有的 w 都是词向量。softmax在推断时可以不计算分母,因为只有分子不同,分母都一样,直接比较分子即可。而在训练时因为交叉熵计算的是概率分布,所以分母不能省。在推断时如果没有特殊设计,则需要把所有词的分子都计算一遍,这样也就顺便得出了分母。所以当 N 很大时,softmax的计算量无法忽视。而 N 代表词表的大小,如果是1万个词,那么每个时间步的输出都需要1万次的计算量。由此提出了两种减少softmax计算量的方法。
基于层级的softmax,就是使用一个层级结构进行最后的预测。可以简单地设想一下,把网络的最后一层softmax层变成一棵平衡二叉树,二叉树的每个非叶节点用于给预测向量分类,最后到叶节点就可以确定下一个词了。
Morin和Bengio在2005年提出了一个方案,即把输出层改造为层级二叉树,其中,词汇表中所有的词被视为它的叶子节点,然后基于WordNet的词义相似性进行叶子合并,形成父节点,这样一级级地进行合并,最终到达根节点。
换种说法就是基于WordNet里的词义相似性对词表进行从下到上的层次聚类,并生成一个二叉树。
图2.7 层级softmax示意图
所有的词都在叶子节点上,非叶子节点上没有词,只有词义的聚类表示,即词义向量。在实际计算softmax时,并非每个节点都计算,而是像二分查找一样从上到下一级一级选择性地进行计算,如图2.7所示。
例如第一级,计算两个节点,一个是“生物”子树,一个是“物品”子树。假设“生物”子树的概率为0.6,物品子树的概率为0.4;那么第二级则计算“生物”节点的两个子节点——“动物”和“植物”,假设“动物”的概率为0.7,“植物”的概率为0.3;那么第三级则计算“动物”子树的两个子节点——“人类”和“非人类”。如此一级级地算下去,最终每个单词所在的叶子节点的softmax概率可以描述为:
P ( v )= P ( b 1 ( v ∈生物)) P ( b 2 ( v ∈动物)| b 1 ( v ∈生物)) P ( b 3 ( v ∈人类)|( b 1 ( v ∈生物), b 2 ( v ∈动物))
b 1 ( v ∈ K )的意思就是在第一层中,单词 v 是属于子节点 K 的,即 v 在 K 节点之下的某个叶子节点上。这里是二叉树,只有两个节点,可以把 b 化简为 b 1 ( v )=0,视为 v 属于第一个子节点或左子节点, b 1 ( v )=1视为 v 属于第二个子节点或右子节点。
这样每个单词即每个叶子节点都可以用一个0/1串表示其从根节点到叶节点的路径,如上面的“人类”就是010。
标准的公式为:
w t −1 ,…, w t − n +1 是 v 依赖的上下文,即前面 n -1个词,所有LM都需要,在这里不是重点,暂时忽略。
可以通过以下公式计算每个节点的概率:
P ( b j ( v ))=sigmoid( b + Wh + UN node )
其中, W 、 U 是训练权重, b 是偏置, h 依然是softmax层的输入, N node 则是当前节点的节点向量,可以通过子节点的向量加权求和得出,也可以直接通过训练得出。如果概率大于0.5则认为在右节点,如果小于0.5则认为在左节点。
在训练时,因为有标签,知道哪个词的概率最高,就可以基于这个标签把对应的路径概率最大化。在推断时,就基于最大概率的路径计算下去,找到总概率最高的那个词即最终输出。
注意:
上面例子中的“物品”“生物”“动物”“植物”都是中间节点,是由语义生成的,并非最终的单词,单词都在叶子节点上,如例子中的“人类”与“非人类”。
普通的softmax可以视为遍历,也就是每个节点都需要计算一次,这样如果有1万个节点,那么遍历的成本就比较高。如果使用二分查找或者二叉树查找,那么只需要计算log 2 10000≈13.3次即可。如果这里的softmax也被设计为二叉树,那么次数是一样的,如果是非常平衡的二叉树,那么基本上只需要计算14次就可以出结果了,速度提升非常快。然而,这个方法还是有缺陷,就是基于层级softmax计算出来的语言概率的PPL比正常的softmax差很多,即使引入了专家知识WordNet,也还是要差一些。
这是因为WordNet并非基于数据自动生成的,而是基于外部知识硬性生成的。这样每层选择概率最大的一边计算,最终并不一定会找到总概率最高的那个节点。举个极端点的例子,上面计算“生物”和“物品”时,一边是0.6,一边是0.4,于是选择0.6这一边继续计算,如果0.6的下一层概率最大的是0.5,而0.4的一边概率最大的是0.9,那么0.4×0.9=0.36就大于0.6×0.5=0.3了,这样就出现了选择错误的情况。Mnih和Hinton希望模型能从语料中通过自动学习而构建出一棵树,并能达到比人工构建的树更好的效果。他们在2008年使用一种启发式的方法来构建这棵树。先随机构建一棵二叉树,基于这棵树进行训练,然后基于训练出的词向量修改这棵树,根据分类结果不断调整和迭代。最后得到的是一棵稳定的平衡二叉树。
Le等人在2013年引入了一种新的基于类的NNLM,它有一个结构化的输出层,称为结构化输出层NNLM。给定单词wi的历史 h ,条件概率可表示为:
与前面的二叉树softmax其实没有本质不同,其实上面的条件概率公式也没有显示出哪里不同,只是不再是二叉树,也不再平衡。
主要的不同还是在层级树的构造上,这里的做法是:先进行一次预训练,设定一个非常小的词向量空间。例如,目标设定的词向量为200维,而预训练阶段再把维度设定为20维,甚至更低。预训练时的softmax使用正常的全量计算,不过这里因为维度低,权重维度也相应降低,最终的计算量其实是不大的。
然后基于这个20维的词向量进行聚类,跟前面一样一层层地往上聚类直到合并到一个或几个根节点上。这里其实不再是一棵树,而是一片森林,即多棵树,每个中间节点可能有几个子节点,也可能没有子节点,如果没有子节点,那么其就是叶子节点,对应的是词向量本身。
构建好层级也就是树后,再进行正常的训练。
虽然这个方法有一个预训练的过程,让训练的整体耗时没有以指数级下降,但是依然比全量softmax快很多。而且因为层级构建更加直观,直接以预训练的最终词向量进行聚类,最终的效果也相对较好,困惑度PPL指标下降得较少。
无论上面哪种层级softmax最终都要构建一个固定的层级树或者平衡二叉树,或者非二叉树。这种分类属于硬分类,最终导致NNLMs的性能变差,也就是PPL指标数值上升。
因此,有必要研究一种基于软分类的方法。我们希望在降低NNLM训练成本的同时PPL能够保持不变,甚至降低。
当使用NNLMs计算下一个单词的条件概率时,输出层使用softmax函数,其中规范化分母的计算成本非常高。因此,一种方法是随机或启发式地选择输出的一小部分,从中估计概率。
Bengio和Senecal在2003年提出了一种重要的抽样方法和自适应重要抽样算法,以加速NNLMs的训练。
softmax的公式为:
这里因为 h t 不影响后面的公式推导,所以可以忽略,公式改为:
计算softmax的目的是什么呢?计算loss后可以求导计算梯度。loss一般指交叉熵,最终是对log P ( w )求导。
log P ( w )的softmax:
求导:
上面的公式可以分为左右两部分,等式右边可以合并,其中间其实还是一个softmax,只不过参数变为了 w ':
等式左边比较简单,需要优化的关键部分在右边,下面我们只关注右边。
后半部分基于期望定义,可被视为基于 P ( w )分布的期望: E P ( w ) ∇ f ( w )。
求一个分布的期望,显然不用把所有元素都计算一遍。就像求身高均值,只要抽一部分人即可,这个抽一部分人的动作就是采样。
如果要采样,就要基于 P ( w )进行分布采样。这意味着必须计算 P ( w ),但我们并不想计算 P ( w ),准确说是 P ( w )的分母,因此才要采样。
有没有其他办法呢?可以用另外一个分布 Q 去近似代替 P :
这样就不用基于 P ( w )分布采样了,改为 Q ( w )分布,这个 Q ( w )分布理论上是随意取的,但其实是越接近 P ( w )效果越好,我们可以使用uni-gram,即词频分布。
但这里采样不需要基于 P ( w ),式子里还是需要计算 P ( w )。我们来看看分母有没有什么特征可以用其他方法代替而不用直接计算。
观察一下, P ( w )的分母可以认为是一个均匀分布概率的期望:
其实任何一个所有元素求和的操作,都可以被视为一个均匀分布的期望。如此,也可以用 Q 函数去代替原来的均匀分布:
代回上面的右边部分:
这样就可以完全基于 Q 函数采样近似计算softmax了。极限情况下 N 为1,即只采一个样本计算也是可以工作的。
上面的数学推导,怎么理解呢?就是损失函数的对数的导数的右边部分是个期望,也就是均值,不需要全量计算,只需要抽一部分计算即可,与计算平均身高一样。但每次采样不能直接作为一个贡献项,还需要计算一下权重,也就是基于原分布与新分布里的概率值的不同调整一下权重再计算,这个权重就是上面公式里最后行中间的那一部分。
实验结果表明,采用重要抽样的方法,在不显著提高PPL的前提下,使NNLMs的训练速度提高了10倍。
Bengio和Senecal在2008年又提出了一种使用自适应n-gram模型代替简单uni-gram模型的自适应重要性抽样方法,就是基于uni-gram的 Q 函数的缺陷,用了一个权重可学习的 Q 函数去采样。此外,他们还提出了其他改进,如并行训练小模型来作为重要采样的 Q 函数、多重重要性抽样和似然加权方案、两阶段抽样等。
对于训练NNLMs的采样方法,还有其他不同的方法,包括噪声比较估计、局部敏感哈希(LSH)技术和BlackOut等。