Tensorflow dynamic rnn,源代码的逐行解读

前两天和领导讨论模型,领导拍拍脑袋想了个主意让我实现,开始着手之后才发现要对dynamic rnn进行改动才行。无奈之下,只好一把梭看完整个dynamic rnn的实现,先理解再改。说句实在话那个代码理解起来还是非常困难,我花了好几天才全部看明白。为了造福大众,我还算是比较认真地对源代码添加了大量的注释,添加过注释的代码已经贴在github上,在此我就不贴又臭又长的代码了。


但是,读完代码之后,还是解决了长期困扰我的几个问题,在此说明,和大家分享。

sequence length这一参数重要吗?

在使用Tensorflow处理RNN时,有一个天然的问题:序列数据往往是变长的,但是Tensorflow规定Tensor必须是齐整的,定长的张量。因此,在实践中,规定输入Tensor的尺寸时,往往都是往大了设,较长的序列可以尽量多保留信息,如果数据不够长,就补零补到目标长度。补零之后这个事情就显得比较复杂,因为补零事实上使得输入数据变化了。随之,也就有了针对变长数据的参数squence length。不过官方文档里的一句话一直让我觉得非常疑惑,Used to copy-through state and zero-out outputs when past a batch element's sequence length. So it's more for performance than correctness. 经过阅读源代码,我可以很笃定的说,这句话问题很大。是否设置sequence length参数关乎性能,也关乎代码的正确性,两者并没有谁更重要,一荣俱荣(设),一损俱损(不设)。

更具体的说,假如输入Tensor所定义的最大序列长度是100,而我们的实际输入长度只有10。则,在定义Squence Length之后,实际输入时,第21个hidden state到第100个hidde state是不断复制第20个hidden state得到,而非通过计算得到;第21个output到底100个output会被全部直接置零。但是如果不定义Sequence Length,就会把填零的部分也作为有效输入,一路计算完100个状态的state和output。

static rnn比dynamic rnn慢吗?

这里其实有两个问题,创建速度哪个快?运行速度哪个快?

创建速度明显是dynamic rnn更快。两者的区别其实主要在于,static rnn的迭代是通过for循环完成的,而dynamic rnn的迭代是通过while loop完成的。for 循环生成的计算图很好理解,是完全展开的。但是为什么while loop不会生成展开的计算图?我认为是因为while loop的参数列表中,显示的定义了用于迭代的节点(loop var),因此在创建计算图时,tensorflow可以经由这一参数明确的知道哪些ops节点是可以循环执行的。而for 循环则没有这个优势。

至于实际跑起来,看上去两者应该没有区别,两者真正用于迭代隐层的代码是复用的,看上去差不出多少速度。

parallel iterations参数是拿来干什么用的?

dynamic rnn的这一参数令人十分费解,因为rnn是一个串行的模型,为什么可以存在几个平行的迭代?经过完整的阅读代码,这个参数事实上是while loop的参数,仅仅是在dynamic rnn中暴露了而已,dynamic rnn中设置这一参数基本没有什么效果。我不是很清楚谷歌为什么要在外层接口暴露这样一个令人费解的参数。

parallel iteration参数在while loop中是有明确作用的,这是因为while loop的确可能出现一些情况,在这些情况下,下次迭代所需的计算并不依赖于上次迭代的结果,因此两次迭代可以平行或者部分平行进行。详细可以参考tensorflow在github上的issue。但是很明确的是,因为rnn的上下依赖关系,这个参数在dynamic rnn中基本是没什么大用场的。

Nested Tuple是什么?

在dynamic rnn的官方文档中,里面有这么一句话

This may also be a (possibly nested) tuple of Tensors satisfying this property. The first two dimensions must match across all the inputs, but otherwise the ranks and other shape components may differ.

此处的Nested Tuple似乎表明dynamic rnn可以同时接受多个mini batch的数据并行训练。这个feature在事实上其实没有并没有实现,我已经给Tensorflow团队报了issue,正在等待回复。目前,dynamic rnn只支持单个TBD或者BTD形式的张量输入。

发布于 2018-12-07 22:03