TensorFlow 2.0卷积神经网络实战
上QQ阅读APP看书,第一时间看更新

2.1 配角转成主角——从TensorFlow Eager Execution转正谈起

最早追溯到TensorFlow 1.10版本,那个时候TensorFlow由于强大的深度学习计算能力被众多的深度学习从业人员所使用。但是盛名之下还是有一些小的确定让人诟病,例如程序编写的困难,代码格式和其他深度学习框架有较大差异,运行时占用资源较多等。

TensorFlow开发组为了解决这些问题,在TensorFlow 1.10版本的时候就引入了一种新的程序运行机制——TensorFlow Eager Execution。其目的是为了解决程序开发人员使用TensorFlow作为深度学习框架时学习坡度不是很友善的缺点,同时也为了增加程序编写的方便,使用了一种新的简化的TensorFlow运行机制Eager Execution。结果一经推出就大受好评,使得很多原先使用别的机器学习框架的程序编写人员、机器学习爱好者转投TensorFlow的怀抱中。

TensorFlow Eager Execution(动态图)是一个命令式的编程环境,不建立图而是立即运算求值:运算返回具体值替换(以前)先构建运算图然后执行的机制。使得(使用)TensorFlow和调试模型变得简单,而且减少了多余(模板化、公式化操作)。

动态图是一个灵活的机器学习平台,用于研究和实验,提供以下功能:


●直观的接口:方便编码使用,基于Python数据结构。快速迭代小模型和小数据。

●调试简单:直接调用ops来检查运行模型和测试变更。使用标准Python调试工具进行即时错误报告。

●自然控制流:使用Python控制流替换图控制流,简化动态模型规范。


2.1.1 Eager简介与调用

TensorFlow的开发团队曾经表示,Eager Execution的主要优点如下:


●快速调试即刻的运行错误并通过Python工具进行整合。

●借助易于使用的Python控制流支持动态模型。

●为自定义和高阶梯度提供强大的支持。

●适用于几乎所有可用的TensorFlow运算。


1. Eager Execution的调用

Eager Execution的调用非常简单,可以直接使用代码如下:

这是因为在TensorFlow 2.0中,Eager Execution是默认开启的,因此直接引入TensorFlow即可。

而在TensorFlow 1.X版本中,Eager Execution需要手动开启,代码如下:

这些代码是在1.X版本中开启Eager Execution的方法,首先第1行是引入TensorFlow,第2~3行是显示调用Eager模式,使之可以在本段代码中使用。

此外,更为常见的是,读者安装了TensorFlow 2.0或者更高版本,对于运行在1.X版本下编写的代码,可能会产生一些问题。因此需要重新引入TensorFlow 1.X的运行模式,在引入TensorFlow的时候需要修改代码如下:

这样显式地调用TensorFlow 1.X版本的API使用。

2. Eager模式的使用

Eager Execution一个非常有意思的、作为宣传点的功能就是允许用户在不创建Graph(图)的情况下运行TensorFlow代码,代码如下:

【程序2-1】

这里默认启动了Eager模式,在使用TensorFlow读入一个序列后将其打印,结果如下:

可以看到,结果打印出了读入数据后的Tensor数据格式,即具体数值为[1,2],维度大小为2,数据类型为int32。

如果此时需要将这个数据的具体内容打印出来,代码可以改成如下:

【程序2-2】

打印结果如下:

可以看到,此时由于加上了数据自带的numpy()函数,Tensor数据被显示转化为常用的NumPy数据格式,即常数格式。

这里顺带提一下,如果使用传统的TensorFlow编写模式,代码要修改为:

【程序2-3】

打印结果如下:

可以看到,此时数据被读入到图中而不是被直接计算,因此打印出的结果并没有具体数据,而具体数据的计算请读者自行完成。

2.1.2 读取数据

TensorFlow 1.X数据的读取是采用占位符的形式,首先将数据读取到内存中,之后建立整体的TensorFlow图,在运行图以后读取数据并显示。

