详解TensorFlow最新发布的「eager」极大优点和命令执行环节!

2017 年 11 月 1 日 全球人工智能

——免费加入AI技术专家社群>>

——免费加入AI高管投资者群>>

今天,我们在TensorFlow中引入了Eager Execution。Eager Execution是一个命令式、运行定义式的接口,其中,操作一旦从Python中调用便立刻得以执行。这样TensorFlow的入门使用就变得相对简单,并可以使研究和开发过程更为直观。


Eager Execution的优点:

  1. 具有面对即时运行错误的快速调试和Python工具的集成。

  2. 支持使用易用的Python控制流的动态模型。

  3. 大力支持自定义的、高阶梯度。

  4. 适用几乎所有可用的TensorFlow操作。


使用 Eager Execution

当你启动 Eager Execution 时,运算会即刻执行,无需 Session.run() 就可以把它们的值返回到 Python。比如,要想使两个矩阵相乘,我们这样写代码:

  
    
    
    
  1. import tensorflow as tf

  2. import tensorflow.contrib.eager as tfe

  3. tfe.enable_eager_execution()

  4. x = [[2.]]

  5. m = tf.matmul(x, x)



使用 print 或者 Python 调试器检查中间结果非常直接。

  
    
    
    
  1. print(m)

  2. # The 1x1 matrix [[4.]]



动态模型的构建可使用 Python 控制流。下面是使用 TensorFlow 算术操作的考拉兹猜想(Collatz conjecture)的一个示例:

  
    
    
    
  1. a = tf.constant(12)

  2. counter = 0

  3. while not tf.equal(a, 1):

  4.  if tf.equal(a % 2, 0):

  5.    a = a / 2

  6.  else:

  7.    a = 3 * a + 1

  8.  print(a)


这里,tf.constant(12) 张量对象的使用将把所有数学运算提升为张量运算,从而所有的返回值将是张量。


梯度

多数 TensorFlow 用户对自动微分(automatic differentiation)很感兴趣。因为每次调用都有可能出现不同的运算,可以理解为我们把所有的正向运算录到「磁带」上,然后在计算梯度时进行「倒放」。梯度计算完成后,「磁带」就没用了。


如果你熟悉 autograd 包,我们提供的 API 与之非常类似。例如:

  
    
    
    
  1. def square(x):

  2.  return tf.multiply(x, x)

  3. grad = tfe.gradients_function(square)

  4. print(square(3.))    # [9.]

  5. print(grad(3.))      # [6.]


gradients_function 的调用使用一个 Python 函数 square() 作为参数,然后返回 Python callable,用于计算输入的 square() 偏导数。因此,为了得到输入为 3.0 时的 square() 导数,激活 grad(3.0),也就是 6。


同样的 gradient_function 调用可用于计算 square() 的二阶导数。

  
    
    
    
  1. gradgrad = tfe.gradients_function(lambda x: grad(x)[0])

  2. print(gradgrad(3.))  # [2.]


如前所述,控制流(control flow)会引起不同的运算,下面是一个示例:

  
    
    
    
  1. def abs(x):

  2.  return x if x > 0. else -x

  3. grad = tfe.gradients_function(abs)

  4. print(grad(2.0))  # [1.]

  5. print(grad(-2.0)) # [-1.]



自定义梯度

用户或许想为运算或函数自定义梯度。这可能有用,原因之一是它为一系列运算提供了更高效、数值更稳定的梯度。


下面的示例使用了自定义梯度。我们先来看函数 log(1 + e^x),它通常用于计算交叉熵和 log 似然。

  
    
    
    
  1. def log1pexp(x):

  2.  return tf.log(1 + tf.exp(x))

  3. grad_log1pexp = tfe.gradients_function(log1pexp)

  4. # The gradient computation works fine at x = 0.

  5. print(grad_log1pexp(0.))

  6. # [0.5]

  7. # However it returns a `nan` at x = 100 due to numerical instability.

  8. print(grad_log1pexp(100.))

  9. # [nan]


