【问题标题】:Convolutional layer in Python using NumpyPython中使用Numpy的卷积层
【发布时间】:2019-09-28 20:08:40
【问题描述】:

我正在尝试使用 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 维输出?

【问题讨论】:

  • 我在尝试重现这一点时遇到了困难。你能给一个样品过滤器吗?由此判断,filter.shape[3],是 4 维的吗?
  • 过滤器是 4 维的。样本过滤器可以是filter = np.random.randint(0, 2, [5, 5, 3, 16])。这将是一个 5 X 5 过滤器,它对三通道输入图像进行操作并生成具有 16 个通道的输出“图像”。
  • 好的,我有时间去看看

标签: python arrays numpy conv-neural-network


【解决方案1】:

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

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])

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

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

编辑: 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)

【讨论】:

  • 感谢您的回答。但是,这似乎不会产生正确的结果。比如我和tensorflow.conv2d(a, b, strides=[1,1,1,1], padding="VALID")比较,结果不一样。输出张量具有相同的形状,但值不同。知道为什么会这样吗?
  • 我发现了问题。好像和convolve的定义有关。对于[1,2,3][1,2,3] 的卷积,据我了解,它应该是1*3+2*2+3*1(否则称为相关)。但是tensorflow似乎已经将卷积定义为1*1+2*2+3*3。无论如何,要获得 tensorflow 的结果,只需删除最后一行的 [::-1,::-1]
  • 该方法看起来很有趣,因此评估了所有 3 个变体的性能。这是结果。 TF 只使用 CPU。 gist.github.com/prabindh/beea050b379e6e9a52699b66d7ce227f
  • @Prabindh 是的,一般numpy在相同的cpu上在类似算法实现下应该不会比tensorflow慢,而且这个实现基本上相当于tf.nn.conv2d文档中的伪代码:生成虚拟立即矩阵(除了这里是一个 6 张量),并进行右乘法(在einsum 中)。您可以通过删除 [::-1,::-1] 来修复结果不同的问题
  • 感谢@ZisIsNotZis 和@Prabindh 的贡献。建议使用np.tensordot(a, b, axes=3) 而不是np.einsum("abcijk,ijkd", a, b)。我注意到前者的工作速度比后者快得多,尤其是在越来越大的数据集上。
猜你喜欢
  • 1970-01-01
  • 2021-02-11
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
  • 2018-06-21
  • 2018-02-11
  • 1970-01-01
相关资源
最近更新 更多