TensorFlow 和 Keras 入门:过去 (TF1) 现在 (TF2)

thu*_*v89 6 python machine-learning keras tensorflow tensorflow2.0

这个问题的目的是寻求一个最低限度的指南,让某人快速了解 TensorFlow 1 和 TensorFlow 2。我觉得没有一个连贯的指南来解释 TF1 和 TF2 之间的差异,并且 TF 已经通过了专业修订和快速发展。

我说的时候供参考,

  • v1 或 TF1 - 我指的是 TF 1.15.0
  • v2 或 TF2 - 我指的是 TF 2.0.0

我的问题是,

  • TF1/TF2 如何工作?它们的主要区别是什么?

  • TF1 和 TF2 中有哪些不同的数据类型/数据结构?

  • 什么是 Keras,它如何适应所有这些?Keras 提供了哪些不同的 API 来实现深度学习模型?你能提供每个例子吗?

  • 在使用 TF 和 Keras 时,我必须注意的最经常出现的警告/错误是什么?

  • TF1 和 TF2 之间的性能差异

thu*_*v89 22

TF1/TF2 如何工作?以及他们的不同

TF1

TF1 遵循称为定义然后运行的执行方式。这与按运行定义相反,例如 Python 执行风格。但是,这是什么意思?定义然后运行意味着,仅仅因为你调用/定义了它没有执行的东西。您必须明确执行您定义的内容。

TF 有一个Graph 的概念。首先你定义你需要的所有计算(例如神经网络的所有层计算、损失计算和最小化损失的优化器——这些被表示为opsoperations)。定义计算/数据流图后,您可以使用Session执行其中的部分内容。让我们看一个简单的例子。

# Graph generation
tf_a = tf.placeholder(dtype=tf.float32)
tf_b = tf.placeholder(dtype=tf.float32)
tf_c = tf.add(tf_a, tf.math.multiply(tf_b, 2.0))

# Execution
with tf.Session() as sess:
    c = sess.run(tf_c, feed_dict={tf_a: 5.0, tf_b: 2.0})
    print(c)
Run Code Online (Sandbox Code Playgroud)

计算图(也称为数据流图)如下所示。

     tf_a      tf_b   tf.constant(2.0)
       \         \   /
        \      tf.math.multiply
         \     /
         tf.add
            |
          tf_c
Run Code Online (Sandbox Code Playgroud)

类比:想想你做蛋糕。您从互联网上下载食谱。然后你开始按照步骤来实际制作蛋糕。配方是图形,制作蛋糕的过程是会话所做的(即图形的执行)。

TF2

TF2 遵循立即执行风格或按运行定义。你调用/定义一些东西,它被执行。让我们看一个例子。

a = tf.constant(5.0)
b = tf.constant(3.0)
c = tf_a + (tf_b * 2.0)
print(c.numpy())
Run Code Online (Sandbox Code Playgroud)

哇!与 TF1 示例相比,它看起来非常干净。一切看起来都那么 Pythonic。

类比:现在想想你在一个动手做蛋糕的工作室。您正在按照讲师的解释制作蛋糕。讲师会立即解释每一步的结果。因此,与前面的示例不同,您不必等到烤完蛋糕再看看是否正确(这是对您无法调试代码的事实的引用)。但是你会得到关于你的行为的即时反馈(你知道这意味着什么)。

这是否意味着 TF2 不构建图形?惊恐发作

嗯,是和否。关于Eager ExecutionAutoGraph函数,您应该了解 TF2 中的两个功能。

提示:确切地说,TF1 也有急切执行(默认关闭)并且可以使用tf.enable_eager_execution(). TF2 默认开启了eager_execution。

热切执行

Eager execution 可以立即执行Tensors 和Operations。这是您在 TF2 示例中观察到的。但另一方面,它没有构建图形。因此,例如您使用 Eager Execution 来实现和运行神经网络,它会非常慢(因为神经网络一遍又一遍地执行非常重复的任务(前向计算 - 损失计算 - 后向传递))。

签名

这就是 AutoGraph 功能派上用场的地方。AutoGraph 是我最喜欢的 TF2 功能之一。这样做的作用是,如果您在函数中执行“TensorFlow”操作,它会分析该函数并为您构建图形(脑洞大开)。例如,您执行以下操作。TensorFlow 构建图形。

@tf.function
def do_silly_computation(x, y):
    a = tf.constant(x)
    b = tf.constant(y)
    c = tf_a + (tf_b * 2.0)
    return c

print(do_silly_computation(5.0, 3.0).numpy())
Run Code Online (Sandbox Code Playgroud)

所以你需要做的就是定义一个函数,它接受必要的输入并返回正确的输出。最重要的是添加@tf.function装饰器,因为这是 TensorFlow AutoGraph 分析给定函数的触发器。

