【问题标题】:Self-Attention GAN in KerasKeras 中的自注意力 GAN
【发布时间】:2018-06-12 14:43:05
【问题描述】:

我目前正在考虑在 keras 中实现 Self-Attention GAN。 我想实现的方式如下:

def Attention(X, channels):
    def hw_flatten(x):
        return np.reshape(x, (x.shape[0], -1, x.shape[-1]))

    f = Conv2D(channels//8, kernel_size=1, strides=1, padding='same')(X)  # [bs, h, w, c']
    g = Conv2D(channels//8, kernel_size=1, strides=1, padding='same')(X)  # [bs, h, w, c']
    h = Conv2D(channels, kernel_size=1, strides=1, padding='same')(X)  # [bs, h, w, c]

    # N = h * w
    flatten_g = hw_flatten(g)
    flatten_f = hw_flatten(f)
    s = np.matmul(flatten_g, flatten_f.reshape((flatten_f.shape[0], flatten_f.shape[-1], -1)))  # [bs, N, N]

    beta = softmax(s, axis=-1)  # attention map

    flatten_h = hw_flatten(h)   # [bs, N, C]
    o = np.matmul(beta, flatten_h)  # [bs, N, C]
    gamma = 0

    o = np.reshape(o, X.shape)  # [bs, h, w, C]
    y = gamma * o + X

    return y

但我不知道如何添加可训练的标量伽马,如本文所述:SAGAN

我也希望有人能提供一些关于如何初始化可训练 keras 标量的想法。


编辑:

我现在的实现是:

class Attention(Layer):
    def __init__(self, ch, **kwargs):
        super(Attention, self).__init__(**kwargs)
        self.channels = ch
        self.filters_f_g = self.channels // 8
        self.filters_h = self.channels

    def build(self, input_shape):
        kernel_shape_f_g = (1, 1) + (self.channels, self.filters_f_g)
        print(kernel_shape_f_g)
        kernel_shape_h = (1, 1) + (self.channels, self.filters_h)

        # Create a trainable weight variable for this layer:
        self.gamma = self.add_weight(name='gamma', shape=[1], initializer='zeros', trainable=True)
        self.kernel_f = self.add_weight(shape=kernel_shape_f_g,
                                        initializer='glorot_uniform',
                                        name='kernel_f')
        self.kernel_g = self.add_weight(shape=kernel_shape_f_g,
                                        initializer='glorot_uniform',
                                        name='kernel_g')
        self.kernel_h = self.add_weight(shape=kernel_shape_h,
                                        initializer='glorot_uniform',
                                        name='kernel_h')
        self.bias_f = self.add_weight(shape=(self.filters_f_g,),
                                      initializer='zeros',
                                      name='bias_F')
        self.bias_g = self.add_weight(shape=(self.filters_f_g,),
                                      initializer='zeros',
                                      name='bias_g')
        self.bias_h = self.add_weight(shape=(self.filters_h,),
                                      initializer='zeros',
                                      name='bias_h')
        super(Attention, self).build(input_shape)
        # Set input spec.
        self.input_spec = InputSpec(ndim=4,
                                    axes={3: input_shape[-1]})
        self.built = True


    def call(self, x):
        def hw_flatten(x):
            return K.reshape(x, shape=[K.shape(x)[0], K.shape(x)[1]*K.shape(x)[2], K.shape(x)[-1]])

        f = K.conv2d(x,
                     kernel=self.kernel_f,
                     strides=(1, 1), padding='same')  # [bs, h, w, c']
        f = K.bias_add(f, self.bias_f)
        g = K.conv2d(x,
                     kernel=self.kernel_g,
                     strides=(1, 1), padding='same')  # [bs, h, w, c']
        g = K.bias_add(g, self.bias_g)
        h = K.conv2d(x,
                     kernel=self.kernel_h,
                     strides=(1, 1), padding='same')  # [bs, h, w, c]
        h = K.bias_add(h, self.bias_h)

        s = tf.matmul(hw_flatten(g), hw_flatten(f), transpose_b=True)  # # [bs, N, N]

        beta = K.softmax(s, axis=-1)  # attention map

        o = K.batch_dot(beta, hw_flatten(h))  # [bs, N, C]

        o = K.reshape(o, shape=K.shape(x))  # [bs, h, w, C]
        x = self.gamma * o + x

        return x

    def compute_output_shape(self, input_shape):
        return input_shape

【问题讨论】:

  • 你能分享你的完整代码吗?我尝试使用您的实现,但失败了,即使我找到了spectral_norm和hw_flatten的有效实现。
  • @maniac 嗨,抱歉耽搁了。我已将我的代码添加到编辑部分。它有效。

标签: tensorflow keras conv-neural-network attention-model generative-adversarial-network


【解决方案1】:

你对original code的修改有几个问题:

  • 您不能在 Keras/TF 图的中间使用 numpy 操作。首先因为numpy 将尝试直接操作,而输入张量实际上只会在图形运行时被评估/接收它们的值。其次是因为 Keras/TF 无法通过非 Keras/TF 操作进行反向传播。

    您应该将原来的 tensorflow 操作替换为 keraskeras.backend 操作(例如,tf.matmul() 替换为 keras.backend.batch_dot()tf.nn.doftmax() 替换为 keras.backend.softmax() 等)

  • 您正在混合 Keras Layers(例如 Conv2D)和 Keras 操作(例如 np/keras.backend.reshape)。 Keras 操作应封装在Lambda 层中,以便与其他操作一起使用。

由于此自定义层具有可训练参数 (gamma),因此您需要 write your own custom layer,例如:

from keras import backend as K
from keras.engine.topology import Layer

class AttentionLayer(Layer):

    def __init__(self, output_dim, **kwargs):
        self.output_dim = output_dim
        super(AttentionLayer, self).__init__(**kwargs)

    def build(self, input_shape):
        # Create a trainable weight variable for this layer:
        self.gamma = self.add_weight(name='gamma', shape=[1], initializer='uniform', trainable=True)

        super(AttentionLayer, self).build(input_shape)

    def call(self, x):
        channels = K.int_shape(x)[-1]

        x = activation(x, channels) # use TF implementation, or reimplement with Keras operations
        return x

    def compute_output_shape(self, input_shape):
        return input_shape

【讨论】:

  • 您好,感谢您的指导。我在张量流方面太糟糕了。我更新了实现注意力块的代码,希望你看看?。
  • 我这里其实有两个问题: 1.我可以像我一样在调用函数中使用Conv2d层吗? 2. 可以只在调用函数中使用tensorflow代码吗?会被纳入 keras 实现吗?
  • 1. 不;正如我的回答中所解释的,您不能在 Keras 中混合使用 Layers 和操作。您应该使用keras.backend.conv2d 而不是keras.layers.Conv2D2. 是的,如果你有 TF 作为后端,你可以使用 Tensorflow 可微分运算。
  • 可以使用 y = self.gamma * x + x 吗?或者说我们必须使用后端函数,因为它不运行代码而实际上只声明
  • 嗯,是的,应该没问题。代码对我来说看起来不错。 :) 有效吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
  • 1970-01-01
  • 2016-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-15
相关资源
最近更新 更多