【问题标题】:Tensorflow - does autodiff relives us from the back-prop implementation?Tensorflow - autodiff 是否让我们从 back-prop 实现中解脱出来?
【发布时间】:2021-04-06 07:35:09
【问题描述】:

问题

在使用 Tensorflow 时,例如实现自定义神经网络层,实现反向传播的标准做法是什么?我们不需要研究自微分公式吗?

背景

使用 numpy,在创建层时,例如matmul,反向传播梯度首先被解析推导并相应编码。

def forward(self, X):
    self._X = X
    np.matmul(self.X, self.W.T, out=self._Y)
    return self.Y

def backward(self, dY):
    """dY = dL/dY is a jacobian where L is loss and Y is matmul output"""
    self._dY = dY
    return np.matmul(self.dY, self.W, out=self._dX)

在 TensorFlow 中,autodiff 似乎负责雅可比计算。这是否意味着我们不必手动推导出梯度公式,而是让 Tensorflow 磁带处理它?

计算梯度

为了自动区分,TensorFlow 需要记住在前向传递过程中以什么顺序发生的操作。然后,在反向传递期间,TensorFlow 会以相反的顺序遍历这个操作列表来计算梯度。

【问题讨论】:

    标签: python tensorflow deep-learning neural-network backpropagation


    【解决方案1】:

    正确,您只需要定义正向传递,Tensorflow 就会生成一个适当的反向传递。来自tf2 autodiff

    TensorFlow 提供 tf.GradientTape API 用于自动 分化;也就是说,计算计算的梯度 关于一些输入,通常是 tf.Variables。 TensorFlow“记录” 在 tf.GradientTape 的上下文中执行的相关操作 到“磁带”上。 TensorFlow 然后使用该磁带来计算梯度 使用反向模式微分的“记录”计算。

    为此,Tensorflow 被赋予前向传递(或损失)和一组tf.Variable 变量来计算导数。此过程仅适用于 Tensorflow 本身定义的一组特定操作。为了创建一个自定义的 NN 层,您需要使用这些操作定义它的前向通道(它们都是 TF 的一部分或由某些转换器转换为它)。*

    由于您似乎有 numpy 背景,您可以使用 numpy 定义您的自定义前向传递,然后使用 tf_numpy API 将其转换为 Tensorflow。您也可以使用tf.numpy_function。之后,TF 会为你创建反向传播。

    (*) 请注意,控制语句等某些操作本身是不可微的,因此它们对于基于梯度的优化器是不可见的。关于这些有一些警告。

    【讨论】:

      【解决方案2】:

      基本上,Tensorflow 是一个基于数据流和可微编程 的符号数学库。我们不必手动处理自动微分公式。所有这些数学运算都将在后台自动完成。您从官方文档中正确引用了有关梯度计算的内容。但是,如果您想知道如何使用 numpy 手动完成,我建议您查看 Neural Networks and Deep Learning 的精彩课程,尤其是第 4 周,或其他来源 here


      仅供参考,在TF 2 中,我们可以通过覆盖tf.keras.Model 类的train_step 从头开始​​进行自定义训练,并且我们可以使用tf.GradientTape API 进行自动区分;也就是说,计算一些输入的计算梯度。同一官方页面包含有关此的更多信息。此外,必须在tf.GradientTape 上看到这篇写得很好的文章。例如,使用此 API,我们可以轻松计算梯度,如下所示:

      import tensorflow as tf 
      
      # some input 
      x = tf.Variable(3.0, trainable=True)
      
      with tf.GradientTape() as tape:
          # some output 
          y = x**3 + x**2 + x + 5
      
      # compute gradient of y wrt x 
      print(tape.gradient(y, x).numpy()) 
      # 34
      

      此外,我们可以计算更高阶的导数,例如

      x = tf.Variable(3.0, trainable=True)
      
      with tf.GradientTape() as tape1:
      
          with tf.GradientTape() as tape2:
              y = x**3 + x**2 + x + 5
          # first derivative 
          order_1 = tape2.gradient(y, x)
      
      # second derivative 
      order_2 = tape1.gradient(order_1, x)
      
      print(order_2.numpy()) 
      # 20.0
      

      现在,在tf. keras 中的自定义模型训练中,我们首先进行forward 传递并计算loss,然后计算gradients 模型的可训练变量相对于loss。稍后,我们根据这些gradients 更新模型的权重。下面是它的代码 sn-p,这里是端到端的详细信息。 Writing a training loop from scratch.

      # Open a GradientTape to record the operations run
      # during the forward pass, which enables auto-differentiation.
      with tf.GradientTape() as tape:
      
          # Run the forward pass of the layer.
          # The operations that the layer applies
          # to its inputs are going to be recorded
          # on the GradientTape.
          logits = model(x_batch_train, training=True)  # Logits for this minibatch
      
          # Compute the loss value for this minibatch.
          loss_value = loss_fn(y_batch_train, logits)
      
      # Use the gradient tape to automatically retrieve
      # the gradients of the trainable variables with respect to the loss.
      grads = tape.gradient(loss_value, model.trainable_weights)
      
      # Run one step of gradient descent by updating
      # the value of the variables to minimize the loss.
      optimizer.apply_gradients(zip(grads, model.trainable_weights))
      

      【讨论】:

      • 感谢您的回答和示例。非常感激。在另一个答案中,可能需要考虑并想知道 autodiff 是否是保证工作的灵丹妙药。浮动精度 32 可能不够,梯度计算可能会导致 Nan 或 inf,添加 epsilon 以避免 div 0 可能会剪切输出值,梯度可能为零,等等可能需要手动/人工设计自动微分。如果您知道这些注意事项,请告知。
      • 我可能完全错了,但是当查看 GRU 实现前向路径时,通过 sigmoid 的切换是逐元素乘法,以控制当前存储单元(隐藏状态)的哪些特征进入下一个以及哪个新的记忆单元经过。 autodiff 还可以处理这种选择性过滤和添加的反向传播(GRU 中的最后一个求和部分)吗?
      猜你喜欢
      • 1970-01-01
      • 2019-12-29
      • 2021-04-08
      • 2021-04-23
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 2018-09-10
      • 1970-01-01
      相关资源
      最近更新 更多