警告:AutoGraph 不是灵丹妙药,不要天真地使用。AutoGraph 也有各种限制

TF1 和 TF2 之间的差异

  • TF1 需要一个tf.Session()对象来执行图形,而 TF2 不需要
  • 在 TF1 中,Python GC 没有收集未引用的变量,但在 TF2 中它们是
  • TF1 不促进代码模块化,因为您需要在开始计算之前定义完整的图。但是,鼓励使用 AutoGraph 函数代码模块化

TF1 和 TF2 中有哪些不同的数据类型?

您已经看到了很多主要的数据类型。但是您可能对他们做什么以及他们的行为方式有疑问。嗯,本节就是关于这个的。

TF1 数据类型/数据结构

  • tf.placeholder:这就是您向计算图提供输入的方式。顾名思义,它没有附加值。相反,您在运行时提供一个值。tf_a并且tf_b是这些的例子。把它想象成一个空盒子。您可以根据需要用水/沙子/蓬松的泰迪熊填充它。

  • tf.Variable:这就是你用来定义你的神经网络参数的东西。与占位符不同,变量是用某个值初始化的。但它们的价值也可以随着时间的推移而改变。这就是反向传播过程中神经网络参数发生的情况。

  • tf.Operation:操作是您可以在占位符、张量和变量上执行的各种转换。例如tf.add()tf.mul()是操作。这些操作返回一个张量(大部分时间)。如果你想要一个不返回张量的操作的证明,请查看这个

  • tf.Tensor:这类似于变量,因为它具有初始值。然而,一旦它们被定义,它们的值就不能改变(即它们是不可变的)。例如,tf_c在前面的示例中是一个tf.Tensor.

TF2 数据类型/数据结构

  • tf.Variable
  • tf.Tensor
  • tf.Operation

就行为而言,从 TF1 到 TF2 的数据类型没有太大变化。唯一的主要区别是,tf.placeholders已经消失了。您还可以查看完整的数据类型列表

什么是 Keras,它如何适应所有这些?

Keras 曾经是一个单独的库,提供主要用于深度学习模型的组件(例如层和模型)的高级实现。但是自从 TensorFlow 的更高版本之后,Keras 被集成到了 TensorFlow 中。

因此,正如我所解释的,Keras 隐藏了许多不必要的复杂性,如果您要使用准系统 TensorFlow,则必须处理这些问题。Keras 提供Layer对象和Model用于实现神经网络的对象有两个主要内容。Keras 还有两个最常见的模型 API 可以让您开发模型:Sequential APIFunctional API。让我们在一个快速示例中看看 Keras 和 TensorFlow 有何不同。让我们构建一个简单的 CNN。

提示:Keras 使您可以更轻松地使用 TF 实现您可以实现的目标。但 Keras 也提供了 TF 尚不强大的能力(例如文本处理能力)。

height=64
width = 64
n_channels = 3
n_outputs = 10
Run Code Online (Sandbox Code Playgroud)

Keras(顺序 API)示例

model = Sequential()
model.add(Conv2D(filters=32, kernel_size=(2,2), 
activation='relu',input_shape=(height, width, n_channels)))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=64, kernel_size=(2,2), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(n_outputs, activation='softmax'))
model.compile(loss='binary_crossentropy', optimizer='adam')
model.summary()
Run Code Online (Sandbox Code Playgroud)

优点

直接实现简单模型

缺点

不能用于实现复杂模型(例如具有多个输入的模型)

Keras(函数式 API)示例

inp = Input(shape=(height, width, n_channels))
out = Conv2D(filters=32, kernel_size=(2,2), activation='relu',input_shape=(height, width, n_channels))(inp)
out = MaxPooling2D(pool_size=(2,2))(out)
out = Conv2D(filters=64, kernel_size=(2,2), activation='relu')(out)
out = MaxPooling2D(pool_size=(2,2))(out)
out = Flatten()(out)
out = Dense(n_outputs, activation='softmax')(out)
model = Model(inputs=inp, outputs=out)
model.compile(loss='binary_crossentropy', optimizer='adam')
model.summary()
Run Code Online (Sandbox Code Playgroud)

优点

可用于实现涉及多个输入和输出的复杂模型

缺点

需要很好地理解输入输出的形状以及每一层的预期输入

TF1 示例

# Input
tf_in = tf.placeholder(shape=[None, height, width, n_channels], dtype=tf.float32)

# 1st conv and max pool
conv1 = tf.Variable(tf.initializers.glorot_uniform()([2,2,3,32]))
tf_out = tf.nn.conv2d(tf_in, filters=conv1, strides=[1,1,1,1], padding='SAME') # 64,64
tf_out = tf.nn.max_pool2d(tf_out, ksize=[2,2], strides=[1,2,2,1], padding='SAME') # 32,32

