这个工作刚出来的时候我就读了这篇论文,没想到知乎竟然看到了这个问题,就贴一下吧。
个人很喜欢这篇工作,可以说是16年读过的论文里面我非常喜欢的一篇了。里面的想法很简单同时很有效,并且有一些很有趣的结论。
总结一下LightRNN,模型方面:核心的想法就是通过修改常见的word embedding为2-Component (2C) shared embedding。即对某一词向量变为。其中,2C体现在分解词向量为两部,shared embedding体现在这样的分解向量是放在一个二维表中,其中每一行由一个向量表示(上角标 r),每一列由另一个向量表示(上角标 c)。根据一个词在二维表中的位置,该词可由行向量和列向量联合表示(这里注意一下,下角标因此分别用不同的符号表示)。“因为该表中同一行里的所有词共享相同的行向量,同一列的所有词共享相同的列向量,所以LightRNN仅仅需要2√|V|个向量来表示有|V|个词的词汇表,远远少于标准的RNN所需要的向量数|V|。这种词嵌入的做法被称做两部共享嵌入”。
预测阶段,这样在一个大词表的RNN语言模型中为了预测下一个词,LightRNN需要首先基于前面所有的词预测其行向量,然后基于前面所有的词和已经预测出来的行向量预测其列向量。虽然这样会将一个句子的预测长度翻了倍,但是实际中,因为词表的规模极大地减小,根据论文,在One Billion数据集上,“标准的RNN的模型参数个数为16-41亿,而LightRNN的模型参数个数仅为4千万,达到了40-100倍的压缩比”,模型训练时间快了一倍。一次预测的平均时间论文中没有给出(个人比较关心,因为关系到部署,实际能不能上线还是跟这个时间关系很大的),个人经验的结论是:虽然预测时长度变为2倍,但是因为最后Softmax显著减小了,总时间应该是少了的,但是有没有到一个显著的程度需要实验验证。
另外值得一提的是,这个二维词表在训练过程中是如何分配行列向量呢?这是一个很关键的问题。LightRNN的做法是:初始阶段,随机初始化;训练RNN直到模型收敛,收敛后,固定好学到的embedding参数,但是词需要被重新地分配到词表中合适的位置。语言模型的训练目标是最大化对数似然。类似地,词表重分配也应该满足这样的一个过程。(省略公式推导细节)通过一些简单的推导,这个重分配问题最终变为最小割最大流问题(见下图),求解这个问题完成一次词表的分配。完成一次重新分配,按照新的位置和参数继续训练RNN到收敛。论文中表示大概经历3-4次词表重分配就可以完成整个的训练了;而且虽然求一次重分配要花时间,但是相对于整个训练时间来讲这个开销还是很低的。
相关结论中:与标准的RNN进行单模型比对,LightRNN还达到了更好的PPL。这个可能是通过shared embedding的参数减少带来的。除了前面提到的模型大小、训练时间等问题,一个很有趣的现象是:LightRNN发现了词表中的一些语义。比如某一行或者某一列的词有着近似的表达;“其次,通过让有语义关联的词共享行或列向量,可以使低频的词的向量表达得到更充分的训练”(这一点是我第一次读这个论文没有注意到的点)。
最后简单评价一下:
LightRNN的优点中,模型大小的显著减小在嵌入式设备中的意义更大(考虑到ngram模型动辄几个几十个G的大小,对于Server类任务这个大小无所谓);而对于多GPU的训练也是能减小通讯开销(但是这一点个人认为意义不是特别大,因为当下及未来对于RNN的训练的问题会越来越小,训练时间往往大家不是特别在意,堆机器堆数据对大公司是家常便饭)。当然,“它使得单GPU训练超大词表的语料库成为现实”(对于穷人意义还是很大的!)。
进一步的研究可能是存在的:词的二部共享嵌入得到了一些有趣的语义上的结果(包括近似单词在一行/一列,以及低频词的向量表达),是不是有进一步研究的可能?(个人不会完全乐观于论文中挑出来的那些语义相近的例子);另外就是如果做k(>2)部分解呢?
这样的论文还是非常有趣的!
*答案中带引号的句子引用了MSRA自己的评价,参见:
Sina Visitor System;论文链接:
Memory and Computation-Efficient Recurrent Neural Networks