精通 TensorFlow 1.x:1~5

2023-04-23 11:30:25 浏览数 (1)

一、TensorFlow 101

TensorFlow 是解决机器学习和深度学习问题的流行库之一。在开发供 Google 内部使用后,它作为开源发布供公众使用和开发。让我们理解 TensorFlow 的三个模型:数据模型编程模型执行模型

TensorFlow 数据模型由张量组成,编程模型由数据流图或计算图组成。 TensorFlow 执行模型包括基于依赖条件从序列中触发节点,从依赖于输入的初始节点开始。

在本章中,我们将回顾构成这三个模型的 TensorFlow 元素,也称为核心 TensorFlow。

我们将在本章中介绍以下主题:

  • TensorFlow 核心
    • 张量
    • 常量
    • 占位符
    • 操作
    • 从 Python 对象创建张量
    • 变量
    • 从库函数生成的张量
  • 数据流图或计算图
    • 执行顺序和延迟加载
    • 跨计算设备执行图 - CPU 和 GPU
    • 多个图
  • TensorBoard 概述

本书的编写注重实际,因此您可以从本书的 GitHub 仓库中克隆代码或从 Packt Publishing 下载它。 您可以使用代码包中包含的 Jupyter 笔记本ch-01_TensorFlow_101来遵循本章中的代码示例。

什么是 TensorFlow?

根据 TensorFlow 网站:

TensorFlow 是一个使用数据流图进行数值计算的开源库。

最初由谷歌开发用于其内部消费,它于 2015 年 11 月 9 日开源发布。从那时起,TensorFlow 已被广泛用于开发各种领域的机器学习和深度神经网络模型,并继续在谷歌内部用于研究和产品开发。 TensorFlow 1.0 于 2017 年 2 月 15 日发布。让人怀疑这是否是 Google 向机器学习工程师赠送的情人节礼物!

TensorFlow 可以用数据模型,编程模型和执行模型来描述:

  • 数据模型由张量组成,它们在 TensorFlow 程序中创建,操作和保存的基本数据单元。
  • 编程模型由数据流图或计算图组成。在 TensorFlow 中创建程序意味着构建一个或多个 TensorFlow 计算图。
  • 执行模型包括以依赖序列触发计算图的节点。执行从运行直接连接到输入的节点开始,仅依赖于存在的输入。

要在项目中使用 TensorFlow,您需要学习如何使用 TensorFlow API 进行编程。 TensorFlow 有多个 API,可用于与库交互。 TF API 或库分为两个级别:

  • 低级库:低级库,也称为 TensorFlow 核心,提供非常细粒度的低级功能,从而提供对如何在模型中使用和实现库的完全控制。我们将在本章介绍 TensorFlow 核心。
  • 高级库:这些库提供高级功能,并且在模型中相对容易学习和实现。一些库包括 TFEstimators,TFLearn,TFSlim,Sonnet 和 Keras。我们将在下一章介绍其中一些库。

TensorFlow 核心

TensorFlow 核心是较低级别的库,其上构建了更高级别的 TensorFlow 模块。在深入学习高级 TensorFlow 之前,学习低级库的概念非常重要。在本节中,我们将快速回顾所有这些核心概念。

代码预热 - Hello TensorFlow

作为学习任何新编程语言,库或平台的习惯传统,让我们在深入探讨之前,编写简单的 Hello TensorFlow 代码作为热身练习。

我们假设您已经安装了 TensorFlow。 如果还没有,请参阅 TensorFlow 安装指南,了解安装 TensorFlow 的详细说明。

打开 Jupyter 笔记本中的文件ch-01_TensorFlow_101.ipynb,在学习文本时关注并运行代码。

  1. 使用以下代码导入 TensorFlow 库:
代码语言:javascript复制
import tensorflow as tf
  1. 获取 TensorFlow 会话。 TensorFlow 提供两种会话:Session()InteractiveSession()。我们将使用以下代码创建交互式会话:
代码语言:javascript复制
tfs = tf.InteractiveSession()

Session()InteractiveSession()之间的唯一区别是用InteractiveSession()创建的会话成为默认会话。因此,我们不需要指定会话上下文以便稍后执行与会话相关的命令。例如,假设我们有一个会话对象tfs和一个常量对象hello。如果tfs是一个InteractiveSession()对象,那么我们可以使用代码hello.eval()来求值hello。如果tfsSession()对象,那么我们必须使用tfs.hello.eval()with块。最常见的做法是使用with块,这将在本章后面介绍。

  1. 定义 TensorFlow 常量,hello
代码语言:javascript复制
hello = tf.constant("Hello TensorFlow !!")
  1. 在 TensorFlow 会话中执行常量并打印输出:
代码语言:javascript复制
print(tfs.run(hello))
  1. 您将获得以下输出:
代码语言:javascript复制
'Hello TensorFlow !!'

现在您已经使用 TensorFlow 编写并执行了前两行代码,让我们来看看 TensorFlow 的基本组成部分。

张量

张量是 TensorFlow 中计算的基本元素和基本数据结构。可能是您需要学习使用 TensorFlow 的唯一数据结构。张量是由维度,形状和类型标识的 n 维数据集合。

阶数是张量的维数,形状是表示每个维度的大小的列表。张量可以具有任意数量的尺寸。您可能已经熟悉零维集合(标量),一维集合(向量),二维集合(矩阵)的数量,以及多维集合。

标量值是等级 0 的张量,因此具有[1]的形状。向量或一维数组是秩 1 的张量,并且具有列或行的形状。矩阵或二维数组是秩 2 的张量,并且具有行和列的形状。三维数组将是秩 3 的张量,并且以相同的方式,n 维数组将是秩n的张量。

请参阅以下资源以了解有关张量及其数学基础的更多信息:

  • 维基百科上的张量页面
  • 来自美国国家航空航天局的张量导言

张量可以在其所有维度中存储一种类型的数据,并且其元素的数据类型被称为张量的数据类型。

您还可以在这里查看最新版本的 TensorFlow 库中定义的数据类型。

在编写本书时,TensorFlow 定义了以下数据类型:

TensorFlow Python API 数据类型

描述

tf.float16

16 位半精度浮点

tf.float32

32 位单精度浮点

tf.float64

64 位双精度浮点

tf.bfloat16

16 位截断浮点

tf.complex64

64 位单精度复数

tf.complex128

128 位双精度复数

tf.int8

8 位有符号整数

tf.uint8

8 位无符号整数

tf.uint16

16 位无符号整数

tf.int16

16 位有符号整数

tf.int32

32 位有符号整数

tf.int64

64 位有符号整数

tf.bool

布尔

tf.string

字符串

tf.qint8

量化的 8 位有符号整数

tf.quint8

量化的 8 位无符号整数

tf.qint16

量化的 16 位有符号整数

tf.quint16

量化的 16 位无符号整数

tf.qint32

量化的 32 位有符号整数

tf.resource

可变资源

我们建议您避免使用 Python 本地数据类型。 使用 TensorFlow 数据类型来定义张量,而不是 Python 本地数据类型。

可以通过以下方式创建张量:

  • 通过定义常量,操作和变量,并将值传递给它们的构造器。
  • 通过定义占位符并将值传递给session.run()
  • 通过tf.convert_to_tensor()函数转换 Python 对象,如标量值,列表和 NumPy 数组。

让我们来看看创建张量的不同方法。

常量

使用具有以下签名的tf.constant()函数创建常量值张量:

代码语言:javascript复制
tf.constant(
  value,
  dtype=None,
  shape=None,
  name='Const',
  verify_shape=False
)

让我们看看本书中 Jupyter 笔记本中提供的示例代码:

代码语言:javascript复制
c1=tf.constant(5,name='x')
c2=tf.constant(6.0,name='y')
c3=tf.constant(7.0,tf.float32,name='z')

让我们详细研究一下代码:

  • 第一行定义一个常数张量c1,给它值 5,并将其命名为x
  • 第二行定义一个常数张量c2,存储值为 6.0,并将其命名为y
  • 当我们打印这些张量时,我们看到c1c2的数据类型由 TensorFlow 自动推导出来。
  • 要专门定义数据类型,我们可以使用dtype参数或将数据类型作为第二个参数。在前面的代码示例中,我们将tf.float32的数据类型定义为tf.float32

让我们打印常量c1c2c3

代码语言:javascript复制
print('c1 (x): ',c1)
print('c2 (y): ',c2)
print('c3 (z): ',c3)

当我们打印这些常量时,我​​们得到以下输出:

代码语言:javascript复制
c1 (x):  Tensor("x:0", shape=(), dtype=int32)
c2 (y):  Tensor("y:0", shape=(), dtype=float32)
c3 (z):  Tensor("z:0", shape=(), dtype=float32)

为了打印这些常量的值,我们必须使用tfs.run()命令在 TensorFlow 会话中执行它们:

代码语言:javascript复制
print('run([c1,c2,c3]) : ',tfs.run([c1,c2,c3]))

我们看到以下输出:

代码语言:javascript复制
run([c1,c2,c3]) :  [5, 6.0, 7.0]

操作

TensorFlow 为我们提供了许多可以应用于张量的操作。通过传递值并将输出分配给另一个张量来定义操作。例如,在提供的 Jupyter 笔记本文件中,我们定义了两个操作,op1op2

代码语言:javascript复制
op1 = tf.add(c2,c3)
op2 = tf.multiply(c2,c3)

当我们打印op1op2时,我们发现它们被定义为张量:

代码语言:javascript复制
print('op1 : ', op1)
print('op2 : ', op2)

输出如下:

代码语言:javascript复制
op1 :  Tensor("Add:0", shape=(), dtype=float32)
op2 :  Tensor("Mul:0", shape=(), dtype=float32)

要打印这些操作的值,我们必须在 TensorFlow 会话中运行它们:

代码语言:javascript复制
print('run(op1) : ', tfs.run(op1))
print('run(op2) : ', tfs.run(op2))

输出如下:

代码语言:javascript复制
run(op1) :  13.0
run(op2) :  42.0

下表列出了一些内置操作:

操作类型

操作

算术运算

tf.add,tf.subtract,tf.multiply,tf.scalar_mul,tf.div,tf.divide,tf.truediv,tf.floordiv,tf.realdiv,tf.truncatediv,tf.floor_div,tf.truncatemod,tf.floormod,tf.mod,tf.cross

基本的数学运算

tf.add_n,tf.abs,tf.negative,tf.sign,tf.reciprocal,tf.square,tf.round,tf.sqrt,tf.rsqrt,tf.pow,tf.exp,tf.expm1,tf.log,tf.log1p,tf.ceil,tf.floor,tf.maximum,tf.minimum,tf.cos,tf.sin,tf.lbeta,tf.tan,tf.acos,tf.asin,tf.atan,tf.lgamma,tf.digamma,tf.erf,tf.erfc,tf.igamma,tf.squared_difference,tf.igammac,tf.zeta,tf.polygamma,tf.betainc,tf.rint

矩阵数学运算

tf.diag,tf.diag_part,tf.trace,tf.transpose,tf.eye,tf.matrix_diag,tf.matrix_diag_part,tf.matrix_band_part,tf.matrix_set_diag,tf.matrix_transpose,tf.matmul,tf.norm,tf.matrix_determinant ],tf.matrix_inverse,tf.cholesky,tf.cholesky_solve,tf.matrix_solve,tf.matrix_triangular_solve,tf.matrix_solve_ls,tf.qr, tf.self_adjoint_eig,tf.self_adjoint_eigvals,tf.svd

张量数学运算

tf.tensordot

复数运算

tf.complex,tf.conj,tf.imag,tf.real

字符串操作

tf.string_to_hash_bucket_fast,tf.string_to_hash_bucket_strong,tf.as_string,tf.encode_base64,tf.decode_base64,tf.reduce_join,tf.string_join,tf.string_split,tf.substr,tf.string_to_hash_bucket

占位符

虽然常量允许我们在定义张量时提供值,但占位符允许我们创建可在运行时提供其值的张量。 TensorFlow 为tf.placeholder()函数提供以下签名以创建占位符:

代码语言:javascript复制
tf.placeholder(
  dtype,
  shape=None,
  name=None
  )

例如,让我们创建两个占位符并打印它们:

代码语言:javascript复制
p1 = tf.placeholder(tf.float32)
p2 = tf.placeholder(tf.float32)
print('p1 : ', p1)
print('p2 : ', p2)

我们看到以下输出:

代码语言:javascript复制
p1 :  Tensor("Placeholder:0", dtype=float32)
p2 :  Tensor("Placeholder_1:0", dtype=float32)

现在让我们使用这些占位符定义一个操作:

代码语言:javascript复制
op4 = p1 * p2

TensorFlow 允许使用速记符号进行各种操作。在前面的例子中,p1 * p2tf.multiply(p1,p2)的简写:

代码语言:javascript复制
print('run(op4,{p1:2.0, p2:3.0}) : ',tfs.run(op4,{p1:2.0, p2:3.0}))

上面的命令在 TensorFlow 会话中运行op4,为p1p2的值提供 Python 字典(run()操作的第二个参数)。

输出如下:

代码语言:javascript复制
run(op4,{p1:2.0, p2:3.0}) :  6.0

我们还可以使用run()操作中的feed_dict参数指定字典:

代码语言:javascript复制
print('run(op4,feed_dict = {p1:3.0, p2:4.0}) : ',
      tfs.run(op4, feed_dict={p1: 3.0, p2: 4.0}))

输出如下:

代码语言:javascript复制
run(op4,feed_dict = {p1:3.0, p2:4.0}) :  12.0

让我们看一下最后一个例子,向量被送到同一个操作:

代码语言:javascript复制
print('run(op4,feed_dict = {p1:[2.0,3.0,4.0], p2:[3.0,4.0,5.0]}) : ',
    tfs.run(op4,feed_dict = {p1:[2.0,3.0,4.0], p2:[3.0,4.0,5.0]}))

输出如下:

代码语言:javascript复制
run(op4,feed_dict={p1:[2.0,3.0,4.0],p2:[3.0,4.0,5.0]}):[  6.  12.  20.]

两个输入向量的元素以元素方式相乘。

从 Python 对象创建张量

我们可以使用带有以下签名的tf.convert_to_tensor()操作从 Python 对象(如列表和 NumPy 数组)创建张量:

代码语言:javascript复制
tf.convert_to_tensor(
  value,
  dtype=None,
  name=None,
  preferred_dtype=None
  )

让我们创建一些张量并打印出来进行练习:

  1. 创建并打印 0D 张量:
代码语言:javascript复制
tf_t=tf.convert_to_tensor(5.0,dtype=tf.float64)

print('tf_t : ',tf_t)
print('run(tf_t) : ',tfs.run(tf_t))

输出如下:

代码语言:javascript复制
tf_t :  Tensor("Const_1:0", shape=(), dtype=float64)
run(tf_t) : 5.0
  1. 创建并打印 1D 张量:
代码语言:javascript复制
a1dim = np.array([1,2,3,4,5.99])
print("a1dim Shape : ",a1dim.shape)

tf_t=tf.convert_to_tensor(a1dim,dtype=tf.float64)

print('tf_t : ',tf_t)
print('tf_t[0] : ',tf_t[0])
print('tf_t[0] : ',tf_t[2])
print('run(tf_t) : n',tfs.run(tf_t))

输出如下:

代码语言:javascript复制
a1dim Shape :  (5,)
tf_t :  Tensor("Const_2:0", shape=(5,), dtype=float64)
tf_t[0] :  Tensor("strided_slice:0", shape=(), dtype=float64)
tf_t[0] :  Tensor("strided_slice_1:0", shape=(), dtype=float64)
run(tf_t) : 
 [ 1.    2.    3.    4.    5.99]
  1. 创建并打印 2D 张量:
代码语言:javascript复制
a2dim = np.array([(1,2,3,4,5.99),
                  (2,3,4,5,6.99),
                  (3,4,5,6,7.99)
                 ])
print("a2dim Shape : ",a2dim.shape)

tf_t=tf.convert_to_tensor(a2dim,dtype=tf.float64)

print('tf_t : ',tf_t)
print('tf_t[0][0] : ',tf_t[0][0])
print('tf_t[1][2] : ',tf_t[1][2])
print('run(tf_t) : n',tfs.run(tf_t))

输出如下:

代码语言:javascript复制
a2dim Shape :  (3, 5)
tf_t :  Tensor("Const_3:0", shape=(3, 5), dtype=float64)
tf_t[0][0] :  Tensor("strided_slice_3:0", shape=(), dtype=float64)
tf_t[1][2] :  Tensor("strided_slice_5:0", shape=(), dtype=float64)
run(tf_t) : 
 [[ 1.    2.    3.    4.    5.99]
  [ 2.    3.    4.    5.    6.99]
  [ 3.    4.    5.    6.    7.99]]
  1. 创建并打印 3D 张量:
代码语言:javascript复制
a3dim = np.array([[[1,2],[3,4]],
                  [[5,6],[7,8]]
                 ])
print("a3dim Shape : ",a3dim.shape)

tf_t=tf.convert_to_tensor(a3dim,dtype=tf.float64)

print('tf_t : ',tf_t)
print('tf_t[0][0][0] : ',tf_t[0][0][0])
print('tf_t[1][1][1] : ',tf_t[1][1][1])
print('run(tf_t) : n',tfs.run(tf_t))

输出如下:

代码语言:javascript复制
a3dim Shape :  (2, 2, 2)
tf_t :  Tensor("Const_4:0", shape=(2, 2, 2), dtype=float64)
tf_t[0][0][0] :  Tensor("strided_slice_8:0", shape=(), dtype=float64)
tf_t[1][1][1] :  Tensor("strided_slice_11:0", shape=(), dtype=float64)
run(tf_t) : 
 [[[ 1.  2.][ 3.  4.]]
  [[ 5.  6.][ 7.  8.]]]

TensorFlow 可以将 NumPy ndarray无缝转换为 TensorFlow 张量,反之亦然。

变量

到目前为止,我们已经看到了如何创建各种张量对象:常量,操作和占位符。在使用 TensorFlow 构建和训练模型时,通常需要将参数值保存在可在运行时更新的内存位置。该内存位置由 TensorFlow 中的变量标识。

在 TensorFlow 中,变量是张量对象,它们包含可在程序执行期间修改的值。

虽然tf.Variable看起来与tf.placeholder类似,但两者之间存在细微差别:

tf.placeholder

tf.Variable

tf.placeholder定义了不随时间变化的输入数据

tf.Variable定义随时间修改的变量值

tf.placeholder在定义时不需要初始值

tf.Variable在定义时需要初始值

在 TensorFlow 中,可以使用tf.Variable()创建变量。让我们看一个带有线性模型的占位符和变量的示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vIVy2mAR-1681566326302)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/e7e3cb1f-7f3a-4ff6-9902-2295abdd0822.png)]

  1. 我们将模型参数wb分别定义为具有[.3][-0.3]初始值的变量:
代码语言:javascript复制
w = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
  1. 输入x定义为占位符,输出y定义为操作:
代码语言:javascript复制
x = tf.placeholder(tf.float32)
y = w * x   b
  1. 让我们打印wvxy,看看我们得到了什么:
代码语言:javascript复制
print("w:",w)
print("x:",x)
print("b:",b)
print("y:",y)

我们得到以下输出:

代码语言:javascript复制
w: <tf.Variable 'Variable:0' shape=(1,) dtype=float32_ref>
x: Tensor("Placeholder_2:0", dtype=float32)
b: <tf.Variable 'Variable_1:0' shape=(1,) dtype=float32_ref>
y: Tensor("add:0", dtype=float32)

输出显示x是占位符张量,y是操作张量,而wb是形状(1,)和数据类型float32的变量。

在 TensorFlow 会话中使用变量之前,必须先初始化它们。您可以通过运行其初始化程序操作来初始化单个变量。

例如,让我们初始化变量w

代码语言:javascript复制
tfs.run(w.initializer)

但是,在实践中,我们使用 TensorFlow 提供的便利函数来初始化所有变量:

