本文的主要内容参考斯坦福大学CS20SI课程,有兴趣的同学请点击链接SC20查看课程讲义。
今天我们通常使用的Tensorflow是声明式的(Declarative)。这意味着我们在运行我们的Graph的时候必须提前先声明好其中的所有内容,然后再运行它。
对于图,它是...
可优化的(Optimizable)
-自动缓冲区重用(automatic buffer reuse)
-可以不断折叠的(constant folding)
-op之间是并行处理的(inter-op parallelism)
-自动在计算和内存资源之间进行权衡(automatic trade-off between compute and memory)
可展开的(Deployable)
-图是一个对于刻画一个模型的中介。
可重写的(Rewritable)
-experiment with automatic device placement or quantization.
但是,图也是...
难以调试的(Difficult to debug)
-在组成图后,如果有问题会报告很长的错误。
-不能通过pdb或者打印状态来对图的执行进行调试。
不够Python(Un-Pythonic)
-编写TensorFlow程序是一个元编程(metaprogramming)的练习。
-Tensorflow控制流和Python有很大的不同。
-不能用传统的数据结构来完成图的构建。
所以,为了解决图具有的这一系列的问题。Tensorflow的开发者引入了Eager execution.
"A NumPy-like library for numerical computation with support for GPU acceleration and automatic differentiation, and a flexible platform for machine learning research and experimentation."
----the eager execution user guide
一个调用eager execution的demo:
$python
import tensorflow # version >= 1.50
import tensorflow.contrib.eager as tfe
tfe.enable_eager_execution()
重要的优点:
你终于可以使用pbd.set_trace()了!
你再也不需要担心...
1.占位符(placeholders)
2.sessions
3.控制依赖(control dependencies)
4.lazy loading
5.{name, variable, op}
使用eager execution 前:
在这里我们实现了一个矩阵和自身相乘的操作。
x = tf.placeholder(tf.float32, shape=[1, 1])
m = tf.matmul(x, x)
print(m)
# Tensor("MatMul:0", shape=(1, 1), dtype=float32)
with tf.Session() as sess:
m_out = sess.run(m, feed_dict={x: [[2.]]})
print(m_out)
# [[4.]]
使用eager execution后:
x = [[2.]] # No need for placeholders!
m = tf.matmul(x, x)
print(m) # No sessions!
# tf.Tensor([[4.]], shape=(1, 1), dtype=float32)
我们看到在我们使用eager execution后,三行代码就足以让我们完成之前的任务。没有placeholder,没有session,这极大的简化了我们的代码。
对于Lazy loading:
x = tf.random_uniform([2, 2])
with tf.Session() as sess:
for i in range(x.shape[0]):
for j in range(x.shape[1]):
print(sess.run(x[i, j]))
在这个操作中,我们会在每次迭代时都要向图中添加一个op。而当我们在使用eager execution 时,由于我们不再需要图或者对一个op进行重复的操作,因此我们的代码会变得更加简洁,如下:
x = tf.random_uniform([2, 2])
for i in range(x.shape[0]):
for j in range(x.shape[1]):
print(x[i, j])
另外,我们在这里介绍一个小技巧,即如何让Tensors像Numpy数组一样,下面是一个小实例:
x = tf.constant([1.0, 2.0, 3.0])
# Tensors are backed by NumPy arrays
assert type(x.numpy()) == np.ndarray
squared = np.square(x) # Tensors are compatible with NumPy functions
# Tensors are iterable!
for i in x:
print(i)
在eager execution 中已经构建了微分的方法。
在这一框架下...
举个例子来说:
def square(x):
return x ** 2
grad = tfe.gradients_function(square)
print(square(3.)) # tf.Tensor(9., shape=(), dtype=float32)
print(grad(3.)) # [tf.Tensor(6., shape=(), dtype=float32))]
x = tfe.Variable(2.0)
def loss(y):
return (y - x ** 2) ** 2
grad = tfe.implicit_gradients(loss)
print(loss(7.)) # tf.Tensor(9., shape=(), dtype=float32)
print(grad(7.)) # [(,
)]
当我们使用eager execution时,需要使用tfe.Variable来声明变量。同样的,tfe.implicit_gradients()会根据变量来计算梯度。
下面的API均可以被用来计算梯度,即使当eager execution 没有被使用。
tfe.gradients_function()
tfe.value_and_gradients_function()
tfe.implicit_gradients()
tfe.implicit_value_and_gradients()
和没有Eager Execution的模式相比,没有那么多的不同。
Tensorflow = Operation Kernels + Execution
构建图的模式:使用Session来执行一系列op的组合。
Eager execution 模式:用Python来执行一系列op的组合。
对于Tensorflow ,一种可以用来理解的方式是可以将它视为一系列operation的组合,这些operation包括数学,线性代数,图像处理,用来生成TensorBoard可视化的代码等等,也包括执行这些组成部分的一个计算操作。Session提供了一种执行这些op的方法。而在Eager execution模式下,相当于是使用python直接执行这些操作。
但是二者基本的操作是相同的,因此API的形式也大体相当。
一般情况下,无论你是否启用了eager execution,Tensorflow的API都是可以使用的。但是当eager execution 模式被启用的情况下......
-只有当功能层(例如tf.layers.dense)包装进tfe.make_template的时候才能发挥功效。
必须要声明并且返回。
模型只需要定义一次。
-相同的代码能够在一个Python进程中执行op,同时能够在另外一个进程中组成一个图。
Checkpoints是兼容的。
-Train eagerly, checkpoint, load in a graph, or vice-versa.
在eager execution 模式下创建图
-tfe.defun:将“Complie”编译成图然后再执行。
如果你是一个想使用灵活框架的研究者,或者想要开发一个新的机器学习模型,或者是Tensorflow的初学者,我们都很推荐你去使用eager exexecution。