TensorFlow conv2d 内核的强制对称性
Force symmetry for a TensorFlow conv2d kernel
我想强制变量内的权重对称。我真的想要一个近似圆对称。但是,我可以想象行或列强制对称。
目标是通过减少自由变量的数量来减少训练时间。我知道我的问题需要一个对称数组,但我可能想同时包含对称变量和 "free" 变量。我现在正在使用 conv2d,所以我认为我需要继续使用它。
这里是一个函数,它创建一个关于其中心行上的反射对称的内核:
def SymmetricKernels(height,width,in_channels,out_channels,name=None):
half_kernels = tf.Variable(initial_value=tf.random_normal([(height+1)//2,width,in_channels,out_channels]))
half_kernels_reversed = tf.reverse(half_kernels[:(height//2),:,:,:],[0])
kernels = tf.concat([half_kernels,half_kernels_reversed],axis=0,name=name)
return kernels
用法示例:
w = SymmetricKernels(5,5,1,1)
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
w_ = sess.run(w)
w_[:,:,0,0]
# output:
# [[-1.299 -1.835 -1.188 0.093 -1.736]
# [-1.426 -2.087 0.434 0.223 -0.65 ]
# [-0.217 -0.802 -0.892 -0.229 1.383]
# [-1.426 -2.087 0.434 0.223 -0.65 ]
# [-1.299 -1.835 -1.188 0.093 -1.736]]
想法是使用 tf.Variable()
仅创建内核的上半部分变量 (half_kernels
),然后将对称内核作为上半部分及其反映版本的串联.
这个想法可以扩展到创建具有 left-right 和 up-down 对称性的内核。
您可以尝试的另一件事是通过两次卷积来束缚网络的手,重新使用内核但将其翻转以进行第二次卷积(未经测试的代码):
def symmetric_convolution(input_tensor, n_filters, size, name, dilations=[1,1,1,1]):
with tf.variable_scope("", reuse=tf.AUTO_REUSE):
kernel = tf.get_variable(shape=[*size, input_tensor.shape[-1], n_filters], name='conv_kernel_' + name, ...)
lr_flipped_kernel = tf.reverse(kernel, axis=[1], name='conv_kernel_flipped_lr_' + name)
conv_l = tf.nn.conv2d(input=input_tensor, filter=kernel, strides=[1, 1, 1, 1], padding='SAME', dilations=dilations)
conv_r = tf.nn.conv2d(input=input_tensor, filter=lr_flipped_kernel, strides=[1, 1, 1, 1], padding='SAME', dilations=dilations)
return tf.reduce_max(tf.concat([conv_l, conv_r], axis=-1), keepdims=True, axis=[-1])
您可以根据需要添加偏差、激活等。我过去使用过类似的东西——reduce_max
将允许你的内核采用任何形状,并有效地为你提供两个卷积;如果您改用 reduce_sum
,任何不对称都会很快平均,您的内核将是对称的。哪种效果最好取决于您的用例。
我想强制变量内的权重对称。我真的想要一个近似圆对称。但是,我可以想象行或列强制对称。
目标是通过减少自由变量的数量来减少训练时间。我知道我的问题需要一个对称数组,但我可能想同时包含对称变量和 "free" 变量。我现在正在使用 conv2d,所以我认为我需要继续使用它。
这里是一个函数,它创建一个关于其中心行上的反射对称的内核:
def SymmetricKernels(height,width,in_channels,out_channels,name=None):
half_kernels = tf.Variable(initial_value=tf.random_normal([(height+1)//2,width,in_channels,out_channels]))
half_kernels_reversed = tf.reverse(half_kernels[:(height//2),:,:,:],[0])
kernels = tf.concat([half_kernels,half_kernels_reversed],axis=0,name=name)
return kernels
用法示例:
w = SymmetricKernels(5,5,1,1)
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
w_ = sess.run(w)
w_[:,:,0,0]
# output:
# [[-1.299 -1.835 -1.188 0.093 -1.736]
# [-1.426 -2.087 0.434 0.223 -0.65 ]
# [-0.217 -0.802 -0.892 -0.229 1.383]
# [-1.426 -2.087 0.434 0.223 -0.65 ]
# [-1.299 -1.835 -1.188 0.093 -1.736]]
想法是使用 tf.Variable()
仅创建内核的上半部分变量 (half_kernels
),然后将对称内核作为上半部分及其反映版本的串联.
这个想法可以扩展到创建具有 left-right 和 up-down 对称性的内核。
您可以尝试的另一件事是通过两次卷积来束缚网络的手,重新使用内核但将其翻转以进行第二次卷积(未经测试的代码):
def symmetric_convolution(input_tensor, n_filters, size, name, dilations=[1,1,1,1]):
with tf.variable_scope("", reuse=tf.AUTO_REUSE):
kernel = tf.get_variable(shape=[*size, input_tensor.shape[-1], n_filters], name='conv_kernel_' + name, ...)
lr_flipped_kernel = tf.reverse(kernel, axis=[1], name='conv_kernel_flipped_lr_' + name)
conv_l = tf.nn.conv2d(input=input_tensor, filter=kernel, strides=[1, 1, 1, 1], padding='SAME', dilations=dilations)
conv_r = tf.nn.conv2d(input=input_tensor, filter=lr_flipped_kernel, strides=[1, 1, 1, 1], padding='SAME', dilations=dilations)
return tf.reduce_max(tf.concat([conv_l, conv_r], axis=-1), keepdims=True, axis=[-1])
您可以根据需要添加偏差、激活等。我过去使用过类似的东西——reduce_max
将允许你的内核采用任何形状,并有效地为你提供两个卷积;如果您改用 reduce_sum
,任何不对称都会很快平均,您的内核将是对称的。哪种效果最好取决于您的用例。