我正在尝试实现在" On Complex value Convolutional Neural networks "中提出的zReLU。选自《尼赞·古曼》(2016)。
如果实部和虚部都为正,则该激活函数将输出作为输入。我想有几种方法来实现它,但它们都使用tf.keras.backend.switch
,这只是else if
语句的一种方式。这里有一个例子。
def zrelu(z: Tensor) -> Tensor:
angle = tf.math.angle(z)
return tf.keras.backend.switch(0 <= angle,
tf.keras.backend.switch(angle <= pi / 2,
z,
tf.cast(0., dtype=z.dtype)),
tf.cast(0., dtype=z.dtype))
这给了我想要的输出,当用数据测试激活函数时,它可以正常工作,但是,当我在这样的模型上使用它时,我有一个问题:
model = tf.keras.Sequential([
cvnn.layers.ComplexInput((4)),
cvnn.layers.ComplexDense(1, activation=tf.keras.layers.Activation(zrelu)),
cvnn.layers.ComplexDense(1, activation='linear')
])
在初始化行给出TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
:return tf.math.sqrt(6. / (fan_in + fan_out))
。我相信,因为有一个开关,tf忽略了激活函数输出的大小,因此输出None
形状,然后与下一层冲突。这很奇怪,因为输出形状实际上是由tf.keras.layers.Activation
强制的,因为有函数compute_output_shape
,根据我的理解,它告诉tf输出将具有该形状。
我的问题可以通过以下两个选项中的任何一个来解决:
- 了解为什么
compute_output_shape
和如何告诉tf不要担心 - 实现激活函数的另一种方法,其中tensorflow可以理解输出形状。
我找到了一个解决这个问题的选项:
def zrelu(z: Tensor, epsilon=1e-7) -> Tensor:
imag_relu = tf.nn.relu(tf.math.imag(z))
real_relu = tf.nn.relu(tf.math.real(z))
ret_real = imag_relu*real_relu / (imag_relu + epsilon)
ret_imag = imag_relu*real_relu / (real_relu + epsilon)
ret_val = tf.complex(ret_real, ret_imag)
return ret_val
这个工作,但必须使用一个epsilon值,我不喜欢这个想法,因为它改变了一点结果。我仍然愿意接受更好的选择(如果更好,我会把它们标记为新的解决方案)。