【问题标题】:How to specify sample dependent kernels/filters in Conv2d?如何在 Conv2d 中指定依赖于样本的内核/过滤器?
【发布时间】:2020-12-31 23:05:06
【问题描述】:

我正在尝试实现一个卷积自动编码器,其中一些卷积过滤器依赖于输入内容。例如,在一个简单的玩具示例中,了解 MNIST 的数字标签可以进一步帮助在自动编码器设置中进行重建。

更一般的想法是,可能存在一些有用的相关辅助信息(无论是类标签还是其他一些信息)。虽然有多种方法可以使用此标签/辅助信息,但我将通过创建一个单独的卷积过滤器来实现。假设该模型有 15 个典型的卷积滤波器,我想添加一个对应于 MNIST 数字的额外卷积滤波器,可以将其视为 3x3 内核形式的数字嵌入。我们将使用该数字作为网络的附加输入,然后为每个数字学习不同的内核/过滤器嵌入。

但是,我在实现依赖于输入的卷积过滤器/内核时遇到了困难。我没有使用tf.keras.layers.Conv2D 层,因为它接受了要使用的过滤器的数量,而不是实际的过滤器参数来使这个输入依赖。

# load and preprocess data
num_classes = 10    
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train, x_test = np.float32(x_train)/255, np.float32(x_test)/255
x_train, x_test = np.expand_dims(x_train, axis=-1), np.expand_dims(x_test, axis=-1)
y_train = keras.utils.to_categorical(y_train, num_classes=num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes=num_classes)

num_filters = 15
input_img = layers.Input(shape=(28,28,1))
conv_0 = keras.layers.Conv2D(num_filters, (3,3), strides=2, padding='same', activation='relu')(input_img) 

# embed the target as a 3x3 kernel/filter -> this should map to a distinct embedding for 
# each target 
target = layers.Input(shape=(10,))  
target_encoded = layers.Dense(9, activation='relu')(target) 
target_encoded = layers.Reshape((3,3,1,1))(target_encoded) 

# Using tf.nn.conv2d so that I can specify kernel
# Kernel needs to be a 4D tensor of dimensions (filter_height, filter_width, input_channels, output_channels) 
# which in this case is (3,3,1,1)
# However it is currently (None,3,3,1,1) because the first dimension is batch size so this doesn't work
target_conv = tf.nn.conv2d(input_img, target_encoded, strides=[1, 1, 1, 1], padding='SAME')

我目前正在使用tf.nn.conv2d,它将内核作为格式的输入(filter_height、filter_width、input_channels、output_channels)。但是,这不起作用,因为数据是分批输入的。因此,批次中的每个样本都有一个标签,因此有一个相应的内核,因此内核的形状 (None, 3, 3, 1, 1) 与预期格式不兼容。这在上面的代码块中进行了说明(不起作用)。什么是潜在的解决方法?有没有更简单的方法来实现这个依赖于输入的 conv2d 过滤器的概念?

【问题讨论】:

  • 您将如何在网络上执行推理?听起来您需要输入包含真实数字才能使网络正常工作。理想结构的问题在于,将真实标签作为输入和输出,优化的 CNN 将学习身份函数f(x)=x。也就是说,您的网络将学会仅考虑输入标签,将所有其他像素乘以 0,将输入标签乘以 1。这样,您的 CNN 根本不会学习。
  • @ibarrond 这将是在自动编码器或变分自动编码器的上下文中,而不是在我们试图预测标签的分类中。因此网络获取图像和一些辅助信息(可能是标签)并输出重建图像(或在 vaes 上下文中生成的图像)​​。目标是看看知道标签或其他一些信息是否有助于重建/生成。

标签: tensorflow keras neural-network conv-neural-network convolution


【解决方案1】:

使用 SWAPPABLE 内核制作 Conv2D!

您需要制作自己的 Conv2D,将要处理的图像和要使用的内核作为输入。

