我正在尝试使用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维输出的卷积层?
答案 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)