代码语言:javascript复制
tfs.run(tf.global_variables_initializer())

您还可以使用tf.variables_initializer()函数来初始化一组变量。

也可以通过以下方式调用全局初始化程序便利函数,而不是在会话对象的run()函数内调用:

代码语言:javascript复制
tf.global_variables_initializer().run()

在初始化变量之后,让我们运行我们的模型来给出x = [1,2,3,4]的值的输出:

代码语言:javascript复制
print('run(y,{x:[1,2,3,4]}) : ',tfs.run(y,{x:[1,2,3,4]}))

我们得到以下输出:

代码语言:javascript复制
run(y,{x:[1,2,3,4]}) :  [ 0.          0.30000001  0.60000002  0.90000004]

从库函数生成的张量

张量也可以从各种 TensorFlow 函数生成。这些生成的张量可以分配给常量或变量,也可以在初始化时提供给它们的构造器。

例如,以下代码生成 100 个零的向量并将其打印出来:

代码语言:javascript复制
a=tf.zeros((100,))
print(tfs.run(a))

TensorFlow 提供了不同类型的函数来在定义时填充张量:

  • 使用相同的值填充所有元素
  • 用序列填充元素
  • 使用随机概率分布填充元素,例如正态分布或均匀分布

使用相同的值填充张量元素

下表列出了一些张量生成库函数,用于使用相同的值填充张量的所有元素:

代码语言:javascript复制
zeros(
 shape,
 dtype=tf.float32,
 name=None
)

创建所提供形状的张量,所有元素都设置为零


代码语言:javascript复制
zeros_like(
 tensor,
 dtype=None,
 name=None,
 optimize=True
)

创建与参数形状相同的张量,所有元素都设置为零


代码语言:javascript复制
ones(
 shape,
 dtype=tf.float32,
 name=None
)

创建所提供形状的张量,所有元素都设置为 1


代码语言:javascript复制
ones_like(
 tensor,
 dtype=None,
 name=None,
 optimize=True
)

创建与参数形状相同的张量,所有元素都设置为 1


代码语言:javascript复制
fill(
 dims,
 value,
 name=None
)

创建一个形状的张量作为dims参数,,所有元素都设置为value;例如,a = tf.fill([100],0)

用序列填充张量元素

下表列出了一些张量生成函数,用于使用序列填充张量元素:

代码语言:javascript复制
lin_space(
 start,
 stop,
 num,
 name=None
)

[start, stop]范围内的num序列生成 1D 张量。张量与start参数具有相同的数据类型。例如,a = tf.lin_space(1,100,10)生成值为[1,12,23,34,45,56,67,78,89,100]的张量。


代码语言:javascript复制
range(
 limit,
 delta=1,
 dtype=None,
 name='range'
)

range(
 start, 
 limit, 
 delta=1,
 dtype=None,
 name='range'
)

[start, limit]范围内的数字序列生成 1D 张量,增量为delta。如果未指定dtype参数,则张量具有与start参数相同的数据类型。 此函数有两个版本。在第二个版本中,如果省略start参数,则start变为数字 0。例如,a = tf.range(1,91,10)生成具有值[1,11,21,31,41,51,61,71,81]的张量。请注意,limit参数的值(即 91)不包含在最终生成的序列中。

使用随机分布填充张量元素

TensorFlow 为我们提供了生成填充随机值分布的张量的函数。

生成的分布受图级别或操作级别种子的影响。使用tf.set_random_seed设置图级种子,而在所有随机分布函数中给出操作级种子作为参数seed。如果未指定种子,则使用随机种子。

有关 TensorFlow 中随机种子的更多详细信息,请访问此链接。

下表列出了一些张量生成函数,用于使用随机值分布填充张量元素:

代码语言:javascript复制
random_normal(
 shape,
 mean=0.0,
 stddev=1.0,
 dtype=tf.float32,
 seed=None,
 name=None
)

生成指定形状的张量,填充正态分布的值:normal(mean, stddev)


代码语言:javascript复制
truncated_normal(
 shape,
 mean=0.0,
 stddev=1.0,
 dtype=tf.float32,
 seed=None,
 name=None
)

生成指定形状的张量,填充来自截断的正态分布的值:normal(mean, stddev)。截断意味着返回的值始终与平均值的距离小于两个标准偏差。


代码语言:javascript复制
random_uniform(
 shape,
 minval=0,
 maxval=None,
 dtype=tf.float32,
 seed=None,
 name=None
) 

生成指定形状的张量,填充均匀分布的值:uniform([minval, maxval))


代码语言:javascript复制
random_gamma(
 shape,
 alpha,
 beta=None,
 dtype=tf.float32,
 seed=None,
 name=None
)

生成指定形状的张量,填充来自伽马分布的值:gamma(alpha,beta)。有关random_gamma函数的更多详细信息,请访问此链接。

使用tf.get_variable()获取变量

如果使用之前定义的名称定义变量,则 TensorFlow 会抛出异常。因此,使用tf.get_variable()函数代替tf.Variable()很方便。函数tf.get_variable()返回具有相同名称的现有变量(如果存在),并创建具有指定形状的变量和初始化器(如果它不存在)。例如:

代码语言:javascript复制
w = tf.get_variable(name='w',shape=[1],dtype=tf.float32,initializer=[.3])
b = tf.get_variable(name='b',shape=[1],dtype=tf.float32,initializer=[-.3])

初始化程序可以是上面示例中显示的张量或值列表,也可以是内置初始化程序之一:

  • tf.constant_initializer
  • tf.random_normal_initializer
  • tf.truncated_normal_initializer
  • tf.random_uniform_initializer
  • tf.uniform_unit_scaling_initializer
  • tf.zeros_initializer
  • tf.ones_initializer
  • tf.orthogonal_initializer

在分布式 TensorFlow 中,我们可以跨机器运行代码,tf.get_variable()为我们提供了全局变量。要获取局部变量,TensorFlow 具有类似签名的函数:tf.get_local_variable()

共享或重用变量:获取已定义的变量可促进重用。但是,如果未使用tf.variable_scope.reuse_variable()tf.variable.scope(reuse=True)设置重用标志,则会抛出异常。

现在您已经学会了如何定义张量,常量,运算,占位符和变量,让我们了解 TensorFlow 中的下一级抽象,它将这些基本元素组合在一起形成一个基本的计算单元,即数据流图或计算图。

数据流图或计算图

数据流图计算图是 TensorFlow 中的基本计算单元。从现在开始,我们将它们称为计算图。计算图由节点和边组成。每个节点代表一个操作(tf.Operation),每个边代表一个在节点之间传递的张量(tf.Tensor)。

TensorFlow 中的程序基本上是计算图。您可以使用表示变量,常量,占位符和操作的节点创建图,并将其提供给 TensorFlow。 TensorFlow 找到它可以触发或执行的第一个节点。触发这些节点会导致其他节点触发,依此类推。

因此,TensorFlow 程序由计算图上的两种操作组成:

  • 构建计算图
  • 运行计算图

TensorFlow 附带一个默认图。除非明确指定了另一个图,否则会将新节点隐式添加到默认图中。我们可以使用以下命令显式访问默认图:

代码语言:javascript复制
graph = tf.get_default_graph()

例如,如果我们想要定义三个输入并添加它们以产生输出y = x1 x2 x3,我们可以使用以下计算图来表示它:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pPtSdvP4-1681566326303)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/09c14036-55e8-48e6-b22b-f126211aae0c.png)]

在 TensorFlow 中,前一图像中的添加操作将对应于代码y = tf.add( x1 x2 x3 )

在我们创建变量,常量和占位符时,它们会添加到图中。然后我们创建一个会话对象,以执行操作对象,求值张量对象。

让我们构建并执行一个计算图来计算y = w × x b,正如我们在前面的例子中已经看到的那样:

代码语言:javascript复制
# Assume Linear Model y = w * x   b
# Define model parameters
w = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
# Define model input and output
x = tf.placeholder(tf.float32)
y = w * x   b
output = 0

with tf.Session() as tfs:
   # initialize and print the variable y
   tf.global_variables_initializer().run()
   output = tfs.run(y,{x:[1,2,3,4]})
print('output : ',output)

with块中创建和使用会话可确保在块完成时会话自动关闭。否则,必须使用tfs.close()命令显式关闭会话,其中tfs是会话名称。

执行顺序和延迟加载

节点按依赖顺序执行。如果节点a依赖于节点b,则a将在执行b之前执行请求b。除非未请求执行节点本身或取决于它的其他节点,否则不执行节点。这也称为延迟加载;即,在需要之前不创建和初始化节点对象。

有时,您可能希望控制在图中执行节点的顺序。这可以通过tf.Graph.control_dependencies()函数实现。 例如,如果图具有节点a, b, cd并且您想在ab之前执行cd,请使用以下语句:

代码语言:javascript复制
with graph_variable.control_dependencies([c,d]):
  # other statements here

这确保了在执行了节点cd之后,才执行前面with块中的任何节点。

跨计算设备执行图 - CPU 和 GPU

图可以分为多个部分,每个部分可以放置在不同的设备上执行,例如 CPU 或 GPU。您可以使用以下命令列出可用于执行图的所有设备:

代码语言:javascript复制
from tensorflow.python.client import device_lib
print(device_lib.list_local_devices())

我们得到以下输出(您的输出会有所不同,具体取决于系统中的计算设备):

代码语言:javascript复制
[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 12900903776306102093
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 611319808
locality {
  bus_id: 1
}
incarnation: 2202031001192109390
physical_device_desc: "device: 0, name: Quadro P5000, pci bus id: 0000:01:00.0, compute capability: 6.1"
]

TensorFlow 中的设备用字符串/device:<device_type>:<device_idx>标识。在上述输出中,CPUGPU表示器件类型,0表示器件索引。

关于上述输出需要注意的一点是它只显示一个 CPU,而我们的计算机有 8 个 CPU。原因是 TensorFlow 隐式地在 CPU 单元中分配代码,因此默认情况下CPU:0表示 TensorFlow 可用的所有 CPU。当 TensorFlow 开始执行图时,它在一个单独的线程中运行每个图中的独立路径,每个线程在一个单独的 CPU 上运行。我们可以通过改变inter_op_parallelism_threads的数量来限制用于此目的的线程数。类似地,如果在独立路径中,操作能够在多个线程上运行,TensorFlow 将在多个线程上启动该特定操作。可以通过设置intra_op_parallelism_threads的数量来更改此池中的线程数。

将图节点放置在特定的计算设备上

让我们通过定义配置对象来启用变量放置的记录,将log_device_placement属性设置为true,然后将此config对象传递给会话,如下所示:

代码语言:javascript复制
tf.reset_default_graph()

# Define model parameters
w = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
# Define model input and output
x = tf.placeholder(tf.float32)
y = w * x   b

config = tf.ConfigProto()
config.log_device_placement=True

with tf.Session(config=config) as tfs:
  # initialize and print the variable y
  tfs.run(global_variables_initializer())
   print('output',tfs.run(y,{x:[1,2,3,4]}))

我们在 Jupyter 笔记本控制台中获得以下输出:

代码语言:javascript复制
b: (VariableV2): /job:localhost/replica:0/task:0/device:GPU:0
b/read: (Identity): /job:localhost/replica:0/task:0/device:GPU:0
b/Assign: (Assign): /job:localhost/replica:0/task:0/device:GPU:0
w: (VariableV2): /job:localhost/replica:0/task:0/device:GPU:0
w/read: (Identity): /job:localhost/replica:0/task:0/device:GPU:0
mul: (Mul): /job:localhost/replica:0/task:0/device:GPU:0
add: (Add): /job:localhost/replica:0/task:0/device:GPU:0
w/Assign: (Assign): /job:localhost/replica:0/task:0/device:GPU:0
init: (NoOp): /job:localhost/replica:0/task:0/device:GPU:0
x: (Placeholder): /job:localhost/replica:0/task:0/device:GPU:0
b/initial_value: (Const): /job:localhost/replica:0/task:0/device:GPU:0
Const_1: (Const): /job:localhost/replica:0/task:0/device:GPU:0
w/initial_value: (Const): /job:localhost/replica:0/task:0/device:GPU:0
Const: (Const): /job:localhost/replica:0/task:0/device:GPU:0

因此,默认情况下,TensorFlow 会在设备上创建变量和操作节点,从而获得最高表现。 可以使用tf.device()函数将变量和操作放在特定设备上。让我们把图放在 CPU 上:

代码语言:javascript复制
tf.reset_default_graph()

with tf.device('/device:CPU:0'):
  # Define model parameters
  w = tf.get_variable(name='w',initializer=[.3], dtype=tf.float32)
    b = tf.get_variable(name='b',initializer=[-.3], dtype=tf.float32)
    # Define model input and output
  x = tf.placeholder(name='x',dtype=tf.float32)
    y = w * x   b

config = tf.ConfigProto()
config.log_device_placement=True

with tf.Session(config=config) as tfs:
  # initialize and print the variable y
  tfs.run(tf.global_variables_initializer())
   print('output',tfs.run(y,{x:[1,2,3,4]}))

在 Jupyter 控制台中,我们看到现在变量已经放在 CPU 上,并且执行也发生在 CPU 上:

代码语言:javascript复制
b: (VariableV2): /job:localhost/replica:0/task:0/device:CPU:0
b/read: (Identity): /job:localhost/replica:0/task:0/device:CPU:0
b/Assign: (Assign): /job:localhost/replica:0/task:0/device:CPU:0
w: (VariableV2): /job:localhost/replica:0/task:0/device:CPU:0
w/read: (Identity): /job:localhost/replica:0/task:0/device:CPU:0
mul: (Mul): /job:localhost/replica:0/task:0/device:CPU:0
add: (Add): /job:localhost/replica:0/task:0/device:CPU:0
w/Assign: (Assign): /job:localhost/replica:0/task:0/device:CPU:0
init: (NoOp): /job:localhost/replica:0/task:0/device:CPU:0
x: (Placeholder): /job:localhost/replica:0/task:0/device:CPU:0
b/initial_value: (Const): /job:localhost/replica:0/task:0/device:CPU:0
Const_1: (Const): /job:localhost/replica:0/task:0/device:CPU:0
w/initial_value: (Const): /job:localhost/replica:0/task:0/device:CPU:0
Const: (Const): /job:localhost/replica:0/task:0/device:CPU:0

简单放置

TensorFlow 遵循这些简单的规则,也称为简单放置,用于将变量放在设备上:

代码语言:javascript复制
If the graph was previously run, 
    then the node is left on the device where it was placed earlier
Else If the tf.device() block is used,
    then the node is placed on the specified device
Else If the GPU is present
    then the node is placed on the first available GPU
Else If the GPU is not present
    then the node is placed on the CPU

动态展示放置

tf.device()也可以传递函数名而不是设备字符串。在这种情况下,该函数必须返回设备字符串。此函数允许使用复杂的算法将变量放在不同的设备上。例如,TensorFlow 在tf.train.replica_device_setter()中提供循环设备设置器,我们将在下一节中讨论。

软放置

当您在 GPU 上放置 TensorFlow 操作时,TF 必须具有该操作的 GPU 实现,称为内核。如果内核不存在,则放置会导致运行时错误。此外,如果您请求的 GPU 设备不存在,您将收到运行时错误。处理此类错误的最佳方法是,如果请求 GPU 设备导致错误,则允许将操作置于 CPU 上。这可以通过设置以下config值来实现:

代码语言:javascript复制
config.allow_soft_placement = True

GPU 内存处理

当您开始运行 TensorFlow 会话时,默认情况下它会抓取所有 GPU 内存,即使您将操作和变量仅放置在多 GPU 系统中的一个 GPU 上也是如此。如果您尝试同时运行另一个会话,则会出现内存不足错误。这可以通过多种方式解决:

  • 对于多 GPU 系统,请设置环境变量CUDA_VISIBLE_DEVICES=<list of device idx>
代码语言:javascript复制
os.environ['CUDA_VISIBLE_DEVICES']='0'

在此设置之后执行的代码将能够获取仅可见 GPU 的所有内存。

  • 当您不希望会话占用 GPU 的所有内存时,您可以使用配置选项per_process_gpu_memory_fraction来分配一定百分比的内存:
代码语言:javascript复制
config.gpu_options.per_process_gpu_memory_fraction = 0.5

这将分配所有 GPU 设备的 50% 的内存。

  • 您还可以结合上述两种策略,即只制作一个百分比,同时只让部分 GPU 对流程可见。
  • 您还可以将 TensorFlow 进程限制为仅在进程开始时获取所需的最小内存。随着进程的进一步执行,您可以设置配置选项以允许此内存的增长。
代码语言:javascript复制
config.gpu_options.allow_growth = True

此选项仅允许分配的内存增长,但内存永远不会释放。

您将学习在后面的章节中跨多个计算设备和多个节点分配计算的技术。

多个图

您可以创建与默认图分开的图,并在会话中执行它们。但是,不建议创建和执行多个图,因为它具有以下缺点:

  • 在同一程序中创建和使用多个图将需要多个 TensorFlow 会话,并且每个会话将消耗大量资源
  • 您无法直接在图之间传递数据

因此,推荐的方法是在单个图中包含多个子图。如果您希望使用自己的图而不是默认图,可以使用tf.graph()命令执行此操作。下面是我们创建自己的图g并将其作为默认图执行的示例:

代码语言:javascript复制
g = tf.Graph()
output = 0

# Assume Linear Model y = w * x   b

with g.as_default():
 # Define model parameters
 w = tf.Variable([.3], tf.float32)
 b = tf.Variable([-.3], tf.float32)
 # Define model input and output
 x = tf.placeholder(tf.float32)
 y = w * x   b

with tf.Session(graph=g) as tfs:
 # initialize and print the variable y
 tf.global_variables_initializer().run()
 output = tfs.run(y,{x:[1,2,3,4]})

print('output : ',output)

TensorBoard

即使对于中等大小的问题,计算图的复杂性也很高。代表复杂机器学习模型的大型计算图可能变得非常混乱且难以理解。可视化有助于轻松理解和解释计算图,从而加速 TensorFlow 程序的调试和优化。 TensorFlow 附带了一个内置工具,可以让我们可视化计算图,即 TensorBoard。

TensorBoard 可视化计算图结构,提供统计分析并绘制在计算图执行期间作为摘要捕获的值。让我们看看它在实践中是如何运作的。

TensorBoard 最小示例

  1. 通过定义线性模型的变量和占位符来实现:
代码语言:javascript复制
# Assume Linear Model y = w * x   b
# Define model parameters
w = tf.Variable([.3], name='w',dtype=tf.float32)
b = tf.Variable([-.3], name='b', dtype=tf.float32)
# Define model input and output
x = tf.placeholder(name='x',dtype=tf.float32)
y = w * x   b
  1. 初始化会话,并在此会话的上下文中,执行以下步骤:
    • 初始化全局变量
    • 创建tf.summary.FileWriter将使用默认图中的事件在tflogs文件夹中创建输出
    • 获取节点y的值,有效地执行我们的线性模型
代码语言:javascript复制
with tf.Session() as tfs:
    tfs.run(tf.global_variables_initializer())
    writer=tf.summary.FileWriter('tflogs',tfs.graph)
    print('run(y,{x:3}) : ', tfs.run(y,feed_dict={x:3}))
  1. 我们看到以下输出:
代码语言:javascript复制
run(y,{x:3}) :  [ 0.60000002]

当程序执行时,日志将收集在tflogs文件夹中,TensorBoard 将使用该文件夹进行可视化。打开命令行界面,导航到运行ch-01_TensorFlow_101笔记本的文件夹,然后执行以下命令:

代码语言:javascript复制
tensorboard --logdir='tflogs'

您会看到类似于此的输出:

代码语言:javascript复制
Starting TensorBoard b'47' at http://0.0.0.0:6006

打开浏览器并导航到http://0.0.0.0:6006。看到 TensorBoard 仪表板后,不要担心显示任何错误或警告,只需单击顶部的 GRAPHS 选项卡即可。您将看到以下屏幕:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jjkEvBCb-1681566326304)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/0651fc7c-9a4d-42c3-98e0-efc991572877.png)]TensorBoard console

