TensorFlow 2.0 概述

2020-07-03 14:11:03 浏览数 (1)

阅读本文大概需要 16 分钟。

前言

在本文中将介绍与我的毕设论文演示案例相关的TensorFlow的一些基础知识,包括张量计算图操作数据类型和维度以及模型的保存,接着在第二部分,本文将介绍演示案例代码中用到的一些TensorFlow 2.0中的高阶API,代码中不会涉及像TensorFlow 1.x版本中的Session等一些较为复杂的东西,所有的代码都是基于高阶API中的tf.keras.models来构建的(具体模型构建使用Sequential按层顺序构建),可以大大的方便读者更好的理解代码。

需要注意的一点,本论文中所实现的两个案例均在本机CPU上进行运算,对于更大数量级的数据训练建议采用添加GPU的方法或者托管在Google cloud、AWS云平台上进行数据的处理。

1.1 基础知识概述

1.1.1 张量

第一次看到TensorFlow这个名词,第一反应是去翻译一下这代表什么意思,通过查阅相关字典可以知道,Tensor被翻译为张量,Flow被翻译为流或者流动,组合起来TensorFlow可以被翻译为张量流。那什么是张量,什么又是流呢?

一般来将,把任意维度的数据称为张量,比如说一维数组(任意一门编程语言里都会学到一维数组的概念)、二维矩阵(我们在线性代数中学过关于矩阵的概念,这里不做赘述)以及N维数据。而是指让数据在不同的计算设备上进行传输并计算(因为只有Tensor形式的数据可以实现在不同的设备之间进行传递)。

总结起来,我们可以认为TensorFlow的意思就是:让Tensor类型的数据在各个计算设备之间进行流动并完成计算。那为什么要让数据流动起来呢?Tensor类型又具体包括什么呢?接下来先来看一段演示代码:

代码语言:javascript复制
# 将通过清华镜像下载的tensorflow包导入
import tensorflow as tf
a = tf.constant([[1.0,-2],[-3,4]])
print(a)

控制台输出结果如下:

