【问题标题】:col2im implementation in ConvNetConvNet 中的 col2im 实现
【发布时间】:2019-01-13 03:53:00
【问题描述】:

我正在尝试仅使用 numpy 来实现 CNN

在进行反向传播时,我发现我必须使用 col2im 来重塑 dx,因此我检查了 https://github.com/huyouare/CS231n/blob/master/assignment2/cs231n/im2col.py 的实现。

import numpy as np


def get_im2col_indices(x_shape, field_height, field_width, padding=1, stride=1):
  # First figure out what the size of the output should be
  N, C, H, W = x_shape
  assert (H + 2 * padding - field_height) % stride == 0
  assert (W + 2 * padding - field_height) % stride == 0
  out_height = (H + 2 * padding - field_height) / stride + 1
  out_width = (W + 2 * padding - field_width) / stride + 1

  i0 = np.repeat(np.arange(field_height), field_width)
  i0 = np.tile(i0, C)
  i1 = stride * np.repeat(np.arange(out_height), out_width)
  j0 = np.tile(np.arange(field_width), field_height * C)
  j1 = stride * np.tile(np.arange(out_width), out_height)
  i = i0.reshape(-1, 1) + i1.reshape(1, -1)
  j = j0.reshape(-1, 1) + j1.reshape(1, -1)

  k = np.repeat(np.arange(C), field_height * field_width).reshape(-1, 1)

  return (k, i, j)


def im2col_indices(x, field_height, field_width, padding=1, stride=1):
  """ An implementation of im2col based on some fancy indexing """
  # Zero-pad the input
  p = padding
  x_padded = np.pad(x, ((0, 0), (0, 0), (p, p), (p, p)), mode='constant')

  k, i, j = get_im2col_indices(x.shape, field_height, field_width, padding,
                               stride)

  cols = x_padded[:, k, i, j]
  C = x.shape[1]
  cols = cols.transpose(1, 2, 0).reshape(field_height * field_width * C, -1)
  return cols


def col2im_indices(cols, x_shape, field_height=3, field_width=3, padding=1,
                   stride=1):
  """ An implementation of col2im based on fancy indexing and np.add.at """
  N, C, H, W = x_shape
  H_padded, W_padded = H + 2 * padding, W + 2 * padding
  x_padded = np.zeros((N, C, H_padded, W_padded), dtype=cols.dtype)
  k, i, j = get_im2col_indices(x_shape, field_height, field_width, padding,
                               stride)
  cols_reshaped = cols.reshape(C * field_height * field_width, -1, N)
  cols_reshaped = cols_reshaped.transpose(2, 0, 1)
  np.add.at(x_padded, (slice(None), k, i, j), cols_reshaped)
  if padding == 0:
    return x_padded
  return x_padded[:, :, padding:-padding, padding:-padding]

pass

我预计当我将 X 放入 im2col_indices 并将该输出放回 col2im_indices 时将返回相同的 X,但它没有。

我不明白 col2im 到底做了什么。