TensorFlow 2.0简化了数据读取,其相似度NumPy的数据迭代风格,只使用TensorFlow中自带的Dateset API即可完成数据的迭代,代码如下:

1. 第一步:生成数据

首先是数据的生成,作者使用Numpy做了数据生成,产生了100个由0~99的数据并存储在arr_list中。

2. 第二步:使用Dataset API读取数据

下面使用Dataset读取API,代码如下:

这里首先使用Dataset.from_tensor_slices读取数据,之后使用shuffle函数打乱顺序,最终将数据以每个batch为10输出。

3. 第三步:创建计算模型

创建计算模型是数据处理的关键,这里为了简化起见,作者创建了一个非常简单的模型,即使用TensorFlow将输入的数据乘以0.1并输出,代码如下:

这里的model是一个简单函数的实现,有兴趣的读者可以往里添加更多的内容。

4. 第四步:数据的迭代输出

最后就是读取数据的迭代输出,前面已经做了说明,在Eager模式中,Dataset API是可以自动生成一个新的迭代器,将数据迭代出来。代码如下:

这样就构成了一个完整的使用Eager模型进行简单数据计算的模型,全部代码如下:

【程序2-4】

最终打印结果如图2.2所示。

图2.2 打印结果

输出结果显然不符合在程序中既定的模型,即将数列中的数乘以0.1并显示,而这里的输出数据却都显示为0。

究其原因是在Numpy数据生成的时候,以int32格式为数据的基本生成格式,因此Eager在进行计算时无法隐式地将数据转化成float类型,从而造成计算失败。

解决的办法也很方便,将数据生成代码改成如下形式:

具体内容请读者自行完成。

2.1.3 使用TensorFlow 2.0模式进行线性回归的一个简单的例子

下面我们就以一个线性回归的模型为例,介绍使用Eager模型进行机器学习计算的方法,其中涉及到模型参数的保存,以及读取已经保存的模型重新计算。

1. 第一步:模型的工具与数据的生成

首先是模型的定义,这里我们使用一个简单的一元函数模型作为待测定的模型基础,公式如下:

即3倍的输入值加上0.217作为输出值。

2. 第二步:模型的定义

在这里由于既定的模型是一个一元线性方程,因此在使用Eager模型时自定义一个类似的数据模型,代码如下:

首先使用固定数据定义模型初始化参数weight和bias,之后一个线性回归模型在初始状态拟合了一元回归模型。

3. 第三步:损失函数的定义

对于使用机器学习进行数据拟合,一个非常重要的内容就是损失函数的编写,它往往决定着数据从空间中的哪个角度去拟合真实数据。

本例使用均方差(MSE)去计算拟合的数据与真实数据之间的误差,代码如下:

这是使用TensorFlow自带损失函数计算MSE(均方差)的表示方法,当然也可以使用自定义的损失函数,代码如下:

这两者是等效的,不过自定义的损失函数可以使程序编写者获得更大的自由度,对新手来说,还是使用定制的损失函数去计算较好,这一点请读者自行斟酌。

4. 第四步:梯度函数的更新计算

下面就是梯度的函数计算,这里可以直接调用TensorFlow的优化器,作者选择使用Adam优化器作为优化工具,代码如下:

opt对应的是TensorFlow优化器的对应写法。全部代码如下:

【程序2-5】

打印结果如图2.3所示。

图2.3 打印结果

可以看到经过迭代计算以后,生成的weight值和bias值较好地拟合成预定的数据。有TensorFlow 1.X编程经验的读者可能会对这种数据更新的方式不习惯,但是需要记住这种写法:

除此之外Keras对于梯度的更新采用回调的方式对权重进行更新,代码如下:

全部代码如下(函数调用过于复杂,仅供参考):

【程序2-6】

在这里函数会直接调用,其内部多次用到回调函数,对Python有较多研究的读者可以尝试运行一下。