我们可以将自定义梯度应用于上述函数,简化梯度表达式。注意下面的梯度函数实现重用了前向传导中计算的 (tf.exp(x)),避免冗余计算,从而提高梯度计算的效率。

  
    
    
    
  1. @tfe.custom_gradient

  2. def log1pexp(x):

  3.  e = tf.exp(x)

  4.  def grad(dy):

  5.    return dy * (1 - 1 / (1 + e))

  6.  return tf.log(1 + e), grad

  7. grad_log1pexp = tfe.gradients_function(log1pexp)

  8. # Gradient at x = 0 works as before.

  9. print(grad_log1pexp(0.))

  10. # [0.5]

  11. # And now gradient computation at x=100 works as well.

  12. print(grad_log1pexp(100.))

  13. # [1.0]



建立模型

模型可以分成几类。此处我们要提的模型可以通过创建一个简单的两层网络对标准的 MNIST 手写数字进行分类。

  
    
    
    
  1. class MNISTModel(tfe.Network):

  2.  def __init__(self):

  3.    super(MNISTModel, self).__init__()

  4.    self.layer1 = self.track_layer(tf.layers.Dense(units=10))

  5.    self.layer2 = self.track_layer(tf.layers.Dense(units=10))

  6.  def call(self, input):

  7.    """Actually runs the model."""

  8.    result = self.layer1(input)

  9.    result = self.layer2(result)

  10.    return result


我们推荐使用 tf.layers 中的类别(而非函数),这是因为它们创建并包含了模型参数(变量,variables)。变量的有效期和层对象的有效期紧密相关,因此需要对它们进行追踪。


为什么要使用 tfe.Network?一个网络包含了多个层,是 tf.layer.Layer 本身,允许将 Network 的对象嵌入到其它 Network 的对象中。它还包含能够协助检查、保存和修复的工具。


即使没有训练模型,我们也可以命令式地调用它并检查输出:

  
    
    
    
  1. # Let's make up a blank input image

  2. model = MNISTModel()

  3. batch = tf.zeros([1, 1, 784])

  4. print(batch.shape)

  5. # (1, 1, 784)

  6. result = model(batch)

  7. print(result)

  8. # tf.Tensor([[[ 0.  0., ...., 0.]]], shape=(1, 1, 10), dtype=float32)


注意我们在这里不需要任何的占位符或会话(session)。一旦数据被输入,层的参数就被设定好了。


训练任何模型都需要定义一个损失函数,计算梯度,并使用一个优化器更新参数。首先定义一个损失函数:

  
    
    
    
  1. def loss_function(model, x, y):

  2.  y_ = model(x)

  3.  return tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=y_)


然后是训练的循环过程:

  
    
    
    
  1. optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)

  2. for (x, y) in tfe.Iterator(dataset):

  3.  grads = tfe.implicit_gradients(loss_function)(model, x, y)

  4.  optimizer.apply_gradients(grads)


implicit_gradients() 计算损失函数关于计算使用的所有 TensorFlow 变量的导数。


我们可以按往常使用 TensorFlow 的方式将计算转移到 GPU 上

  
    
    
    
  1. with tf.device("/gpu:0"):

  2.  for (x, y) in tfe.Iterator(dataset):

  3.    optimizer.minimize(lambda: loss_function(model, x, y))


(注意:我们简化然后保存损失损失函数并直接调用 optimizer.minimize,但你也可以使用上面的 apply_gradients() 方法,它们是等价的。)


使用 Eager 和 Graphs

Eager execution 使开发和调试互动性更强,但是 TensorFlow graph 在分布式训练、性能优化和生产部署中也有很多优势。


启用 eager execution 时,执行运算的代码还可以构建一个描述 eager execution 未启用时的计算图。为了将模型转换成图,只需要在 eager execution 未启用的 Python session 中运行同样的代码。示例:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/python/examples/mnist。我们可以从检查点保存和修复模型变量值,这允许我们在 eager(命令式)和 graph(声明式)编程之间轻松转换。这样,启用 eager execution 开发出的模型可以轻松导出到生产部署中。


