任意批量大小,但输出通道为1。
from typing import List
# 互相关计算
def crossCorrelation(x: List[List[int]], conv_kernel: List[List[int]]):
res = 0
for k in range(len(x)):
for i in range(len(x[0])):
for j in range(len(x[0][0])):
res += conv_kernel[k][i][j] * x[k][i][j]
return res
def conv2d(image: List[List[List[List[int]]]], conv_kernel: List[List[List[int]]], kernel_size=3, stride=2, pad=1):
# b, c, h, w
b, c, h, w = len(image), len(image[0]), len(image[0][0]), len(image[0][0][0])
c_c, c_h, c_w = len(conv_kernel), len(conv_kernel[0]), len(conv_kernel[0][0])
assert c == c_c
# 计算输出特征图大小
out_h = (h - kernel_size + 2 * pad) // stride + 1
out_w = (w - kernel_size + 2 * pad) // stride + 1
# 计算padding后的输入
if pad > 0:
pad_x = [[[[0] * (w + 2 * pad) for _ in range(h + 2 * pad)] for __ in range(c)] for ___ in range(b)]
for bb in range(b):
for cc in range(c):
for i in range(pad, h + pad):
for j in range(pad, w + pad):
pad_x[bb][cc][i][j] = image[bb][cc][i - pad][j - pad]
# 构建特征图 b h w
out = [[[0] * out_w for _ in range(out_h)] for __ in range(b)]
# 滑动窗口实现卷积运算
for i in range(out_h):
for j in range(out_w):
# 生成在高度和宽度与卷积核同样大小的输入
roi_x = [[[[0] * kernel_size for ___ in range(kernel_size)] for __ in range(c)] for _ in range(b)]
for bb in range(b):
for cc in range(c):
row = 0
for hh in range(i * stride, i * stride + kernel_size):
col = 0
for ww in range(j * stride, j * stride + kernel_size):
roi_x[bb][cc][row][col] = pad_x[bb][cc][hh][ww]
col += 1
row += 1
# 在批量维度上执行互相关运算,输出通道为1
for bb in range(len(roi_x)):
out[bb][i][j] = crossCorrelation(roi_x[bb], conv_kernel)
return out
测试代码
image = [[[[w + h * 3 + c * 3 for w in range(3)] for h in range(3)] for c in range(3)]]
conv_kernel = [[[1] * 3 for h in range(3)] for c in range(3)]
print(image)
print(conv_kernel)
print(conv2d(image, conv_kernel, stride=1))
如果想要多个输出通道,可以遍历函数conv2d,传入不同的卷积核。
# for conv_kernel in conv_kernels:
# conv2d(image, conv_kernel, stride=1)