您可以看到 TensorBoard 将我们的第一个简单模型可视化为计算图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VG0Ejbi3-1681566326304)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/02f0f1ee-75f1-4da4-bd65-9fd36e21f3b7.png)]Computation graph in TensorBoard

现在让我们试着了解 TensorBoard 的详细工作原理。

TensorBoard 详情

TensorBoard 通过读取 TensorFlow 生成的日志文件来工作。因此,我们需要修改此处定义的编程模型,以包含其他操作节点,这些操作节点将在我们想要使用 TensorBoard 可视化的日志中生成信息。编程模型或使用 TensorBoard 的程序流程通常可以说如下:

  1. 像往常一样创建计算图。
  2. 创建摘要节点。将tf.summary包中的摘要操作附加到输出您要收集和分析的值的节点。
  3. 运行摘要节点以及运行模型节点。通常,您将使用便捷函数tf.summary.merge_all()将所有汇总节点合并到一个汇总节点中。然后执行此合并节点将基本上执行所有摘要节点。合并的摘要节点生成包含所有摘要的并集的序列化Summary ProtocolBuffers 对象。
  4. 通过将Summary ProtocolBuffers 对象传递给tf.summary.FileWriter对象将事件日志写入磁盘。
  5. 启动 TensorBoard 并分析可视化数据。

在本节中,我们没有创建汇总节点,而是以非常简单的方式使用 TensorBoard。我们将在本书后面介绍 TensorBoard 的高级用法。

总结

在本章中,我们快速回顾了 TensorFlow 库。我们了解了可用于构建 TensorFlow 计算图的 TensorFlow 数据模型元素,例如常量,变量和占位符。我们学习了如何从 Python 对象创建 Tensors。张量对象也可以作为特定值,序列或来自 TensorFlow 中可用的各种库函数的随机值分布生成。

TensorFlow 编程模型包括构建和执行计算图。计算图具有节点和边。节点表示操作,边表示将数据从一个节点传输到另一个节点的张量。我们介绍了如何创建和执行图,执行顺序以及如何在不同的计算设备(如 GPU 和 CPU)上执行图。我们还学习了可视化 TensorFlow 计算图 TensorBoard 的工具。

在下一章中,我们将探索构建在 TensorFlow 之上的一些高级库,并允许我们快速构建模型。

二、TensorFlow 的高级库

TensorFlow 有几个高级库和接口(API),允许我们使用 TFLearn,TFSlim,Sonnet,PrettyTensor,Keras 和最近发布的 TensorFlow Estimators 轻松构建和训练模型。

我们将在本章中介绍以下高级库,同时将下一章专门用于 Keras:

  • TFEstimator - 以前的 TF 学习
  • TFSlim
  • TFLearn
  • PrettyTensor
  • Sonnet

我们将提供使用所有五个库构建 MNIST 数据集模型的示例。当我们从第 4 章开始介绍模型的细节时,不要担心理解模型的细节。

您可以使用代码包中包含的 Jupyter 笔记本ch-02_TF_High_Level_Libraries来遵循本章中的代码示例。 尝试修改笔记本中的示例以进行实验和游戏。

TFEstimator - 以前的 TF 学习

TFEstimator 是一个高级 API,通过封装训练,评估,预测和导出函数,可以轻松创建和训练模型。 TensorFlow 最近重新命名并在 TensorFlow 中以新名称 TFEstimator 发布了 TFLearn 包,可能是为了避免与 tflearn.org 的 TFLearn 包混淆。 TFEstimator API 对原始 TF 学习包进行了重大改进,这些包在 KDD 17 会议上提供的研究论文中有所描述,可以在此链接中找到。

TFEstimator 接口设计灵感来自流行的机器学习库 SciKit Learn,允许从不同类型的可用模型创建估计器对象,然后在任何类型的估计器上提供四个主要函数:

  • estimator.fit()
  • estimator.evaluate()
  • estimator.predict()
  • estimator.export()

函数的名称是不言自明的。估计器对象表示模型,但模型本身是从提供给估计器的模型定义函数创建的。

我们可以在下图中描述估计器对象及其接口:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bokRyUJ9-1681566326305)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/f96f0dc6-5093-419b-8c1c-1b0eb1cf388a.png)]

使用 Estimator API 而不是在核心 TensorFlow 中构建所有内容,可以不用担心图,会话,初始化变量或其他低级细节。在撰写本书时,TensorFlow 提供了以下预构建的估计器:

  • tf.contrib.learn.KMeansClustering
  • tf.contrib.learn.DNNClassifier
  • tf.contrib.learn.DNNRegressor
  • tf.contrib.learn.DNNLinearCombinedRegressor
  • tf.contrib.learn.DNNLinearCombinedClassifier
  • tf.contrib.learn.LinearClassifier
  • tf.contrib.learn.LinearRegressor
  • tf.contrib.learn.LogisticRegressor

TFEstimator API 中的简单工作流程如下:

  1. 找到与您要解决的问题相关的预构建 Estimator。
  2. 编写导入数据集的函数。
  3. 定义包含特征的数据中的列。
  4. 创建在步骤 1 中选择的预构建估计器的实例。
  5. 训练估计器。
  6. 使用经过训练的估计器进行评估或预测。

下一章讨论的 Keras 库提供了将 Keras 模型转换为 Estimators 的便捷函数:keras.estimator.model_to_estimator()

笔记本ch-02_TF_High_Level_Libraries中提供了 MNIST 分类示例的完整代码。 TFEstimator MNIST 示例的输出如下:

