TensorFlow Eager 官方教程 * * * * *
TensorFlow 版本:1.10.0 > Guide > Eager Execution
Eager 模式介绍
TensorFlow 的 eager execution 是一个即时运行环境。在该模式下,op 在定义时即时运行(eager 模式下,op 返回具体值,而不是图节点)。Eager 模式使得 TensorFlow 的使用和调试变得简单。为了真实地感受 Eager 模式的魅力,推荐大家在交互式 python 解释器中运行本文的代码。
Eager execution 是进行机器学习研究、实验的一个灵活的平台。Eager 模式有以下优点:
- 更好的交互 ---------- 可以更自然地组织你的代码,可以直接使用 Python 数据结构。
- 更容易的调试 ---------- 可以直接调用 op 去检查模型的运行过程,测试更改后的模型。 使用标准的 Python 调试工具来检测模型的错误。
- 自然的控制流 ---------- 可以使用 Python 控制流,从而简化动态模型的搭建。
Eager 模式支持绝大多数 TensorFlow op 和 GPU 加速。Eager 模式的使用示例,详见tensorflow/contrib/eager/python/examples。
注意:虽然一些 model 可能会因 Eager 模式的开启变慢,但性能的提升持续在进行。
文章目录
1. Eager 模式的开启、基本使用 ¶
请大家首先将 TensorFlow 更到最新:
$ pip install --upgrade tensorflow
要开启 Eager 模式,只需要在 “程序” 或 “控制台会话” 的开始添加 tf.enable_eager_execution()
。不要 将开启 Eager 模式的语句放在程序的其它地方。
import tensorflow as tf
tf.enable_eager_execution()
现在你运行 TensorFlow 的 op 后,op 的运行结果会立即返回:
tf.executing_eagerly() # => True
x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m)) # => "hello, [[4.]]"
Eager 模式的开启会改变 TensorFlow op 的运行 ---------- 开启 eager 后 op 会立即会将运行结果返回 Python。tf.Tensor
对象引用(reference)具体的值,而不是计算图中节点的句柄。因为 Eager 模式不构建计算图,因此可以很方便地使用 print()
或 python调试器 来检查结果。评估、print、检查 tensor 的值不影响梯度的计算。
Eager 模式和 NumPy 的兼容性很好。NumPy op 可以以 tf.Tensor
为参数。TensorFlow math operations 将 Python 对象和 NumPy 数组转换为 tf.Tensor
对象。tf.Tensor.numpy
方法将 tf.Tensor
转换为 NumPy 数组。
a = tf.constant([[1, 2],
[3, 4]])
print(a)
# => tf.Tensor([[1 2]
# [3 4]], shape=(2, 2), dtype=int32)
# Broadcasting support
b = tf.add(a, 1)
print(b)
# => tf.Tensor([[2 3]
# [4 5]], shape=(2, 2), dtype=int32)
# Operator overloading is supported
print(a * b)
# => tf.Tensor([[ 2 6]
# [12 20]], shape=(2, 2), dtype=int32)
# Use NumPy values
import numpy as np
c = np.multiply(a, b)
print(c)
# => [[ 2 6]
# [12 20]]
# Obtain numpy value from a tensor:
print(a.numpy())
# => [[1 2]
# [3 4]]
tf.contrib.eager
模块的 op 可以同时适用于 eager 和 graph 环境。这在编写兼容 graph 模式的的代码时非常有用。
tfe = tf.contrib.eager
2. Eager 模式下 model 的建立 ¶
很多机器学习模型通常由很多层组合而成。开启 eager 模式后,你可以自定义层 或 使用 tf.keras.layers
里的层。
在使用 Python 对象来表示一个层时,需要从 tf.keras.layers.Layer
继承来编写自定义层:
class MySimpleLayer(tf.keras.layers.Layer):
def __init__(self, output_units):
super(MySimpleLayer, self).__init__()
self.output_units = output_units
def build(self, input_shape):
# The build method gets called the first time your layer is used.
# Creating variables on build() allows you to make their shape depend
# on the input shape and hence removes the need for the user to specify
# full shapes. It is possible to create variables during __init__() if
# you already know their full shapes.
self.kernel = self.add_variable(
"kernel", [input_shape[-1], self.output_units])
def call(self, input):
# Override call() instead of __call__ so we can perform some bookkeeping.
return tf.matmul(input, self.kernel)
使用 tf.keras.layers.Dense
来取代 MySimpleLayer
非常有必要(因为 Dense 的功能包含了 MySimpleLayer 的功能)。
将层组合成模型时,你可以使用 tf.keras.Sequential
来表示模型,其可以线性地多个层堆叠起来。Sequential 在搭建简单模型时非常有用。
model = tf.keras.Sequential([
tf.keras.layers.Dense(10, input_shape=(784,)), # must declare input shape
tf.keras.layers.Dense(10)
])
除了使用 Sequential 来构建模型,也可以从 tf.keras.Model
继承来编写一个模型类表示模型。构建的模型包含了很多层,允许 tf.keras.Model
包含其它 tf.keras.Model
对象。
class MNISTModel(tf.keras.Model):
def __init__(self):
super(MNISTModel, self).__init__()
self.dense1 = tf.keras.layers.Dense(units=10)
self.dense2 = tf.keras.layers.Dense(units=10)
def call(self, input):
"""Run the model."""
result = self.dense1(input)
result = self.dense2(result)
result = self.dense2(result) # reuse variables from dense2 layer
return result
model = MNISTModel()
在 tf.keras.Model
类中不需要设置输入的 shape。
tf.keras.layers
类 创建、包含了该层的模型参数,这些参数 与 实例化出的对象 共存亡。当需要共享层的参数时,共享 实例化出的对象 即可。
3. Eager 模式下 model 的训练 ¶
3.1 计算梯度 ¶
自动微分对于机器学习算法的训练至关重要。在 Eager 模式,为了计算梯度,需要使用 tf.GradientTape
来追踪 op。
tf.GradientTape
是一个
3.2 训练一个模型 ¶
即使不进行训练,也可以在 eager 模式调用模型并检查模型的输出。
# Create a tensor representing a blank image
batch = tf.zeros([1, 1, 784])
print(batch.shape) # => (1, 1, 784)
result = model(batch)
# => tf.Tensor([[[ 0. 0., ..., 0.]]], shape=(1, 1, 10), dtype=float32)
下面的例子使用 TensorFlow MNIST example 中的 dataset.py module 来导入数据,请将该模块下载到你的机器。运行下面的代码去下载 MNIST 数据集到你的机器,并以 tf.data.Dataset
的形式将数据集用于训练:
import dataset # download dataset.py file
dataset_train = dataset.train('./datasets').shuffle(60000).repeat(4).batch(32)
为了训练一个模型,需要去定义一个损失函数、计算梯度。使用优化器去更新模型变量:
def loss(model, x, y):
prediction = model(x)
return tf.losses.sparse_softmax_cross_entropy(labels=y, logits=prediction)
def grad(model, inputs, targets):
with tf.GradientTape() as tape:
loss_value = loss(model, inputs, targets)
return tape.gradient(loss_value, model.variables)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
x, y = iter(dataset_train).next()
print("Initial loss: {:.3f}".format(loss(model, x, y)))
# Training loop
for (i, (x, y)) in enumerate(dataset_train):
# Calculate derivatives of the input function with respect to its parameters.
grads = grad(model, x, y)
# Apply the gradient to the model
optimizer.apply_gradients(zip(grads, model.variables),
global_step=tf.train.get_or_create_global_step())
if i % 200 == 0:
print("Loss at step {:04d}: {:.3f}".format(i, loss(model, x, y)))
print("Final loss: {:.3f}".format(loss(model, x, y)))
输出(具体的数字可能不同):
Initial loss: 2.674
Loss at step 0000: 2.593
Loss at step 0200: 2.143
Loss at step 0400: 2.009
Loss at step 0600: 2.103
Loss at step 0800: 1.621
Loss at step 1000: 1.695
...
Loss at step 6600: 0.602
Loss at step 6800: 0.557
Loss at step 7000: 0.499
Loss at step 7200: 0.744
Loss at step 7400: 0.681
Final loss: 0.670
并且为了更快地训练,将上面的计算使用 GPU 进行加速:
with tf.device("/gpu:0"):
for (i, (x, y)) in enumerate(dataset_train):
# minimize() is equivalent to the grad() and apply_gradients() calls.
optimizer.minimize(lambda: loss(model, x, y),
global_step=tf.train.get_or_create_global_step())
3.3 变量和优化器 ¶
tfe.Variable
对象存储的 tf.Tensor
的值可变,这使得自动微分过程变得简单。模型的变量可以被封装到类中。
通过使用 tfe.Variable
及 tf.GradientTape
可以更好地封装模型参数。例如,上面的自动微分例子可以重写为:
class Model(tf.keras.Model):
def __init__(self):
super(Model, self).__init__()
self.W = tfe.Variable(5., name='weight')
self.B = tfe.Variable(10., name='bias')
def call(self, inputs):
return inputs * self.W + self.B
# A toy dataset of points around 3 * x + 2
NUM_EXAMPLES = 2000
training_inputs = tf.random_normal([NUM_EXAMPLES])
noise = tf.random_normal([NUM_EXAMPLES])
training_outputs = training_inputs * 3 + 2 + noise
# The loss function to be optimized
def loss(model, inputs, targets):
error = model(inputs) - targets
return tf.reduce_mean(tf.square(error))
def grad(model, inputs, targets):
with tf.GradientTape() as tape:
loss_value = loss(model, inputs, targets)
return tape.gradient(loss_value, [model.W, model.B])
# Define:
# 1. A model.
# 2. Derivatives of a loss function with respect to model parameters.
# 3. A strategy for updating the variables based on the derivatives.
model = Model()
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
print("Initial loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
# Training loop
for i in range(300):
grads = grad(model, training_inputs, training_outputs)
optimizer.apply_gradients(zip(grads, [model.W, model.B]),
global_step=tf.train.get_or_create_global_step())
if i % 20 == 0:
print("Loss at step {:03d}: {:.3f}".format(i, loss(model, training_inputs, training_outputs)))
print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
输出(具体值可能会有变化):
Initial loss: 69.066
Loss at step 000: 66.368
Loss at step 020: 30.107
Loss at step 040: 13.959
Loss at step 060: 6.769
Loss at step 080: 3.567
Loss at step 100: 2.141
Loss at step 120: 1.506
Loss at step 140: 1.223
Loss at step 160: 1.097
Loss at step 180: 1.041
Loss at step 200: 1.016
Loss at step 220: 1.005
Loss at step 240: 1.000
Loss at step 260: 0.998
Loss at step 280: 0.997
Final loss: 0.996
W = 2.99431324005, B = 2.02129220963
4. Eager 模式下,模型状态(变量的状态)及存活时间由与之对应的 Python 对象决定 ¶
使用 graph 模式时,程序的状态(例如变量)储存在 “全局容器” 中,并且它们的存活时间由 tf.Session
对象来管理。与 graph 模式不同,在 eager 模式,程序的状态由其对应的 Python 对象决定,并且状态的存活时间由对应 Python 对象决定。
4.1 Variables 是对象 ¶
在 Eager 模式下,变量的存活时间 由 与之对应的Python对象的存活时间决定。
with tf.device("gpu:0"):
v = tfe.Variable(tf.random_normal([1000, 1000]))
v = None # v no longer takes up GPU memory
4.2 基于对象的保存(Object-based saving) ¶
tfe.Checkpoint
能够将 tfe.Variables
储存为 checkpoints,能够从 checkpoints 中恢复 tfe.Variable
。
x = tfe.Variable(10.)
checkpoint = tfe.Checkpoint(x=x) # save as "x"
x.assign(2.) # Assign a new value to the variables and save.
save_path = checkpoint.save('./ckpt/')
x.assign(11.) # Change the variable after saving.
# Restore values from the checkpoint
checkpoint.restore(save_path)
print(x) # => 2.0
为了保存和加载模型,tfe.Checkpoint
存储对象的内部状态,
为了记录 [模型、优化器、global step],只需要将它们传给一个 tfe.Checkpoint
:
model = MyModel()
optimizer = tf.train.AdamOptimizer(learning_rate=0.001)
checkpoint_dir = ‘/path/to/model_dir’
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
root = tfe.Checkpoint(optimizer=optimizer,
model=model,
optimizer_step=tf.train.get_or_create_global_step())
root.save(file_prefix=checkpoint_prefix)
# or
root.restore(tf.train.latest_checkpoint(checkpoint_dir))
4.3 面对对象的指标 ¶
tfe.metrics
被当做对象来存储。通过给指标传递新值来更新指标的值,取回指标的值使用 tfe.metrics.result
方法。例如:
m = tfe.metrics.Mean("loss")
m(0)
m(5)
m.result() # => 2.5
m([8, 9])
m.result() # => 5.5
Summaries and TensorBoard ¶
TensorBoard 是一个用来理解、调试、优化模型训练过程的可视化工具。
tf.contrib.summary
同时兼容 eager 和 graph 模式。
writer = tf.contrib.summary.create_file_writer(logdir)
global_step=tf.train.get_or_create_global_step() # return global step var
writer.set_as_default()
for _ in range(iterations):
global_step.assign_add(1)
# Must include a record_summaries method
with tf.contrib.summary.record_summaries_every_n_global_steps(100): # record summary every 100 steps
# your model code goes here
tf.contrib.summary.scalar('loss', loss)
...
5. Eager 自动微分的高级应用 ¶
5.1 动态模型 ¶
tf.GradientTape
可以用于动态模型。下面的例子以类 NumPy 的代码实现了回溯搜索算法。
def line_search_step(fn, init_x, rate=1.0):
with tf.GradientTape() as tape:
# Variables are automatically recorded, but manually watch a tensor
tape.watch(init_x)
value = fn(init_x)
grad = tape.gradient(value, init_x)
grad_norm = tf.reduce_sum(grad * grad)
init_value = value
while value > init_value - rate * grad_norm:
x = init_x - rate * grad
value = fn(x)
rate /= 2.0
return x, value
5.2 计算梯度的其它函数 ¶
虽然 tf.GradientTape
是计算梯度的一个强有力 API,但有另一种 Autograd 风格的 API 来进行自动微分。如果运算的代码只与 tensor 及 梯度函数 相关,并且没有 tfe.Variables
,这些函数是非常有用的。
-
tfe.gradients_function
---------- 返回一个函数,其计算输出相对于输入的梯度。 -
tfe.value_and_gradients_function
---------- 与tfe.gradients_function
类似,但是当调用返回的函数时,其函数的输出值 和 梯度。
在下面的例子中,tfe.gradients_function
以 square
函数为输入,返回一个计算 square 偏微分的函数。
def square(x):
return tf.multiply(x, x)
grad = tfe.gradients_function(square)
square(3.) # => 9.0
grad(3.) # => [6.0]
# The second-order derivative of square:
gradgrad = tfe.gradients_function(lambda x: grad(x)[0])
gradgrad(3.) # => [2.0]
# The third-order derivative is None:
gradgradgrad = tfe.gradients_function(lambda x: gradgrad(x)[0])
gradgradgrad(3.) # => [None]
# With flow control:
def abs(x):
return x if x > 0. else -x
grad = tfe.gradients_function(abs)
grad(3.) # => [1.0]
grad(-3.) # => [-1.0]
5.3 自定义梯度 ¶
tf.custom_gradient
是一种覆盖梯度的简单方法。
@tf.custom_gradient
def clip_gradient_by_norm(x, norm):
y = tf.identity(x)
def grad_fn(dresult):
return [tf.clip_by_norm(dresult, norm), None]
return y, grad_fn
tf.custom_gradient
通常被用来稳定梯度。
def log1pexp(x):
return tf.log(1 + tf.exp(x))
grad_log1pexp = tfe.gradients_function(log1pexp)
# The gradient computation works fine at x = 0.
grad_log1pexp(0.) # => [0.5]
# However, x = 100 fails because of numerical instability.
grad_log1pexp(100.) # => [nan]
这里,log1pexp
函数可以被简化为一个custom gradient。下面的实现重用了 tf.exp(x)
的值,通过消除冗余的计算,使它变得更高效。
@tf.custom_gradient
def log1pexp(x):
e = tf.exp(x)
def grad(dy):
return dy * (1 - 1 / (1 + e))
return tf.log(1 + e), grad
grad_log1pexp = tfe.gradients_function(log1pexp)
# As before, the gradient computation works fine at x = 0.
grad_log1pexp(0.) # => [0.5]
# And the gradient computation also works at x = 100.
grad_log1pexp(100.) # => [1.0]
6. Eager 模式的性能 ¶
在 eager 模式下,模型的计算默认不使用 GPU。如果你想在 GPU 上运行模型,可以使用 tf.device('/gpu:0')
。
import time
def measure(x, steps):
# TensorFlow initializes a GPU the first time it's used, exclude from timing.
tf.matmul(x, x)
start = time.time()
for i in range(steps):
x = tf.matmul(x, x)
_ = x.numpy() # Make sure to execute op and not just enqueue it
end = time.time()
return end - start
shape = (1000, 1000)
steps = 200
print("Time to multiply a {} matrix by itself {} times:".format(shape, steps))
# Run on CPU:
with tf.device("/cpu:0"):
print("CPU: {} secs".format(measure(tf.random_normal(shape), steps)))
# Run on GPU, if available:
if tfe.num_gpus() > 0:
with tf.device("/gpu:0"):
print("GPU: {} secs".format(measure(tf.random_normal(shape), steps)))
else:
print("GPU: not found")
性能基准 ¶
对于计算量较大的模型(比如 ResNet-50)在一个 GPU 上的计算,eager 模式的性能和 graph 是相当的。但是当 “模型的计算量比较小” 时,两个模式之间的性能差异就会变大。
7. Eager 与 Graph 的兼容 ¶
Eager 使得开发、调试的交互性更好,而 graph 在分布式训练、性能优化、生产部署 方面有优势。但是编写 graph 代码比编写普通的 Python 代码难,并且不容易调试。
为了建立、训练 graph 类型的模型,Python 程序首先建立一个计算图,然后调用 Session.run
来执行图。这提供了:
- 静态的自动微分
- 简单部署(在平台无关的服务器)
- 基于图的优化(常见的子表示式的合并,常量合并等)
- 编译和内核融合
- 自动分布式和复制(将节点放置在分布式系统上)
为 eager 模式编写部署用的代码是非常困难的。难点1:从 model 中生成一个 graph;难点2:在服务器上直接运行Python控制台 及 代码。
7.1 编写兼容的代码 ¶
Eager 模式的代码也可以用于 graph 模式。要实现这个转换,重开一个 Python 控制台,不开启 eager 即可。
大多数的 TensorFlow op 兼容 eager 模式,但是请注意以下几点:
- 请使用
tf.data
,而不是 queues。它更快且更易用。 - 请使用 “面向对象的 layer API” --------- 比如:
tf.keras.layers
,因为它们有显式的变量存储。 - 大多数的模型代码在 eager 和 graph 模式的行为一致,但也有例外(例如,动态模型使用 Python 控制流基于输入改变了模型的计算过程)。
- Eager 模式的开始不可逆。想返回 graph 模式,请开启一个新的 Python 控制台。
编写的代码最好同时支持 eager 和 graph 模式。这使得你可以在利用 eager 良好的交互、调试;同时拥有 graph 的分布式性能优势。
在 eager 模式下进行写代码、调试、迭代,然后以 graph 来进行生产的部署。使用 tfe.Checkpoint
去保存、恢复模型变量这使得你可以很方便地在 eager 和 graph 模式之间切换。示例详见:tensorflow/contrib/eager/python/examples。
7.2 在 graph 的局部使用 eager ¶
当 eager 模式未开启的情况下,想在 graph 的局部使用 eager,可以使用 tfe.py_func
。
def my_py_func(x):
x = tf.matmul(x, x) # You can use tf ops
print(x) # but it's eager!
return x
with tf.Session() as sess:
x = tf.placeholder(dtype=tf.float32)
# Call eager function in graph!
pf = tfe.py_func(my_py_func, [x], tf.float32)
sess.run(pf, feed_dict={x: [[2.0]]}) # [[4.0]]
推荐阅读
-
tensorflow eager 模式下打印dataset中的数据
-
TensorFlow发布Eager,便于Debug!
-
TensorFlow Eager 官方教程 * * * * *
-
TensorFlow Eager模式--数据导入(tf.data)
-
java官方教程
-
Hibernate官方教程
-
unity官方教程项目在哪里(unity自带官方教程)
-
搭建PHP官方框架zend framework 2(LINUX),zendframework_PHP教程
-
网开EIM(及时通信软件) v3.1.0 官方安装使用教程
-
php官方微信接口大全(微信支付、微信红包、微信摇一摇、微信小店),_PHP教程