# Define our new Convolution
class DynamicConv2D(tf.keras.layers.Layer):
    def __init__(self, padding='SAME'):
        super(DynamicConv2D, self).__init__()
        self.padding = padding
    def call(self, input, kernel):
        return tf.nn.conv2d(input=input, filters=kernel, 
                            strides=(1,1), padding=self.padding)

让我们测试一下

dc2d = DynamicConv2D(padding='VALID')
input_tensor = np.ones([1,4,4,3],dtype=np.float32)
kernel_tensor = np.ones([2,2,3,1],dtype=np.float32)
dc2d(input_tensor, kernel_tensor)

返回

array([[[[12.], [12.], [12.]],
        [[12.], [12.], [12.]],
        [[12.], [12.], [12.]]]])

看起来效果很好......但是有一个巨大的问题

KERAS 的大问题 - 默认批量处理

是的,这就是交易:tensorflow keras 真的真的真的设置了所有正在设置的东西,所以第一个维度是批次。但是,如果您在上面查看,我们必须为整个批次指定 ONE KERNEL。我们不能传入一批 kernel_tensorS,只能传入一个。

有一个解决办法!

让我们从 RNN 训练方案中借用一些东西,具体来说,我们将通过注意每批发送的内容来解决这个问题。更具体地说,对于一个批次,我们将确保所有输入图像使用相同的 kernel_tensor。您必须弄清楚如何使用数据管道有效地做到这一点,但这里有一个示例可以帮助您进行操作。

工作代码

(我们将重写动态 conv2d 以便它获取一个类别并存储其 每个类别都有自己的内核)

# Define our new Convolution
class DynamicConv2D(tf.keras.layers.Layer):

    def __init__(self, padding='SAME', input_dim=10, kernel_shape=[3,3,1,8]):
        super(DynamicConv2D, self).__init__()
        self.padding = padding
        self.input_dim = input_dim
        self.kernel_shape = kernel_shape
        self.kernel_size = kernel_shape[0]*kernel_shape[1]*kernel_shape[2]*kernel_shape[3] # = 3*3*1*8
        self.category_to_kernel = tf.keras.layers.Embedding(self.input_dim,self.kernel_size)

    def call(self, input, categories):
        just_first_category = tf.slice(categories,(0,0),(1,1))
        flat_kernel = self.category_to_kernel(just_first_category)
        kernel = tf.reshape(flat_kernel,self.kernel_shape)
        return tf.nn.conv2d(input=input, filters=kernel, strides=(1,1), padding=self.padding)

这个类默认做一个 3x3 的卷积,从前一层读入 1 个过滤器并输出 8 个

# Example output
dc2d = DynamicConv2D(padding='VALID')
image_data = np.ones([4,10,10,1],dtype=np.float32)

# prove that you can send in a different category and get different results
print( dc2d(image_data, [[3]]*4).numpy()[0,0,0,:3] )
print( dc2d(image_data, [[4]]*4).numpy()[0,0,0,:3] )

--------

[ 0.014 -0.002  0.108]
[ 0.021  0.014 -0.034]

用它来制作一个 tf.Keras 模型

# model input
image_input = tf.keras.Input(shape=(28,28,1), dtype=tf.float32)
category_input = tf.keras.Input(shape=(1,), dtype=tf.int32)

# do covolution
dynamic_conv2d = DynamicConv2D(padding='VALID')(image_input, category_input)

# make the model
model = tf.keras.Model(inputs=[image_input, category_input], outputs=dynamic_conv2d)

我们可以像这样使用模型

# use the model
input_as_tensor = tf.constant(image_data,dtype=tf.float32)
category_as_tensor = tf.constant([[4]]*4,dtype=tf.int32)
result = model.predict(x=(input_as_tensor, category_as_tensor))

print('The output shape is',result.shape)
print('The first 3 values of the first output image are', result[0,0,0,:3])

---------

The output shape is (4, 8, 8, 8)
The first 3 values of the first output image are [-0.028 -0.009  0.015]

【讨论】:

    猜你喜欢
    • 2018-01-16
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多