代码语言:javascript复制
INFO:tensorflow:Using default config.
WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmprvcqgu07
INFO:tensorflow:Using config: {'_save_checkpoints_steps': None, '_task_type': 'worker', '_save_checkpoints_secs': 600, '_service': None, '_task_id': 0, '_master': '', '_session_config': None, '_num_worker_replicas': 1, '_keep_checkpoint_max': 5, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7ff9d15f5fd0>, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_is_chief': True, '_save_summary_steps': 100, '_model_dir': '/tmp/tmprvcqgu07', '_num_ps_replicas': 0, '_tf_random_seed': None}
INFO:tensorflow:Create CheckpointSaverHook.
INFO:tensorflow:Saving checkpoints for 1 into /tmp/tmprvcqgu07/model.ckpt.
INFO:tensorflow:loss = 2.4365, step = 1
INFO:tensorflow:global_step/sec: 597.996
INFO:tensorflow:loss = 1.47152, step = 101 (0.168 sec)
INFO:tensorflow:global_step/sec: 553.29
INFO:tensorflow:loss = 0.728581, step = 201 (0.182 sec)
INFO:tensorflow:global_step/sec: 519.498
INFO:tensorflow:loss = 0.89795, step = 301 (0.193 sec)
INFO:tensorflow:global_step/sec: 503.414
INFO:tensorflow:loss = 0.743328, step = 401 (0.202 sec)
INFO:tensorflow:global_step/sec: 539.251
INFO:tensorflow:loss = 0.413222, step = 501 (0.181 sec)
INFO:tensorflow:global_step/sec: 572.327
INFO:tensorflow:loss = 0.416304, step = 601 (0.174 sec)
INFO:tensorflow:global_step/sec: 543.99
INFO:tensorflow:loss = 0.459793, step = 701 (0.184 sec)
INFO:tensorflow:global_step/sec: 687.748
INFO:tensorflow:loss = 0.501756, step = 801 (0.146 sec)
INFO:tensorflow:global_step/sec: 654.217
INFO:tensorflow:loss = 0.666772, step = 901 (0.153 sec)
INFO:tensorflow:Saving checkpoints for 1000 into /tmp/tmprvcqgu07/model.ckpt.
INFO:tensorflow:Loss for final step: 0.426257.
INFO:tensorflow:Starting evaluation at 2017-12-15-02:27:45
INFO:tensorflow:Restoring parameters from /tmp/tmprvcqgu07/model.ckpt-1000
INFO:tensorflow:Finished evaluation at 2017-12-15-02:27:45
INFO:tensorflow:Saving dict for global step 1000: accuracy = 0.8856, global_step = 1000, loss = 0.40996

{'accuracy': 0.88559997, 'global_step': 1000, 'loss': 0.40995964}

您将在第 5 章中看到如何使用核心 TensorFlow 创建此类模型。

TFSlim

TFSlim 是一个基于 TensorFlow 核心构建的轻量级库,用于定义和训练模型。 TFSlim 可与其他 TensorFlow 低级和高级库(如 TFLearn)结合使用。 TFSlim 是包装中 TensorFlow 安装的一部分:tf.contrib.slim。运行以下命令以检查 TFSlim 安装是否正常工作:

代码语言:javascript复制
python3 -c 'import tensorflow.contrib.slim as slim; eval = slim.evaluation.evaluate_once'

TFSlim 提供了几个模块,可以独立挑选和应用,并与其他 TensorFlow 包混合使用。例如,在撰写本书时,TFSlim 有以下主要模块:

TFSlim 模块

模块说明

arg_scope

提供将元素应用于作用域下定义的所有图节点的机制。

layer

提供几种不同的层,如fully_connected,conv2d等等。

loss

提供用于训练优化器的损失函数

learn

提供训练模型的函数

evaluate

提供评估函数

metics

提供用于评估模型的度量函数

regularizers

提供用于创建正则化方法的函数

variable

提供变量创建的函数

net

提供各种预制和预训练模型,如 VGG16,InceptionV3,ResNet

TFSlim 的简单工作流程如下:

  1. 使用 Slim 层创建模型。
  2. 提供层的输入以实例化模型。
  3. 使用对率和标签来定义损失。
  4. 使用便利函数get_total_loss()获得全部损失。
  5. 创建一个优化器。
  6. 使用便利函数slim.learning.create_train_op()total_lossoptimizer创建训练函数。
  7. 使用上一步中定义的便捷函数slim.learning.train()和训练函数运行训练。

笔记本ch-02_TF_High_Level_Libraries中提供了 MNIST 分类示例的完整代码。 TFSlim MNIST 示例的输出如下:

代码语言:javascript复制
INFO:tensorflow:Starting Session.
INFO:tensorflow:Saving checkpoint to path ./slim_logs/model.ckpt
INFO:tensorflow:global_step/sec: 0
INFO:tensorflow:Starting Queues.
INFO:tensorflow:global step 100: loss = 2.2669 (0.010 sec/step)
INFO:tensorflow:global step 200: loss = 2.2025 (0.010 sec/step)
INFO:tensorflow:global step 300: loss = 2.1257 (0.010 sec/step)
INFO:tensorflow:global step 400: loss = 2.0419 (0.009 sec/step)
INFO:tensorflow:global step 500: loss = 1.9532 (0.009 sec/step)
INFO:tensorflow:global step 600: loss = 1.8733 (0.010 sec/step)
INFO:tensorflow:global step 700: loss = 1.8002 (0.010 sec/step)
INFO:tensorflow:global step 800: loss = 1.7273 (0.010 sec/step)
INFO:tensorflow:global step 900: loss = 1.6688 (0.010 sec/step)
INFO:tensorflow:global step 1000: loss = 1.6132 (0.010 sec/step)
INFO:tensorflow:Stopping Training.
INFO:tensorflow:Finished training! Saving model to disk.
final loss=1.6131552457809448

从输出中可以看出,便捷函数slim.learning.train()将训练输出保存在指定日志目录中的检查点文件中。如果重新开始训练,它将首先检查检查点是否存在,并且默认情况下将从检查点恢复训练。

在撰写本文时,TFSlim 的文档页面在此链接中被发现为空。 但是,可以在此链接的源代码中找到一些文档。

我们将使用 TFSlim 来学习如何在后面的章节中使用预训练的模型,如 VGG16 和 InceptionV3。

TFLearn

TFLearn 是 Python 中的模块化库,它构建在核心 TensorFlow 之上。

TFLearn 与 TensorFlow Learn 包不同,后者也称为 TFLearn(TF 和 Learn 之间有一个空格)。TFLearn 可从此链接获得,源代码可在 GitHub 上的此链接获得。

可以使用以下命令在 Python 3 中安装 TFLearn:

代码语言:javascript复制
pip3 install tflearn

要在其他环境或源中安装 TFLearn,请参阅此链接。

TFLearn 中的简单工作流程如下:

  1. 首先创建一个输入层。
  2. 传递输入对象以创建更多层。
  3. 添加输出层。
  4. 使用估计器层(例如regression)创建网络。
  5. 从上一步中创建的网络创建模型。
  6. 使用model.fit()方法训练模型。
  7. 使用训练的模型进行预测或评估。

创建 TFLearn 层

让我们学习如何在 TFLearn 中创建神经网络模型的层:

  1. 首先创建一个输入层:
代码语言:javascript复制
input_layer = tflearn.input_data(shape=[None,num_inputs]
  1. 传递输入对象以创建更多层:
代码语言:javascript复制
layer1 = tflearn.fully_connected(input_layer,10,
                                 activation='relu')
layer2 = tflearn.fully_connected(layer1,10,
                                 activation='relu')
  1. 添加输出层:
代码语言:javascript复制
output = tflearn.fully_connected(layer2,n_classes,
                                 activation='softmax')
  1. 从估计器层创建最终网络,例如regression
代码语言:javascript复制
net = tflearn.regression(output,
                         optimizer='adam',
                         metric=tflearn.metrics.Accuracy(),
                         loss='categorical_crossentropy'
                        )

TFLearn 为以下子部分中描述的层提供了几个类。

TFLearn 核心层

TFLearn 在tflearn.layers.core模块中提供以下层:

层类

描述

input_data

该层用于指定神经网络的输入层。

fully_connected

该层用于指定一个层,其中所有神经元都连接到前一层中的所有神经元。

dropout

该层用于指定丢弃正则化。输入元素由1/keep_prob缩放,同时保持预期的总和不变。

custom_layer

此层用于指定要应用于输入的自定义函数。此类包装我们的自定义函数并将该函数显示为层。

reshape

此层将输入重新整形为指定形状的输出。

flatten

该层将输入张量转换为 2D 张量。

activation

该层将指定的激活函数应用于输入张量。

single_unit

该层将线性函数应用于输入。

highway

该层实现了完全连接的公路函数。

one_hot_encoding

此层将数字标签转换为二元向量单热编码表示。

time_distributed

该层将指定的函数应用于输入张量的每个时间步长。

multi_target_data

此层创建并连接多个占位符,特别是在层使用来自多个源的目标时使用。

TFLearn 卷积层

TFLearn 在tflearn.layers.conv模块中提供以下层:

层类

描述

conv_1d

该层将 1D 卷积应用于输入数据

conv_2d

该层将 2D 卷积应用于输入数据

conv_3d

该层将 3D 卷积应用于输入数据

conv_2d_transpose

该层将conv2_d的转置应用于输入数据

conv_3d_transpose

该层将conv3_d的转置应用于输入数据

atrous_conv_2d

该层计算二维动态卷积

grouped_conv_2d

该层计算深度 2D 卷积

max_pool_1d

该层计算 1D 最大池化

max_pool_2d

该层计算 2D 最大池化

avg_pool_1d

该层计算 1D 平均池化

avg_pool_2d

该层计算 2D 平均池化

upsample_2d

该层应用行和列 2D 重复操作

upscore_layer

该层实现了这个页面中规定的最高分。

global_max_pool

该层实现全局最大池化操作

global_avg_pool

该层实现全局平均池化操作

residual_block

该层实现残差块以创建深度残差网络

residual_bottleneck

该层实现深度残差网络的残差瓶颈块

resnext_block

该层实现 ResNext 块

TFLearn 循环层

TFLearn 在tflearn.layers.recurrent模块中提供以下层:

层类

描述

simple_rnn

该层实现了简单的循环神经网络模型

bidirectional_rnn

该层实现双向 RNN 模型

lstm

该层实现了 LSTM 模型

gru

该层实现 GRU 模型

TFLearn 正则化层

TFLearn 在tflearn.layers.normalization模块中提供以下层:

层类

描述

batch_normalization

该层正则化每个批次的先前层激活的输出

local_response_normalization

该层实现 LR 正则化

l2_normalization

该层将 L2 归一化应用于输入张量

TFLearn 嵌入层

TFLearn 在tflearn.layers.embedding_ops模块中只提供一层:

层类

描述

embedding

该层实现整数 ID 或浮点序列的嵌入函数

TFLearn 合并层

TFLearn 在tflearn.layers.merge_ops模块中提供以下层:

层类

描述

merge_outputs

该层将张量列表合并为单个张量,通常用于合并相同形状的输出张量

merge

该层将张量列表合并为单个张量;您可以指定需要进行合并的轴

TFLearn 估计层

TFLearn 在tflearn.layers.estimator模块中只提供一层:

层类

描述

regression

该层实现线性或逻辑回归

在创建回归层时,您可以指定优化器以及损失和度量函数。

TFLearn 在tflearn.optimizers模块中提供以下优化器函数作为类:

  • SGD
  • RMSprop
  • Adam
  • Momentum
  • AdaGrad
  • Ftrl
  • AdaDelta
  • ProximalAdaGrad
  • Nesterov

您可以通过扩展tflearn.optimizers.Optimizer基类来创建自定义优化器。

TFLearn 在tflearn.metrics模块中提供以下度量函数作为类或操作:

  • Accuracyaccuracy_op
  • Top_ktop_k_op
  • R2r2_op
  • WeightedR2weighted_r2_op
  • binary_accuracy_op

您可以通过扩展tflearn.metrics.Metric基类来创建自定义指标。

TFLearn 在tflearn.objectives模块中提供以下损失函数,称为目标:

  • softymax_categorical_crossentropy
  • categorical_crossentropy
  • binary_crossentropy
  • weighted_crossentropy
  • mean_square
  • hinge_loss
  • roc_auc_score
  • weak_cross_entropy_2d

在指定输入,隐藏和输出层时,您可以指定要应用于输出的激活函数。 TFLearn 在tflearn.activations模块中提供以下激活函数:

  • linear
  • tanh
  • sigmoid
  • softmax
  • softplus
  • softsign
  • relu
  • relu6
  • leaky_relu
  • prelu
  • elu
  • crelu
  • selu

创建 TFLearn 模型

从上一步创建的网络创建模型(创建 TFLearn 层部分的步骤 4):

代码语言:javascript复制
model = tflearn.DNN(net)

TFLearn 模型的类型

TFLearn 提供两种不同的模型:

  • DNN(深度神经网络)模型:此类允许您从通过层创建的网络创建多层感知机
  • SequenceGenerator模型:该类允许您创建可以生成序列的深度神经网络

训练 TFLearn 模型

创建后,使用model.fit()方法训练模型:

代码语言:javascript复制
model.fit(X_train, 
          Y_train, 
          n_epoch=n_epochs,  
          batch_size=batch_size, 
          show_metric=True, 
          run_id='dense_model')

使用 TFLearn 模型

使用训练的模型预测或评估:

代码语言:javascript复制
score = model.evaluate(X_test, Y_test)
print('Test accuracy:', score[0])

笔记本ch-02_TF_High_Level_Libraries中提供了 TFLearn MNIST 分类示例的完整代码。 TFLearn MNIST 示例的输出如下:

代码语言:javascript复制
Training Step: 5499  | total loss: 0.42119 | time: 1.817s
| Adam | epoch: 010 | loss: 0.42119 - acc: 0.8860 -- iter: 54900/55000
Training Step: 5500  | total loss: 0.40881 | time: 1.820s
| Adam | epoch: 010 | loss: 0.40881 - acc: 0.8854 -- iter: 55000/55000
--
Test accuracy: 0.9029

您可以从此链接获得有关 TFLearn 的更多信息。

PrettyTensor

PrettyTensor 在 TensorFlow 上提供了一个薄包装器。 PrettyTensor 提供的对象支持可链接的语法来定义神经网络。例如,可以通过链接层来创建模型,如以下代码所示:

代码语言:javascript复制
model = (X.
         flatten().
         fully_connected(10).
         softmax_classifier(n_classes, labels=Y))

可以使用以下命令在 Python 3 中安装 PrettyTensor:

代码语言:javascript复制
pip3 install prettytensor

PrettyTensor 以名为apply()的方法提供了一个非常轻量级和可扩展的接口。可以使用.apply(function, arguments)方法将任何附加函数链接到 PrettyTensor 对象。 PrettyTensor 将调用function并提供当前张量作为function的第一个参数。

用户创建的函数可以使用@prettytensor.register装饰器来装饰。详细信息在这里。

在 PrettyTensor 中定义和训练模型的工作流程如下:

  1. 获取数据。
  2. 定义超参数和参数。
  3. 定义输入和输出。
  4. 定义模型。
  5. 定义评估器,优化器和训练器函数。
  6. 创建运行器对象。
  7. 在 TensorFlow 会话中,使用runner.train_model()方法训练模型。
  8. 在同一会话中,使用runner.evaluate_model()方法评估模型。

笔记本ch-02_TF_High_Level_Libraries中提供了 PrettyTensor MNIST 分类示例的完整代码。 PrettyTensor MNIST 示例的输出如下:

代码语言:javascript复制
[1] [2.5561881]
[600] [0.3553167]
Accuracy after 1 epochs 0.8799999952316284 

[601] [0.47775066]
[1200] [0.34739292]
Accuracy after 2 epochs 0.8999999761581421 

[1201] [0.19110668]
[1800] [0.17418651]
Accuracy after 3 epochs 0.8999999761581421 

[1801] [0.27229539]
[2400] [0.34908807]
Accuracy after 4 epochs 0.8700000047683716 

[2401] [0.40000191]
[3000] [0.30816519]
Accuracy after 5 epochs 0.8999999761581421 

[3001] [0.29905257]
[3600] [0.41590339]
Accuracy after 6 epochs 0.8899999856948853 

[3601] [0.32594997]
[4200] [0.36930788]
Accuracy after 7 epochs 0.8899999856948853 

[4201] [0.26780865]
[4800] [0.2911002]
Accuracy after 8 epochs 0.8899999856948853 

[4801] [0.36304188]
[5400] [0.39880857]
Accuracy after 9 epochs 0.8999999761581421 

[5401] [0.1339224]
[6000] [0.14993289]
Accuracy after 10 epochs 0.8899999856948853 

Sonnet

Sonnet 是一个用 Python 编写的面向对象的库。它是由 DeepMind 在 2017 年发布的。Sonnet 打算从对象中清晰地分离构建计算图的以下两个方面:

  • 称为模块的对象的配置
  • 对象与计算图的连接

可以使用以下命令在 Python3 中安装 Sonnet:

代码语言:javascript复制
pip3 install dm-sonnet

可以按照此链接的说明从源安装 Sonnet。

模块被定义为抽象类sonnet.AbstractModule的子类。在编写本书时,Sonnet 中提供了以下模块:

基本模块

AddBias,BatchApply,BatchFlatten,BatchReshape,FlattenTrailingDimensions,Linear,MergeDims,SelectInput,SliceByDim,TileByDim和TrainableVariable

循环模块

DeepRNN,ModelRNN,VanillaRNN,BatchNormLSTM,GRU和LSTM

Recurrent ConvNet 模块

Conv1DLSTM和Conv2DLSTM

ConvNet 模块

Conv1D,Conv2D,Conv3D,Conv1DTranspose,Conv2DTranspose,Conv3DTranspose,DepthWiseConv2D,InPlaneConv2D和SeparableConv2D

ResidualNets

Residual,ResidualCore和SkipConnectionCore

其他

BatchNorm,LayerNorm,clip_gradient和scale_gradient

我们可以通过创建sonnet.AbstractModule的子类来定义我们自己的新模块。从函数创建模块的另一种非推荐方法是通过传递要包装为模块的函数来创建sonnet.Module类的对象。

在 Sonnet 库中构建模型的工作流程如下:

  1. 为从sonnet.AbstractModule继承的数据集和网络架构创建类。在我们的示例中,我们创建了一个 MNIST 类和一个 MLP 类。
  2. 定义参数和超参数。
  3. 从上一步中定义的数据集类定义测试和训练数据集。
  4. 使用定义的网络类定义模型。例如,在我们的案例中,model = MLP([20, n_classes])创建了一个 MLP 网络,其中包含两层 20 和每个神经元n_classes的数量。
  5. 使用模型为训练和测试集定义y_hat占位符。
  6. 定义训练和测试集的损失占位符。
  7. 使用训练损失占位符定义优化器。
  8. 在 TensorFlow 会话中执行所需数量的周期的损失函数以优化参数。

笔记本电脑ch-02_TF_High_Level_Libraries中提供了 Sonnet MNIST 分类示例的完整代码。 每个类中的__init__方法初始化类和相关的超类。_build方法在调用类时创建并返回数据集或模型对象。 Sonnet MNIST 示例的输出如下:

代码语言:javascript复制
Epoch : 0 Training Loss : 236.79913330078125
Epoch : 1 Training Loss : 227.3693084716797
Epoch : 2 Training Loss : 221.96337890625
Epoch : 3 Training Loss : 220.99142456054688
Epoch : 4 Training Loss : 215.5921173095703
Epoch : 5 Training Loss : 213.88958740234375
Epoch : 6 Training Loss : 203.7091064453125
Epoch : 7 Training Loss : 204.57427978515625
Epoch : 8 Training Loss : 196.17218017578125
Epoch : 9 Training Loss : 192.3954315185547
Test loss : 192.8847198486328

由于神经网络中计算的随机性,您的输出可能会有所不同。这包括我们对 Sonnet 模块的概述。

有关 Sonnet 的更多详细信息,您可以浏览此链接。

总结

在本章中,我们浏览了一些构建在 TensorFlow 之上的高级库。我们了解了 TFEstimator,TFSlim,TFLearn,PrettyTensor 和 Sonnet。我们为所有五个实现了 MNIST 分类示例。如果您无法理解模型的细节,请不要担心,因为为 MNIST 示例构建的模型将在以下章节中再次介绍。

我们总结了下表中提供的库和框架,如下表所示:

高级库

文档链接

源代码链接

PIP3 安装包

TFEstimator

https://www.tensorflow.org/get_started/estimator

https://github.com/tensorflow/tensorflow/tree/master/tensorflow/python/estimator

TensorFlow 预置

TFSlim

https://github.com/tensorflow/tensorflow/tree/r1.4/tensorflow/contrib/slim

https://github.com/tensorflow/tensorflow/tree/r1.4/tensorflow/contrib/slim/python/slim

TensorFlow 预置

TFLearn

http://tflearn.org/

https://github.com/tflearn/tflearn

tflearn

PrettyTensor

https://github.com/google/prettytensor/tree/master/docs

https://github.com/google/prettytensor

prettytensor

Sonnet

https://deepmind.github.io/sonnet/

https://github.com/deepmind/sonnet

dm-sonnet

在下一章中,我们将了解 Keras,这是用于创建和训练 TensorFlow 模型的最流行的高级库。

三、Keras 101

Keras 是一个高级库,允许使用 TensorFlow 作为后端深度学习库。 TensorFlow 团队将 TrasorFlow Core 中的 Keras 作为模块tf.keras。除了 TensorFlow 之外,Keras 在撰写本书时还支持 Theano 和 CNTK。

以下 Keras 的指导原则使其在深度学习社区中非常受欢迎:

  • 极简主义提供一致且简单的 API
  • 模块化允许将各种元素表示为可插拔模块
  • 将新模块添加为类和函数的可扩展性
  • 用于代码和模型配置的 Python 原生
  • 开箱即用的通用网络架构,支持 CNN,RNN 或两者的组合

在本书的其余部分中,我们将学习如何使用低级 TensorFlow API 和高级 Keras API 构建不同类型的深度学习和机器学习模型。

我们将在本章中介绍以下主题:

  • 安装 Keras
  • 在 Keras 中创建模型的工作流程
  • 使用顺序和函数式 API 创建 Keras 模型
  • Keras 层
  • 使用顺序和函数式 API 创建和添加层
  • 编译 Keras 模型
  • 训练 Keras 模型
  • 使用 Keras 模型进行预测
  • Keras 的附加模块
  • MNIST 数据集的 Keras 序列模型示例

安装 Keras

使用以下命令可以在 Python 3 中安装 Keras:

代码语言:javascript复制
pip3 install keras

要在其他环境或源代码中安装 Keras,请参阅此链接。

Keras 中的神经网络模型

Keras 中的神经网络模型将定义为层图。 Keras 中的模型可以使用顺序或函数式 API 创建。函数式和顺序 API 都可用于构建任何类型的模型。函数式 API 可以更轻松地构建具有多个输入,多个输出和共享层的复杂模型。

因此,根据经验,我们已经看到工程师将顺序 API 用于从简单层构建的简单模型,以及用于涉及分支和共享层的复杂模型的函数式 API。我们还观察到,使用函数式 API 构建简单模型可以更轻松地将模型扩展为具有分支和共享的复杂模型。因此,对于我们的工作,我们总是使用函数式 API。

在 Keras 中创建模型的工作流程

Keras 的简单工作流程如下:

  1. 创建模型
  2. 创建层并将其添加到模型中
  3. 编译模型
  4. 训练模型
  5. 使用该模型进行预测或评估

我们来看看每个步骤。

您可以使用代码包中包含的 Jupyter 笔记本ch-03_Keras_101来遵循本章中的代码示例。 尝试修改笔记本中的代码以探索各种选项。

创建 Keras 模型

可以使用顺序 API 或函数式 API 创建 Keras 模型。以下两小节给出了以两种方式创建模型的示例 。

用于创建 Keras 模型的顺序 API

在顺序 API 中,使用以下代码创建空模型:

代码语言:javascript复制
model = Sequential()

您现在可以将层添加到此模型中,我们将在下一节中看到。

或者,您也可以将所有层作为列表传递给构造器。例如,我们通过使用以下代码将它们传递给构造器来添加四个层:

代码语言:javascript复制
model = Sequential([ Dense(10, input_shape=(256,)), 
                    Activation('tanh'), 
                    Dense(10), 
                    Activation('softmax')
                   ])

用于创建 Keras 模型的函数式 API

在函数式 API 中,模型创建为Model类的一个实例,它接受输入和输出参数。输入和输出参数分别代表一个或多个输入和输出张量。

例如,使用以下代码从函数式 API 实例化模型:

代码语言:javascript复制
model = Model(inputs=tensor1, outputs=tensor2)

在上面的代码中,tensor1tensor2是张量或对象,可以像张量一样对待,例如,Keras layer对象。

如果有多个输入和输出张量,则可以将它们作为列表传递,如以下示例所示:

代码语言:javascript复制
model = Model(inputs=[i1,i2,i3], outputs=[o1,o2,o3])

Keras 层

Keras 为网络架构的简单构建提供了几个内置层类。以下部分概述和描述了 Keras2 在撰写本书时提供的各种类型的层。

Keras 核心层

Keras 核心层实现基本操作,几乎用于各种网络架构。下表给出了 Keras2 提供的层的摘要和说明:

层名称

描述

Dense

这是一个简单的完全连接的神经网络层。该层生成以下函数的输出:激活(输入 x 权重 偏差),其中激活是指传递给层的激活函数,默认为None。

Activation

该层将指定的激活函数应用于输出。该层生成以下函数的输出:激活(输入),其中激活是指传递给该层的激活函数。以下激活函数可用于实例化层:softmax,elu,selu,softplus,softsign,relu,tanh,sigmoid,hard_sigmoid和linear

Dropout

该层以指定的丢弃率将丢弃正则化应用于输入。

Flatten

该层使输入变平,即对于三维输入,它变平并产生一维输出。

Reshape

此层将输入转换为指定的形状。

Permute

此层按照指定的模式重新排序输入尺寸。

RepeatVector

该层以给定次数重复输入。因此,如果输入是 2D 张量的形状(#samples, #feature)并且该层被赋予n次重复,那么输出将是 3D 张量的形状(#samples, n, #feature)。

Lambda

该层将提供的函数包装为层。因此,输入通过提供的自定义函数传递以产生输出。该层为 Keras 用户提供了最终的可扩展性,可以将自己的自定义函数添加为层。

ActivityRegularization

该层将 L1 或 L2 或两种正则化的组合应用于其输入。该层应用于激活层的输出或具有激活函数的层的输出。

Masking

此层在输入张量中屏蔽或跳过这些时间步长,其中输入张量中的所有值都等于作为层参数提供的屏蔽值。

Keras 卷积层

这些层为卷积神经网络实现了不同类型的卷积,采样和裁剪操作:

层名称

描述

Conv1D

该层将单个空间或时间维度上的卷积应用于输入。

Conv2D

该层将二维卷积应用于输入。

SeparableConv2D

该层在每个输入通道上应用深度方式空间卷积,然后是逐点卷积,将所得到的输出通道混合在一起。

Conv2DTranspose

该层将卷积的形状恢复为产生这些卷积的输入的形状。

Conv3D

该层将三维卷积应用于输入。

Cropping1D

该层沿时间维度裁剪输入数据。

Cropping2D

此层沿空间维度裁剪输入数据,例如图像的宽度和高度。

Cropping3D

该层沿着时空裁剪输入数据,即所有三维。

UpSampling1D

该层按时间轴指定的时间重复输入数据。

UpSampling2D

此层沿两个维度按指定时间重复输入数据的行和列维度。

UpSampling3D

该层按三个维度的指定时间重复输入数据的三个维度。

ZeroPadding1D

该层将零添加到时间维度的开头和结尾。

ZeroPadding2D

此层将行和列的零添加到 2D 张量的顶部,底部,左侧或右侧。

ZeroPadding3D

该层将零添加到 3D 张量的三个维度。

Keras 池化层

这些层为卷积神经网络实现不同的池化操作:

层名称

描述

MaxPooling1D

该层实现一维输入数据的最大池化操作。

MaxPooling2D

该层实现二维输入数据的最大池化操作。

MaxPooling3D

该层实现三维输入数据的最大池化操作。

AveragePooling1D

该层实现一维输入数据的平均池化操作。

AveragePooling2D

该层实现二维输入数据的平均池化操作。

AveragePooling3D

该层实现三维输入数据的平均吃阿虎操作。

GlobalMaxPooling1D

该层实现一维输入数据的全局最大池化操作。

GlobalAveragePooling1D

该层实现一维输入数据的全局平均池化操作。

GlobalMaxPooling2D

该层实现二维输入数据的全局最大池化操作。

GlobalAveragePooling2D

该层实现二维输入数据的全局平均池化操作。

Keras 本地连接层

这些层在卷积神经网络中很有用:

层名称

描述

LocallyConnected1D

该层通过在输入的每个不同补丁上应用不同的滤波器组,将单个空间或时间维度上的卷积应用于输入,从而不共享权重。

LocallyConnected2D

该层通过在输入的每个不同补丁上应用不同的滤波器组,将两个维上的卷积应用于输入,从而不共享权重。

Keras 循环层

这些层实现循环神经网络的不同变体:

层名称

描述

SimpleRNN

该层实现了完全连接的循环神经网络。

GRU

该层实现了门控循环单元网络。

LSTM

该层实现了长期短期记忆网络。

Keras 嵌入层

目前,只有一个嵌入层选项可用:

层名称

描述

Embedding

该层采用由下标组成的 2D 张量,形状为(batch_size, sequence_length),并产生由形状(batch_size, sequence_length, output_dim)的密集向量组成的张量。

Keras 合并层

这些层合并两个或多个输入张量,并通过应用每个层表示的特定操作产生单个输出张量:

层名称

描述

Add

该层计算输入张量的逐元素加法。

Multiply

该层计算输入张量的逐元素乘法

Average

该层计算输入张量的逐元素平均值。

Maximum

该层计算输入张量的逐元素最大值。

Concatenate

此层沿指定轴连接输入张量。

Dot

该层计算两个输入张量中样本之间的点积。

add,multiply,average,maximum,concatenate和dot

这些函数表示此表中描述的各个合并层的函数接口。

Keras 高级激活层

这些层实现了高级激活函数,这些函数无法作为简单的底层后端函数实现。它们的操作类似于我们在核心层部分中介绍的Activation()层:

层名称

描述

LeakyReLU

该层计算ReLU激活函数的泄漏版本。

PReLU

该层计算参数化ReLU激活函数。

ELU

该层计算指数线性单元激活函数。

ThresholdedReLU

该层计算阈值版本的ReLU激活函数。

Keras 正则化层

目前,只有一个标准化层可用:

层名称

描述

BatchNormalization

该层标准化前一层的每个批量的输出,使得该层的输出近似为具有接近零的平均值和接近 1 的标准偏差。

Keras 噪音层

这些层可以添加到模型中,以防止过拟合添加噪音;它们也被称为正则化层。这些层的操作方式与核心层部分中的Dropout()ActivityRegularizer()层相同。

层名称

描述

GaussianNoise

该层将附加的零中心高斯噪声应用于输入。

GaussianDropout

该层将乘法的单中心高斯噪声应用于输入。

AlphaDropout

该层丢弃一定百分比的输入,使得丢弃后输出的均值和方差与输入的均值和方差紧密匹配。

将层添加到 Keras 模型

上一节中提到的所有层都需要添加到我们之前创建的模型中。在以下部分中,我们将介绍如何使用函数式 API 和顺序 API 添加层。

用于向 Keras 模型添加层的顺序 API

在顺序 API 中,可以通过实例化前面部分中给出的某个层类型的对象来创建层。然后使用model.add()函数将创建的层添加到模型中。作为示例,我们将创建一个模型,然后为其添加两个层:

代码语言:javascript复制
model = Sequential() 
model.add(Dense(10, input_shape=(256,)) 
model.add(Activation('tanh'))
model.add(Dense(10))
model.add(Activation('softmax'))

用于向 Keras 模型添加层的函数式 API

在函数式 API 中,首先以函数方式创建层,然后在创建模型时,输入和输出层作为张量参数提供,如我们在上一节。

这是一个例子:

  1. 首先,创建输入层:
代码语言:javascript复制
input = Input(shape=(64,))
  1. 接下来,以函数方式从输入层创建密集层:
代码语言:javascript复制
hidden = Dense(10)(inputs)
  1. 以同样的方式,以函数方式创建更多隐藏层,构建在前面的层之上:
代码语言:javascript复制
hidden = Activation('tanh')(hidden)
hidden = Dense(10)(hidden)
output = Activation('tanh')(hidden)
  1. 最后,使用输入和输出层实例化模型对象:
代码语言:javascript复制
model = Model(inputs=input, outputs=output)

有关创建顺序和函数式 Keras 模型的更深入细节,您可以阅读由 Antonio Gulli 和 Sujit Pal,Packt Publishing,2017 年出版的题为 Deep Learning with Keras 的书。

编译 Keras 模型

前面部分中构建的模型需要使用model.compile()方法进行编译,然后才能用于训练和预测。compile()方法的完整签名如下:

代码语言:javascript复制
compile(self, optimizer, loss, metrics=None, sample_weight_mode=None)

compile方法有三个参数:

  • optimizer:您可以指定自己的函数或 Keras 提供的函数之一。此函数用于更新优化迭代中的参数。 Keras 提供以下内置优化器函数:
    • SGD
    • RMSprop
    • Adagrad
    • Adadelta
    • Adam
    • Adamax
    • Nadam
  • loss:您可以指定自己的损失函数或使用提供的损失函数之一。优化器函数优化参数,以便最小化此损失函数的输出。 Keras 提供以下损失函数:
    • mean_squared_error
    • mean_absolute_error
    • mean_absolute_pecentage_error
    • mean_squared_logarithmic_error
    • squared_hinge
    • hinge
    • categorical_hinge
    • sparse_categorical_crossentropy
    • binary_crossentropy
    • poisson
    • cosine proximity
    • binary_accuracy
    • categorical_accuracy
    • sparse_categorical_accuracy
    • top_k_categorical_accuracy
    • sparse_top_k_categorical_accuracy
  • metrics:第三个参数是训练模型时需要收集的指标列表。如果启用了详细输出,则会为每次迭代打印度量标准。指标就像损失函数;一些由 Keras 提供,能够编写您自己的度量函数。所有损失函数也可用作度量函数。

训练 Keras 模型

训练 Keras 模型就像调用model.fit()方法一样简单。该方法的完整签名如下:

代码语言:javascript复制
fit(self, x, y, batch_size=32, epochs=10, verbose=1, callbacks=None, 
    validation_split=0.0, validation_data=None, shuffle=True, 
    class_weight=None, sample_weight=None, initial_epoch=0)

我们不会详细介绍这种方法的参数; 您可以在 Keras 网站上阅读详细信息。

对于我们之前创建的示例模型,使用以下代码训练模型:

代码语言:javascript复制
model.fit(x_data, y_labels)

使用 Keras 模型进行预测

经过训练的模型可用于使用model.predict()方法来预测值,或用model.evaluate()方法评估模型。

这两种方法的签名如下:

代码语言:javascript复制
predict(self, x, batch_size=32, verbose=0)
代码语言:javascript复制
evaluate(self, x, y, batch_size=32, verbose=1, sample_weight=None)

Keras 的附加模块

Keras 提供了几个补充基本工作流程的附加模块(附加函数在本章开头描述)。部分模块如下:

  • preprocessing模块提供了几种预处理序列,图像和文本数据的函数。
  • datasets模块提供了多种函数,可以快速访问几个流行的数据集,如 CIFAR10 图像,CIFAR100 图像,IMDB 电影评论,路透社新闻专线主题,MNIST 手写数字和波士顿房价。
  • initializers模块提供了几种设置层初始随机权重参数的函数,如ZerosOnesConstantRandomNormalRandomUniformTruncatedNormalVarianceScalingOrthogonalIdentitylecun_normallecun_uniformglorot_normalglorot_uniformhe_normalhe_uniform
  • models模块提供了几种恢复模型架构和权重的函数,如model_from_jsonmodel_from_yaml,和load_model。可以使用model.to_yaml()model.to_json()方法保存模型架构。通过调用model.save()方法可以保存模型权重。权重保存在 HDF5 文件中。
  • applications模块提供了几种预先构建和预训练的模型,例如 Xception,VGG16,VGG19,ResNet50,InceptionV3,InceptionResNetV2 和 MobileNet。我们将学习如何使用预建模型来预测我们的数据集。我们还将学习,如何使用来自略有不同的域的数据集,再训练applications模块中的预训练模型。

这就结束了我们对 Keras 的简要介绍,这是 TensorFlow 的高级框架。我们将在本书中提供使用 Keras 构建模型的示例。

用于 MNIST 数据集的 Keras 序列模型示例

以下是构建简单多层感知机(在第 5 章中详细介绍)的一个小例子,用于对 MNIST 集中的手写数字进行分类:

代码语言:javascript复制
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.optimizers import SGD
from keras import utils
import numpy as np

# define some hyper parameters
batch_size = 100 
n_inputs = 784
n_classes = 10 
n_epochs = 10 

# get the data
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# reshape the two dimensional 28 x 28 pixels
# sized images into a single vector of 784 pixels
x_train = x_train.reshape(60000, n_inputs)
x_test = x_test.reshape(10000, n_inputs)

# convert the input values to float32
x_train = x_train.astype(np.float32)
x_test = x_test.astype(np.float32)

# normalize the values of image vectors to fit under 1
x_train /= 255
x_test /= 255

# convert output data into one hot encoded format
y_train = utils.to_categorical(y_train, n_classes)
y_test = utils.to_categorical(y_test, n_classes)

# build a sequential model
model = Sequential()
# the first layer has to specify the dimensions of the input vector
model.add(Dense(units=128, activation='sigmoid', input_shape=(n_inputs,)))
# add dropout layer for preventing overfitting
model.add(Dropout(0.1))
model.add(Dense(units=128, activation='sigmoid'))
model.add(Dropout(0.1))
# output layer can only have the neurons equal to the number of outputs
model.add(Dense(units=n_classes, activation='softmax'))

# print the summary of our model
model.summary()

# compile the model
model.compile(loss='categorical_crossentropy',
              optimizer=SGD(),
              metrics=['accuracy'])

# train the model
model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=n_epochs)

# evaluate the model and print the accuracy score
scores = model.evaluate(x_test, y_test)

print('n loss:', scores[0])
print('n accuracy:', scores[1]) 

我们从描述和训练 Keras 模型得到以下输出:

代码语言:javascript复制
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_7 (Dense)              (None, 128)               100480    
_________________________________________________________________
dropout_5 (Dropout)          (None, 128)               0         
_________________________________________________________________
dense_8 (Dense)              (None, 128)               16512     
_________________________________________________________________
dropout_6 (Dropout)          (None, 128)               0         
_________________________________________________________________
dense_9 (Dense)              (None, 10)                1290      
=================================================================
Total params: 118,282
Trainable params: 118,282
Non-trainable params: 0
_________________________________________________________________
Epoch 1/10
60000/60000 [========================] - 3s - loss: 2.3018 - acc: 0.1312     
Epoch 2/10
60000/60000 [========================] - 2s - loss: 2.2395 - acc: 0.1920     
Epoch 3/10
60000/60000 [========================] - 2s - loss: 2.1539 - acc: 0.2843     
Epoch 4/10
60000/60000 [========================] - 2s - loss: 2.0214 - acc: 0.3856     
Epoch 5/10
60000/60000 [========================] - 3s - loss: 1.8269 - acc: 0.4739     
Epoch 6/10
60000/60000 [========================] - 2s - loss: 1.5973 - acc: 0.5426     
Epoch 7/10
60000/60000 [========================] - 2s - loss: 1.3846 - acc: 0.6028     
Epoch 8/10
60000/60000 [========================] - 3s - loss: 1.2133 - acc: 0.6502     
Epoch 9/10
60000/60000 [========================] - 3s - loss: 1.0821 - acc: 0.6842     
Epoch 10/10
60000/60000 [========================] - 3s - loss: 0.9799 - acc: 0.7157     

 loss: 0.859834249687
 accuracy: 0.788

您可以看到,在 Keras 中构建和训练模型是多么容易。

您可以从他们记录完备的网站获取有关 Keras 的更多信息。

总结

在本章中,我们了解了 Keras。 Keras 是 TensorFlow 最受欢迎的高级库。我个人更喜欢将 Keras 用于我为商业制作和学术研究开发的所有模型。我们学习了使用函数式和顺序 API 在 Keras 中创建和训练模型所遵循的工作流程。我们了解了各种 Keras 层以及如何将层添加到顺序和函数式模型中。我们还学习了如何编译,训练和评估 Keras 模型。我们还看到了 Keras 提供的一些附加模块。

在本书的其余章节中,我们将介绍核心 TensorFlow 和 Keras 中的大多数示例。 在下一章中,我们将学习如何使用 TensorFlow 构建传统的机器学习模型进行分类和回归。

四、TensorFlow 中的经典机器学习

机器学习是计算机科学领域,涉及算法的研究,开发和应用,以使计算机器从数据中学习。计算机学习的模型用于进行预测和预测。机器学习研究人员和工程师通过构建模型然后使用这些模型进行预测来实现这一目标。现在众所周知,机器学习已成功地应用于各种领域,如自然语言理解,视频处理,图像识别,语音和视觉。

我们来谈谈模型。所有机器学习问题都以一种或另一种形式抽象为以下等式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wyTw8KUu-1681566326305)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/3d553b03-80b7-4120-9df0-a607ad64af36.png)]