【问题讨论】:

    标签: python neural-network deep-learning conv-neural-network convolution


    【解决方案1】:

    如果我是对的,那么输出不是同一个 X,因为 X 的每个单元格都被转换为多个 cols,并且在 im2col_indices 期间被相乘。

    假设你有一个像这样的简单图像X

     1 2 3
     4 5 6
     7 8 9
    

    然后你用内核大小 3、步幅 1 和 same 填充转换它,结果将是

    0 0 0 0 1 2 0 4 5
    0 0 0 1 2 3 4 5 6
    0 0 0 2 3 0 5 6 0
    0 1 2 0 4 5 0 7 8
    1 2 3 4 5 6 7 8 9
    2 3 0 5 6 0 8 9 0
    0 4 5 0 7 8 0 0 0
    4 5 6 7 8 9 0 0 0
    5 6 0 8 9 0 0 0 0
    * *   * *
    

    如您所见,第一个值为 1 的单元格显示在四个 cols 中:0、1、3、4。

    im2col_indices第一个零初始化一个填充大小的图像,然后将每个col添加到它。关注第一个单元格,过程应该是这样的

    1.零初始化图像

    0 0 0 0 0
    0 0 0 0 0
    0 0 0 0 0
    0 0 0 0 0
    0 0 0 0 0
    

    2.添加col 0

    0 0 0 0 0     0 0 0 - -     0 0 0 0 0
    0 0 0 0 0     0 1 2 - -     0 1 2 0 0
    0 0 0 0 0  +  0 4 5 - -  =  0 4 5 0 0
    0 0 0 0 0     - - - - -     0 0 0 0 0
    0 0 0 0 0     - - - - -     0 0 0 0 0
    

    3.添加col 1

    0 0 0 0 0     - 0 0 0 -     0  0  0  0  0
    0 1 2 0 0     - 1 2 3 -     0  2  4  3  0
    0 4 5 0 0  +  - 4 5 6 -  =  0  8 10  6  0
    0 0 0 0 0     - - - - -     0  0  0  0  0
    0 0 0 0 0     - - - - -     0  0  0  0  0
    

    4.添加col 3

    0  0  0  0  0     - - - - -     0  0  0  0  0
    0  2  4  3  0     0 1 2 - -     0  3  6  3  0
    0  8 10  6  0  +  0 4 5 - -  =  0 12 15  6  0
    0  0  0  0  0     0 7 8 - -     0  7  8  0  0 
    0  0  0  0  0     - - - - -     0  0  0  0  0
    

    5.添加col 4

    0  0  0  0  0     - - - - -     0  0  0  0  0
    0  3  6  3  0     - 1 2 3 -     0  4  8  6  0
    0 12 15  6  0  +  - 4 5 6 -  =  0 16 20 12  0
    0  7  8  0  0     - 7 8 9 -     0 14 16  9  0
    0  0  0  0  0     - - - - -     0  0  0  0  0 
    

    第一个单元格在转换回来时乘以 4。对于这个简单的图像,col2im_indices(im2col_indices(X)) 应该给你

     4  12  12
    24  45  36
    28  48  36
    

    对比原图,四个角单元1 3 7 9乘以4,四个边缘单元2 4 6 8乘以6,中心单元5乘以9。

    对于大图像,大部分单元格会乘以 9,我认为这大致意味着你的学习率实际上比你想象的要大 9 倍。

    【讨论】:

    • 好的,现在我明白了 col2im_indices 是如何工作的,以及为什么 col2im_indices(im2col_indices(X)) 不给你 X。但为什么会这样呢?我想在反向传递中,我们计算 dx_col(dx 的 col 向量),然后用 col2im 将其改回 dx,所以 col2im 不应该只是撤消 im2col 所做的吗?我是否理解后向传递部分有问题?
    • 恕我直言,你是对的。 col2im 应该撤消 im2col 所做的事情。但是,您发布的代码来自 cs231n 分配,我认为这样实现起来要容易得多。我不知道这将如何影响融合,但我知道的并不比写这篇文章的人好,所以我认为对于一些“从头开始”的项目来说至少是可以的。
    【解决方案2】:

    回复这个2年前的帖子,将来可能会对某人有所帮助。

    这是我的理解。在 CNN 反向传播上下文中,col2im 矩阵是滤波器和反向传播误差 (dout) 的乘积。必须注意,该矩阵已经是两个矩阵的乘积,这与前向传播中的 im2col 用例不同,在该用例中,我们刚刚将输入拉伸到 im2col 矩阵中,准备进行乘法(卷积)。由于 im2col 和 col2im 之间的这种差异,在 col2im 中,我们需要将反向传播的误差添加到所有贡献的输入索引中。

    让我们考虑一个 1x5x5 输入、单个 1x3x3 过滤器、0 填充、步幅 1 的示例。输入的索引将如下所示:

    [0,0] [0,1] [0,2] [0,3] [0,4]
    [1,0] [1,1] [1,2] [1,3] [1,4]
    [2,0] [2,1] [2,2] [2,3] [2,4]
    [3,0] [3,1] [3,2] [3,3] [3,4]
    [4,0] [4,1] [4,2] [4,3] [4,4]
    

    为前向传播计算得到的 9x9 im2col 索引 矩阵乘法看起来像:

    im2col 索引

    <-----------------------  9 ----------------------------->
    [ 0] [0,0] [0,1] [0,2] [1,0] [1,1] [1,2] [2,0] [2,1] [2,2] 
    [ 1] [0,1] [0,2] [0,3] [1,1] [1,2] [1,3] [2,1] [2,2] [2,3] 
    [ 2] [0,2] [0,3] [0,4] [1,2] [1,3] [1,4] [2,2] [2,3] [2,4] 
    [ 3] [1,0] [1,1] [1,2] [2,0] [2,1] [2,2] [3,0] [3,1] [3,2] 
    [ 4] [1,1] [1,2] [1,3] [2,1] [2,2] [2,3] [3,1] [3,2] [3,3] 
    [ 5] [1,2] [1,3] [1,4] [2,2] [2,3] [2,4] [3,2] [3,3] [3,4] 
    [ 6] [2,0] [2,1] [2,2] [3,0] [3,1] [3,2] [4,0] [4,1] [4,2] 
    [ 7] [2,1] [2,2] [2,3] [3,1] [3,2] [3,3] [4,1] [4,2] [4,3] 
    [ 8] [2,2] [2,3] [2,4] [3,2] [3,3] [3,4] [4,2] [4,3] [4,4] 
    

    在反向传播中,当我们通过将反向传播的误差 DOUT 和过滤器相乘生成 col2im 矩阵时,生成的索引如上所示已经是乘法的结果。当我们将其转换回输入错误时,我们需要在输入错误数组的给定位置添加相应的索引。

    例如:

    input_error[0,0] = im2col_error[0,0]
    input_error[0,1] = im2col_error[0,1] + im2col_error[1,0]
    input_error[0,2] = im2col_error[0,2] + im2col_error[1,1] + im2col_error[2,0]
    ....
    ....
    

    从上面的索引矩阵中可以明显看出这一点。

    【讨论】:

      猜你喜欢
      • 2023-03-24
      • 2020-09-19
      • 1970-01-01
      • 2021-06-19
      • 2017-02-11
      • 2017-11-28
      • 2018-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多