# 2nd conv and max pool
conv2 = tf.Variable(tf.initializers.glorot_uniform()([2,2,32,64]))
tf_out = tf.nn.conv2d(tf_out, filters=conv2, strides=[1,1,1,1], padding='SAME') # 32, 32
tf_out = tf.nn.max_pool2d(tf_out, ksize=[2,2], strides=[1,2,2,1], padding='SAME') # 16, 16
tf_out = tf.reshape(tf_out, [-1, 16*16*64])

# Dense layer
dense = conv1 = tf.Variable(tf.initializers.glorot_uniform()([16*16*64, n_outputs]))
tf_out = tf.matmul(tf_out, dense)
Run Code Online (Sandbox Code Playgroud)

优点

非常适合涉及非典型操作的前沿研究(例如动态更改层的大小)

缺点

可读性差

注意事项和陷阱

在这里,我将列出使用 TF 时必须注意的一些事项(根据我的经验)。

TF1 - 忘记提供所有依赖占位符来计算结果

tf_a = tf.placeholder(dtype=tf.float32)
tf_b = tf.placeholder(dtype=tf.float32)
tf_c = tf.add(tf_a, tf.math.multiply(tf_b, 2.0))

with tf.Session() as sess:
    c = sess.run(tf_c, feed_dict={tf_a: 5.0})
    print(c)
Run Code Online (Sandbox Code Playgroud)

InvalidArgumentError:您必须为占位符张量“Placeholder_8”提供一个值,其数据类型为 float [[node Placeholder_8(定义于 /usr/local/lib/python3.6/dist-packages/tensorflow_core/python/framework/ops.py:1748) ]]

您在此处收到错误的原因是,您尚未向 提供值tf_b。因此,请确保将值提供给所有相关占位符以计算结果。

TF1 - 非常小心的数据类型

tf_a = tf.placeholder(dtype=tf.int32)
tf_b = tf.placeholder(dtype=tf.float32)
tf_c = tf.add(tf_a, tf.math.multiply(tf_b, 2.0))

with tf.Session() as sess:
    c = sess.run(tf_c, feed_dict={tf_a: 5, tf_b: 2.0})
    print(c)
Run Code Online (Sandbox Code Playgroud)

类型错误:“添加”操作的输入“y”的类型为 float32,与参数“x”的类型 int32 不匹配。

你能发现错误吗?这是因为在将数据类型传递给操作时必须匹配数据类型。否则,使用tf.cast()operation 将您的数据类型转换为兼容的数据类型。

Keras - 了解每层期望的输入形状

model = Sequential()
model.add(Conv2D(filters=32, kernel_size=(2,2), 
activation='relu',input_shape=(height, width)))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=64, kernel_size=(2,2), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(n_outputs, activation='softmax'))
model.compile(loss='binary_crossentropy', optimizer='adam')
Run Code Online (Sandbox Code Playgroud)

ValueError: 层 conv2d_8 的输入 0 与层不兼容:预期 ndim=4,发现 ndim=3。收到完整形状:[无、64、64]

在这里,您已经定义了一个输入形状[None, height, width](当您添加批次维度时)。但Conv2D需要 4D 输入[None, height, width, n_channels]。因此,您会收到上述错误。一些通常被误解/容易出错的层是,

  • Conv2Dlayer - 需要 4D 输入[None, height, width, n_channels]。要更详细地了解卷积层/操作,请查看此答案
  • LSTM layer - 需要 3D 输入 [None, timesteps, n_dim]
  • ConvLSTM2D 层 - 需要 5D 输入 [None, timesteps, height, width, n_channels]
  • Concatenate layer - 除了轴之外,所有其他维度上连接的数据都需要相同

Keras - 期间输入错误的输入/输出形状 fit()

height=64
width = 64
n_channels = 3
n_outputs = 10

Xtrain = np.random.normal(size=(500, height, width, 1))
Ytrain = np.random.choice([0,1], size=(500, n_outputs))

# Build the model

# fit network
model.fit(Xtrain, Ytrain, epochs=10, batch_size=32, verbose=0)
Run Code Online (Sandbox Code Playgroud)

ValueError:检查输入时出错:预期 conv2d_9_input 具有形状 (64, 64, 3) 但得到形状为 (64, 64, 1) 的数组

你应该知道这个。[batch size, height, width, 1]当我们应该提供输入时,我们正在提供形状的[batch size, height, width, 3]输入。

TF1 和 TF2 之间的性能差异

这已经在这里讨论。所以我不会重复那里的内容。

我希望我可以谈论但不能谈论的事情

我将留下一些链接以供进一步阅读。