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

Posted

技术标签:

【中文标题】TensorFlow 和 Keras 入门:过去 (TF1) 现在 (TF2)【英文标题】:Primer on TensorFlow and Keras: The past (TF1) the present (TF2) 【发布时间】:2020-03-25 12:11:36 【问题描述】:

这个问题的目的是要求提供一个最基本的指南,以帮助人们快速了解 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 的性能差异

【问题讨论】:

您似乎在使用 SO,就好像它是一个论坛,但它不是,它是一个问答网站。这里有什么问题? 嗨@MatiasValdenegro,感谢您的评论,我希望在这里提供一个答案,我想看看我是否是TensorFlow初学者(遵循guide)。问题是在底部列出的要点。这些问题似乎难以掌握/混淆(根据我的经验和 SO 问题)。但是,如果您看到可以改进以使其更适合的任何内容,我很乐意编辑/更改任何需要的内容。 不错的文章;作为提示,请尝试将这些问答框架作为问答 - 原始帖子应包含一个问题或一些待解决的问题 .这样,没有人会抱怨——这只是使用系统的一个技巧。例如,我可以轻松地将this question 写成“这篇文章是为了向您展示如何在 Keras 中可视化内容”——但这很可能会被扔进垃圾桶。 【参考方案1】:

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)

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

     tf_a      tf_b   tf.constant(2.0)
       \         \   /
        \      tf.math.multiply
         \     /
         tf.add
            |
          tf_c

类比:想想你在做蛋糕。你从网上下载食谱。然后你开始按照步骤实际制作蛋糕。配方就是图表,而制作蛋糕的过程就是 Session 所做的事情(即图表的执行)。

TF2

TF2 遵循立即执行样式或逐个运行定义。你调用/定义一些东西,它就会被执行。让我们看一个例子。

a = tf.constant(5.0)
b = tf.constant(3.0)
c = tf_a + (tf_b * 2.0)
print(c.numpy())

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

类比:现在假设您在动手做蛋糕工作坊。正如教练解释的那样,你正在做蛋糕。教练会立即解释每一步的结果是什么。因此,与前面的示例不同,您不必等到烤好蛋糕后再查看是否正确(这是对您无法调试代码的事实的引用)。但是你会得到关于你的表现的即时反馈(你知道这意味着什么)。

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

嗯,是的,不是的。关于eager executionAutoGraph 函数,您应该了解TF2 中的两个功能。

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

急切执行

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

自动图表

这就是 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())

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

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

TF1 和 TF2 的区别

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

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

您已经看到了很多主要的数据类型。但是您可能对他们的工作和行为方式有疑问。好吧,本节就是关于这些的。

TF1 数据类型/数据结构

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

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

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

tf.Tensor:这类似于变量,因为它具有初始值。但是,一旦它们被定义,它们的值就不能改变(即它们是不可变的)。例如,上例中的tf_ctf.Tensor

TF2 数据类型/数据结构

tf.Variable tf.Tensor tf.Operation

就行为而言,从 TF1 到 TF2 的数据类型没有太大变化。唯一的主要区别是,tf.placeholders 消失了。你也可以看看full list of data types。

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

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

正如我所解释的,如果您要使用准 TensorFlow,Keras 隐藏了许多您必须处理的不必要的复杂问题。 Keras 为实现 NN 提供了两个主要的东西 Layer 对象和 Model 对象。 Keras 还有两个最常见的模型 API,可让您开发模型:Sequential APIFunctional API。让我们通过一个简单的示例来看看 Keras 和 TensorFlow 的不同之处。让我们构建一个简单的 CNN。

提示:Keras 让您可以更轻松地实现使用 TF 可以实现的目标。但是 Keras 也提供了 TF 中还不强大的功能(例如text processing 功能)。

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

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()

优点

直接实现简单的模型

缺点

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

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()

优点

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

缺点

需要非常了解输入输出的形状以及每一层的输入预期

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)

优点

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

缺点

可读性差

注意事项和注意事项

在这里,我将列出您在使用 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)

InvalidArgumentError:您必须使用 dtype float 为占位符张量“Placeholder_8”提供一个值 [[node Placeholder_8(定义在/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/framework/ops.py:1748)]]

您在此处收到错误的原因是,您没有向tf_b 提供值。因此,请确保将值提供给 all 依赖占位符以计算结果。

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)

TypeError: 'Add' Op 的输入 'y' 的 float32 类型与参数 'x' 的 int32 类型不匹配。

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

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')

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

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

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

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)

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

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

TF1 和 TF2 的性能差异

这已经在讨论here。所以我不会重复其中的内容。

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

我留下了一些进一步阅读的链接。

tf.data.Dataset tf.RaggedTensor

【讨论】:

以上是关于TensorFlow 和 Keras 入门:过去 (TF1) 现在 (TF2)的主要内容,如果未能解决你的问题,请参考以下文章

TensorFlow2.0教程-Keras 快速入门:用于构建和训练深度学习模型的高阶 API

TensorFlow2 入门指南 | 11 Keras 与 tf.keras 总体框架介绍

TensorFlow 和 Keras 中的符号张量是啥?

TensorFlow2 入门指南 | 10 Keras 与 tf.keras 总体框架介绍

早停 tf.keras.callbacks.EarlyStopping() 详解TensorFlow2入门手册

TensorFlow2 入门指南 keras.callbacks 回调使用方法