CS231n笔记6--Convolution与Pooling

Convolution与Pooling

卷积-Convolution

总于写到卷积层这一部分了,呼。为了给大家一个比较容易理解的解释,我这一部分会以例子的形式来讲解如何做卷积。
相信如果有读者有信号处理或者图像处理基础的都对卷积这个方法不陌生,下面举个例子,一步一步演示如何卷积。

如何卷积

首先给出要卷积的数据

这里我们随便给一个3*3*1的矩阵。注意矩阵的第三维度是1,不要认为这个第三维度没有意义,很快我将会讲这个第三维有什么用,之所以定为1只是方便我们下面演示。
那么数据如下:
147258369

设置卷积的参数

是的,卷积还有一些参数要设置,包括了padding和stride。padding主要是为了处理数据的边缘问题,如果设置了padding,一般都会在扩充数据的第一二维,填充数值为0;而stride则是卷积过程中过滤器移动的步伐。不理解不要急,很快就跟大家演示了。那我们不妨就设置padding=1,stride=2吧。
由于padding不为0,因此我们要卷积的数据就不再是3*3*1维的了,而是5*5*1维,如下所示:
0000001470025800369000000

根据数据,使用对应的过滤器

根据我们上面给的数据,过滤器的第三维度等于要卷积的数据的第三维度,而其他两维一般则是相等的奇数维度。由于第三维度是1,因此过滤器的第三维度也会是1,我们给定过滤器的空间展度为3,也就是第一二维等于3(注意这里第一二维跟数据并没要相等,这里只是碰巧,实际上你可以让他们等于1的),下面是随便给出的过滤器。
935839219
并且还给出这个过滤器的偏移Bias = -40(又是乱设的)

开始卷积

其实卷积的过程很简单,我们拿着我们的过滤器,从数据的左上角从右向左,从上到下扫描一遍就可以了。
比如一开始,我们先用过滤器瞄准数据的左上角,如下(瞄准的区域已加深)
0000001470025800369000000
此时这个区域和过滤器它们的维数应该是完全一样的!然后我们让过滤器和这个区域的每个数字相乘后求和,得到的结果应该是
0*9+0*8+0*2+0*3+1*3+2*1+0*5+4*9+5*9 -40 = 46
然后我们把它放到一个目标矩阵里去
[46]

然后将过滤器往右移stride格,由于我们设置的stride是2,所以现在瞄准的区域就变成了
0000001470025800369000000
与上一步一样算出内积然后放到目标矩阵里,此时目标矩阵如下
[4644]
然后由于数据右边已经到了尽头,就回到左边,但是向下移stride格,此时瞄准的区域为
0000001470025800369000000
同样的操作,目标矩阵变成
[463144]
然后再平移,到这里相信读者都知道下面要怎么做了,这里直接给出目标矩阵
[463144104]
最后得到的这个目标矩阵就是我们这一个过滤器在数据中卷积的结果。由整个过程我们也可以看出来,一个过滤器卷积最后的结果会是一个二维的矩阵,这时候可能有读者就奇怪了,原数据好好的一个三维矩阵,怎么就就变二维了?这里我想说的是,是的,但是,注意到我们这里只使用了一个过滤器啊!试想我们再返回到根据数据,使用对应的过滤器用多一个过滤器,不就又多了一个二维矩阵吗,与之前的矩阵叠在一起就成了3*3*2的矩阵了!也就是说越多过滤器,第三维就越大。
下面给出卷积参数的计算公式

卷积参数计算公式

设原数据的大小为 W1H1D1
四个超参数分别如下:

  • 过滤器的个数K
  • 过滤器的空间展度F
  • 步长stride为S
  • 填充padding为P

可得到目标矩阵的尺寸为 W2H2D2 ,其中

  • W2=(W1F+2P)/S+1
  • H2=(H1F+2P)/S+1
  • D2=K

相信读者到这里都有一个比较直观的认识了,也应该掌握了卷积的方法,所以深度学习在学习的时候就是调整过滤器的数值以及他们的偏移!这些也就是所谓的权重。那好吧,如果读者真的理解了,不妨算算就我们上面的一个过滤器的例子,我们有多少个权重需要调整?如果有十个过滤器呢?可以把答案写在评论区,如果有错误的话,小编会毫不犹豫的指出的:)。读者也是,如果觉得小编哪里写错了,也请毫不犹豫的指出,共同学习~


什么?权重的初始值应该怎么设?还不快去看看小编的笔记4,那里有分析哦!


非向量化的卷积层代码

下面的代码是小编在上课时写的,并非向量化代码,因此执行效率不高,但是读者可以从代码里看出小编是如何利用后向传播求梯度的。(啥?还不知道怎么后向传播求梯度?快去看看小编之前的CS231n笔记吧!)

def conv_forward_naive(x, w, b, conv_param):
  """
  A naive implementation of the forward pass for a convolutional layer.

  The input consists of N data points, each with C channels, height H and width
  W. We convolve each input with F different filters, where each filter spans
  all C channels and has height HH and width HH.

  Input:
  - x: Input data of shape (N, C, H, W)
  - w: Filter weights of shape (F, C, HH, WW)
  - b: Biases, of shape (F,)
  - conv_param: A dictionary with the following keys:
    - 'stride': The number of pixels between adjacent receptive fields in the
      horizontal and vertical directions.
    - 'pad': The number of pixels that will be used to zero-pad the input.

  Returns a tuple of:
  - out: Output data, of shape (N, F, H', W') where H' and W' are given by
    H' = 1 + (H + 2 * pad - HH) / stride
    W' = 1 + (W + 2 * pad - WW) / stride
  - cache: (x, w, b, conv_param)
  """
  out = None

  N, C, H, W = x.shape
  F, C, HH, WW = w.shape
  pad = conv_param['pad']
  stride = conv_param['stride']
  H_ = 1 + (H + 2 * pad - HH) / stride
  W_ = 1 + (W + 2 * pad - WW) / stride
  paddedX = np.zeros((N, C, H+2*pad, W+2*pad))
  paddedX[:, :, pad:-pad, pad:-pad] = x
  out = np.zeros((N, F, H_, W_))


  for p in xrange(N):
    for f in xrange(F):
      for row in xrange(H_):
        for col in xrange(W_):
          out[p, f, row, col] = (paddedX[p, :, \
            row*stride:HH+row*stride, col*stride:WW+col*stride]*w[f]).sum()+b[f]


  cache = (x, w, b, conv_param)
  return out, cache


