【问题标题】:Advice on how to create a custom tf.keras optimizer (optimizer_v2)关于如何创建自定义 tf.keras 优化器 (optimizer_v2) 的建议
【发布时间】:2021-10-10 16:43:01
【问题描述】:

我想为tf.keras(不是独立的keras)制作一个累积的SGD优化器。我在 pypi 上找到了几个独立的keras 累积 SGD 优化器的实现,包括这个one。尽管如此,我正在使用一个使用tf.keras 的项目。正如我所见,将它们混合在一起并不是一个好主意。

问题在于实现这个自定义优化器的文档并不是很直接。基类(我应该继承自)是Optimizer_v2.py,它在评论部分包含有关任务的一些信息。

需要重写的方法是: - resource_apply_dense (更新变量给定梯度张量是密集的) -resource_apply_sparse(给定梯度张量的更新变量是稀疏的) - create_slots(如果您的优化器算法需要额外的变量) - get_config(优化器的序列化,包含所有超参数)

当然这些只有get_config() 实际存在于基类中。 resource_apply_dense 实际上是 _resource_apply_denseresource_apply_sparse_resource_apply_sparsecreate_slots 甚至在基类中都不存在。在gradient_decent.py 中的SGD 子类中,create_slots 也作为_create_slots 存在。

无论如何,显然文档没有更新(在 git 中也有一个关于此的问题,但我不记得指出与文档缺乏一致性的链接)但这使得整个过程变得困难。例如,在SGD 中,我必须重写_resource_apply_dense() 方法,但我无法理解梯度的计算位置和更新位置。

实际代码如下:

def _resource_apply_dense(self, grad, var, apply_state=None):
        var_device, var_dtype = var.device, var.dtype.base_dtype
        coefficients = ((apply_state or {}).get((var_device, var_dtype))
                        or self._fallback_apply_state(var_device, var_dtype))

        if self._momentum:
          momentum_var = self.get_slot(var, "momentum")
          return training_ops.resource_apply_keras_momentum(
              var.handle,
            momentum_var.handle,
            coefficients["lr_t"],
            grad,
            coefficients["momentum"],
            use_locking=self._use_locking,
            use_nesterov=self.nesterov)
        else:
            return training_ops.resource_apply_gradient_descent(
                var.handle, coefficients["lr_t"], grad, use_locking=self._use_locking)

显然依赖training_ops.resource_apply_keras_momentumtraining_ops.resource_apply_gradient_descent 来完成实际工作。如何从上述代码中拆分OptimizerV2minimize() 方法中提到的两部分? 2部分是: _compute_gradients()apply_gradients()

在这个 cmets 中有很多令人困惑的部分,例如在基类中:

许多优化器子类,例如AdamAdagrad 分配和 管理与要训练的变量相关的其他变量。 这些称为插槽。插槽有名称,您可以询问 它使用的插槽名称的优化器。

尽管如果我声明一个 Adam 优化器并询问插槽名称,我会得到一个空列表 (?)。

optimizer = Adam(lr=1e-3)    
optimizer.get_slot_names()

[]

另一个令人困惑的问题是私有方法的使用,不清楚它们何时被调用以及它们的目的是什么。例如_prepare_local() 包含在SGD 中并包含一行:

apply_state[(var_device, var_dtype)]["momentum"] = array_ops.identity(self._get_hyper("momentum", var_dtype))

无论如何,这里的问题是我不知道要遵循哪种方法来创建自定义 tf.keras 优化器。 cmets 中包含的指令似乎与实际实现的子类相矛盾,后者似乎也将肮脏的工作分配给实际的 C++ 函数,但不清楚这是如何完成的或如何(在我的情况下)分离动作(如渐变计算和应用)。那么,是否有人可以就如何进行以及完成这项(相对)简单的任务所遵循的步骤提供任何建议?

顺便说一句,我使用的是 tf 1.15(所以链接来自那里)。

【问题讨论】:

    标签: python tensorflow keras


    【解决方案1】:

    优化器参考:DiffGrad(有点像 Adam)
    https://github.com/evanatyourservice/diffGrad-tf/blob/master/diffgrad.py
    它基于一篇名为 DiffGrad 的论文,它们有很好的解释,一般都很好读。

    首先是好问题,其次 TensorFlow 文档可以做得更好。各种问题的答案不分先后:

    参考 Adam 的空槽列表,据我所知,您必须在模型上运行一次 model.fit 才能对其进行初始化。请记住在查找保存和加载优化器状态时阅读它(检查它是否适用于 model.compile)。

    至于 _prepare_local,该行根据您在创建时设置的超参数创建动量变量。我想它可以让优化器尝试更新的所有权重都可以访问,为什么他们使用身份是深度 TensorFlow 图的东西。

    他们使用 _prepare_local 的原因通常是创建在所有正在更新的权重中通用的变量,例如衰减或学习率或时间步长等。对于每个迭代,这些变量在优化器的 var_list 中跟踪的所有变量中都是通用的。

    与上面的 _prepare_local 不同,slots 是优化器跟踪的每个权重的单独变量,因此您可能有时刻或历史或累积总和。与特定个人体重有关的任何事情。

    梯度计算和应用:如果我理解正确,计算梯度会导致损失进行反向传播和自动微分,并为您提供每个权重的“梯度”。当你去应用它时,优化器开始使用它的槽和变量。 finally 优化器使用计算的梯度作为输入进行更新。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多