这里,y是输出或目标,x是输入或特征。如果x是一组特征,我们也将其称为特征向量,并用X表示。当我们说模型时,我们的意思是找到将特征映射到目标的函数f。因此,一旦我们找到f,我们就可以使用x的新值来预测y的值。

机器学习的核心是找到可用于从x的值预测y的函数f。正如您可能从高中数学周期回忆的那样,该线的等式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a6E2bnuo-1681566326306)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/ba408e0e-2d86-4499-bc6d-a8fbeaf344d7.png)]

我们可以重写前面的简单等式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LQ2bS5Xm-1681566326306)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/fc46f48c-ee0d-43bd-b8bf-998f54b785c6.png)]

这里,W称为权重,b称为偏差。不要担心现在的权重和偏置,我们稍后会介绍它们。现在,您可以将W视为等效于mb等效于c。因此,现在机器学习问题可以说是从X的当前值找到Wb的问题,这样该方程可用于预测y的值。

回归分析或回归建模是指用于估计变量之间关系的方法和技术。输入到回归模型的变量称为独立变量或预测变量或特征,而回归模型的输出变量称为因变量或目标。回归模型定义如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W5h3zBa3-1681566326306)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/5fc17d1b-7188-4209-bce2-77f97aa72877.png)]

其中Y是目标变量,X是特征向量,β是参数向量

通常,我们使用一种非常简单的回归形式,称为简单线性回归来估计参数 β。

在机器学习问题中,我们必须从给定数据中学习模型参数β0β1,以便我们有一个估计模型,从X的未来值预测Y的值。我们对偏置使用β1,对权重项使用β0,分别用wb代表它们。

因此模型如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qsQe0BCO-1681566326306)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/ff368a23-744e-4bc3-9c9a-82686936c8a1.png)]

分类是机器学习中的经典问题之一。正在考虑的数据可以属于一个或其他类别,例如,如果提供的图像是数据,则它们可以是猫或狗的图片。因此,在这种情况下,类别是猫和狗。分类是指识别或识别所考虑的数据或对象的标签或类别。分类属于监督机器学习的范畴。在分类问题中,提供具有特征或输入及其相应输出或标签的训练数据集。使用该训练数据集,训练模型;换句话说,计算模型的参数。然后将训练的模型用于新数据以找到其正确的标签。

分类问题可以有两种类型:两类多类。两类意味着数据被分类为两个不同且不连续的标签,例如,患者患有癌症或患者没有癌症,图像是猫或狗。多类意味着数据将被分类到多个类别中,例如,电子邮件分类问题会将电子邮件分成社交媒体电子邮件,与工作相关的电子邮件,个人电子邮件,与家人相关的电子邮件,垃圾邮件,购物优惠电子邮件等等。 。另一个例子是数字图片的例子;每张图片可以标记在 0 到 9 之间,具体取决于图片所代表的数字。在本章中,我们将看到两种分类的示例。

在本章中,我们将进一步扩展以下主题:

  • 回归
    • 简单的线性回归
    • 多元回归
    • 正则化回归
    • LASSO 正则化
    • 岭正则化
    • ElasticNet 正则化
  • 分类
    • 使用逻辑回归的分类
    • 二分类
    • 多类分类

简单的线性回归

您可能使用过其他机器学习库;现在让我们练习使用 TensorFlow 学习简单的线性回归模型。在继续讨论特定于域的示例之前,我们将首先使用生成的数据集解释这些概念。

我们将使用生成的数据集,以便来自所有不同域的读者可以学习而不会被示例的特定域的细节所淹没。

您可以按照 Jupyter 笔记本中的代码ch-04a_Regression

数据准备

要生成数据集,我们使用sklearn库的datasets模块中的make_regression函数:

代码语言:javascript复制
from sklearn import datasets as skds
X, y = skds.make_regression(n_samples=200,
                            n_features=1,
                            n_informative=1,
                            n_targets=1,
                            noise = 20.0)

这将生成一个回归数据集,其中包含一个特征的 200 个样本值和每个特征的一个目标,并添加了一些噪声。因为我们只生成一个目标,所以该函数使用一维 NumPy 数组生成y;因此,我们重塑y有两个维度:

代码语言:javascript复制
if (y.ndim == 1):
    y = y.reshape(len(y),1)

我们使用以下代码绘制生成的数据集以查看数据:

代码语言:javascript复制
import matplotlib.pyplot as plt
plt.figure(figsize=(14,8))
plt.plot(X,y,'b.')
plt.title('Original Dataset')
plt.show()

我们得到以下绘图。由于生成的数据是随机的,您可能会得到不同的绘图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vcgMegEv-1681566326307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/9dd24ca6-c0a6-427a-bc83-e12c3480d5fa.png)]

现在让我们将数据分为训练集和测试集:

代码语言:javascript复制
X_train, X_test, y_train, y_test = skms.train_test_split(X, y, 
                                                 test_size=.4, 
                                                 random_state=123)

构建一个简单的回归模型

要在 TensorFlow 中构建和训练回归模型,通常采取以下步骤:

  1. 定义输入,参数和其他变量。
  2. 定义模型。
  3. 定义损失函数。
  4. 定义优化器函数。
  5. 训练模型进行多次迭代,称为周期。

定义输入,参数和其他变量

在我们使用 TensorFlow 构建和训练回归模型之前,让我们定义一些重要的变量和操作。我们从X_trainy_train中找出输出和输入变量的数量,然后使用这些数字来定义xx_tensor),yy_tensor),权重(w)和偏置(b):

代码语言:javascript复制
num_outputs = y_train.shape[1] 
num_inputs = X_train.shape[1]

x_tensor = tf.placeholder(dtype=tf.float32, 
                   shape=[None, num_inputs], 
                   name="x") 
y_tensor = tf.placeholder(dtype=tf.float32, 
                   shape=[None, num_outputs], 
                   name="y")

w = tf.Variable(tf.zeros([num_inputs,num_outputs]), 
                dtype=tf.float32, 
                name="w") 
b = tf.Variable(tf.zeros([num_outputs]), 
                dtype=tf.float32, 
                name="b")
  • x_tensor被定义为具有可变行和num_inputs列的形状,并且在我们的示例中列数仅为 1
  • y_tensor定义为具有可变行和num_outputs列的形状,列数在我们的示例中只有一个
  • w被定义为维度num_inputs x num_outputs的变量,在我们的例子中是1 x 1
  • b被定义为维度num_outputs的变量,在我们的例子中是一个

定义模型

接下来,我们将模型定义为x_tensor × w b

代码语言:javascript复制
model = tf.matmul(x_tensor, w)   b

定义损失函数

接下来,我们使用均方误差MSE)定义损失函数。 MSE 定义如下:

有关 MSE 的更多详细信息,请访问此链接

y的实际值和估计值的差异称为残差。损失函数计算残差平方的平均值。我们通过以下方式在 TensorFlow 中定义它:

代码语言:javascript复制
loss = tf.reduce_mean(tf.square(model - y_tensor))
  • model - y_tensor计算残差
  • tf.square(model - y_tensor)计算每个残差的平方
  • tf.reduce_mean( ... )最终计算在前一步骤中计算的平方均值

我们还定义均方误差MSE)和 R 平方RS)函数来评估训练模型。我们使用单独的mse函数,因为在接下来的章节中,损失函数将改变但mse函数将保持不变。

代码语言:javascript复制
# mse and R2 functions
mse = tf.reduce_mean(tf.square(model - y_tensor))
y_mean = tf.reduce_mean(y_tensor)
total_error = tf.reduce_sum(tf.square(y_tensor - y_mean))
unexplained_error = tf.reduce_sum(tf.square(y_tensor - model))
rs = 1 - tf.div(unexplained_error, total_error)

定义优化器函数

接下来,我们实例化学习率为 0.001 的theGradientDescentOptimizer函数并将其设置为最小化损失函数:

代码语言:javascript复制
learning_rate = 0.001
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)

有关梯度下降的更多详细信息,请访问此链接:

https://en.wikipedia.org/wiki/Gradient_descent

https://www.analyticsvidhya.com/blog/2017/03/introduction-to-gradient-descent-algorithm-along-its-variants/

TensorFlow 提供了许多其他优化器函数,如 Adadelta,Adagrad 和 Adam。我们将在以下章节中介绍其中一些内容。

训练模型

现在我们已经定义了模型,损失函数和优化函数,训练模型来学习参数wb。要训​​练模型,请定义以下全局变量:

  • num_epochs:运行训练的迭代次数。每次迭代,模型都会学习更好的参数,我们将在后面的图中看到。
  • w_hatb_hat:收集估计的wb参数。
  • loss_epochsmse_epochsrs_epochs:收集训练数据集中的总误差值,以及每次迭代中测试数据集上模型的 mse 和 R 平方值。
  • mse_scorers_score:收集最终训练模型的 mse 和 R 平方值。
代码语言:javascript复制
num_epochs = 1500
w_hat = 0
b_hat = 0
loss_epochs = np.empty(shape=[num_epochs],dtype=float)
mse_epochs = np.empty(shape=[num_epochs],dtype=float)
rs_epochs = np.empty(shape=[num_epochs],dtype=float)

mse_score = 0
rs_score = 0

初始化会话和全局变量后,运行num_epoch次的训练循环:

代码语言:javascript复制
with tf.Session() as tfs:
   tf.global_variables_initializer().run()
   for epoch in range(num_epochs):

在循环的每次迭代中,在训练数据上运行优化器:

代码语言:javascript复制
tfs.run(optimizer, feed_dict={x_tensor: X_train, y_tensor: y_train})

使用学习的wb值,计算误差并将其保存在loss_val中以便稍后绘制:

代码语言:javascript复制
loss_val = tfs.run(loss,feed_dict={x_tensor: X_train, y_tensor: y_train})
loss_epochs[epoch] = loss_val

计算测试数据预测值的均方误差和 R 平方值:

代码语言:javascript复制
mse_score = tfs.run(mse,feed_dict={x_tensor: X_test, y_tensor: y_test})
mse_epochs[epoch] = mse_score

rs_score = tfs.run(rs,feed_dict={x_tensor: X_test, y_tensor: y_test})
rs_epochs[epoch] = rs_score

最后,一旦完成循环,保存wb的值以便稍后绘制它们:

代码语言:javascript复制
w_hat,b_hat = tfs.run([w,b])
w_hat = w_hat.reshape(1)

让我们在 2,000 次迭代后打印模型和测试数据的最终均方误差:

代码语言:javascript复制
print('model : Y = {0:.8f} X   {1:.8f}'.format(w_hat[0],b_hat[0]))
print('For test data : MSE = {0:.8f}, R2 = {1:.8f} '.format(
    mse_score,rs_score))

This gives us the following output:

代码语言:javascript复制
model : Y = 20.37448120 X   -2.75295663
For test data : MSE = 297.57995605, R2 = 0.66098368 

因此,我们训练的模型不是一个非常好的模型,但我们将在后面的章节中看到如何使用神经网络来改进它。

本章的目标是介绍如何使用 TensorFlow 构建和训练回归模型,而无需使用神经网络。

让我们绘制估计模型和原始数据:

代码语言:javascript复制
plt.figure(figsize=(14,8))
plt.title('Original Data and Trained Model')
x_plot = [np.min(X)-1,np.max(X) 1]
y_plot = w_hat*x_plot b_hat
plt.axis([x_plot[0],x_plot[1],y_plot[0],y_plot[1]])
plt.plot(X,y,'b.',label='Original Data')
plt.plot(x_plot,y_plot,'r-',label='Trained Model')
plt.legend()
plt.show()

我们得到以下原始数据与受训模型数据的关系图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HL0lY202-1681566326307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/987b1418-d3bc-40d6-9412-0826b81fded5.png)]

让我们绘制每次迭代中训练和测试数据的均方误差:

代码语言:javascript复制
plt.figure(figsize=(14,8))

plt.axis([0,num_epochs,0,np.max(loss_epochs)])
plt.plot(loss_epochs, label='Loss on X_train')
plt.title('Loss in Iterations')
plt.xlabel('# Epoch')
plt.ylabel('MSE')

plt.axis([0,num_epochs,0,np.max(mse_epochs)])
plt.plot(mse_epochs, label='MSE on X_test')
plt.xlabel('# Epoch')
plt.ylabel('MSE')
plt.legend()

plt.show()

我们得到以下图,显示每次迭代时,均方误差减小,然后保持在 500 附近的相同水平:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hwTFmUAz-1681566326307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/02e135a4-4a8a-47ad-a85e-48bec81ce693.png)]

让我们绘制 R 平方的值:

