论文: ICML, 2019. Making Convolutional Networks Shift-Invariant Again
GitHub地址: Github
1.现有网络存在的问题
很多时候都认为max pool或者avg pool可以引入一定的平移不变性,使网络对于平移鲁棒,但是实际是当输入的图像有较小的平移时可以很大程度上影响网络的输出(横轴是图像的偏移量,如果不进行处理,网络的输出震荡很大)
实验的结果中可以看出maxpool并没有表现出很好的平移不变性。为了保持平移不变性,可以在下采样(maxpool/strided conv)之前进行低通滤波,但是插入低通滤波器的位置很重要,进行低通滤波的位置不正确反而会导致网络的效果下降。
一个常见的现象就是汽车快速行驶时车轮看起来会向后转,产生这个现象的原因就是我们的眼睛采样频率太低,导致高频信号发生了混叠,为了防止混叠,就要进行低通滤波
常见的抗混叠方法是在降采样之前加一个低通滤波器,滤除信号中大于奈奎斯特频率的成分,防止混叠,但是这种操作往往导致性能劣化,因为低通滤波会损失信息导致失真
论文中给出的原因,是因为stride=2的时候卷积和pool等下采样操作时,违反了采样定理,会导致信号走样,其实这个问题在很久之前就有所说明,就比如我们在构建高斯、拉布普斯金字塔的时候,下采样之前都需要先使用高斯模糊对图像进行处理,就是为了防止下采样出现走样的情况,也就是anti-aliasing by low-pass filtering before downsampling.
2.BlurPool
传统的max pool可以分解为两部分,stride = 1的max + subsample(下采样) 。第一部分stride=1的max具有平移不变性,引起混叠的过程是subsample
作者提出的MaxBlurPool = max + blur + subsample
max 和 blur操作都是平移不变的
(低通滤波blur的加入并不能完全消除混叠,只是减小了混叠)
3.Periodic-N shift invariance (周期为N的平移不变性)
在第一张图上可以看到,在使用maxpool的网络中,随着偏移量逐渐增大,准确率并不是一直下降的,而是周期性的震荡,这说明不是偏移量越大网络的效果就越差。导致这个现象的原因就是某些情况下周期性的平移不变性。
当平移量为N的整数倍的时候,有些情况下仍可满足平移不变性,具体的可以自己写一个一维的数组如[0,0,0,1,1,0,0]进行shift-n 和maxpool观察输出
4.tensorflow 代码
代码中BatchNorm是自己加入的,作者的代码中并没有BatchNorm
加入BatchNorm是因为在实验时发现收敛变慢,自己的理解是低通滤波后丢失了部分高频的信息,经过blur pool后数据的方差变小,也就是使得特征的显著性降低了,所以网络提取特征的难度变大,通过batch norm强行拉大特征间数值上的差异可以加快网络的学习。加batch norm后网络学习变快
def BatchNorm(x, momentum=0.997, epsilon=1e-3, train=True, name='bn'):
return tf.layers.batch_normalization(x,
momentum=momentum, ###动量参数
epsilon=epsilon, ###接近0的数,防止分母出现0
scale=True,
center=True,
training=train,
name=name)
def BlurPool(X, kernel_size = 3, pool_size = 2, mutichanel = 1, bn = True, name = 'BlurPoolLayer'):
with tf.variable_scope(name):
input_shape = X.get_shape().as_list()
# print('input_shape:',input_shape)
if kernel_size == 3:
bk = np.array([[1, 2, 1],
[2, 4, 2],
[1, 2, 1]])
bk = bk / np.sum(bk)
elif kernel_size == 5:
bk = np.array([[1, 4, 6, 4, 1],
[4, 16, 24, 16, 4],
[6, 24, 36, 24, 6],
[4, 16, 24, 16, 4],
[1, 4, 6, 4, 1]])
bk = bk / np.sum(bk)
bk = np.repeat(bk, input_shape[-1])
bk = np.reshape(bk,[kernel_size, kernel_size, input_shape[3],mutichanel])
bk = tf.convert_to_tensor(bk, dtype = tf.float32, name='blur-kernel')
# print('bk:',bk)
X = tf.nn.max_pool(X,ksize=[1,pool_size,pool_size,1],strides=[1,1,1,1],padding='SAME')
###通过bk进行,滤波 + subsample
X = tf.nn.depthwise_conv2d(X, bk, strides=[1, pool_size, pool_size, 1], padding='SAME')
if bn:
X = BatchNorm(X)
# print('X blur pool shape:',X.get_shape().as_list())
return X