问题描述
我正在尝试实现一个卷积自动编码器,其中一些卷积滤波器与输入内容有关。例如,在一个简单的玩具示例中,了解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,1)
# However it is currently (None,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],padding='SAME')
我当前正在使用tf.nn.Conv2d
,它将内核作为输入格式(filter_height,filter_width,input_channels,output_channels)。但是,这不能按原样工作,因为数据是分批提供的。因此,批次中的每个样品都有一个标签,因此也有一个相应的内核,因此内核的形状(无,3、3、1、1)与预期格式不兼容。上面的代码块对此进行了说明(不起作用)。什么是可能的解决方法?有没有更简单的方法来实现这种依赖于输入的conv2d过滤器的概念?
解决方法
使用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,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.]]]])
看起来不错,但是有个大问题
与KERAS有关的问题-默认情况下分批
是的,这就是问题所在:tensorflow keras确实在所有设置的东西上都设置了,因此第一个维度是批处理。但是,如果您在上方查看,则必须为整个批次指定一个内核。我们不能传入一批kernel_tensor S ,而只能传入一个。
周围有事!
让我们从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,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,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,padding=self.padding)
默认情况下,此类进行3x3卷积,从上一层读取1个滤镜并输出8
# Example output
dc2d = DynamicConv2D(padding='VALID')
image_data = np.ones([4,10,dtype=np.float32)
# prove that you can send in a different category and get different results
print( dc2d(image_data,[[3]]*4).numpy()[0,:3] )
print( dc2d(image_data,[[4]]*4).numpy()[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,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,:3])
---------
The output shape is (4,8,8)
The first 3 values of the first output image are [-0.028 -0.009 0.015]