kar*_*TUM 5 python deep-learning tensorflow
我想实现本文中介绍的空间金字塔池化层。
正如论文设置,关键点是定义 max_pooling 层的变体内核大小和步幅大小,即:
kernel_size = ceil(a/n)
stride_size = floor(a/n)
Run Code Online (Sandbox Code Playgroud)
其中a
是输入张量空间大小,n
是金字塔级别,即池化输出的空间箱。
我尝试用张量流实现这一层:
import numpy as np
import tensorflow as tf
def spp_layer(input_, name='SPP_layer'):
"""
4 level SPP layer.
spatial bins: [6_6, 3_3, 2_2, 1_1]
Parameters
----------
input_ : tensor
name : str
Returns
-------
tensor
"""
shape = input_.get_shape().as_list()
with tf.variable_scope(name):
spp_6_6_pool = tf.nn.max_pool(input_,
ksize=[1,
np.ceil(shape[1]/6).astype(np.int32),
np.ceil(shape[2]/6).astype(np.int32),
1],
strides=[1, shape[1]//6, shape[2]//6, 1],
padding='SAME')
print('SPP layer level 6:', spp_6_6_pool.get_shape().as_list())
spp_3_3_pool = tf.nn.max_pool(input_,
ksize=[1,
np.ceil(shape[1]/3).astype(np.int32),
np.ceil(shape[2]/3).astype(np.int32),
1],
strides=[1, shape[1]//3, shape[2]//3, 1],
padding='SAME')
print('SPP layer level 3:', spp_3_3_pool.get_shape().as_list())
spp_2_2_pool = tf.nn.max_pool(input_,
ksize=[1,
np.ceil(shape[1]/2).astype(np.int32),
np.ceil(shape[2]/2).astype(np.int32),
1],
strides=[1, shape[1]//2, shape[2]//2, 1],
padding='SAME')
print('SPP layer level 2:', spp_2_2_pool.get_shape().as_list())
spp_1_1_pool = tf.nn.max_pool(input_,
ksize=[1,
np.ceil(shape[1]/1).astype(np.int32),
np.ceil(shape[2]/1).astype(np.int32),
1],
strides=[1, shape[1]//1, shape[2]//1, 1],
padding='SAME')
print('SPP layer level 1:', spp_1_1_pool.get_shape().as_list())
spp_6_6_pool_flat = tf.reshape(spp_6_6_pool, [shape[0], -1])
spp_3_3_pool_flat = tf.reshape(spp_3_3_pool, [shape[0], -1])
spp_2_2_pool_flat = tf.reshape(spp_2_2_pool, [shape[0], -1])
spp_1_1_pool_flat = tf.reshape(spp_1_1_pool, [shape[0], -1])
spp_pool = tf.concat(1, [spp_6_6_pool_flat,
spp_3_3_pool_flat,
spp_2_2_pool_flat,
spp_1_1_pool_flat])
return spp_pool
Run Code Online (Sandbox Code Playgroud)
但当输入大小不同时,它不能保证相同长度的池化输出。
如何解决这个问题呢?
小智 0
是的,现在的输出大小不是恒定的,并且查看您的代码,您的各个池操作似乎将具有在两个数字之间交替的输出大小。原因是输出大小(至少对于“SAME”而言)是通过以下公式计算的
out_height = ceil(float(in_height) / float(strides[1]))
Run Code Online (Sandbox Code Playgroud)
如果对于步幅,我们使用本质上是 in_height/n 的下限,那么输出将在 n 和 n+1 之间波动。为了确保稳定性,您需要做的是使用 ceil 操作来代替步幅值。spp_6_6 池的更改代码为
ksize=[1, np.ceil(shape[1]/6).astype(np.int32), np.ceil(shape[2]/6).astype(np.int32), 1]
spp_6_6_pool = tf.nn.max_pool(input_, ksize=ksize,strides=ksize, padding='SAME')
Run Code Online (Sandbox Code Playgroud)
为了清楚起见,我在 tf.nn.max_pool() 调用之外定义了 ksize。因此,如果您也使用 ksize 来确定步幅,那么应该会有效。如果您在数学上进行舍入,只要输入维度的大小至少是最大金字塔大小 n 的值的两倍,您的输出大小就应该与“相同”填充保持一致!
与您的问题有些相关,在您的第一个最大池操作中,您的 ksize 参数是
ksize=[1, np.ceil(shape[1]/6).astype(np.int32), np.ceil(shape[1]/6).astype(np.int32), 1]
Run Code Online (Sandbox Code Playgroud)
对于 ksize 的第三个元素,您使用了 shape[1]/6 而不是 shape[2]/6。我认为这是一个拼写错误,所以我在上面的代码中更改了它。
我知道在论文中,步幅被视为 a/n 的下限而不是 ceil,但到目前为止,使用张量流的本机池化操作,无法使其按预期工作。“有效”池不会产生任何接近您想要的结果。
好吧...如果您真的愿意投入时间,您可以将输入大小以最大金字塔尺寸(在本例中为 6)为模,并独立处理所有这六种情况。但我找不到一个很好的理由。Tensorflow 的 pad 与其他库(例如 Caffe)不同,因此本质上会存在差异。上述解决方案将为您提供他们在论文中的目标,即一个池化层的金字塔,其中图像的不相交区域以不同的粒度级别进行最大池化。
编辑:实际上,如果您使用 tf.pad() 自己手动填充输入并为每个最大池操作创建一个新输入,以便新输入的高度和宽度是 n 的整齐倍数,那么它将与您已经拥有的代码。