【问题标题】:How to implement the fixed length spatial pyramid pooling layer?如何实现定长空间金字塔池化层?
【发布时间】:2016-12-01 14:57:46
【问题描述】:

我想实现in this paper 介绍的空间金字塔池化层。

作为论文设置,关键是定义max_pooling层的variant kernel size和stride size,即:

kernel_size = ceil(a/n)
stride_size = floor(a/n)

其中a 是输入张量空间大小,n 是金字塔级别,即池化输出的空间箱。

我尝试用 tensorflow 实现这一层:

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

但是当输入大小不同时,它不能保证相同长度的池输出。

如何解决这个问题?

【问题讨论】:

标签: python tensorflow deep-learning


【解决方案1】:

我认为论文的作者是错误的,公式应该是:

stride_size = floor(a/n)
kernel_size = floor(a/n) + (a mod n)

请注意,对于 n

我修改了在https://github.com/tensorflow/tensorflow/issues/6011 找到的代码,这里是:

def spp_layer(input_, levels=(6, 3, 2, 1), name='SPP_layer'):
    shape = input_.get_shape().as_list()
    with tf.variable_scope(name):
        pyramid = []
        for n in levels:

            stride_1 = np.floor(float(shape[1] / n)).astype(np.int32)
            stride_2 = np.floor(float(shape[2] / n)).astype(np.int32)
            ksize_1 = stride_1 + (shape[1] % n)
            ksize_2 = stride_2 + (shape[2] % n)
            pool = tf.nn.max_pool(input_,
                                  ksize=[1, ksize_1, ksize_2, 1],
                                  strides=[1, stride_1, stride_2, 1],
                                  padding='VALID')

            # print("Pool Level {}: shape {}".format(n, pool.get_shape().as_list()))
            pyramid.append(tf.reshape(pool, [shape[0], -1]))
        spp_pool = tf.concat(1, pyramid)
    return spp_pool

【讨论】:

    【解决方案2】:

    是的,现在的输出大小不是恒定的,并且查看您的代码似乎您的各个池操作将具有在两个数字之间交替的输出大小。原因是输出大小,至少对于'SAME',是由公式计算的

    out_height = ceil(float(in_height) / float(strides[1]))
    

    如果我们使用实质上是 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')
    

    为了清楚起见,我在对 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]
    

    对于 ksize 的第三个元素,您使用 shape[1]/6 而不是 shape[2]/6。我认为这是一个错字,所以我在上面的代码中进行了更改。

    我知道,在论文中,步幅被认为是 a/n 的底,而不是 ceil,但截至目前,使用 tensorflow 的本机池操作,没有办法让它工作如预期的。 “有效”池不会产生任何接近您想要的结果。

    好吧...如果您真的愿意花时间,您可以将输入大小取模您的最大金字塔维度,在本例中为 6,并独立处理所有这六种情况。不过,我找不到很好的理由。 Tensorflow pads 与其他库(例如 Caffe)不同,因此本质上会有差异。上述解决方案将为您提供他们在论文中的目标,一个池化层金字塔,其中图像的不相交区域以不同的粒度级别被最大池化。

    编辑:实际上,如果您使用 tf.pad() 自己手动填充输入并为每个最大池操作创建一个新输入,以便新输入的高度和宽度是 n 的整数倍,那么它会起作用使用您已有的代码。

    【讨论】:

    • 谢谢。我有一个测试,它也无法达到论文的目标,例如shape = 13, stride=13/6=3, output_size=13/3=5, 但是 'shape=16, stride=16/6=3, output_size=16/3=6`
    猜你喜欢
    • 1970-01-01
    • 2017-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-19
    • 2017-10-09
    • 1970-01-01
    相关资源
    最近更新 更多