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.TPUEstimatortf.contrib.tpu.CrossShardOptimizer 中tf.contrib.tpu.keras_to_tpu_model 将进一步简化使 Keras 模型在 TPU 上运行的过程。tf.GradientTape 与梯度计算的简化相结合,无需额外的正向传递,这使我们能够实现 RevNet 的可逆反向传播,其计算开销仅为常规反向传播的 25%。
为了验证内存节省,我们在训练过程中绘制内存使用情况。蓝色和黑色曲线分别表示常规反向传播和可逆反向传播。该图记录了 RevNet-104 图模式训练 100 次迭代,模拟 ImageNet 数据,批次大小为 128。该图由 mprof 生成,训练在 CPU 上执行,因此我们可以在常规反向传播中使用相同的批次大小。 
tf.keras API 快速地对机器学习模型进行原型设计。这简化了模型构建体验,此外,只需做少量额外工作,我们就可以将模型转换为 Estimator 并将其部署在云 TPU 上,以实现高性能。您可以在 此处 找到本文的完整代码。此外,请务必查看使用 Eager Execution 的 其他示例。
2018 年 8 月 10 日 - 作者:Xuechen Li,软件工程实习生
概述Eager Execution 简化了 TensorFlow 中的模型构建体验,而图执行可以提供优化,使模型运行更快,内存效率更高。这篇博文展示了如何编写 TensorFlow 代码,以便使用 Eager Execution 和 tf.keras API 构建的模型可以转换为图,并最终部署在…