使用Numpy的Python卷积层

时间:2019-05-10 22:43:37

标签: python arrays numpy conv-neural-network

我正在尝试使用Numpy在Python中实现卷积层。 输入是形状为[N, H, W, C]的4维数组,其中:

  • N:批量大小
  • H:图像高度
  • W:图像宽度
  • C:频道数

卷积滤波器也是形状为[F, F, Cin, Cout]的4维数组,其中

  • F:正方形滤镜的高度和宽度
  • Cin:输入通道数(Cin = C
  • Cout:输出通道数

假设沿所有轴的步幅为1,并且没有填充,则输出应为[N, H - F + 1, W - F + 1, Cout]形状的4维数组。

我的代码如下:

import numpy as np

def conv2d(image, filter):
  # Height and width of output image
  Hout = image.shape[1] - filter.shape[0] + 1
  Wout = image.shape[2] - filter.shape[1] + 1

  output = np.zeros([image.shape[0], Hout, Wout, filter.shape[3]])

  for n in range(output.shape[0]):
    for i in range(output.shape[1]):
      for j in range(output.shape[2]):
        for cout in range(output.shape[3]):
          output[n,i,j,cout] = np.multiply(image[n, i:i+filter.shape[0], j:j+filter.shape[1], :], filter[:,:,:,cout]).sum()

  return output

这可以正常工作,但是使用四个for循环并且非常慢。是否有更好的方法来实现使用Numpy进行4维输入和过滤并返回4维输出的卷积层?

1 个答案:

答案 0 :(得分:1)

这是一种类似keras的(?)卷积的简单实现。对于初学者来说可能很难理解,因为它使用了很多广播和跨步技巧。

from numpy.lib.stride_tricks import as_strided
def conv2d(a, b):
    a = as_strided(a,(len(a),a.shape[1]-len(b)+1,a.shape[2]-b.shape[1]+1,len(b),b.shape[1],a.shape[3]),a.strides[:3]+a.strides[1:])
    return np.einsum('abcijk,ijkd', a, b[::-1,::-1])

顺便说一句:如果您正在使用非常大的内核进行卷积,请改用基于傅立叶的算法。

编辑:在卷积不涉及首先翻转内核的情况下(例如[::-1,::-1]中的内容),应删除tensorflow

编辑np.tensordot(a, b, axes=3)的性能比np.einsum("abcijk,ijkd", a, b)好得多,因此强烈建议使用。  因此,该函数变为:

from numpy.lib.stride_tricks import as_strided

def conv2d(a, b):
  Hout = a.shape[1] - b.shape[0] + 1
  Wout = a.shape[2] - b.shape[1] + 1

  a = as_strided(a, (a.shape[0], Hout, Wout, b.shape[0], b.shape[1], a.shape[3]), a.strides[:3] + a.strides[1:])

  return np.tensordot(a, b, axes=3)