【问题标题】:How to create a custom layer in Keras with 'stateful' variables/tensors?如何使用“有状态”变量/张量在 Keras 中创建自定义层?
【发布时间】:2020-06-20 16:25:48
【问题描述】:

我想请您帮忙创建我的自定义图层。 我想做的其实很简单:生成一个带有“有状态”变量的输出层,即每批更新其值的张量。

为了让一切更清楚,这里是我想做的一个sn-p:

def call(self, inputs)

   c = self.constant
   m = self.extra_constant

   update = inputs*m + c 
   X_new = self.X_old + update 

   outputs = X_new

   self.X_old = X_new   

   return outputs

这里的想法很简单:

  • X_olddef__ init__(self, ...) 中初始化为 0
  • update 是根据层的输入计算得出的
  • 计算层的输出(即X_new
  • X_old 的值设置为等于 X_new,这样在下一批中,X_old 不再等于 0,而是等于上一批中的 X_new

我发现K.update 可以完成这项工作,如示例所示:

 X_new = K.update(self.X_old, self.X_old + update)

这里的问题是,如果我尝试将层的输出定义为:

outputs = X_new

return outputs

我在尝试 model.fit() 时会收到以下错误:

ValueError: An operation has `None` for gradient. Please make sure that all of your ops have 
gradient defined (i.e. are differentiable). Common ops without gradient: K.argmax, K.round, K.eval.

即使我强加了layer.trainable = False 并且我没有为图层定义任何偏差或权重,我仍然会遇到此错误。另一方面,如果我只做self.X_old = X_newX_old 的值不会更新。

你们有解决方案来实现这个吗?我相信这不应该那么难,因为有状态的 RNN 也有“类似”的功能。

提前感谢您的帮助!

【问题讨论】:

    标签: tensorflow keras recurrent-neural-network layer stateful


    【解决方案1】:

    定义自定义层有时会变得令人困惑。您覆盖的某些方法将被调用一次,但它给您的印象是,就像许多其他 OO 库/框架一样,它们将被调用多次。

    我的意思是:当您定义一个层并在模型中使用它时,您为覆盖call 方法而编写的python 代码不会在向前或向后传递中直接调用。相反,它只在您调用model.compile 时调用一次。它将 python 代码编译为计算图,张量将在其中流动的图是训练和预测期间的计算。

    这就是为什么如果你想通过添加print 语句来调试你的模型是行不通的;您需要使用tf.print 向图形添加打印命令。

    你想要的状态变量也是一样的情况。您需要调用 Keras 函数将该操作添加到图中,而不是简单地将 old + update 分配给 new

    请注意,张量是不可变的,因此您需要在 __init__ 方法中将状态定义为 tf.Variable

    所以我相信这段代码更像你正在寻找的:

    class CustomLayer(tf.keras.layers.Layer):
      def __init__(self, **kwargs):
        super(CustomLayer, self).__init__(**kwargs)
        self.state = tf.Variable(tf.zeros((3,3), 'float32'))
        self.constant = tf.constant([[1,1,1],[1,0,-1],[-1,0,1]], 'float32')
        self.extra_constant = tf.constant([[1,1,1],[1,0,-1],[-1,0,1]], 'float32')
        self.trainable = False
    
      def call(self, X):
        m = self.constant    
        c = self.extra_constant
        outputs = self.state + tf.matmul(X, m) + c
        tf.keras.backend.update(self.state, tf.reduce_sum(outputs, axis=0))
    
        return outputs
    

    【讨论】:

    • 嗨,Mohammad,我尝试了您的建议,但实际上对我没有用。我不知道为什么,但是 tf.keras.backend.update() 不会更新 self.sate 的值,除非我将此更新分配给一个新变量,例如 X_tmp = tf.keras.backend.update(self.state, ...)然后我将网络的输出强加为等于 X_tmp(但随后我得到了 ValueError)。无论如何感谢您的回复!
    • 请在发布答案之前查看我为测试代码而制作的这个笔记本:colab.research.google.com/gist/MJafarMashhadi/… 它可以在不将 update 的返回值分配给其他任何东西的情况下工作
    • 我的错,它实际上看起来像是在工作。还有一个问题:如何在每个 epoch 结束时将 self.state 的值重置为 0?否则它的价值只会在训练期间不断增加。提前谢谢!
    • 那将是一个新问题。 AFAIK 有可以在 fit 函数上使用的训练回调,其中之一是 on_epoch_end。您可以在那里访问模型及其所有层,我尚未对其进行测试,但您可以在那里将其重置为零
    猜你喜欢
    • 2020-12-19
    • 2019-09-17
    • 2020-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多