自 2015 年开源以来,TensorFlow 凭借性能、易用、配套资源丰富,一举成为当今最炙手可热的 AI 框架之一,当前无数前沿技术、企业项目都基于它来开发。一开始,TensorFlow 的主要目的是为构建神经网络提供高性能 API。然而,借助于机器学习社区对它的兴趣以及时间上的优势,这个类库已经演变成了一个完整的机器学习生态系统。
然而最近几个月,TensorFlow 正在经历推出以来最大规模的变化。TensorFlow 2.0 已经推出 beta 版本,同 TensorFlow 1.x 版本相比,新版本带来了太多的改变,最大的问题在于不兼容很多 TensorFlow 1.x 版本的 API。这不禁让很多 TensorFlow 1.x 用户感到困惑和无从下手。一般来讲,他们大量的工作和成熟代码都是基于 TensorFlow 1.x 版本开发的。面对版本不能兼容的问题,该如何去做?
本文将跟大家分享作者在处理 TensorFlow 适配和版本选择问题方面的经验,希望对你有所帮助。内容节选自《深度学习之TensorFlow工程化项目实战》一书,文末有送书福利!
一、新项目的版本选择
虽然 TensorFlow 的 2.0 版本中有很多光鲜靓丽的新功能。但是 TensorFlow 1.x 目前比较稳定,建议读者使用 TensorFlow 1.x 版本开发实际项目,并跟进 2.x 版本所更新的技术。待 2.x 版本迭代到 2.3 以上,再考虑使用 2.x 版本开发实际项目。
同时开发新项目时,尽量使用动态图+tf.keras 接口进行。这样,在以后的移植过程中,可以减少很多不兼容的问题。
如果选择 1.x 版本进行开发,尽量使用 TensorFlow 1.13.1、1.14 版本为主。因为 TensorFlow 2.x 版本的代码是基于 TensorFlow 1.13.1 转化而来。TensorFlow 1.13.1 版本可以部分支持 TensorFlow 2.0 版本的代码。而 1.14 版本在 1.13 基础上又更新了一代,相对更为稳定。
二、TensorFlow 1.x 版本与 2.x 版本共存的解决方案
由于 TensorFlow 框架的 1.x 版本与 2.x 版本差异较大。在 1.x 版本上实现的项目,有些并不能直接运行在 2.x 版本上,而新开发的项目推荐使用 2.x 版本。这就需要解决 1.x 版本与 2.x 版本共存的问题。
如用 Anaconda 软件创建虚环境的方法,则可以在同一个主机上安装不同版本的 TensorFlow。
1. 查看 Python 虚环境及 Python 的版本
在装完 Anaconda 软件之后,默认会创建一个虚环境。该虚环境的名字是“base”,是当前系统的运行主环境。可以用“conda info --envs”命令进行查看。
(1)在 Linux 系统中查看所有的 Python 虚环境。
以 Linux 系统为例,查看所有的 Python 虚环境具体命令如下:
该命令执行后,会显示如下内容:
在显示结果中可以看到,当前虚环境的名字是“base”,是 Anaconda 默认的 Python 环境。
(2)在 Linux 系统中查看当前 Python 的版本
可以通过“python --version”命令查看当前 Python 的版本。具体命令如下:
执行该命令后会显示如下内容:
在显示结果中可以看到,当前 Python 的版本是 3.6.4。
2. 创建 Python 虚环境
创建 Python 虚环境的命令是“conda create”。在创建时,应指定好虚环境的名字和需要使用的版本。
(1)在 Linux 系统中创建 Python 虚环境。
下面以在 Linux 系统中创建一个 Python 版本为 3.6.4 的虚环境为例(在 Windows 系统中,创建方法完全一致)。具体命令如下:
该命令创建一个名为“tf2”的 Python 虚环境。具体步骤如下:
① 在创建过程中会提示是否安装对应软件包,如下图所示。输入“Y”,则下载及安装软件包。
② 安装完软件包后,系统将会自动进行其他配置。如果出现如下图所示的界面,则表示创建 Python 虚拟环境成功。
在上图中显示了使用虚拟环境的命令:
提示:
在 Windows 中,激活和取消激活虚拟环境的命令如下:
activate tf2 deactivate
(2)检查 Python 虚环境是否创建成功。
再次输入“conda info --envs”命令,查看所有的 Python 虚环境。具体命令如下:
该命令执行后,会显示如下内容:
可以看到,虚环境中多了一个“tf2”,表示创建成功。
(3)删除 Python 虚环境。
如果想删除已经创建的虚环境,则可以使用“conda remove”命令。具体命令如下:
该命令执行后没有任何显示。可以再次通过“conda info --envs”命令查看 Python 虚环境是否被删除。
3. 在 Python 虚环境中安装 TensorFlow
激活新创建的虚拟环境“tf2”,然后按照《深度学习之 TensorFlow 工程化项目实战》一书 2.3 节中介绍的方法安装 TensorFlow。具体命令如下:
三、2.x 版本对于静态图的影响
“静态图”是 TensorFlow 1.x 版本中张量流的主要运行方式。其运行机制是将“定义”与“运行”相分离。相当于:先用程序搭建起一个结构(即在内存中构建一个图),让数据(张量流)按照图中的结构顺序进行计算,最终运行出结果。
虽然在 TensorFlow 2.x 版本中默认的是动态图,但是也可以使用静态图。
在 TensorFlow 2.x 版本中,使用静态图的步骤与在 TensorFlow 1.x 版本中使用静态图的步骤完全一致。但是,由于静态图不是 TensorFlow 2.x 版本中的默认工作模式,所以在使用时还需要注意两点:
在代码的最开始处,用 tf.compat.v1.disable_v2_behavior 函数关闭动态图模式。
将 TensorFlow 1.x 版本中的静态图接口,替换成 tf.compat.v1 模块下的对应接口。
例如:
将函数 tf.placeholder 替换成函数 tf.compat.v1.placeholder。
将函数 tf.session 替换成函数 tf.compat.v1.session。
四、将 1.x 的动态图代码升级到 2.x 版本
在 TensorFlow 2.x 版本中,已经将动态图设为了默认的工作模式。使用动态图时,直接编写代码即可。
TensorFlow 1.x 中的 tf.enable_eager_execution 函数在 TensorFlow 2.x 版本中已经被删除,另外在 TensorFlow 2.x 版本中还提供了关闭动态图与启用动态图的两个函数。
关闭动态图函数:tf.compat.v1.disable_v2_behavior。
启用动态图函数:tf.compat.v1.enable_v2_behavior。
五、2.x 版本中的反向传播
在 1.x 版本中动态图的反向传播函数有多个:tf.GradientTape、tfe.implicit_gradients、tfe.implicit_value_and_gradients,可以根据实际的需要来灵活选择,使用起来非常灵活。(具体区别和实例演示可以参考《深度学习之 TensorFlow 工程化项目实战》一书)
但在 2.x 中,只保留了 tf.GradientTape 函数用于计算梯度。tfe.implicit_gradients 与 tfe.implicit_value_and_gradients 函数在 TensorFlow 2.x 中将不再被支持。
六、2.x 版本对于估算器的影响
TensorFlow 2.x 版本可以完全兼容 TensorFlow 1.x 版本的估算器框架代码。用估算器框架开发模型代码,不需要考虑版本移植的问题。
七、用工具进行代码的版本升级——适用于原生的 API 代码
如果手里的 1.x 代码只使用了原生的 API,那么可以直接使用 TensorFlow 2.x 版本中提供的工具对 TensorFlow 1.x 版本的代码进行升级。
TensorFlow 2.x 版本提供了一个升级 TensorFlow 1.x 版本代码的工具——tf_upgrade_v2,该工具可以非常方便地将 TensorFlow 1.x 版本中编写的代码移植到 TensorFlow 2.x 中。具体命令如下:
该命令主要做的是名字匹配,实现了在 TensorFlow 2.x 版本中,将 TensorFlow 1.x 版本中的部分函数名字进行调整,部分例子如下:
将函数 tf.random_uniform 改成了 tf.random.uniform。
将函数 tf.random_crop 改成了 tf.image.random_crop。
将函数 tf.random_shuffle 改成了 tf.random.shuffle。
将函数 tf.read_file 改成了 tf.io.read_file。
tf_upgrade_v2 工具支持单文件转换和多文件批量转换两种方式。
1. 对单个代码文件进行转换
在命令行里输入 tf_upgrade_v2 命令,用“–infile”参数来指定输入文件,用“–outfile”参数来指定输出文件。具体命令如下:
该命令可以将 TensorFlow 1.x 版本中编写的代码文件 foo_v1.py 转成可以支持 TensorFlow 2.x 版本的代码 foo_v2.py。
2. 批量转化多个代码文件
在命令行里输入 tf_upgrade_v2 命令,用“-intree”参数来指定输入文件路径,用“-outtree”参数来指定输出文件路径。具体命令如下:
该命令可以将目录为 foo_v1 下的所有代码文件转成支持 TensorFlow 2.x 版本的代码文件,并保存到目录 foo_v2 中。
八、2.x 版本对于 TF-Hub、T2T 等库的影响
非常庆幸的是,TF-Hub、T2T 等库可以同时支持 TensorFlow 的 1.x 与 2.x 版本。
1.TF-Hub 库
TF-Hub 库是 TensorFlow 中专门用于预训练模型的库,其中包含很多在大型数据集上训练好的模型。如需在较小的数据集上实现识别任务,则可以通过微调这些预训练模型来实现。另外,它还能够提升原有模型在具体场景中的泛化能力,加快训练的速度。
在 GitHub 网站上有 TF-Hub 库的源码链接,其中包含了众多详细的说明文档。地址如下:
https://github.com/tensorflow/hub
2.T2T
Tensor2Tensor(T2T)是谷歌开源的一个模块化深度学习框架,其中包含当前各个领域中最先进的模型,以及训练模型时常用到的数据集。
如想了解更多关于 T2T 的细节,可以在以下链接中查看 T2T 框架的源码及教程:
https://github.com/tensorflow/tensor2tensor
九、2.x 版本对于 tf.layers 接口的影响
用 tf.layers 接口开发模型代码需要考虑版本移植的问题。在 TensorFlow 2.x 版本中,所有 tf.layers 接口都需要被换作 tf.compat.v1.layers。
另外,在 TensorFlow 2.x 版本中,tf.layers 模块更多用于 tf.keras 接口的底层实现。如果是开发新项目,则建议直接使用 tf.keras 接口;如果要重构已有的项目,也建议使用 tf.keras 接口进行替换。
十、2.x 版本的新特性——自动图
在 2.x 版本中,加入了很多新特性,自动图是最为实用的特性之一。
在 TensorFlow 1.x 版本中,要开发基于张量控制流的程序,必须使用 tf.conf、tf. while_loop 之类的专用函数。这增加了开发的复杂度。
在 TensorFlow 2.x 版本中,可以通过自动图(AutoGraph)功能,将普通的 Python 控制流语句转成基于张量的运算图,大大简化了开发工作。
在 TensorFlow 2.x 版本中,可以用 tf.function 装饰器修饰 Python 函数,将其自动转化成张量运算图。示例代码如下:
从上面代码的输出结果中可以看到,程序运行了控制流“tf.reduce_mean(input_data) > 0”语句的两个分支。这表明被装饰器 tf.function 修饰的函数具有张量图的控制流功能。
在使用自动图功能时,如果在被修饰的函数中有多个返回分支,则必须确保所有的分支都返回相同类型的张量,否则会报错。
TensorFlow 2.x 版本还有更多新特性,比如 TensorFLow.js、TF-Lite、模型保存和恢复的新 API 等都可以使 AI 的开发和应用变得更加快捷、方便。具体可以参考《深度学习之 TensorFlow 工程化项目实战》一书的介绍和实例演示。
十一、将代码升级到 TensorFlow 2.x 版本的经验总结
下面将升级代码到 TensorFlow 2.x 版本的方法汇总起来,有如下几点。
1. 最快速转化的方法
在代码中没有使用 contrib 模块的情况下,可以在代码最前端加上如下两句,直接实现代码升级。
这种方法只是保证代码在 TensorFlow 2.x 版本上能够运行,并不能发挥 TensorFlow 的最大性能。
2. 使用工具进行转化的方法
在代码中没有使用 contrib 模块的情况下,用 tf_upgrade_v2 工具可以快速实现代码升级。当然 tf_upgrade_v2 工具并不是万能的,它只能实现基本的 API 升级。一般在转化完成之后还需要手动二次修改。
3. 将静态图改成动态图的方法
静态图可以看作程序的运行框架,可以将输入输出部分原样套用在函数的调用框架中。具体步骤如下:
(1)将会话(session)转化成函数。
(2)将注入机制中的占位符(tf.placeholder)和字典(feed_dict)转化成函数的输入参数。
(3)将会话运行(session.run)后的结果转化成函数的返回值。
在实现过程中,可以通过自动图功能,用简单的函数逻辑替换静态图的运算结构。
4. 将共享变量转成 Python 对象的命名空间
在定义权重参数时,用 tf.Variable 函数替换 tf.get_variable 函数。每个变量的命名空间(variable_scope)用类对象空间进行替换,即将网络封装成类的形式来搭建模型。
在封装类的过程中,可以继承 tf.keras 接口(如:tf.keras.layers.Layer、tf.keras.Model),也可以继承更底层的接口(如 tf.Module、tf.layers.Layer)。
在对模型进行参数更新时,可以使用实例化类对象的 variables 和 trainable_variables 属性来控制参数。
5. 升级 TF-slim 接口开发的程序
TensorFlow 2.x 版本将彻底抛弃 TF-slim 接口,所以升级 TF-slim 接口程序需要较大的工作量。官方网站给出的指导建议是:如果手动将 TF-slim 接口程序转化为 tf.layers 接口实现,则可以满足基本使用;如果想与 TensorFlow 2.x 版本结合得更加紧密,则可以再将其转化为 tf.keras 接口。
以上内容来自于《深度学习之TensorFlow工程化项目实战》一书,如果你想了解 TensorFlow 的更多使用技巧,或有关新旧版本的升级方法,或更多实例演示,请参考此书。
评论