如何实现超像素池层?

时间:2017-05-11 02:58:36

标签: tensorflow neural-network deep-learning keras theano

我想实现在下面的论文中定义的超像素池化层"使用超像素池网络的弱监督语义分割",最初在Torch中实现(实现不可用)。我希望在Keras与Theano后端(最好)这样做。

我将举一个小例子来说明图层的作用。它需要以下输入:

feature_map:shape = (batch_size, height, width, feature_dim)

superpixel_map:shape = (batch_size, height, width)

让我们假设两个小矩阵batch_size = 1, height = width = 2, feature_dim = 1

feature_map = np.array([[[[ 0.1], [ 0.2 ]], [[ 0.3], [ 0.4]]]])  
superpixel_map = np.array([[[ 0,  0], [ 1,  2]]])

现在,输出的形状为(batch_size, n_superpixels, feature_dim)。这里n_superpixels基本上是= np.amax(superpixel_map) + 1

输出计算如下。

找到superpixel_map == i的位置,其中i0n_superpixels - 1不等。我们来考虑i = 0i = 0的排名为(0, 0, 0)(0, 0, 1)

现在平均要素贴图中这些位置的元素。这为我们提供了值(0.1 + 0.2) / 2 = 0.15。为i = 1i = 2执行此操作,分别为0.30.4提供值。

现在,问题变得复杂,因为通常是batch_size > 1height, width >> 1

我在Keras中实现了一个新层,基本上是这样做但我用于循环。现在,如果height = width = 32。 Theano给出了最大的递归深度误差。谁知道如何解决这个问题?如果TensorFlow提供了新的东西,那么我也准备切换到TensorFlow后端了。

我的新图层的代码如下:

class SuperpixelPooling(Layer):
    def __init__(self, n_superpixels=None, n_features=None, batch_size=None, 
                 input_shapes=None, **kwargs):
        super(SuperpixelPooling, self).__init__(**kwargs)
        self.n_superpixels = n_superpixels
        self.n_features = n_features
        self.batch_size = batch_size
        self.input_shapes = input_shapes  # has to be a length-2 tuple, First tuple has the
                                          # shape of feature map and the next tuple has the
                                          # length of superpixel map. Shapes are of the
                                          # form (height, width, feature_dim)
    def compute_output_shape(self, input_shapes):
        return (input_shapes[0][0],
                    self.n_superpixels,
                    self.n_features)
    def call(self, inputs):
        # x = feature map
        # y = superpixel map, index from [0, n-1]
        x = inputs[0]  # batch_size x m x n x k
        y = inputs[1]  # batch_size x m x n
        ht = self.input_shapes[0][0]
        wd = self.input_shapes[0][1]
        z = K.zeros(shape=(self.batch_size, self.n_superpixels, self.n_features), 
                    dtype=float)
        count = K.zeros(shape=(self.batch_size, self.n_superpixels, self.n_features), 
                        dtype=int)
        for b in range(self.batch_size):
            for i in range(ht):
                for j in range(wd):
                    z = T.inc_subtensor(z[b, y[b, i, j], :], x[b, i, j, :])
                    count = T.inc_subtensor(count[b, y[b, i, j], :], 1)
        z /= count   
        return z

我认为递归深度超出问题是由于我使用的嵌套for循环。我没有看到避免这些循环的方法。如果有人有任何建议,请告诉我。

交叉发布here。如果我在那里得到任何答案,我会更新这篇文章。

1 个答案:

答案 0 :(得分:1)

我在my GitHub上进行了初步实施。它还没有准备好使用。请阅读以获得更多详情。为了完整起见,我将在此处发布实现及其简要说明(主要来自自述文件)。

class SuperpixelPooling(Layer):
    def __init__(self, n_superpixels=None, n_features=None, batch_size=None, input_shapes=None, positions=None, superpixel_positions=None, superpixel_hist=None, **kwargs):
        super(SuperpixelPooling, self).__init__(**kwargs)

        # self.input_spec = InputSpec(ndim=4)
        self.n_superpixels = n_superpixels
        self.n_features = n_features
        self.batch_size = batch_size
        self.input_shapes = input_shapes  # has to be a length-2 tuple, First tuple has shape of feature map and the next tuple has 
                                          # length of superpixel map. Shapes are of the form (height, width, feature_dim)
        self.positions = positions  # has three columns
        self.superpixel_positions = superpixel_positions  # has two columns
        self.superpixel_hist = superpixel_hist  # is a vector
    def compute_output_shape(self, input_shapes):
        return (self.batch_size, self.n_superpixels, self.n_features)
    def call(self, inputs):
        # x = feature map
        # y = superpixel map, index from [0, n-1]
        x = inputs[0]  # batch_size x k x m x n
        y = inputs[1]  # batch_size x m x n
        ht = self.input_shapes[0][0]
        wd = self.input_shapes[0][1]
        z = K.zeros(shape=(self.batch_size, self.n_superpixels, self.n_features), dtype=float)
        z = T.inc_subtensor(z[self.superpixel_positions[:, 0], self.superpixel_positions[:, 1], :], x[self.positions[:, 0], :, self.positions[:, 1], self.positions[:, 2]])
        z /= self.superpixel_hist
        return z

说明:

Keras中超像素池层的实现。请参阅keras.layers.pooling以进行实施。

超像素池层的概念可以在论文中找到:"使用超像素池网络的弱监督语义分割",AAAI 2017.该层采用两个输入,一个超像素映射(大小{{1} })和一个特征图(大小M x N)。它汇集了属于同一个超像素的特征(在此实现中,平均池)并形成K x M x N向量,其中1 x K是特征映射深度/通道。

一个简单的实现将需要三个for循环:一个遍历批处理,另一个遍历行,最后一个遍历特征映射的列并在运行中汇集它。但是,这会导致超出最大递归深度"每当您尝试编译包含此图层的模型时,Theano中的错误。即使要素图宽度和高度仅为32,也会发生此错误。

为了克服这个问题,我认为将所有的东西作为参数传递给这一层将消除至少两个for循环。最终,我能够创建一个单行程来实现整个平均池操作的核心。你需要通过:

  1. 图像中的超像素数
  2. 功能图深度/频道
  3. 批量大小
  4. 要素图和超像素图的形状
  5. K矩阵,其中包含与名为N x 3的{​​{1}}对应的索引的所有可能组合。如果您的输入图像大小和批量大小保持不变,则只需在培训期间生成一次。
  6. 名为(batch_size, row, column)的{​​{1}}矩阵。行i包含对应于矩阵positions的行N x 2中的索引的超像素索引。例如,如果矩阵superpixel_positions的行i包含positions,那么同一行超像素位置将包含i positions
  7. (12, 10, 20)矩阵 - (12, sp_i) - 其中sp_i = superpixel_map[12, 10, 20]是该图像中超像素的nubmer。顾名思义,此矩阵保留当前图像中存在的超像素的直方图。
  8. 这种实现的缺点是每个图像必须改变这些参数(具体地说,是第6点和第7点中提到的参数)。当GPU一次处理整个批次时,这是不切实际的。我认为这可以通过将所有这些参数作为输入传递到外部层来解决。基本上,它们可以从(例如)HDF5文件中读取。我计划很快就这样做。完成后我会更新。