其实在很久之前小编就想开设这样一个专栏了,毕竟TensorFlow2.x已经问世很久了。跟1.x相比,它真是有了太大的变化,现在TensorFlow已经到了2.4.0的版本了,所以小编就想总结一下,开始适应全新的2.x系列。本文算是整个专栏的开篇吧,主要来谈一下我为什么想要学习并使用TensorFlow2.x版本。下面就要开始我们的TensorFlow2.x的学习之旅了。关注专栏《一起来学TensorFlow2.x》,解锁更多相关内容~
目录
基于TensorFlow1.x的编程
1.1 TensorFlow1.x版本遇到的问题概述
我本机Windows环境下的TensorFlow是1.12.0的版本:
通常情况下,我可以在本地愉快的进行编码,直到一个问题出现了:因为GPU的问题,本地写的神经网络代码运行比较缓慢(本人笔记本的显卡是GTX1050Ti)需要提交到服务器上,服务器设置的显卡是RTX3070。在配置服务器环境的时候配置的是TensorFlow2.x的版本,这也就导致了我之前写的代码都没法在服务器上运行了(众所周知,TensorFlow别说大版本的更新了,甚至很多小版本的更新都会影响代码的运行),于是便有了1.x到2.x的转变。
1.2 TensorFlow1.x下的seq2seq模型
1.2.1 seq2seq模型简述
上面说到的本地代码就是使用的seq2seq模型,我想训练一个智能的问答系统,于是选择了非常受欢迎的seq2seq模型来构建该系统。seq2seq属于encoder-decoder结构的一种,基本思想就是利用两个RNN,一个RNN作为encoder,另一个RNN作为decoder。encoder负责将输入序列压缩成指定长度的向量,这个向量就可以看成是这个序列的语义,这个过程称为编码。获取语义向量最简单的方式就是直接将最后一个输入的隐状态作为语义向量,也可以对最后一个隐含状态做一个变换得到语义向量,还可以将输入序列的所有隐含状态做一个变换得到语义变量。而decoder则负责根据语义向量生成指定的序列,这个过程也称为解码。最简单的方式是将encoder得到的语义变量作为初始状态输入到decoder的RNN中,得到输出序列。可以看到上一时刻的输出会作为当前时刻的输入,而且其中语义向量只作为初始状态参与运算,后面的运算都与语义向量无关。decoder处理方式还有另外一种,就是语义向量参与了序列所有时刻的运算。上一时刻的输出仍然作为当前时刻的输入,但语义向量会参与所有时刻的运算。
1.2.2 TensorFlow1.x下引用seq2seq
基于小编本机的TensorFlow版本(1.12.0),可以直接通过下面的方式引用seq2seq模型:
from tensorflow.contrib.legacy_seq2seq.python.ops import seq2seq
通过上面的引用可以发现,seq2seq模型在contrib库中,但是contrib库在TensorFlow2.x之后被弃用了,原来属于contrib的函数,按照其功能,分散到了对应的模块中。这样的话,我们要想在新的服务器上使用原先1.x的代码,这里就不能直接引用了。TensorFlow2.x中原contrib里的seq2seq模块现在进入了addons里面,具体参考文档可以点击这里,GitHub的地址请点击这里。下面说一下我当时的处理方式,毕竟我当时还不知道seq2seq模块现在进入了addons里面。
1.3 修改源码,自定义RNN模型,实现seq2seq算法
没错,上面的标题就是我接下来进行的操作,通过分析seq2seq的源码我发现,里面大部分的库在TensorFlow2.x中依然可以使用,例如下面的这些:
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import control_flow_ops
from tensorflow.python.ops import embedding_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import nn_ops
from tensorflow.python.ops import rnn
from tensorflow.python.ops import rnn_cell_impl
from tensorflow.python.ops import variable_scope
from tensorflow.python.util import nest
当然也有不可使用的,例如:
from tensorflow.contrib.rnn.python.ops import core_rnn_cell
这样的话,只需要修改core_rnn_cell这一个源代码,基本上就可以实现在目前的环境中运行我之前的代码了。于是我将core_rnn_cell的源码复制了出来,新建了一个core_rnn_cell.py文件:
并且在seq2seq模型中,调用我新建的这个core_rnn_cell.py文件:
from seq2seq import core_rnn_cell
这样一切准备就绪后,再次在服务器上进行测试发现,原先的代码可以跑通了。当然,不建议大家使用我的这个方法,毕竟感觉不是正规的TensorFlow2.x,整个修改过程看似简单,其实花费了我大量的时间。TensorFlow也终于再次“恶心”到了老夫,不过相比于TensorFlow1.x混乱的layer和nn,TensorFlow2.x把所有层级别函数的简单实现放在了tf.nn,复杂实现放在了tf.keras.layer中。所以才有了这么一个专栏,才有了这么一篇文章。这篇文章算是开篇的前言部分,主要就是为了学习TensorFlow2.x做一个引子,起到抛砖引玉的作用,如果你也对TensorFlow2.x感兴趣,那么快跟我一起开启TensorFlow2.x的学习之旅吧~
好了,本文到此基本上结束了,为了防止读友们好奇,小编文章所提到的1.x版本的代码已经修改后的代码均已经上传到了GitHub中,感兴趣的朋友可以点击此处进行查看。你们在这个过程中遇到了什么问题,欢迎留言,让我看看你们都遇到了什么问题~