如何在Keras中构建自定义层?

4

我正在构建一个支持复数的神经网络。目前正在研究复数激活函数。根据Benjio的一篇论文,这是一个不错的选择:

enter image description here

b是一个可训练的参数,因此我正在构建一个特殊的层来执行此激活。我对Keras不熟悉,已经卡住了。我创建了下面的代码,但在build函数中出现错误。我不知道发生了什么事情,只是尝试复制模板。请帮忙。

class modrelu(Layer):
    def __init__(self, **kwargs):
        super(modrelu, self).__init__(**kwargs)

    def build(self):
        self.b= K.variable(value=np.random.rand()-0.5, dtype='float64')
        super(modrelu, self).build()  # Be sure to call this at the end

    def call(self, x):
        assert isinstance(x, list)
        ip_r, ip_i = x
        comp= tf.complex(ip_r, ip_i ) 
        ABS= tf.math.abs(comp)
        ANG= tf.math.angle(comp)

        ABS= K.relu( self.b + ABS) 

        op_r=  ABS * K.sin(angle) #K.dot ??
        op_i= ABS * K.cos(angle)
        return [op_r, op_i]

    def compute_output_shape(self, input_shape):
        assert isinstance(input_shape, list)
        shape_a, shape_b = input_shape
        return [shape_a, shape_b]

对我的代码的评论: 在init中,我没有添加任何内容,因为它是一个激活层,在实例化时不需要输入。

在build方法中,我尝试添加b。不确定是否应该使用self.add_weight方法。理想情况下,我希望有与输入维度相同的b。

在call方法中,这个方法,我很确定我在做什么。这很容易,我只是实现了函数。

最后一个,compute_output_shape,我只是复制粘贴了模板。输出应该与输入相同,因为它只是一个激活层。

最后,这个错误值得一提,我知道它是无意义的。

TypeError                                 Traceback (most recent call last)
<ipython-input-5-3101a9226da5> in <module>
      1 a=K.variable(np.array([1,2]))
      2 b=K.variable(np.array([3,4]))
----> 3 act([a,b])

~\AppData\Local\conda\conda\envs\python36\lib\site-packages\keras\engine\base_layer.py in __call__(self, inputs, **kwargs)
    429                                          'You can build it manually via: '
    430                                          '`layer.build(batch_input_shape)`')
--> 431                 self.build(unpack_singleton(input_shapes))
    432                 self.built = True
    433 

TypeError: build() takes 1 positional argument but 2 were given

你能否附上带有回溯的错误信息? - Anakin
当然,我刚刚把它添加到问题中了。 - Alex Deft
当我定义时没有错误。当我实例化类时也没有错误。但是当我调用该层并给它一些输入张量时,我遇到了错误。请评论我如何出现这个错误。 - Alex Deft
我能看一下你是如何调用自定义层的吗? - Anakin
激活函数= modrelu(); a=K.variable(np.array([1,2])); b=K.variable(np.array([3,4])); 激活函数([a,b]); - Alex Deft
你需要一个 input_shapebuild 中传递参数。 - Anakin
2个回答

2

您的代码存在几个问题。

首先,我应该解决解释器报错的问题:

Original Answer翻译成"最初的回答"

TypeError: build() takes 1 positional argument but 2 were given
build 方法应该接受 input_shape 参数。因此,您应该将 build 方法声明为 build(self, input_shape)
第二个问题是 build 方法中变量的未定义形状。您应该明确声明变量的形状。在您的情况下,np.random.rand 数组应该具有 input_shape 形状。
另一个问题是您试图在 call 方法中返回 2 个结果 ([op_r, op_i])。我不是 Keras 的专家,但据我所知,不能这样做。每个 Keras 层应该只有一个输出。有关详细信息,请参见此处:https://github.com/keras-team/keras/issues/3061 但是,如果您使用 tensorflow 后端,则可以使用复数 (tf.complex) 返回复数的实部 (op_r) 和虚部 (op_i)。
以下是带有简单用法示例的 modrelu 层的工作实现。它是针对 TensorFlow 1.12.0 编写的,该版本附带了自己的 Keras API 实现,但我认为您可以轻松地将其用于原始 Keras。
import tensorflow as tf
from tensorflow.python.keras import backend as K
from tensorflow.python.keras.engine import Layer
import numpy as np


class modrelu(Layer):

    def __init__(self, **kwargs):
        super(modrelu, self).__init__(**kwargs)

    # provide input_shape argument in the build method
    def build(self, input_shape):
        # You should pass shape for your variable
        self.b= K.variable(value=np.random.rand(*input_shape)-0.5,
                           dtype='float32')
        super(modrelu, self).build(input_shape)  # Be sure to call this at the end

    def call(self, inputs, **kwargs):
        assert inputs.dtype == tf.complex64

        ip_r = tf.math.real(inputs)
        ip_i = tf.math.imag(inputs)

        comp = tf.complex(ip_r, ip_i )
        ABS = tf.math.abs(comp)
        ANG = tf.math.angle(comp)

        ABS = K.relu(self.b + ABS)

        op_r = ABS * K.sin(ANG) #K.dot ??
        op_i = ABS * K.cos(ANG)

        # return single tensor in the call method
        return tf.complex(op_r, op_i)


real = tf.constant([2.25, 3.25])
imag = tf.constant([4.75, 5.75])

x = tf.complex(real, imag)

y = modrelu()(x)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    print(sess.run(y))


最初的回答:

附言:我没有检查数学,所以你应该自己检查。


为什么不使用add_weight方法来定义b值呢?就像Keras定义偏差的方式一样。那和self.b=K.variable有什么区别呢? - Alex Deft
我认为你关于add_weight方法的看法是正确的。它应该比K.variable更好。我只是对原始代码进行了最小的更改,使其能够正常工作。 - NShiny
关于K.variable和add_weight之间的区别,我只有部分答案。如果您使用TensorFlow后端,则创建的每个变量在计算图中都有自己的名称。如果变量是由K.variable创建的,则它们的名称将类似于“variable_1”,“variable_2”等。如果使用add_weight创建变量,则它们的名称为“layer_name/variable_1”,“layer_name/variable_2”等。 - NShiny

1
你没有正确编写层,build函数需要一个input_shape参数,你可以使用它来初始化你的层的权重/参数。你可以在Keras源代码中看到一个例子。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接