代码语言:javascript复制
plt.figure(figsize=(14,8))
plt.axis([0,num_epochs,0,np.max(rs_epochs)])
plt.plot(rs_epochs, label='R2 on X_test')
plt.xlabel('# Epoch')
plt.ylabel('R2')
plt.legend()
plt.show()

当我们绘制 R 平方超过周期的值时,我们得到以下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJWxV3C3-1681566326307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/b8c11857-8723-4d5c-9e56-a064607aabe8.png)]

这基本上表明该模型以 R 的平均值开始,但随着模型的训练并减少误差,R 平方的值开始变高,最终在某一点变得稳定略高于 0.6。

绘制 MSE 和 R 平方可以让我们看到我们的模型得到多快的训练以及它开始变得稳定的地方,以便进一步的训练在减少误差方面产生微不足道的好处或几乎没有好处。

使用训练的模型进行预测

现在您已经拥有训练有素的模型,它可用于预测新数据。线性模型的预测是通过理解我们在前一个图中看到的一些最小均方误差得出的,因为直线可能不完全适合数据。

为了获得更好的拟合模型,我们必须使用不同的方法扩展我们的模型,例如添加变量的线性组合。

多元回归

现在您已经学习了如何使用 TensorFlow 创建基本回归模型,让我们尝试在不同域的示例数据集上运行它。我们作为示例数据集生成的数据集是单变量的,即目标仅依赖于一个特征。

实际上,大多数数据集都是多变量的。为了强调一点,目标取决于多个变量或特征,因此回归模型称为多元回归多维回归

我们首先从最受欢迎的波士顿数据集开始。该数据集包含波士顿 506 所房屋的 13 个属性,例如每个住所的平均房间数,一氧化氮浓度,到波士顿五个就业中心的加权距离等等。目标是自住房屋的中位数值。让我们深入探索这个数据集的回归模型。

sklearn库加载数据集并查看其描述:

代码语言:javascript复制
boston=skds.load_boston()
print(boston.DESCR)
X=boston.data.astype(np.float32)
y=boston.target.astype(np.float32)
if (y.ndim == 1):
    y = y.reshape(len(y),1)
X = skpp.StandardScaler().fit_transform(X)

我们还提取X,一个特征矩阵,和y,一个前面代码中的目标向量。我们重塑y使其成为二维的,并将x中的特征缩放为平均值为零,标准差为 1。现在让我们使用这个Xy来训练回归模型,就像我们在前面的例子中所做的那样:

您可能会发现该示例的代码与上一部分有关简单回归的代码相似; 但是,我们正在使用多种特征来训练模型,因此将其称为多元回归。

代码语言:javascript复制
X_train, X_test, y_train, y_test = skms.train_test_split(X, y, 
    test_size=.4, random_state=123)
num_outputs = y_train.shape[1] 
num_inputs = X_train.shape[1]

x_tensor = tf.placeholder(dtype=tf.float32, 
    shape=[None, num_inputs], name="x") 
y_tensor = tf.placeholder(dtype=tf.float32, 
    shape=[None, num_outputs], name="y") 

w = tf.Variable(tf.zeros([num_inputs,num_outputs]), 
    dtype=tf.float32, name="w") 
b = tf.Variable(tf.zeros([num_outputs]), 
    dtype=tf.float32, name="b") 

model = tf.matmul(x_tensor, w)   b
loss = tf.reduce_mean(tf.square(model - y_tensor))
# mse and R2 functions
mse = tf.reduce_mean(tf.square(model - y_tensor))
y_mean = tf.reduce_mean(y_tensor)
total_error = tf.reduce_sum(tf.square(y_tensor - y_mean))
unexplained_error = tf.reduce_sum(tf.square(y_tensor - model))
rs = 1 - tf.div(unexplained_error, total_error)

learning_rate = 0.001
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)

num_epochs = 1500
loss_epochs = np.empty(shape=[num_epochs],dtype=np.float32)
mse_epochs = np.empty(shape=[num_epochs],dtype=np.float32)
rs_epochs = np.empty(shape=[num_epochs],dtype=np.float32)

mse_score = 0
rs_score = 0

with tf.Session() as tfs:
    tfs.run(tf.global_variables_initializer())
    for epoch in range(num_epochs):
        feed_dict = {x_tensor: X_train, y_tensor: y_train}
        loss_val, _ = tfs.run([loss, optimizer], feed_dict)
        loss_epochs[epoch] = loss_val

        feed_dict = {x_tensor: X_test, y_tensor: y_test}
        mse_score, rs_score = tfs.run([mse, rs], feed_dict)
        mse_epochs[epoch] = mse_score
        rs_epochs[epoch] = rs_score

print('For test data : MSE = {0:.8f}, R2 = {1:.8f} '.format(
    mse_score, rs_score))

我们从模型中获得以下输出:

代码语言:javascript复制
For test data : MSE = 30.48501778, R2 = 0.64172244 

让我们绘制 MSE 和 R 平方值。

下图显示了 MSE 的绘图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KScRpOxr-1681566326308)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/b90f3cfb-8b86-47d7-9d0d-64d94acd59f2.png)]

下图显示了 R 平方值的绘图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AfNyl46Z-1681566326308)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/60be36ac-1116-4f4e-a5dc-15fffcea81f1.png)]

正如我们在单变量数据集中看到的那样,我们看到了 MSE 和 R 平方的类似模式。

正则化回归

在线性回归中,我们训练的模型返回训练数据的最佳拟合参数。但是,在训练数据上找到最合适的参数可能会导致过拟合。

过拟合意味着模型最适合训练数据,但会给测试数据带来更大的误差。因此,我们通常在模型中添加惩罚项以获得更简单的模型。

该惩罚项称为正则化项,由此获得的回归模型称为正则化回归模型。正则化模型有三种主要类型:

  • LASSO 回归:在 LASSO 正则化中,也称为 L1 正则化,正则化项是 LASSO 参数α乘以权重w绝对值之和。因此,损失函数如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wLOkeZVN-1681566326308)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/662a9855-a00c-41ad-8b0e-2b65dbfbdd69.png)]

  • 岭回归:在岭正则化中,也称为 L2 正则化,正则化项是岭参数α乘以i-th权重w的平方和。因此,损失函数如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CWgQA3Lf-1681566326309)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/b6aba15b-577d-4a22-a559-bd6f1f2e0d1b.png)]

  • ElasticNet 回归:当我们添加 LASSO 和岭正则化项时,得到的正则化称为 ElasticNet 正则化。因此,损失函数如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pSaFpuE0-1681566326309)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/f6144fe6-9175-4d26-9e6b-f07fb0cd3212.png)]

有关正则化的更多详细信息,请参阅互联网上的这些资源。

一个简单的经验法则是当我们想要删除某些特征时使用 L1 或 LASSO,从而减少计算时间,但代价是降低了准确率。

现在让我们看看在 TensorFlow 中实现的这些正则化损失函数。我们将继续使用前面示例中使用的波士顿数据集。

LASSO 正则化

我们将 lasso 参数定义为值 0.8:

代码语言:javascript复制
lasso_param = tf.Variable(0.8, dtype=tf.float32)
lasso_loss = tf.reduce_mean(tf.abs(w)) * lasso_param

将 LASSO 参数设置为零意味着没有正则化,因为该项变为零。正则化项的值越高,惩罚越高。以下是 LASSO 正则化回归的完整代码,用于训练模型以预测波士顿房屋定价:

下面的代码假定训练和测试数据集已按照前面的示例进行拆分。

代码语言:javascript复制
num_outputs = y_train.shape[1]
num_inputs = X_train.shape[1]

x_tensor = tf.placeholder(dtype=tf.float32,
                          shape=[None, num_inputs], name='x')
y_tensor = tf.placeholder(dtype=tf.float32,
                          shape=[None, num_outputs], name='y')

w = tf.Variable(tf.zeros([num_inputs, num_outputs]),
                dtype=tf.float32, name='w')
b = tf.Variable(tf.zeros([num_outputs]),
                dtype=tf.float32, name='b')

model = tf.matmul(x_tensor, w)   b

lasso_param = tf.Variable(0.8, dtype=tf.float32)
lasso_loss = tf.reduce_mean(tf.abs(w)) * lasso_param

loss = tf.reduce_mean(tf.square(model - y_tensor))   lasso_loss

learning_rate = 0.001
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)

mse = tf.reduce_mean(tf.square(model - y_tensor))
y_mean = tf.reduce_mean(y_tensor)
total_error = tf.reduce_sum(tf.square(y_tensor - y_mean))
unexplained_error = tf.reduce_sum(tf.square(y_tensor - model))
rs = 1 - tf.div(unexplained_error, total_error)

num_epochs = 1500
loss_epochs = np.empty(shape=[num_epochs],dtype=np.float32)
mse_epochs = np.empty(shape=[num_epochs],dtype=np.float32)
rs_epochs = np.empty(shape=[num_epochs],dtype=np.float32)

mse_score = 0.0
rs_score = 0.0

num_epochs = 1500
loss_epochs = np.empty(shape=[num_epochs], dtype=np.float32)
mse_epochs = np.empty(shape=[num_epochs], dtype=np.float32)
rs_epochs = np.empty(shape=[num_epochs], dtype=np.float32)

mse_score = 0.0
rs_score = 0.0

with tf.Session() as tfs:
    tfs.run(tf.global_variables_initializer())
    for epoch in range(num_epochs):
        feed_dict = {x_tensor: X_train, y_tensor: y_train}
        loss_val,_ = tfs.run([loss,optimizer], feed_dict)
        loss_epochs[epoch] = loss_val

        feed_dict = {x_tensor: X_test, y_tensor: y_test}
        mse_score,rs_score = tfs.run([mse,rs], feed_dict)
        mse_epochs[epoch] = mse_score
        rs_epochs[epoch] = rs_score

print('For test data : MSE = {0:.8f}, R2 = {1:.8f} '.format(
    mse_score, rs_score))

我们得到以下输出:

代码语言:javascript复制
For test data : MSE = 30.48978233, R2 = 0.64166653

让我们使用以下代码绘制 MSE 和 R 平方的值:

代码语言:javascript复制
plt.figure(figsize=(14,8))

plt.axis([0,num_epochs,0,np.max([loss_epochs,mse_epochs])])
plt.plot(loss_epochs, label='Loss on X_train')
plt.plot(mse_epochs, label='MSE on X_test')
plt.title('Loss in Iterations')
plt.xlabel('# Epoch')
plt.ylabel('Loss or MSE')
plt.legend()

plt.show()

plt.figure(figsize=(14,8))

plt.axis([0,num_epochs,np.min(rs_epochs),np.max(rs_epochs)])
plt.title('R-squared in Iterations')
plt.plot(rs_epochs, label='R2 on X_test')
plt.xlabel('# Epoch')
plt.ylabel('R2')
plt.legend()

plt.show()

我们得到以下损失绘图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dWpZpUa2-1681566326309)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/e0e1db8b-6a49-4e35-89a8-0acde4ad83cc.png)]

迭代中 R 平方的图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DEVcakta-1681566326310)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/f75f8e2b-1395-4a1c-b50e-1f44ba2dd7da.png)]

让我们用岭回归重复相同的例子。

岭正则化

以下是岭正则化回归的完整代码,用于训练模型以预测波士顿房屋定价:

代码语言:javascript复制
num_outputs = y_train.shape[1]
num_inputs = X_train.shape[1]

x_tensor = tf.placeholder(dtype=tf.float32,
                          shape=[None, num_inputs], name='x')
y_tensor = tf.placeholder(dtype=tf.float32,
                          shape=[None, num_outputs], name='y')

w = tf.Variable(tf.zeros([num_inputs, num_outputs]),
                dtype=tf.float32, name='w')
b = tf.Variable(tf.zeros([num_outputs]),
                dtype=tf.float32, name='b')

model = tf.matmul(x_tensor, w)   b

ridge_param = tf.Variable(0.8, dtype=tf.float32)
ridge_loss = tf.reduce_mean(tf.square(w)) * ridge_param

loss = tf.reduce_mean(tf.square(model - y_tensor))   ridge_loss

learning_rate = 0.001
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)

mse = tf.reduce_mean(tf.square(model - y_tensor))
y_mean = tf.reduce_mean(y_tensor)
total_error = tf.reduce_sum(tf.square(y_tensor - y_mean))
unexplained_error = tf.reduce_sum(tf.square(y_tensor - model))
rs = 1 - tf.div(unexplained_error, total_error)

num_epochs = 1500
loss_epochs = np.empty(shape=[num_epochs],dtype=np.float32)
mse_epochs = np.empty(shape=[num_epochs],dtype=np.float32)
rs_epochs = np.empty(shape=[num_epochs],dtype=np.float32)

mse_score = 0.0
rs_score = 0.0

with tf.Session() as tfs:
    tfs.run(tf.global_variables_initializer())
    for epoch in range(num_epochs):
        feed_dict = {x_tensor: X_train, y_tensor: y_train}
        loss_val, _ = tfs.run([loss, optimizer], feed_dict=feed_dict)
        loss_epochs[epoch] = loss_val

        feed_dict = {x_tensor: X_test, y_tensor: y_test}
        mse_score, rs_score = tfs.run([mse, rs], feed_dict=feed_dict)
        mse_epochs[epoch] = mse_score
        rs_epochs[epoch] = rs_score

print('For test data : MSE = {0:.8f}, R2 = {1:.8f} '.format(
    mse_score, rs_score))

我们得到以下结果:

代码语言:javascript复制
For test data : MSE = 30.64177132, R2 = 0.63988018 

绘制损失和 MSE 的值,我们得到以下损失图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnX5QdGI-1681566326310)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/0f74f654-50a2-4203-b9e8-0a2f29d7cd38.png)]

我们得到以下 R 平方图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UXnOv1Vu-1681566326310)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/56fd975b-a22b-44b4-83aa-dc0d38ad77ce.png)]

让我们来看看 LASSO 和岭正则化方法的组合。

ElasticNet 正则化

笔记本ch-04a_Regression提供了 ElasticNet 正规回归的完整代码,用于训练模型以预测波士顿房屋定价。在运行模型时,我们得到以下结果:

代码语言:javascript复制
For test data : MSE = 30.64861488, R2 = 0.63979971

绘制损失和 MSE 的值,我们得到以下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0sZ64J2O-1681566326311)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/71369789-39e3-4c2c-affb-d46290e841ab.png)]

我们得到以下 R 平方图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Ekt5Ih3-1681566326311)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/c8314797-8170-4325-b9ac-d563348c0609.png)]

逻辑回归和分类

最常用的分类方法是使用逻辑回归。逻辑回归是概率和线性分类器。输入是特征向量,特定类的成员的概率可以正式写成如下等式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZGPZvRTo-1681566326311)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/b54ac424-2f70-4aee-afca-253935b4a455.png)]In the above equation:

  • y代表输出,
  • i代表其中一个类
  • x代表输入
  • w代表权重
  • b代表偏置
  • z代表回归方程z = w × x b
  • φ代表我们案例中的平滑函数或模型

前面的等式表示当wb被给出时x属于i类的概率由函数表示φ((z)。因此,必须训练模型以最大化概率值。

二分类的逻辑回归

对于二分类,我们将模型函数φ(z)定义为 sigmoid 函数,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wc9HKyGp-1681566326312)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/62e26ec4-6cff-4601-b544-cb79aa11a704.png)]

sigmoid 函数在范围[0, 1]之间产生 y 的值。因此,我们可以使用y = φ(z)的值来预测类:如果y > 0.5则类别等于 1,否则类别等于 0。

正如我们在本章的前几节中所见,对于线性回归,可以通过查找最小化损失函数的参数来训练模型,并且损失函数可以是平方误差或均方误差的总和。对于逻辑回归,我们希望最大化可能性:L(w) = P(y|x, w, b)

但是,由于更容易使对数似然最大化,因此我们使用对数似然l(w)作为成本函数。因此,损失函数(J(w))被写为-1(w),其可以使用诸如梯度下降的优化算法来最小化。

二元逻辑回归的损失函数在数学上写成如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dfObkdLX-1681566326312)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/43249834-ffa5-490f-842b-3be538ea34e8.png)]

其中φ(z)是 Sigmoid 函数。

我们将在下一节中实现这个损失函数。

多类分类的逻辑回归

当涉及两个以上的类时,逻辑回归是已知的多项逻辑回归。在多项逻辑回归中,我们使用 softmax 函数代替 sigmoid,它是最受欢迎的函数之一。 Softmax 可以用数学表示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zBfU7tLh-1681566326312)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/31227522-ba57-4b0d-a624-566ce56452aa.png)]

Softmax 函数产生每个类的概率,概率向量和为 1。在预测时,具有最高 softmax 值的类成为输出或预测类。正如我们前面讨论的那样,损失函数是负对数似然函数-l(w),它可以被优化器最小化,例如梯度下降。

多项逻辑回归的损失函数正式写成如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QWvFTLE3-1681566326313)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/8a2c95b7-307d-4e20-8374-6db23980125e.png)]

其中φ(z)是 softmax 函数。

我们将在本章后面实现这种损失函数。

让我们在下一节中深入研究一些例子。

您可以按照 Jupyter 笔记本中的代码ch-04b_Classification

二分类

二分类是指仅有两个不同类的问题。正如我们在上一章中所做的那样,我们将使用 SciKit Learn 库中的便捷函数make_classification()生成数据集:

代码语言:javascript复制
X, y = skds.make_classification(n_samples=200,
   n_features=2,
   n_informative=2, 
   n_redundant=0, 
   n_repeated=0,
   n_classes=2,
   n_clusters_per_class=1)
if (y.ndim == 1):
    y = y.reshape(-1,1)

make_classification()的论据是不言自明的;n_samples是要生成的数据点数,n_features是要生成的特征数,n_classes是类的数量,即 2:

  • n_samples是要生成的数据点数。我们将其保持在 200 以保持数据集较小。
  • n_features是要生成的特征数量;我们只使用两个特征,因此我们可以将它作为一个简单的问题来理解 TensorFlow 命令。
  • n_classes是类的数量,它是 2,因为它是二分类问题。

让我们使用以下代码绘制数据:

代码语言:javascript复制
plt.scatter(X[:,0],X[:,1],marker='o',c=y)
plt.show()

我们得到以下绘图;您可能会得到一个不同的图,因为每次运行数据生成函数时都会随机生成数据:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1EjcDt2E-1681566326313)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/707586af-1084-493a-86a8-f827a115cfa5.png)]

然后我们使用 NumPy eye函数将y转换为单热编码目标:

代码语言:javascript复制
print(y[0:5])
y=np.eye(num_outputs)[y]
print(y[0:5])

单热编码目标如下所示:

代码语言:javascript复制
[1 0 0 1 0]
[[ 0.  1.]
 [ 1.  0.]
 [ 1.  0.]
 [ 0.  1.]
 [ 1.  0.]]

将数据划分为训练和测试类别:

代码语言:javascript复制
X_train, X_test, y_train, y_test = skms.train_test_split(
    X, y, test_size=.4, random_state=42)

在分类中,我们使用 sigmoid 函数来量化模型的值,使得输出值位于范围[0,1]之间。以下等式表示由φ(z)表示的 Sigmoid 函数,其中z是等式w × x b。损失函数现在变为由J(θ)表示的值,其中θ表示参数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9z9iVA6M-1681566326313)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/af47d49d-2bb6-4e48-b6d3-4d7cd65ff6d5.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nBDfxRbg-1681566326314)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/e5b82a11-b867-4a43-9f3d-09d6cb44099b.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GKrvVhY8-1681566326315)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/717945b1-b35a-43d9-a469-44f33de91e8a.png)]

我们使用以下代码实现新模型和损失函数:

代码语言:javascript复制
num_outputs = y_train.shape[1] 
num_inputs = X_train.shape[1]

learning_rate = 0.001

# input images
x = tf.placeholder(dtype=tf.float32, shape=[None, num_inputs], name="x") 
# output labels
y = tf.placeholder(dtype=tf.float32, shape=[None, num_outputs], name="y")

