在实现变分词嵌入版的itag时,我被tensorflow的上古老版本和新版之间的差异折腾麻了,但折腾的过程也让我了解到了很多tensorflow的底层机制。尤其是一大堆eager Excution执行模式的相关报错让我充分了解了计算图机制。
引入
现代机器学习的基础是链式求导法则,计算完损失之后可以沿梯度反向传播以学习参数。
计算图储存了整个模型的的计算流程,即各个变量间的传递、依赖关系。
如果你尝试过在通过tensorflow1.X构建的模型中使用print打印出中间变量的话,你会发现点击运行之后,print函数只会运行一次,并且通常也不会打印出你想要的东西。
静态计算图
这是因为tensorflow使用了静态计算图,当你运行程序时,tensorflow会首先不带数据的运行python代码,验证模型是否可运行,也就是各个输入输出的维度、尺寸是否对的上,并且在这个过程中完成计算图的构建。
在这个过程中,print函数会被运行数次(一般是一次),但是因为这个时候模型的内部变量是没有实际数据的,打印出来也没有任何意义。
当计算图构建完成后,tensorflow就会利用生成的静态计算图完成模型的训练,剩下的计算过程在底层的C++代码中完成,python代码不会再执行,所以print函数不会再触发(假设它能触发也读不到底层C++模块里的值)
静态计算图的好处是计算速度快,并且静态计算图会对计算流程进行一定的优化,例如剪去某些不需要的计算。
代价就是用起来非常的僵硬,中间值是看不了的,无论如何也看不了,因为它在底层的C++代码里,随意的插入python代码对处理某些tensorflow不方便处理的情况也是不可能的,因为静态图一旦生成完毕,实质上就和python无关了。
动态计算图
tensorflow2.0引入了动态计算图(实际上在高版本的tensorflow1.X中就已经有了),动态计算图不需要先以空值运算一遍生成程序的计算图,而是定义后立即可以使用,因此也被称为eager excution,在编程时和普通的python代码一样使用即可。
(eager excution谷歌翻译给翻成急切执行,当时看的我一脸懵逼)
好处就是编程的灵活性大大的提高,可以任意打印中间值调试,可以任意插入python代码 ,可以直接把模块封装成python的函数。
坏处就是计算速度不如静态计算图,因为动态计算图需要与python代码进行大量的交互。
Autograph
tensorflow2.0默认使用动态计算图,上面提到了,动态计算图的效率不如静态计算图。如果确定一个函数不需要与python进行交互,可以使用@tf.function将其静态化,这种情况下生成的计算图称为Autograph。
例如:
1 |
|
所谓的不需要与python进行交互,就是指:
- 函数中的所有方法均是tensorflow的方法
- 或者是可以转换为tensorflow方法的python方法,例如while > tf.while, for > tf.while, if > tf.cond, assert > tf.assert
- 入参,返回值和中间变量均为tensorflow的张量
总结
动态计算图是好文明,eager excution是好文明。
然后我选择用pytorch。