Tensorflow 如何在Conv2d中指定依赖于样本的内核/过滤器?

Tensorflow 如何在Conv2d中指定依赖于样本的内核/过滤器?,tensorflow,keras,neural-network,conv-neural-network,convolution,Tensorflow,Keras,Neural Network,Conv Neural Network,Convolution,我正在尝试实现一个卷积自动编码器,其中一些卷积滤波器依赖于输入内容。例如,在一个简单的玩具示例中,了解MNIST的数字标签可以进一步帮助自动编码器设置中的重建 更一般的想法是,可能有一些相关的、辅助的信息(无论这些信息是类标签还是其他信息)可用于合并。虽然有多种方法可以使用这个标签/辅助信息,但我将通过创建一个单独的卷积滤波器来实现。假设这个模型有15个典型的卷积滤波器,我想添加一个额外的卷积滤波器,它对应于MNIST数字,可以被认为是以3x3内核的形式嵌入数字。我们将使用该数字作为网络的额外输

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

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

然而,我很难实现依赖于输入的卷积滤波器/内核。我没有使用
tf.keras.layers.Conv2D
layer,因为这需要使用的过滤器的数量,而不是使此输入依赖的实际过滤器参数

# 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\u height、filter\u width、input\u channels、output\u channels)的输入。但是,由于数据是成批输入的,所以这不能按原样工作。因此,批次中的每个样本都有一个标签,因此有一个对应的内核,因此内核的形状(无、3、3、1、1)与预期格式不兼容。上面的代码块说明了这一点(这不起作用)。潜在的解决办法是什么?有没有一种更简单的方法来实现依赖输入的conv2d过滤器的概念?

使用可交换内核制作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真的对所有正在设置的东西都有设置,所以第一个维度是批次。但是如果您查看上面的内容,我们必须为整个批指定一个内核。我们不能传入一批内核张量,只能传入一个

有一个工作在附近! 让我们从RNN培训计划中借用一些东西,具体来说,我们将通过谨慎对待每批发送的内容来解决这个问题。更具体地说,对于批处理,我们将确保所有输入图像使用相同的内核张量。您必须弄清楚如何使用数据管道高效地实现这一点,但下面是一个示例

工作代码 (我们将重写dynamic conv2d,以便它获取一个类别并存储其 每个类别拥有内核)

默认情况下,该类执行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模型 我们可以这样使用模型

# 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]
使用可交换内核制作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真的对所有正在设置的东西都有设置,所以第一个维度是批次。但是如果您查看上面的内容,我们必须为整个批指定一个内核。我们不能传入一批内核张量,只能传入一个

有一个工作在附近! 让我们从RNN培训计划中借用一些东西,具体来说,我们将通过谨慎对待每批发送的内容来解决这个问题。更具体地说,对于批处理,我们将确保所有输入图像使用相同的内核张量。您必须弄清楚如何使用数据管道高效地实现这一点,但下面是一个示例

工作代码 (我们将重写dynamic conv2d,以便它获取一个类别并存储其 每个类别拥有内核)

默认情况下,该类执行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模型 我们可以这样使用模型

# 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]

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