2018 年 8 月 10 日 - 作者:Xuechen Li,软件工程实习生
概述Eager Execution 简化了 TensorFlow 中的模型构建体验,而图执行可以提供优化,使模型运行更快,内存效率更高。这篇博文展示了如何编写 TensorFlow 代码,以便使用 Eager Execution 和 tf.keras API 构建的模型可以转换为图,并最终使用 tf.estimator API 的支持部署在云 TPU 上……
tf-nightly
或 tf-nightly-gpu
)。tf.keras.Model
子类化并通过覆盖 call
方法(如上面的方程所示)来定义可逆块和正向传递class Residual(tf.keras.Model):
def __init__(self, filters):
super(Residual, self).__init__()
self.f = ResidualInner(filters=filters, strides=(1, 1))
self.g = ResidualInner(filters=filters, strides=(1, 1))
def call(self, x, training=True):
x1, x2 = tf.split(x, num_or_size_splits=2, axis=self.axis)
f_x2 = self.f(x2, training=training)
y1 = f_x2 + x1
g_y1 = self.g(y1, training=training)
y2 = g_y1 + x2
return tf.concat([y1, y2], axis=self.axis)
这里的 training
参数用于确定批次归一化的状态。启用 Eager Execution 后,当 training=True
时,批次归一化的运行平均值会自动更新。当执行等效的图时,需要使用 get_updates_for
方法手动获取批次归一化更新。tf.GradientTape
作为上下文管理器,仅在需要时跟踪梯度 def backward_grads(self, y, dy, training=True):
dy1, dy2 = dy
y1, y2 = y
with tf.GradientTape() as gtape:
gtape.watch(y1)
gy1 = self.g(y1, training=training)
grads_combined = gtape.gradient(
gy1, [y1] + self.g.trainable_variables, output_gradients=dy2)
dg = grads_combined[1:]
dx1 = dy1 + grads_combined[0]
x2 = y2 - gy1
with tf.GradientTape() as ftape:
ftape.watch(x2)
fx2 = self.f(x2, training=training)
grads_combined = ftape.gradient(
fx2, [x2] + self.f.trainable_variables, output_gradients=dx1)
df = grads_combined[1:]
dx2 = dy2 + grads_combined[0]
x1 = y1 - fx2
x = x1, x2
dx = dx1, dx2
grads = df + dg
return x, dx, grads
梯度计算的精确集可以在论文的算法 1 中找到(我们在代码中简化了使用变量 z1 的中间步骤)。该算法的设计使得在每个可逆块中,相对于输入和模型变量的梯度随着输入的重建而计算,给定输出和损失相对于输出的梯度。调用 tape.gradient(y, x)
计算 y 相对于 x 的梯度。我们也可以使用参数 output_gradients
来显式地应用链式法则。block = Residual()
x = tf.random_normal(shape=(N, C, H, W))
dy = tf.random_normal(shape=(N, C, H, W))
with tf.GradientTape() as tape:
tape.watch(x)
y = block(x)
# Compute true grads
dx_true = tape.gradient(y, x, output_gradients=dy)
# Compute grads from reconstruction
dx, _ = block.backward_grads(x, y, dy)
# Check whether the difference is below a certain threshold
thres = 1e-6
diff_abs = tf.reshape(abs(dx - dx_true), [-1])
assert all(diff_abs < thres)
在上面的代码段中,dx_true
是常规反向传播返回的梯度,而 dx
是我们实现的可逆反向传播返回的梯度。Eager Execution 与原生 Python 集成在一起,因此可以将 all
和 abs
之类的函数直接应用于张量。tf.train.Checkpoint
API。tf.train.Checkpoint
实例。这可能包括我们的模型、我们使用的优化器、学习率计划以及全局步骤checkpoint = tf.train.Checkpoint(model=model, optimizer=optimizer,
learning_rate=learning_rate, global_step=global_step)
我们可以按如下方式保存和恢复特定训练实例checkpoint.save(file_prefix)
checkpoint.restore(save_path)
tf.contrib.eager.defun
将由 TensorFlow 操作组成的 Python 函数编译为可调用的 TensorFlow 图来弥合这种性能差距。在训练深度学习模型时,通常有三个主要地方可以使用 tf.contrib.eager.defun
:1)正向计算,2)梯度的反向计算,以及 3)将梯度应用于变量。例如,我们可以对正向传递和梯度计算进行 defun,如下所示tfe = tf.contrib.eager
model.call = tfe.defun(model.call)
model.compute_gradients = tfe.defun(model.compute_gradients)
为了对优化器的应用梯度步骤进行 defun,我们需要将其包装在另一个函数中def apply_gradients(optimizer, gradients, variables, global_step=None):
optimizer.apply_gradients(
zip(gradients, variables), global_step=global_step)
apply_gradients = tfe.defun(apply_gradients)
tf.contrib.eager.defun
正在积极开发中,使用它是一种不断发展的技术;有关更多信息,请查阅 其文档字符串。tf.contrib.eager.defun
包装 Python 函数会导致 Python 函数中的 TensorFlow API 调用构建一个图,而不是立即执行操作,从而实现整个程序优化。并非所有 Python 函数都能成功地转换为等效的图,特别是那些具有动态控制流的函数(例如,在 Tensor
contents
上的 if
或 while
)。tf.contrib.autograph
是一种相关工具,它可以增加可以转换为 TensorFlow 图的 Python 代码的覆盖范围。截至 2018 年 8 月,autograph 与 defun 的集成正在进行中。tf.data.Dataset
API 兼容。我们可以读取 TFRecords 文件dataset = tf.data.TFRecordDataset(filename)
dataset = dataset.repeat(epochs).map(parser).batch(batch_size)
为了提高性能,我们还可以使用 prefetch
函数并调整 num_parallel_calls
。for image, label in dataset:
logits = model(image, training=True)
...
tf.keras
API 也支持图构建,因此使用 Eager Execution 构建的相同模型也可以用作提供给 Estimator
的图构造函数,代码只需做少量更改。要修改在 Eager Execution 中构建的 RevNet 示例,我们只需要将 keras 模型包装在一个 model_fn
中,并根据 tf.estimator
API 使用它。def model_fn(features, labels, mode, params):
model = RevNet(params["hyperparameters"])
if mode == tf.estimator.ModeKeys.TRAIN:
optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)
logits, saved_hidden = model(features, training=True)
grads, loss = model.compute_gradients(saved_hidden, labels, training=True)
with tf.control_dependencies(model.get_updates_for(features)):
train_op = optimizer.apply_gradients(zip(grads, model.trainable_variables))
return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)
tf.estimator
API 所需的 input_fn
可以像往常一样使用 tf.data
API 定义,从 TFRecords 读取。Estimator
中,可以使模型在 云 TPU 上运行。tf.estimator.Estimator
切换到 tf.contrib.tpu.TPUEstimator
tf.contrib.tpu.CrossShardOptimizer
中tf.contrib.tpu.keras_to_tpu_model
将进一步简化使 Keras 模型在 TPU 上运行的过程。tf.GradientTape
与梯度计算的简化相结合,无需额外的正向传递,这使我们能够实现 RevNet 的可逆反向传播,其计算开销仅为常规反向传播的 25%。tf.keras
API 快速地对机器学习模型进行原型设计。这简化了模型构建体验,此外,只需做少量额外工作,我们就可以将模型转换为 Estimator 并将其部署在云 TPU 上,以实现高性能。您可以在 此处 找到本文的完整代码。此外,请务必查看使用 Eager Execution 的 其他示例。
2018 年 8 月 10 日 - 作者:Xuechen Li,软件工程实习生
概述Eager Execution 简化了 TensorFlow 中的模型构建体验,而图执行可以提供优化,使模型运行更快,内存效率更高。这篇博文展示了如何编写 TensorFlow 代码,以便使用 Eager Execution 和 tf.keras API 构建的模型可以转换为图,并最终部署在…