# model paramteres 
w = tf.Variable(tf.zeros([num_inputs,num_outputs]), name="w") 
b = tf.Variable(tf.zeros([num_outputs]), name="b") 
model = tf.nn.sigmoid(tf.matmul(x, w)   b)

loss = tf.reduce_mean(-tf.reduce_sum(
    (y * tf.log(model))   ((1 - y) * tf.log(1 - model)), axis=1))
optimizer = tf.train.GradientDescentOptimizer(
    learning_rate=learning_rate).minimize(loss)

最后,我们运行我们的分类模型:

代码语言:javascript复制
num_epochs = 1
with tf.Session() as tfs:
    tf.global_variables_initializer().run()
    for epoch in range(num_epochs):
        tfs.run(optimizer, feed_dict={x: X_train, y: y_train})
        y_pred = tfs.run(tf.argmax(model, 1), feed_dict={x: X_test})
        y_orig = tfs.run(tf.argmax(y, 1), feed_dict={y: y_test})

        preds_check = tf.equal(y_pred, y_orig)
        accuracy_op = tf.reduce_mean(tf.cast(preds_check, tf.float32))
        accuracy_score = tfs.run(accuracy_op)
        print("epoch {0:04d} accuracy={1:.8f}".format(
            epoch, accuracy_score))

        plt.figure(figsize=(14, 4))
        plt.subplot(1, 2, 1)
        plt.scatter(X_test[:, 0], X_test[:, 1], marker='o', c=y_orig)
        plt.title('Original')
        plt.subplot(1, 2, 2)
        plt.scatter(X_test[:, 0], X_test[:, 1], marker='o', c=y_pred)
        plt.title('Predicted')
        plt.show()

我们获得了大约 96% 的相当好的准确率,原始和预测的数据图如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SG9WJoX7-1681566326315)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/abc7eea6-1cdf-486e-9f34-0b571ef90b67.png)]

很简约!!现在让我们让我们的问题变得复杂,并尝试预测两个以上的类。

多类分类

多类分类的一个流行示例是标记手写数字的图像。此示例中的类或标签为{0,1,2,3,4,5,6,7,8,9}。在以下示例中,我们将使用 MNIST。让我们像前面章节中所做的那样加载 MNIST 图像,代码如下:

代码语言:javascript复制
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(os.path.join(
    datasetslib.datasets_root, 'mnist'), one_hot=True)

如果已按照前一章的说明下载了 MNIST 数据集,那么我们将获得以下输出:

代码语言:javascript复制
Extracting /Users/armando/datasets/mnist/train-images-idx3-ubyte.gz
Extracting /Users/armando/datasets/mnist/train-labels-idx1-ubyte.gz
Extracting /Users/armando/datasets/mnist/t10k-images-idx3-ubyte.gz
Extracting /Users/armando/datasets/mnist/t10k-labels-idx1-ubyte.gz

现在让我们设置一些参数,如下面的代码所示:

代码语言:javascript复制
num_outputs = 10 # 0-9 digits
num_inputs = 784 # total pixels

learning_rate = 0.001
num_epochs = 1
batch_size = 100
num_batches = int(mnist.train.num_examples/batch_size)

上面代码中的参数如下:

  • num_outputs:由于我们必须预测图像代表十位数中的哪一位,因此我们将输出数设置为 10.数字由打开或设置为 1 的输出表示。
  • num_inputs:我们知道我们的输入数字是28 x 28像素,因此每个像素都是模型的输入。因此,我们总共有 784 个输入。
  • learning_rate:此参数表示梯度下降优化器算法的学习率。我们将学习率任意设定为 0.001。
  • num_epochs:我们将仅针对一次迭代运行我们的第一个示例,因此我们将周期数设置为 1。
  • batch_size:在现实世界中,我们可能拥有庞大的数据集并加载整个数据集以便训练模型可能是不可能的。因此,我们将输入数据分成随机选择的批次。我们将batch_size设置为 100 个图像,可以使用 TensorFlow 的内置算法一次选择。
  • num_batches:此参数设置应从总数据集中选择批次的次数;我们将其设置为等于数据集中的项目数除以批量中的项目数。

我们鼓励您尝试使用这些参数的不同值。

现在让我们使用以下代码定义输入,输出,参数,模型和损失函数:

代码语言:javascript复制
# input images
x = tf.placeholder(dtype=tf.float32, shape=[None, num_inputs], name="x")
# output labels
y = tf.placeholder(dtype=tf.float32, shape=[None, num_outputs], name="y")

# model paramteres
w = tf.Variable(tf.zeros([784, 10]), name="w")
b = tf.Variable(tf.zeros([10]), name="b")
model = tf.nn.softmax(tf.matmul(x, w)   b)

loss = tf.reduce_mean(-tf.reduce_sum(y * tf.log(model), axis=1))
optimizer = tf.train.GradientDescentOptimizer(
    learning_rate=learning_rate).minimize(loss)

代码类似于二分类示例,但有一个显着差异:我们使用softmax而不是sigmoid函数。 Softmax 用于多类分类,而 sigmoid 用于二元类分类。 Softmax 函数是 sigmoid 函数的推广,它将任意实数值的 n 维向量 z 转换为实数值的 n 维向量σ(z),范围(0, 1]和为 1。

现在让我们运行模型并打印精度:

代码语言:javascript复制
with tf.Session() as tfs:
    tf.global_variables_initializer().run()
    for epoch in range(num_epochs):
        for batch in range(num_batches):
            batch_x, batch_y = mnist.train.next_batch(batch_size)
            tfs.run(optimizer, feed_dict={x: batch_x, y: batch_y})
        predictions_check = tf.equal(tf.argmax(model, 1), tf.argmax(y, 1))
        accuracy_function = tf.reduce_mean(
            tf.cast(predictions_check, tf.float32))
        feed_dict = {x: mnist.test.images, y: mnist.test.labels}
        accuracy_score = tfs.run(accuracy_function, feed_dict)
        print("epoch {0:04d} accuracy={1:.8f}".format(
            epoch, accuracy_score))

我们得到以下准确率:

代码语言:javascript复制
epoch 0000  accuracy=0.76109999

让我们尝试在多次迭代中训练我们的模型,以便在每次迭代中学习不同的批次。我们建立了两个支持函数来帮助我们:

代码语言:javascript复制
def mnist_batch_func(batch_size=100):
    batch_x, batch_y = mnist.train.next_batch(batch_size)
    return [batch_x, batch_y]

上述函数将批量中的示例数作为输入,并使用mnist.train.next_batch()函数返回一批特征(batch_x)和目标(batch_y):

代码语言:javascript复制
def tensorflow_classification(num_epochs, num_batches, batch_size,
                              batch_func, optimizer, test_x, test_y):
    accuracy_epochs = np.empty(shape=[num_epochs], dtype=np.float32)
    with tf.Session() as tfs:
        tf.global_variables_initializer().run()
        for epoch in range(num_epochs):
            for batch in range(num_batches):
                batch_x, batch_y = batch_func(batch_size)
                feed_dict = {x: batch_x, y: batch_y}
                tfs.run(optimizer, feed_dict)
            predictions_check = tf.equal(
                tf.argmax(model, 1), tf.argmax(y, 1))
            accuracy_function = tf.reduce_mean(
                tf.cast(predictions_check, tf.float32))
            feed_dict = {x: test_x, y: test_y}
            accuracy_score = tfs.run(accuracy_function, feed_dict)
            accuracy_epochs[epoch] = accuracy_score
            print("epoch {0:04d} accuracy={1:.8f}".format(
                epoch, accuracy_score))

    plt.figure(figsize=(14, 8))
    plt.axis([0, num_epochs, np.min(
        accuracy_epochs), np.max(accuracy_epochs)])
    plt.plot(accuracy_epochs, label='Accuracy Score')
    plt.title('Accuracy over Iterations')
    plt.xlabel('# Epoch')
    plt.ylabel('Accuracy Score')
    plt.legend()
    plt.show()

上述函数获取参数并执行训练迭代,打印每次迭代的准确率分数并打印准确率分数。它还可以保存accuracy_epochs数组中每个周期的准确率分数。之后,它绘制了每个周期的准确率。让我们使用我们之前设置的参数运行此函数 30 个周期,使用以下代码:

代码语言:javascript复制
num_epochs=30
tensorflow_classification(num_epochs=num_epochs, 
    num_batches=num_batches, 
    batch_size=batch_size, 
    batch_func=mnist_batch_func, 
    optimizer=optimizer,
    test_x=mnist.test.images,test_y=mnist.test.labels)

我们得到以下准确率和图表:

代码语言:javascript复制
epoch 0000  accuracy=0.76020002
epoch 0001  accuracy=0.79420000
epoch 0002  accuracy=0.81230003
epoch 0003  accuracy=0.82309997
epoch 0004  accuracy=0.83230001
epoch 0005  accuracy=0.83770001

--- epoch 6 to 24 removed for brevity ---

epoch 0025  accuracy=0.87930000
epoch 0026  accuracy=0.87970001
epoch 0027  accuracy=0.88059998
epoch 0028  accuracy=0.88120002
epoch 0029  accuracy=0.88180000

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cxu4vASV-1681566326315)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/19db0579-c2c6-4d7a-a4ef-a2417fc5fd2f.png)]

从图中我们可以看出,初始迭代中的准确率会急剧提高,然后准确率的提高速度会降低。稍后,我们将看到如何在 TensorFlow 中使用神经网络的全部功能,并将此分类精度提高到更大的值。

总结

在本章中,我们学习了如何在 TensorFlow 中应用经典机器学习算法,而不使用神经网络。在本章的第一部分,我们了解了回归模型。我们解释了如何训练具有一个或多个特征的线性回归模型。我们使用 TensorFlow 编写线性回归代码。我们还讨论了正则化基本上是增加一个惩罚项,以便模型在训练阶段学习参数时不会过拟合训练数据。我们使用 TensorFlow 实现了 Lasso,Ridge 和 ElasticNet 正则化。 TensorFlow 有一些内置的正则化方法,我们将在下一章中学习。

在本章的后续章节中,我们了解了有监督机器学习中的分类问题。我们讨论了两类和多类分类的模型函数,平滑函数和损失函数。我们在本章中使用了逻辑回归,因为这是实现分类的最简单方法。对于二分类,我们使用 sigmoid 函数,对于多类分类,我们使用 softmax 函数来平滑线性模型的值,以产生输出在特定类中的概率。

我们在 TensorFlow 中实现了模型和损失函数的逻辑,并训练模型进行二分类和多类分类。虽然我们在本章中使用了经典的机器学习方法,并使用 TensorFlow 实现了它们,但是当我们实现神经网络和深度神经网络来解决机器学习问题时,TensorFlow 的全部功能得以释放。我们将在本书的神经网络相关章节中研究这些高级方法。

建议您阅读以下书籍以了解有关回归和分类的更多详细信息:

Sebastian Raschka,Python 机器学习,第 2 版,Packt Publishing,2017

Trevor Hastie,Robert Tibshirani,Jerome Friedman,统计学习的要素,第二版,施普林格,2013

五、TensorFlow 和 Keras 中的神经网络和 MLP

神经网络是一种受大脑结构和功能启发的建模技术。正如大脑包含数百万个被称为神经元的微小互连单元一样,今天的神经网络由数百万个分层排列的微小互连计算单元组成。由于神经网络的计算单元仅存在于数字世界中,与大脑的物理神经元相反,它们也被称为人工神经元。类似地,神经网络(NN)也称为人工神经网络(ANN)。

在本章中,我们将进一步扩展以下主题:

  • 感知机(人工神经元)
  • 前馈神经网络
  • 用于图像分类的多层感知机MLP
    • 基于 TensorFlow 的用于 MNIST 图像分类的 MLP
    • 基于 Keras 的用于 MNIST 分类的 MLP
    • 基于 TFLearn 的用于 MNIST 分类的 MLP
  • 用于时间序列回归的 MLP

感知机

让我们了解神经网络的最基本构建块,感知机,也称为人工神经元。感知机的概念起源于 Frank Rosenblatt 于 1962 年的作品。

您可能希望阅读以下工作来探索神经网络的起源:

Frank Rosenblatt,神经动力学原理:感知器和脑机制理论,斯巴达书籍,1962 年

在最简化的视图中,感知机被建模在生物神经元之后,使得它接收一个或多个输入并将它们组合以产生输出。

如下图所示,感知机采用三个输入并将它们相加以生成输出y

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xefYKr7w-1681566326315)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/a19eccda-749c-4dab-9020-41120070fd53.png)]

这种感知机太简单了,不具备任何实际用途。因此,通过添加权重,偏差和激活函数的概念来增强它。将权重添加到每个输入以获得加权和。如果加权和Σw[i]x[i]小于阈值,则输出为 0,否则输出为 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tDa46bxZ-1681566326316)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/ad008099-c40e-4e5f-bc65-721e82255fa8.png)]

阈值称为偏差。让我们将偏差移到等式的左边,用b表示它,Σw·x代表wx的向量点积。感知机的等式现在变为如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NEzmPXAJ-1681566326316)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/144a0cb4-f3e8-440c-9778-28e0f4c8b603.png)]

感知机现在看起来像下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YvbzKvAB-1681566326316)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/6c2085db-a65d-495c-96b9-4524ba54eacf.png)] Simple perceptron with weights and bias

到目前为止,神经元是一个线性函数。为了使这个神经元产生非线性决策边界,通过称为激活或传递函数的非线性函数运行求和输出。有许多流行的激活函数可用:

  • ReLU整流线性单元,将值平滑到范围(0, x), [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ICvcbHeW-1681566326316)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/7538f31f-444e-4922-9d11-7ae37e375e4f.png)]
  • sigmoidSigmoid 将值平滑到(0, 1), [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IOdw2Kfj-1681566326316)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/5433180b-4099-4c59-ad7d-9ca67d6deff1.png)]
  • tanh双曲正切将值平滑到(-1, 1), [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NEXNPiPG-1681566326317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/bf847fab-b08c-41e5-af41-19d2bb877240.png)]

使用激活函数,感知机的等式变为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNDvguh4-1681566326317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/d16676a1-31ee-43ba-9f94-25cc90573568.png)]

其中φ(·)是激活函数。

神经元看起来像下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IoPijA2s-1681566326317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/4ec4a553-192d-42a2-bf66-e6790103a7c6.png)]

多层感知机

当我们将人工神经元连接在一起时,基于明确定义的结构,我们将其称为神经网络。这是一个神经元最简单的神经网络:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DoeDU9ZZ-1681566326317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/ba2849d1-c6bf-42c2-a9d3-d50f9d49de08.png)]Neural network with one neuron

我们连接神经元,使得一层的输出成为下一层的输入,直到最后一层的输出成为最终输出。这种神经网络被称为前馈神经网络(FFNN)。由于这些 FFNN 由连接在一起的神经元层组成,因此它们被称为多层感知机(MLP)或深度神经网络(DNN)

作为示例,下图中描绘的 MLP 具有三个特征作为输入:两个隐藏层,每个神经元包含五个神经元,一个输出 y。神经元完全连接到下一层的神经元。这些层也称为致密层或仿射层,并且这种模型也称为顺序模型。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GGzRhi1L-1681566326318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/2f715ec1-4960-4c19-b0c0-150dadcdadd0.png)]

让我们重温一下我们之前探索过的一些示例数据集,并在 TensorFlow 中构建简单的神经网络(MLP 或 DNN)。

您可以按照 Jupyter 笔记本ch-05_MLP中的代码进行操作。

用于图像分类的 MLP

让我们使用不同的库(例如 TensorFlow,Keras 和 TFLearn)构建用于图像分类的 MLP 网络。 我们将在本节中使用示例的 MNIST 数据集。

MNIST 数据集包含从 0 到 9 的手写数字的28x28像素图像,以及它们的标签,训练集为 60K,测试集为 10K。 MNIST 数据集是使用最广泛的数据集,包括 TensorFlow 示例和教程。

MNIST 数据集和相关文档可从此链接获得。

让我们从纯 TensorFlow 方法开始。

TensorFlow 中的用于 MNIST 分类的 MLP

首先,加载 MNIST 数据集,并使用以下代码定义训练和测试特征以及目标:

代码语言:javascript复制
from tensorflow.examples.tutorials.mnist import input_data
mnist_home = os.path.join(datasetslib.datasets_root, 'mnist')
mnist = input_data.read_data_sets(mnist_home, one_hot=True)

X_train = mnist.train.images
X_test = mnist.test.images
Y_train = mnist.train.labels
Y_test = mnist.test.labels

num_outputs = 10 # 0-9 digits
num_inputs = 784 # total pixels

我们创建了三个辅助函数,它们将帮助我们创建一个只有一个隐藏层的简单 MLP,然后是一个更大的 MLP,每层有多个层和多个神经元。

mlp()函数使用以下逻辑构建网络层:

  1. mlp()函数需要五个输入:
    • x是输入特征张量
    • num_inputs是输入特征的数量
    • num_outputs是输出目标的数量
    • num_layers是所需隐藏层数
    • num_neurons是包含每层神经元数量的列表
  2. 将权重和偏差列表设置为空:
代码语言:javascript复制
 w=[]
 b=[]
  1. 为隐藏层的数量运行循环以创建权重和偏移张量并将它们附加到各自的列表:
    • 张量分别名为w_<layer_num>b_<layer_num>。命名张量有助于调试和查找代码问题。
    • 使用tf.random_normal()以正态分布初始化张量。
    • 权重张量的第一个维度是来自前一层的输入数量。对于第一个隐藏层,第一个维度是num_inputs。权重张量的第二维是当前层中的神经元的数量。
    • 偏差都是一维张量,其中维度等于当前层中的神经元数量。
代码语言:javascript复制
     for i in range(num_layers):
        # weights
        w.append(tf.Variable(tf.random_normal(
            [num_inputs if i == 0 else num_neurons[i - 1],
             num_neurons[i]]),
            name="w_{0:04d}".format(i)
            ))
        # biases
        b.append(tf.Variable(tf.random_normal(
            [num_neurons[i]]),
            name="b_{0:04d}".format(i)
            ))
  1. 为最后一个隐藏层创建权重和偏差。在这种情况下,权重张量的维度等于最后隐藏层中的神经元数量和输出目标的数量。偏差是一个张量,具有输出特征数量大小的单一维度:
代码语言:javascript复制
    w.append(tf.Variable(tf.random_normal(
        [num_neurons[num_layers - 1] if num_layers > 0 else num_inputs,
         num_outputs]), name="w_out"))
    b.append(tf.Variable(tf.random_normal([num_outputs]), 
        name="b_out"))
  1. 现在开始定义层。首先,将x视为第一个最明显的输入层:
代码语言:javascript复制
# x is input layer
layer = x
  1. 在循环中添加隐藏的层。每个隐藏层表示,通过激活函数tf.nn.relu()使线性函数tf.matmul(layer, w[i]) b[i]非线性化:
代码语言:javascript复制
# add hidden layers
for i in range(num_layers):
    layer = tf.nn.relu(tf.matmul(layer, w[i])   b[i])
  1. 添加输出层。输出层和隐藏层之间的一个区别是输出层中没有激活函数:
代码语言:javascript复制
layer = tf.matmul(layer, w[num_layers])   b[num_layers]
  1. 返回包含 MLP 网络的layer对象:
代码语言:javascript复制
return layer

整个 MLP 函数的完整代码如下:

代码语言:javascript复制
def mlp(x, num_inputs, num_outputs, num_layers, num_neurons):
    w = []
    b = []
    for i in range(num_layers):
        # weights
        w.append(tf.Variable(tf.random_normal(
            [num_inputs if i == 0 else num_neurons[i - 1],
             num_neurons[i]]),
            name="w_{0:04d}".format(i)
        ))
        # biases
        b.append(tf.Variable(tf.random_normal(
            [num_neurons[i]]),
            name="b_{0:04d}".format(i)
        ))
    w.append(tf.Variable(tf.random_normal(
        [num_neurons[num_layers - 1] if num_layers > 0 else num_inputs,
         num_outputs]), name="w_out"))
    b.append(tf.Variable(tf.random_normal([num_outputs]), name="b_out"))

    # x is input layer
    layer = x
    # add hidden layers
    for i in range(num_layers):
        layer = tf.nn.relu(tf.matmul(layer, w[i])   b[i])
    # add output layer
    layer = tf.matmul(layer, w[num_layers])   b[num_layers]

    return layer