代码语言:javascript复制
tf.Tensor(
[[ 1. -2.]
 [-3.  4.]], shape=(2, 2), dtype=float32)```

在上述代码中规定了一个2*2的矩阵,并将其打印在控制台。通过结果可以发现控制台输出的Tensor里面有三个参数:

  1. 第一个参数是一个2*2的矩阵,且矩阵中的元素全部为浮点类型。
  2. 第二个参数是shape,也就是输出矩阵的类型,很明显shape(2,2)表示输出矩阵为一个2*2的矩阵;举个例子,一个二阶张量a=[[1,1,1],[2,2,2]]的形状是两行三列,即shape=(2,3)。
  3. dtype=float32表示输出矩阵中元素的数据类型为浮点型(32为浮点数)

【注】:在上述对于代码部分的解释中提到一个名词二阶张量,接下来将通过表格的形式来区分一下标量、向量、矩阵的阶数的细微差异:

表1-1 标量向量和矩阵的阶数

rank(阶)

实例

例子

0

标量(只有大小)

a=1

1

向量(有大小和方向)

b=[1,1,1,1]

2

矩阵(数据表)

c=[[1,1],[1,1]]

3

3阶张量(数据立体)

d=[[[1],[1]],[[1],[1]]]

n

n阶

n层括号

简单解释一下,阶指的就是维度,它与矩阵的阶不同。

举个例子,对于a=[[1,1,1],[2,2,2],[3,3,3]]从矩阵的角度看,这是一个3*3的方阵,也就是说它的阶数为3,而从张量的角度看,它的阶数为2,即维度为2,因为它只有两层中括号。

1.1.2 计算图

首先来看看TensorFlow官网中的这幅图,一方面是帮助我们理解的概念,另一方面是为我们理解的概念做下铺垫。

【注】TensorFlow官网中的动图演示请参考如下网址:

http://www.shipudong.com/2020/03/24/bi-ye-she-ji-nei-rong-bu-chong/

图1.1 TensorFlow官网流图演示

将图一般分为两种,包括动态计算图静态计算图。我以中铁某局修建地铁为例来讲解这两种图的区别:

修建一条地铁需要设计图纸和施工队:

第一种情况,当设计师在设计图纸的时候(包括隧道走向、站点设置等,具体细节不予赘述)施工队什么也不干,必须等到设计工作完成之后,施工队才开始工作,(我们可以把这种情况理解为计算机中的同步方式,把设计工作和施工操作看作两个任务,当前任务未完成之前,不能进行其他操作)也就是说设计工作和具体施工完全分开,这就是所谓的静态计算图,我们称能够支持静态计算图的为静态框架,主要包括TensorFlow、Theano等;

第二种情况,设计工作和施工操作一起进行,设计方要求开凿隧道,施工队立即完成任务,如此下去,一经设计方下达任务,施工队必须立即完成操作,如此良性循环直到项目完成(我们把这种情况理解为计算机中的异步方式),这就是所谓的动态计算图,我们称能够支持动态计算图的为动态框架,主要包括Torch等。

在了解了动态计算图和静态计算图的例子之后,我们很明显的可以看出两种图的差异:静态计算图在未执行之前就必须定义好执行顺序和内存分配,简单来说,在程序未执行之前就知道了所有操作,有助于较快地执行计算操作;相比动态计算图,每次的执行顺序规划和内存分配都是局部的,并非全局最优,虽然灵活性较静态计算图有很大提升,但是代价太高,所以在现在流行的框架中,还是以静态框架为主,比如本论文中的由谷歌公司开源的TensorFlow。

1.1.3 操作

从图1.1可以观察到,数据一经输入(Input),会被进行不同的操作,首先会将数据进行预处理(比如图中的reshape操作),接着给处理好的数据中加入非线性操作(ReLU操作)等,使数据更符合自然界中的普遍关系,然后我们根据输入数据的类型进而采取比较合适的交叉熵函数(Crossentropy),用来衡量真实值与预测值的偏差,最后我们我们将根据项目真实情况选取合适的优化器(图中选用的为sgd,即随机梯度下降法)。图中的一个节点就代表一个操作,我们从计算图中了解到,TensorFlow属于静态计算图,也就是说在未执行前就已经定义好了执行的顺序,简单来讲,图中的各个操作之间是存在执行顺序的,而这些操作之间的依赖就是图中的。我们以一个非常简单的图示来讲解这个关系,首先来看一段代码:

代码语言:javascript复制
# 定义变量a
a = tf.Variable(1.0,name="a")
# 定义操作b为a 1
b = tf.add(a,1,name="b")
# 定义操作c为b 1
c = tf.add(b,1,name="c")
# 定义操作d为b 10
d = tf.add(b,10,name="d")

上述代码认为a、b、c和d均为需要进行的操作,下图中的x表示一个常数,值为1。

图1.2 操作之间的依赖关系

首先定义a=1.0,b=a 1,即b=2.0,以此类推,c=3.0,d=11.0,可以这样理解,操作b的进行需要依赖操作a,操作c的进行需要依赖操作b的完成,操作d的进行需要依赖操作b,且操作c和d之间没有依赖关系。

1.1.4 数据类型和维度

对于任意一门编程语言都会有数据类型,区别就在于每一门编程语言定义不同数据类型的方式不一样,在本章开始的时候了解过,在TensorFlow中,用张量(Tensor)来表示数据结构,接下来我们就将TensorFlow中的的数据类型与Python中的数据类型作以简单的对比,并通过表格的形式清晰的展现出来:

表1-2 TensorFlow和Python中数据类型的对应关系

TensorFlow数据类型

Python中的表示

说明

DT_FLOAT

tf.float32

32位浮点数

DT_DOUBLE

tf.float64

64位浮点数

DT_INT8

tf.int8

8位有符号整数

DT_INT16

tf.int16

16位有符号整数

DT_INT32

tf.int32

32位有符号整数

DT_INT64

tf.int64

64位有符号整数

DT_UINT8

tf.uint8

8位无符号整数

DT_UINT16

tf.uint16

16位无符号整数

DT_STRING

tf.string

byte类型数组

DT_BOOL

tf.bool

布尔型

DT_COMPLEX64

tf.complex64

复数类型,由32位浮点数的实部和虚部组成

DT_COMPLEX128

tf.complex128

复数类型,由64位浮点数的实部和虚部组成

DT_QINT8

tf.qint8

量化操作的8位有符号整数

DT_QINT32

tf.quint32

量化操作的32位有符号整数

DT_QUINT8

tf.quint8

量化操作的8位无符号整数

维度的相关概念,在上述文章中的张量部分已经详细讲过,此处不再赘述。一般来说张量的阶数(维度)就是看有几层中括号,接下来看一段代码:

代码语言:javascript复制
import tensorflow as tf
value_shape_0 = tf.Variable(1002)
value_shape_1 = tf.Variable([1,2,3])
value_shape_2 = tf.Variable([[1,2,3],[3,4,5],[5,6,7]])
print(value_shape_0.get_shape())
print(value_shape_1.get_shape())
print(value_shape_2.get_shape())

控制台输出结果如下:

代码语言:javascript复制
()
(3,)
(3, 3)
(2, 3, 2)

代码解释:

  • value_shape_0:定义了一个标量(只有大小),其维度为0;
  • value_shape_1:定义了一个一维向量(有大小和方向),其维度为3;
  • value_shape_2:定义了一个二维的矩阵,矩阵大小为3*2;
  • value_shape_3:定义了一个三维张量,第一维的维度是2,第二维的维度是3,第三维的维度是2,可以简单理解为:这是一个大小为2*3且深度为2的矩阵。

1.1.5 模型保存

当我们完成一个案例之后,我们想要把当前训练好的模型保存下来(保存模型是指把训练的参数保存下来),方便我们之后重新使用。当我们重新使用的时候,我们只需要重新载入模型即可。

首先我们来看一下保存模型的代码:

代码语言:javascript复制
# 保存模型
model.save("my_model.h5")

在关于MNIST手写字的例子中将我们训练好的模型保存下来,并命名为my_model.h5,接下来我们看一段载入模型的代码:

代码语言:javascript复制
# 加载模型文件
model = tf.keras.models.load_model("my_model.h5")

同样是在MNIST手写字的例子中,我们将保存好的模型导入,并通过matplotlib函数画出模型图,具体模型图我会在本毕设系列推文的案例讲解部分中进行展示。

2. 相关API介绍

一般来讲,TensorFlow共有5个不同的层次结构,从低到高分别是硬件层内核层低阶API中阶API高阶API,我们对每一层作以简单的介绍:

  1. 硬件层:我们知道TensorFlow可以支持CPU、GPU、TPU(受限于硬件条件,我们本文中的项目是在本机CPU上运行的)加入计算资源池,作为一种计算设备参与运算;
  2. 内核层:该层是由C 语言实现的内核,可以支持跨平台的分布运行;
  3. 低阶API:该层主要提供了由Python实现的一些操作符,并对由内核层实现的一些低阶API进行封装,包括各种Tensor(张量)操作算子、计算图、自动微分等;
  4. 中阶API:该层是由Python实现的模型组件,并对低阶API进行了函数封装,主要包括各种模型层(tf.keras.layers)、损失函数(tf.keras.losses)、优化器(tf.keras.optimizers)、数据管道(tf.data.Dataset)等;
  5. 高阶API:该层为由Python实现的模型成品,主要为tf.keras.models提供的模型的类接口,在第四章中实现MNIST手写字识别的例子我们主要使用它。

图2.1 API详解

上述内容是我们对TensorFlow中的API做了宏观的描述,接下来我将着重介绍5个代码案例中较为重要的API:

  • tf.keras.models.Sequential:我们可以通过Sequential按层顺序来构建模型,也可以通过add方法一层一层添加模型(不建议使用),以下为代码演示:
代码语言:javascript复制
model = tf.keras.models.Sequential([
#  里面是添加的模型层,比如说卷积层、池化层等
])
  • tf.keras.layers:我们可以通过此API添加我们需要的不同的模型层(卷积层、池化层等),通过查阅TensorFlow官网关于此API的介绍可以知道,读者可以通过此API添加如下模型层:

图2.2 TensorFlow官网tf.keras.layers部分API

以下是代码演示:

代码语言:javascript复制
tf.keras.layers.Conv2D(input_shape = (28,28,1),filters = 32,kernel_size = 5,strides = 1,padding = "same",activation = "relu"),  # 28*28
  • tf.keras.datasets:用如下代码来加载MNIST收据集:
代码语言:javascript复制
mnist = tf.keras.datasets.mnist
(x_train, y_train),(x_test, y_test) = mnist.load_data()
  • model.compile:可以通过此API来编译经Sequential构建好的模型,同时也可以定义优化器、损失函数、如何对网络参数进行优化以及在训练过程中是否要计算准确率等,我们来看看官网中对此API的解释:

图2.3 compile函数官网介绍

具体代码如下:

代码语言:javascript复制
tf.keras.layers.Conv2D(input_shape = (28,28,1),filters = 32,kernel_size = 5,strides = 1,padding = "same",activation = "relu"),  # 28*28# 编译模型   优化器--adam  (sgd--随机梯度下降法)损失函数---均方误差   metrics---训练过程中计算准确率accuracy
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',metrics=['accuracy'])
  • model.fit:通过此API来训练模型,同时可以定义训练的迭代周期以及每次训练获取样本集的数量(一般默认batch_size=32),我们来看看官网对此API的解释:

图2.4 fit函数官网介绍

具体代码如下:

代码语言:javascript复制
# 训练模型      epochs --- 迭代周期   batch_size默认为32
model.fit(x_train, y_train, batch_size=32,epochs=5)

0 人点赞