def conv_backward_naive(dout, cache):
  """
  A naive implementation of the backward pass for a convolutional layer.

  Inputs:
  - dout: Upstream derivatives.
  - cache: A tuple of (x, w, b, conv_param) as in conv_forward_naive

  Returns a tuple of:
  - dx: Gradient with respect to x
  - dw: Gradient with respect to w
  - db: Gradient with respect to b
  """
  dx, dw, db = None, None, None

  x, w, b, conv_param = cache

  N, C, H, W = x.shape
  F, C, HH, WW = w.shape
  pad = conv_param['pad']
  stride = conv_param['stride']
  H_ = 1 + (H + 2 * pad - HH) / stride
  W_ = 1 + (W + 2 * pad - WW) / stride


  paddedX = np.zeros((N, C, H+2*pad, W+2*pad))
  paddedX[:, :, pad:-pad, pad:-pad] = x

  dx = np.zeros((N, C, H+2*pad, W+2*pad))
  dw = np.zeros(w.shape)

  for p in xrange(N):
    for f in xrange(F):
      for row in xrange(H_):
        for col in xrange(W_):
          dx[p, :, row*stride:HH+row*stride, col*stride:WW+col*stride] += w[f]*dout[p, f, row, col]
          dw[f] += paddedX[p, :, row*stride:HH+row*stride, col*stride:WW+col*stride]*dout[p, f, row, col]

  dx = dx[:, :, pad:-pad, pad:-pad]
  db = np.sum(dout, axis=(0, 2, 3))

  return dx, dw, db


池化-Pooling

之所以把池化放到这里才来讲,是因为池化其实也可以看做卷积,只是在池化的时候,我们不用另一个矩阵作为过滤器,而是用一个函数做为过滤器。
常见的池化分三类

  • Max Pooling
  • Mean Pooling
  • Random Pooling

一般深度学习时都用Max Pooling作为池化,并且池化时不带padding,一般展度为2,步长为2。下面用展度为2,步长为2的Max过滤器,举一个池化的例子。
5640194731580369
根据之前讲过的卷积的流程,我们可以得到池化后的结果应为
[9739]

读者们不妨试下Mean Poolling的话结果是什么吧。

下面给出Max Pooling非向量化的Python代码:

def max_pool_forward_naive(x, pool_param):
  """
  A naive implementation of the forward pass for a max pooling layer.

  Inputs:
  - x: Input data, of shape (N, C, H, W)
  - pool_param: dictionary with the following keys:
    - 'pool_height': The height of each pooling region
    - 'pool_width': The width of each pooling region
    - 'stride': The distance between adjacent pooling regions

  Returns a tuple of:
  - out: Output data
  - cache: (x, pool_param)
  """
  out = None

  N, C, H, W = x.shape
  pool_height = pool_param['pool_height']
  pool_width = pool_param['pool_width']
  stride = pool_param['stride']

  H_ = (H-pool_height)/stride+1
  W_ = (W-pool_width)/stride+1
  out = np.zeros((N, C, H_, W_))


  for row in xrange(H_):
    for col in xrange(W_):
      out[:, :, row, col] = np.max(x[:, :, row*stride:row*stride+pool_height, col*stride:col*stride+pool_width], axis=(2,3))


  cache = (x, pool_param)
  return out, cache


def max_pool_backward_naive(dout, cache):
  """
  A naive implementation of the backward pass for a max pooling layer.

  Inputs:
  - dout: Upstream derivatives
  - cache: A tuple of (x, pool_param) as in the forward pass.

  Returns:
  - dx: Gradient with respect to x
  """
  dx = None


  x, pool_param = cache
  Hp = pool_param['pool_height']
  Wp = pool_param['pool_width']
  S = pool_param['stride']
  N, C, H, W = x.shape
  H1 = (H - Hp) / S + 1
  W1 = (W - Wp) / S + 1

  dx = np.zeros((N, C, H, W))
  for nprime in range(N):
      for cprime in range(C):
          for k in range(H1):
              for l in range(W1):
                  x_pooling = x[nprime, cprime, k *
                                S:k * S + Hp, l * S:l * S + Wp]
                  maxi = np.max(x_pooling)
                  x_mask = x_pooling == maxi
                  dx[nprime, cprime, k * S:k * S + Hp, l * S:l *
                      S + Wp] += dout[nprime, cprime, k, l] * x_mask

  return dx


后记

到这里为止,我们已经掌握了自己做一个深度学习网络的能力啦!莫激动~下面一节,小编会分享下深度学习常用的结构(其实就是告诉大家,需要组合哪些我们之前讲过的内容,怎么组合)。然后呢,会给大家分析一个在ImageNet获得过第一的深度学习网络是怎么组成的,具体分析哪个还没想好,小编这周就恶补去,下周见啦:)(P.S.如不出意外的话…Caffe的学习笔记应该会在这两周开始咯,敬请期待~)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值