辅助函数mnist_batch_func()为 MNIST 数据集包装 TensorFlow 的批量函数,以提供下一批图像:

代码语言:javascript复制
def mnist_batch_func(batch_size=100):
    X_batch, Y_batch = mnist.train.next_batch(batch_size)
    return [X_batch, Y_batch]

此函数不言自明。 TensorFlow 为 MNIST 数据集提供此函数;但是,对于其他数据集,我们可能必须编写自己的批量函数。

辅助函数tensorflow_classification()训练并评估模型。

  1. tensorflow_classification()函数有几个输入:
    • n_epochs是要运行的训练循环的数量
    • n_batches是应该运行每个循环中的训练的随机抽样批次的数量
    • batch_size是每批中的样本数
    • batch_funcbatch_size并返回XY样本批次的函数
    • model是具有神经元的实际神经网络或层
    • optimizer是使用 TensorFlow 定义的优化函数
    • loss是优化器优化参数的成本函数损失
    • accuracy_function是计算准确率分数的函数
    • X_testY_test是测试的数据集
  1. 启动 TensorFlow 会话以运行训练循环:
代码语言:javascript复制
with tf.Session() as tfs:
    tf.global_variables_initializer().run()
  1. 运行n_epoch循环来训练:
代码语言:javascript复制
for epoch in range(n_epochs):
  1. 在每个循环中,取样本集的n_batches数量并训练模型,计算每批的损失,计算每个周期的平均损失:
代码语言:javascript复制
epoch_loss = 0.0
            for batch in range(n_batches):
                X_batch, Y_batch = batch_func(batch_size)
                feed_dict = {x: X_batch, y: Y_batch}
                _, batch_loss = tfs.run([optimizer, loss], feed_dict)
                epoch_loss  = batch_loss
            average_loss = epoch_loss / n_batches
            print("epoch: {0:04d} loss = {1:0.6f}".format(
                epoch, average_loss))
  1. 完成所有周期循环后,计算并打印用accuracy_function计算的精度分数:
代码语言:javascript复制
    feed_dict = {x: X_test, y: Y_test}
    accuracy_score = tfs.run(accuracy_function, 
                        feed_dict=feed_dict)
    print("accuracy={0:.8f}".format(accuracy_score))

tensorflow_classification()函数的完整代码如下:

代码语言:javascript复制
def tensorflow_classification(n_epochs, n_batches,
                              batch_size, batch_func,
                              model, optimizer, loss, accuracy_function,
                              X_test, Y_test):
    with tf.Session() as tfs:
        tfs.run(tf.global_variables_initializer())
        for epoch in range(n_epochs):
            epoch_loss = 0.0
            for batch in range(n_batches):
                X_batch, Y_batch = batch_func(batch_size)
                feed_dict = {x: X_batch, y: Y_batch}
                _, batch_loss = tfs.run([optimizer, loss], feed_dict)
                epoch_loss  = batch_loss
            average_loss = epoch_loss / n_batches
            print("epoch: {0:04d} loss = {1:0.6f}".format(
                epoch, average_loss))
        feed_dict = {x: X_test, y: Y_test}
        accuracy_score = tfs.run(accuracy_function, feed_dict=feed_dict)
        print("accuracy={0:.8f}".format(accuracy_score))

现在让我们定义输入和输出占位符,xy以及其他超参数:

代码语言:javascript复制
# input images
x = tf.placeholder(dtype=tf.float32, name="x", 
                    shape=[None, num_inputs]) 
# target output
y = tf.placeholder(dtype=tf.float32, name="y", 
                    shape=[None, num_outputs])
num_layers = 0
num_neurons = []
learning_rate = 0.01
n_epochs = 50
batch_size = 100
n_batches = int(mnist.train.num_examples/batch_size)

参数如下所述:

  • num_layers是隐藏层数。我们首先练习没有隐藏层,只有输入和输出层。
  • num_neurons是空列表,因为没有隐藏层。
  • learning_rate是 0.01,随机选择的小数。
  • num_epochs代表 50 次迭代,以学习将输入连接到输出的唯一神经元的参数。
  • batch_size保持在 100,这也是一个选择问题。较大的批量大小不一定提供更高的好处。您可能需要探索不同的批量大小,以找到神经网络的最佳批量大小。
  • n_batches:批次数大致计算为示例数除以批次中的样本数。

现在让我们将所有内容放在一起,使用到目前为止定义的变量定义网络,loss函数,optimizer函数和accuracy函数。

代码语言:javascript复制
model = mlp(x=x,
            num_inputs=num_inputs,
            num_outputs=num_outputs,
            num_layers=num_layers,
            num_neurons=num_neurons)

loss = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits(logits=model, labels=y))
optimizer = tf.train.GradientDescentOptimizer(
    learning_rate=learning_rate).minimize(loss)

predictions_check = tf.equal(tf.argmax(model, 1), tf.argmax(y, 1))
accuracy_function = tf.reduce_mean(tf.cast(predictions_check, tf.float32))

在这段代码中,我们使用一个新的 tensorflow 函数来定义损失函数:

代码语言:javascript复制
tf.nn.softmax_cross_entropy_with_logits(logits=model, labels=y)

当使用softmax_cross_entropy_with_logits()函数时,请确保输出未缩放且尚未通过softmax激活函数。 此函数在内部使用softmax来缩放输出。

该函数计算模型之间的 softmax 熵(估计值y)和y的实际值。当输出属于一个类而不是一个类时,使用熵函数。在我们的示例中,图像只能属于其中一个数字。

有关此熵函数的更多信息,请参阅此链接。

一旦定义了所有内容,运行tensorflow_classification函数来训练和评估模型:

代码语言:javascript复制
tensorflow_classification(n_epochs=n_epochs, 
   n_batches=n_batches, 
   batch_size=batch_size, 
   batch_func=mnist_batch_func, 
   model = model, 
   optimizer = optimizer, 
   loss = loss, 
   accuracy_function = accuracy_function, 
   X_test = mnist.test.images, 
   Y_test = mnist.test.labels
   )

我们从运行分类得到以下输出:

代码语言:javascript复制
epoch: 0000   loss = 8.364567
epoch: 0001   loss = 4.347608
epoch: 0002   loss = 3.085622
epoch: 0003   loss = 2.468341
epoch: 0004   loss = 2.099220
epoch: 0005   loss = 1.853206

--- Epoch 06 to 45 output removed for brevity ---

epoch: 0046   loss = 0.684285
epoch: 0047   loss = 0.678972
epoch: 0048   loss = 0.673685
epoch: 0049   loss = 0.668717
accuracy=0.85720009

我们看到单个神经元网络在 50 次迭代中缓慢地将损失从 8.3 降低到 0.66,最终得到几乎 85% 的准确率。对于这个具体的例子,这是非常糟糕的准确率,因为这只是使用 TensorFlow 进行分类使用 MLP 的演示。

我们使用更多层和神经元运行相同的代码,并获得以下准确率:

层数

每个隐藏层中的神经元数量

准确率

0

0

0.857

1

8

0.616

2

256

0.936

因此,通过在每层添加两行和 256 个神经元,我们将精度提高到 0.936。我们鼓励您尝试使用不同变量值的代码来观察它如何影响损失和准确率。

Keras 中的用于 MNIST 分类的 MLP

现在让我们与 Keras 建立相同的 MLP 网络,Keras 是 TensorFlow 的高级库。我们保留所有参数与本章中用于 TensorFlow 示例的参数相同,例如,隐藏层的激活函数保留为 ReLU 函数。

  1. 从 Keras 导入所需的模块:
代码语言:javascript复制
import keras
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import SGD
  1. 定义超参数(我们假设数据集已经加载到X_trainY_trainX_testY_test变量):
代码语言:javascript复制
num_layers = 2
num_neurons = []
for i in range(num_layers):
   num_neurons.append(256)
learning_rate = 0.01
n_epochs = 50
batch_size = 100
  1. 创建顺序模型:
代码语言:javascript复制
model = Sequential()
  1. 添加第一个隐藏层。只有在第一个隐藏层中,我们必须指定输入张量的形状:
代码语言:javascript复制
model.add(Dense(units=num_neurons[0], activation='relu', 
    input_shape=(num_inputs,)))
  1. 添加第二层:
代码语言:javascript复制
model.add(Dense(units=num_neurons[1], activation='relu'))
  1. 使用 softmax 激活函数添加输出层:
代码语言:javascript复制
model.add(Dense(units=num_outputs, activation='softmax'))
  1. 打印模型详细信息:
代码语言:javascript复制
model.summary()

我们得到以下输出:

代码语言:javascript复制
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 256)               200960    
_________________________________________________________________
dense_2 (Dense)              (None, 256)               65792     
_________________________________________________________________
dense_3 (Dense)              (None, 10)                2570      
=================================================================
Total params: 269,322
Trainable params: 269,322
Non-trainable params: 0
_________________________________________________________________
  1. 使用 SGD 优化器编译模型:
代码语言:javascript复制
model.compile(loss='categorical_crossentropy',
   optimizer=SGD(lr=learning_rate),
   metrics=['accuracy'])
  1. 训练模型:
代码语言:javascript复制
model.fit(X_train, Y_train,
   batch_size=batch_size,
   epochs=n_epochs)

在训练模型时,我们可以观察每次训练迭代的损失和准确率:

代码语言:javascript复制
Epoch 1/50
55000/55000 [========================] - 4s - loss: 1.1055 - acc: 0.7413     
Epoch 2/50
55000/55000 [========================] - 3s - loss: 0.4396 - acc: 0.8833     
Epoch 3/50
55000/55000 [========================] - 3s - loss: 0.3523 - acc: 0.9010     
Epoch 4/50
55000/55000 [========================] - 3s - loss: 0.3129 - acc: 0.9112     
Epoch 5/50
55000/55000 [========================] - 3s - loss: 0.2871 - acc: 0.9181     

--- Epoch 6 to 45 output removed for brevity ---     

Epoch 46/50
55000/55000 [========================] - 4s - loss: 0.0689 - acc: 0.9814     
Epoch 47/50
55000/55000 [========================] - 4s - loss: 0.0672 - acc: 0.9819     
Epoch 48/50
55000/55000 [========================] - 4s - loss: 0.0658 - acc: 0.9822     
Epoch 49/50
55000/55000 [========================] - 4s - loss: 0.0643 - acc: 0.9829     
Epoch 50/50
55000/55000 [========================] - 4s - loss: 0.0627 - acc: 0.9829 
  1. 评估模型并打印损失和准确率:
代码语言:javascript复制
score = model.evaluate(X_test, Y_test)
print('n Test loss:', score[0])
print('Test accuracy:', score[1])

我们得到以下输出:

代码语言:javascript复制
Test loss: 0.089410082236
Test accuracy: 0.9727

笔记本ch-05_MLP中提供了使用 Keras 进行 MNIST 分类的 MLP 的完整代码。

TFLearn 中的用于 MNIST 分类的 MLP

现在让我们看看如何使用 TFLearn 实现相同的 MLP,TFLearn 是 TensorFlow 的另一个高级库:

  1. 导入 TFLearn 库:
代码语言:javascript复制
import tflearn
  1. 定义超参数(我们假设数据集已经加载到X_trainY_trainX_testY_test变量):
代码语言:javascript复制
num_layers = 2
num_neurons = []
for i in range(num_layers):
num_neurons.append(256)

learning_rate = 0.01
n_epochs = 50
batch_size = 100
  1. 构建输入层,两个隐藏层和输出层(与 TensorFlow 和 Keras 部分中的示例相同)
代码语言:javascript复制
# Build deep neural network
input_layer = tflearn.input_data(shape=[None, num_inputs])
dense1 = tflearn.fully_connected(input_layer, num_neurons[0], 
    activation='relu')
dense2 = tflearn.fully_connected(dense1, num_neurons[1], 
    activation='relu')
softmax = tflearn.fully_connected(dense2, num_outputs, 
    activation='softmax')
  1. 使用最后一步中构建的 DNN(在变量softmax中)定义优化器函数,神经网络和 MLP 模型(在 TFLearn 中称为 DNN):
代码语言:javascript复制
optimizer = tflearn.SGD(learning_rate=learning_rate)
net = tflearn.regression(softmax, optimizer=optimizer, 
                         metric=tflearn.metrics.Accuracy(), 
                         loss='categorical_crossentropy')
model = tflearn.DNN(net)
  1. 训练模型:
代码语言:javascript复制
model.fit(X_train, Y_train, n_epoch=n_epochs, 
          batch_size=batch_size, 
          show_metric=True, run_id="dense_model")

训练结束后,我们得到以下输出:

代码语言:javascript复制
Training Step: 27499  | total loss: 0.11236 | time: 5.853s
| SGD | epoch: 050 | loss: 0.11236 - acc: 0.9687 -- iter: 54900/55000
Training Step: 27500  | total loss: 0.11836 | time: 5.863s
| SGD | epoch: 050 | loss: 0.11836 - acc: 0.9658 -- iter: 55000/55000
--
  1. 评估模型并打印准确率分数:
代码语言:javascript复制
score = model.evaluate(X_test, Y_test)
print('Test accuracy:', score[0])

我们得到以下输出:

代码语言:javascript复制
Test accuracy: 0.9637

与使用 TFLearn 相比,我们获得了相当的精确度。

在笔记本ch-05_MLP中提供了使用 TFLearn 进行 MNIST 分类的 MLP 的完整代码。

TensorFlow,Keras 和 TFLearn 中的 MLP 总结

在前面的部分中,我们学习了如何使用 TensorFLow 及其高级库构建简单的 MLP 架构。纯 TensorFlow 的准确率约为 0.93-0.94,Keras 的准确率为 0.96-0.98,TFLearn 的准确率为 0.96-0.97。尽管我们的代码的所有示例都使用下面的 TensorFlow,但相同架构和参数的准确率差异可归因于这样的事实:尽管我们初始化了一些重要的超参数,但高级库和 TensorFlow 却抽象了许多其他超级 - 我们没有从默认值修改的参数。

我们观察到,与 Keras 和 TFLearn 相比,TensorFlow 中的代码非常详细和冗长。高级库使我们更容易构建和训练神经网络模型。

用于时间序列回归的 MLP

我们已经看到了图像数据分类的例子;现在让我们看一下时间序列数据的回归。我们将建立并使用 MLP 作为一个较小的单变量时间序列数据集,称为国际航空公司乘客数据集。该数据集包含多年来的乘客总数。该数据集可从此链接获得:

  • https://www.kaggle.com/andreazzini/international-airline-passengers/data
  • https://datamarket.com/data/set/22u3/international-airline-passengers-monthly-totals-in-thousands-jan-49-dec-60

让我们从准备数据集开始。

  1. 首先,使用以下代码加载数据集:
代码语言:javascript复制
filename = os.path.join(datasetslib.datasets_root, 
                        'ts-data', 
                        'international-airline-passengers-cleaned.csv')
dataframe = pd.read_csv(filename,usecols=[1],header=0)
dataset = dataframe.values
dataset = dataset.astype('float32')
  1. 利用datasetslib的效用函数,我们将数据集分成测试和训练集。对于时间序列数据集,我们有一个单独的函数,不会改变观察结果,因为对于时间序列回归,我们需要维持观察的顺序。我们使用 67% 的数据进行训练,33% 的数据用于测试。您可能希望尝试使用不同比例的示例。
代码语言:javascript复制
train,test=dsu.train_test_split(dataset,train_size=0.67)
  1. 对于时间序列回归,我们转换数据集以构建监督数据集。在此示例中,我们使用两个时间步长的滞后。我们将n_x设置为 2,mvts_to_xy()函数返回输入和输出(XY)训练和测试集,使得X是两列具有时间{t-1, t}的值,Y是一列中具有时间{t 1}的值。我们的学习算法假设通过找到时间{t-1, t, t 1}的值之间的关系,可以学习时间t 1的值。
代码语言:javascript复制
# reshape into X=t-1,t and Y=t 1
n_x=2
n_y=1
X_train, Y_train, X_test, Y_test = tsd.mvts_to_xy(train,
                                    test,n_x=n_x,n_y=n_y)

有关将时间序列数据集转换为监督学习问题的更多信息,请访问此链接。

现在我们在我们的训练数据集上构建和训练模型:

  1. 导入所需的 Keras 模块:
代码语言:javascript复制
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import SGD
  1. 设置构建模型所需的超参数:
代码语言:javascript复制
num_layers = 2
num_neurons = [8,8]
n_epochs = 50
batch_size = 2

请注意,我们使用批量大小为 2,因为数据集非常小。我们使用两层 MLP,每层只有八个神经元,因为我们的示例问题的规模很小。

  1. 构建,编译和训练模型:
代码语言:javascript复制
model = Sequential()
model.add(Dense(num_neurons[0], activation='relu', 
    input_shape=(n_x,)))
model.add(Dense(num_neurons[1], activation='relu'))
model.add(Dense(units=1))
model.summary()

model.compile(loss='mse', optimizer='adam')

model.fit(X_train, Y_train,
   batch_size=batch_size,
   epochs=n_epochs)

请注意,我们使用 Adam 优化器而不是 SGD。 您可能想要尝试 TensorFlow 和 Keras 中可用的不同优化器。

  1. 评估模型并打印均方误差(MSE)和均方根误差(RMSE):
代码语言:javascript复制
score = model.evaluate(X_test, Y_test)
print('nTest mse:', score)
print('Test rmse:', math.sqrt(score))

我们得到以下输出:

代码语言:javascript复制
Test mse: 5619.24934188
Test rmse: 74.96165247566114
  1. 使用我们的模型预测值并绘制它们,用于测试和训练数据集:
代码语言:javascript复制
# make predictions
Y_train_pred = model.predict(X_train)
Y_test_pred = model.predict(X_test)

# shift train predictions for plotting
Y_train_pred_plot = np.empty_like(dataset)
Y_train_pred_plot[:, :] = np.nan
Y_train_pred_plot[n_x-1:len(Y_train_pred) n_x-1, :] = Y_train_pred

# shift test predictions for plotting
Y_test_pred_plot = np.empty_like(dataset)
Y_test_pred_plot[:, :] = np.nan
Y_test_pred_plot[len(Y_train_pred) (n_x*2)-1:len(dataset)-1, :] = 
    Y_test_pred

# plot baseline and predictions
plt.plot(dataset,label='Original Data')
plt.plot(Y_train_pred_plot,label='Y_train_pred')
plt.plot(Y_test_pred_plot,label='Y_test_pred')
plt.legend()
plt.show()

我们得到以下关于原始和预测时间序列值的图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O16oIKvo-1681566326318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/3d26fd19-43da-4196-8f1f-4c19ed3040fe.png)]

如你所见,这是一个非常好的估计。然而,在现实生活中,数据本质上是多变量和复杂的。因此,我们将在后面的章节中看到时间序列数据的循环神经网络架构。

总结

在本章中,我们了解了多层感知机。我们解释了如何为分类和回归问题构建和训练 MLP 模型。我们使用纯 TensorFlow,Keras 和 TFLearn 构建了 MLP 模型。对于分类,我们使用图像数据,对于回归,我们使用时间序列数据。

构建和训练 MLP 网络模型的技术对于任何其他类型的数据(例如数字或文本)是相同的。然而,对于图像数据集,CNN 架构已被证明是最佳架构,对于序列数据集,例如时间序列和文本,RNN 模型已被证明是最佳架构。

虽然我们在本章中仅使用简单的数据集示例来演示 MLP 架构,但在后面的章节中,我们将介绍具有一些大型和高级数据集的 CNN 和 RNN 架构。

0 人点赞