自定义TensorFlow Keras优化器


30

假设我想编写一个符合tf.kerasAPI 的自定义优化器类(使用TensorFlow版本> = 2.0)。我对文档记录的执行方式与实现中的执行方式感到困惑。

有关tf.keras.optimizers.Optimizer 状态的文档,

  ### Write a customized optimizer.
  If you intend to create your own optimization algorithm, simply inherit from
  this class and override the following methods:

    - resource_apply_dense (update variable given gradient tensor is dense)
    - resource_apply_sparse (update variable given gradient tensor is sparse)
    - create_slots (if your optimizer algorithm requires additional variables)

但是,当前tf.keras.optimizers.Optimizer实现未定义resource_apply_dense方法,但确实定义了私有_resource_apply_dense方法stub。同样,没有resource_apply_sparsecreate_slots方法,但有一个_resource_apply_sparse方法存根和一个_create_slots方法调用

在官方tf.keras.optimizers.Optimizer亚类(使用tf.keras.optimizers.Adam作为一个例子),还有_resource_apply_dense_resource_apply_sparse_create_slots方法,并且不存在这样的方法没有前导下划线。

有稍微少官方相似领先的下划线方法tf.keras.optimizers.Optimizer的子类(例如,tfa.optimizers.MovingAverage从TensorFlow扩展中心:_resource_apply_dense_resource_apply_sparse_create_slots)。

对我而言,另一个令人困惑的点是,某些TensorFlow插件优化程序覆盖了该apply_gradients方法(例如tfa.optimizers.MovingAverage),而tf.keras.optimizers优化程序却没有。

而且,我注意到方法调用apply_gradientstf.keras.optimizers.Optimizer方法,但是基类没有方法。因此,似乎必须在优化器子类中定义一个方法,如果该子类未重写。_create_slotstf.keras.optimizers.Optimizer_create_slots_create_slotsapply_gradients


问题

子类化的正确方法是tf.keras.optimizers.Optimizer什么?特别,

  1. tf.keras.optimizers.Optimizer顶部列出的文档是否只是意味着要覆盖他们提到的方法的前导下划线版本(例如,_resource_apply_dense代替resource_apply_dense)?如果是这样,是否有关于这些私有方法的API保证不会在TensorFlow的未来版本中更改其行为?这些方法的签名是什么?
  2. apply_gradients除了这些_apply_resource_[dense|sparse]方法之外,何时还能覆盖?

编辑。在GitHub上公开的问题:#36449


1
这可能是作为文档问题报告给开发人员的内容。绝对可以肯定,这些要覆盖的方法应该在文档中包含下划线,但是无论如何,就像您所说的那样,没有关于其签名和确切用途的信息。也可能计划添加不带下划线(并形成文档)的方法名称(与一起使用get_config),但那时它们还不应出现在公共文档中
jdehesa

对于签名,您始终可以查看_resource_apply_dense或的声明_resource_apply_sparse,并查看其在已实现的优化器中的用法。我认为,虽然可能不是,但带有稳定性保证的公共API,我想说使用它们很安全。他们只是应该在这方面提供更好的指导。
jdehesa

我同意这是TensorFlow的文档问题。您是否在tf Github存储库中为此创建了一个问题?如果是这样,您可以在这里共享链接吗?
jpgard

Answers:


3

我已经在所有主要TF和Keras版本中实现了Keras AdamW-我邀请您检查optimizers_v2.py。几点:

  • 您应该继承OptimizerV2,实际上是您链接的内容。这是tf.keras优化程序的最新和最新基类
  • 您在(1)中是正确的-这是文档错误;这些方法是私有的,因为它们并不意味着用户可以直接使用。
  • apply_gradients(或任何其他方法)仅在默认值不能满足给定优化器所需的条件时才覆盖;在您的链接示例中,它只是原始文件的一线附加
  • “因此,_create_slots如果该子类没有重写,则似乎必须在优化器子类中定义一个方法。apply_gradients -两者是不相关的;这是巧合。

  • _resource_apply_dense和之间有什么区别_resource_apply_sparse

后者处理稀疏层-例如Embedding-和前者处理其他所有内容;例子

  • _create_slots()什么时候应该使用?

定义可训练的 tf.Variable s时;例如:重量的一阶和二阶矩(例如亚当)。它使用add_slot()

几乎每当不使用时_create_slots();就像设置类属性一样,但是要执行额外的预处理步骤以确保用法的正确性。所以Python int, floattf.Tensortf.Variable,及其他。(我应该在Keras AdamW中更多地使用它)。


注意:虽然我的链接优化器可以正常工作并且与原始优化器一样快,但是代码遵循最佳的TensorFlow实践,并且仍然可以更快。我不建议将其作为“理想参考”。例如,某些Python对象(例如int)应为张量;eta_t被定义为tf.Variable,但立即被tf.Tensorin _apply方法覆盖。不一定大不了,只是没有时间进行装修。


2
  1. 是的,这似乎是文档错误。前面的下划线名称是要覆盖的正确方法。与此相关的是非Keras Optimizer,所有这些都已定义,但未在基类中实现https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/training/optimizer.py
  def _create_slots(self, var_list):
    """Create all slots needed by the variables.
    Args:
      var_list: A list of `Variable` objects.
    """
    # No slots needed by default
    pass

  def _resource_apply_dense(self, grad, handle):
    """Add ops to apply dense gradients to the variable `handle`.
    Args:
      grad: a `Tensor` representing the gradient.
      handle: a `Tensor` of dtype `resource` which points to the variable
       to be updated.
    Returns:
      An `Operation` which updates the value of the variable.
    """
    raise NotImplementedError()

  def _resource_apply_sparse(self, grad, handle, indices):
    """Add ops to apply sparse gradients to the variable `handle`.
    Similar to `_apply_sparse`, the `indices` argument to this method has been
    de-duplicated. Optimizers which deal correctly with non-unique indices may
    instead override `_resource_apply_sparse_duplicate_indices` to avoid this
    overhead.
    Args:
      grad: a `Tensor` representing the gradient for the affected indices.
      handle: a `Tensor` of dtype `resource` which points to the variable
       to be updated.
      indices: a `Tensor` of integral type representing the indices for
       which the gradient is nonzero. Indices are unique.
    Returns:
      An `Operation` which updates the value of the variable.
    """
    raise NotImplementedError()
  1. 我不知道apply_dense。一方面,如果您重写它,代码中提到每个副本的DistributionStrategy可能是“危险的”
    # TODO(isaprykin): When using a DistributionStrategy, and when an
    # optimizer is created in each replica, it might be dangerous to
    # rely on some Optimizer methods.  When such methods are called on a
    # per-replica optimizer, an exception needs to be thrown.  We do
    # allow creation per-replica optimizers however, because the
    # compute_gradients()->apply_gradients() sequence is safe.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.