在不久的将来,我们将提供工具,可以选择性地将模型的某些部分转换成 graph。用这种方式,你就可以融合部分计算(如自定义 RNN 细胞的内部)实现高性能,同时还能保持 eager execution 的灵活性和可读性。


如何改写我的代码?

Eager execution 的使用方法对现有 TensorFlow 用户来说应是直观的。目前只有少量针对 eager 的 API;大多数现有的 API 和运算需要和启用的 eager 一起工作。请记住以下内容:

  1. 一般对于 TensorFlow,我们建议如果你还没有从排队切换到使用 tf.data 进行输入处理,请抓紧做。它更容易使用,也更快。查看这篇博文(https://developers.googleblog.com/2017/09/introducing-tensorflow-datasets.html)和文档页(https://www.tensorflow.org/programmers_guide/datasets)会有所帮助。

  2. 使用目标导向的层(比如 tf.layer.Conv2D() 或者 Keras 层),它们可以直接存储变量。

  3. 你可以为大多数模型写代码,这对 eager execution 和图构建同样有效。也有一些例外,比如动态模型使用 Python 控制流改变基于输入的计算。

  4. 一旦调用 tfe.enable_eager_execution(),它不可被关掉。为了获得图行为,需要建立一个新的 Python session。

https://research.googleblog.com/2017/10/eager-execution-imperative-define-by.html

https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager


热门文章推荐

黑科技|Adobe出图象技术神器!视频也可以PS了!!

史上第一个被授予公民身份的机器人索菲亚和人对答如流!

浙大90后女黑客在GeekPwn2017上秒破人脸识别系统!

周志华点评AlphaGo Zero:这6大特点非常值得注意!

汤晓鸥教授:人工智能让天下没有难吹的牛!

英伟达发布全球首款人工智能全自动驾驶平台

未来 3~5 年内,哪个方向的机器学习人才最紧缺?

中科院步态识别技术:不看脸 50米内在人群中认出你!

厉害|黄仁勋狂怼CPU:摩尔定律已死 未来属于GPU!

干货|7步让你从零开始掌握Python机器学习!

登录查看更多
6

相关内容

Google发布的第二代深度学习系统TensorFlow
最新《自动微分手册》77页pdf
专知会员服务
100+阅读 · 2020年6月6日
TensorFlow Lite指南实战《TensorFlow Lite A primer》,附48页PPT
专知会员服务
69+阅读 · 2020年1月17日
Tensorflow Eager Execution入门指南
专知
6+阅读 · 2018年4月16日
发布TensorFlow 1.4
谷歌开发者
7+阅读 · 2017年11月23日
终于!TensorFlow引入了动态图机制Eager Execution
机器之心
4+阅读 · 2017年11月1日
手把手教TensorFlow(附代码)
深度学习世界
15+阅读 · 2017年10月17日
手把手教你由TensorFlow上手PyTorch(附代码)
数据派THU
5+阅读 · 2017年10月1日
教程 | 如何从TensorFlow转入PyTorch
机器之心
7+阅读 · 2017年9月30日
Talking-Heads Attention
Arxiv
15+阅读 · 2020年3月5日
Arxiv
4+阅读 · 2018年10月31日
Arxiv
5+阅读 · 2018年5月1日
VIP会员
相关资讯
Tensorflow Eager Execution入门指南
专知
6+阅读 · 2018年4月16日
发布TensorFlow 1.4
谷歌开发者
7+阅读 · 2017年11月23日
终于!TensorFlow引入了动态图机制Eager Execution
机器之心
4+阅读 · 2017年11月1日
手把手教TensorFlow(附代码)
深度学习世界
15+阅读 · 2017年10月17日
手把手教你由TensorFlow上手PyTorch(附代码)
数据派THU
5+阅读 · 2017年10月1日
教程 | 如何从TensorFlow转入PyTorch
机器之心
7+阅读 · 2017年9月30日
Top
微信扫码咨询专知VIP会员