卷积操作转换为矩阵乘法
flyfish
示例代码
import torch
import torch.nn as nn
import numpy as np
# 定义输入图像
input_image = torch.tensor([
[1, 2, 3, 0, 1],
[0, 1, 2, 3, 4],
[2, 3, 0, 1, 2],
[1, 2, 3, 4, 0],
[0, 1, 2, 3, 4]
], dtype=torch.float32).unsqueeze(0).unsqueeze(0) # 添加批次和通道维度
# 定义卷积核
conv_kernel = torch.tensor([
[1, 0, -1],
[1, 0, -1],
[1, 0, -1]
], dtype=torch.float32).unsqueeze(0).unsqueeze(0) # 添加输入和输出通道维度
# 创建卷积层
conv_layer = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=0, bias=False)
# 将卷积核的权重设置为自定义值
with torch.no_grad():
conv_layer.weight = nn.Parameter(conv_kernel)
# 进行卷积操作
output_tensor = conv_layer(input_image)
# 打印输出结果
print("卷积操作输出结果:")
print(output_tensor.squeeze().detach().numpy())
# 使用 im2col 展开输入图像
def im2col(input_data, kernel_size, stride=1, padding=0):
# 添加填充
input_data = np.pad(input_data, ((padding, padding), (padding, padding)), 'constant')
H, W = input_data.shape
out_h = (H - kernel_size) // stride + 1
out_w = (W - kernel_size) // stride + 1
col = np.zeros((out_h * out_w, kernel_size * kernel_size))
out = 0
for y in range(0, H - kernel_size + 1, stride):
for x in range(0, W - kernel_size + 1, stride):
patch = input_data[y:y + kernel_size, x:x + kernel_size].flatten()
col[out, :] = patch
out += 1
return col
# 转换输入图像为 numpy 数组
input_image_np = input_image.squeeze().numpy()
# 展开输入图像
input_col = im2col(input_image_np, 3)
# 展开卷积核
kernel_col = conv_kernel.squeeze().flatten().numpy()
# 进行矩阵乘法
output_col = np.dot(input_col, kernel_col)
# 变形输出以匹配卷积结果形状
output_matrix = output_col.reshape(3, 3)
print("矩阵操作输出结果:")
print(output_matrix)
卷积操作输出结果:
[[-2. 2. -2.]
[-2. -2. -1.]
[-2. -2. -1.]]
矩阵操作输出结果:
[[-2. 2. -2.]
[-2. -2. -1.]
[-2. -2. -1.]]
卷积操作可以转换为矩阵乘法,这是通过一种称为“im2col”的方法实现的。im2col方法将卷积操作展开成矩阵乘法。接下来,我们将手动计算的卷积操作转换成矩阵乘法,并使用PyTorch代码进行验证。
手动卷积计算转换成矩阵操作
首先,我们将输入图像和卷积核转换成适合矩阵乘法的形式。
输入图像和卷积核
输入图像
I
I
I:
[
1
2
3
0
1
0
1
2
3
4
2
3
0
1
2
1
2
3
4
0
0
1
2
3
4
]
\begin{bmatrix} 1 & 2 & 3 & 0 & 1 \\ 0 & 1 & 2 & 3 & 4 \\ 2 & 3 & 0 & 1 & 2 \\ 1 & 2 & 3 & 4 & 0 \\ 0 & 1 & 2 & 3 & 4 \\ \end{bmatrix}
1021021321320320314314204
卷积核
K
K
K:
[
1
0
−
1
1
0
−
1
1
0
−
1
]
\begin{bmatrix} 1 & 0 & -1 \\ 1 & 0 & -1 \\ 1 & 0 & -1 \\ \end{bmatrix}
111000−1−1−1
使用 im2col 展开输入图像
为了方便进行矩阵乘法操作,我们将输入图像展开为一个矩阵,其中每一行表示卷积核在输入图像上每个可能位置的局部感受野。
展开后的矩阵
I
c
o
l
I_{col}
Icol:
[
1
2
3
0
1
2
3
0
1
0
1
2
3
4
1
2
3
4
2
3
0
1
2
3
0
1
2
1
2
3
0
1
2
3
4
0
0
1
2
3
4
1
2
3
4
2
3
0
1
2
3
0
1
2
1
2
3
0
1
2
3
4
0
0
1
2
3
4
1
2
3
4
]
\begin{bmatrix} 1 & 2 & 3 & 0 & 1 & 2 & 3 & 0 & 1 \\ 0 & 1 & 2 & 3 & 4 & 1 & 2 & 3 & 4 \\ 2 & 3 & 0 & 1 & 2 & 3 & 0 & 1 & 2 \\ 1 & 2 & 3 & 0 & 1 & 2 & 3 & 4 & 0 \\ 0 & 1 & 2 & 3 & 4 & 1 & 2 & 3 & 4 \\ 2 & 3 & 0 & 1 & 2 & 3 & 0 & 1 & 2 \\ 1 & 2 & 3 & 0 & 1 & 2 & 3 & 4 & 0 \\ 0 & 1 & 2 & 3 & 4 & 1 & 2 & 3 & 4 \\ \end{bmatrix}
102102102132132132032032031031031421421421321321320320320314314314204204
卷积核展开为行向量
K
r
o
w
K_{row}
Krow:
[
1
0
−
1
1
0
−
1
1
0
−
1
]
\begin{bmatrix} 1 & 0 & -1 & 1 & 0 & -1 & 1 & 0 & -1 \end{bmatrix}
[10−110−110−1]
进行矩阵乘法
我们将 I c o l I_{col} Icol 与 K r o w K_{row} Krow 进行矩阵乘法,得到卷积结果:
O = I c o l ⋅ K r o w T O = I_{col} \cdot K_{row}^T O=Icol⋅KrowT
im2col 是一种将图像块展平为列的技术,用于将卷积操作转换为矩阵乘法。这个过程特别常用于实现卷积神经网络中的高效卷积操作。im2col 的核心思想是将输入图像的每个局部感受野(与卷积核相同大小的子矩阵)展开为一个列向量,然后将这些列向量拼接成一个大矩阵。通过这种方式,卷积操作可以简化为矩阵乘法。
im2col 说明
上面使用im2col这里做一个说明
假设我们有一个输入图像 I I I 和一个卷积核 K K K。我们希望使用卷积核在输入图像上进行卷积操作。卷积核在图像上滑动,每次计算一个局部区域(感受野)与卷积核的点积,生成一个输出值。
为了实现这一点,im2col 将输入图像中的每个局部感受野展平成一个列向量,然后将所有这些列向量堆叠在一起,形成一个矩阵。这样,卷积操作就变成了矩阵乘法。
im2col 实例
假设输入图像 I I I 为 4 × 4 4 \times 4 4×4 矩阵,卷积核 K K K 为 2 × 2 2 \times 2 2×2 矩阵,步长为 1,填充为 0。
输入图像
I
I
I:
[
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
]
\begin{bmatrix} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \\ 9 & 10 & 11 & 12 \\ 13 & 14 & 15 & 16 \\ \end{bmatrix}
15913261014371115481216
卷积核
K
K
K:
[
1
0
0
−
1
]
\begin{bmatrix} 1 & 0 \\ 0 & -1 \\ \end{bmatrix}
[100−1]
使用 im2col 展开输入图像
我们将每个
2
×
2
2 \times 2
2×2 的局部感受野展平成一个列向量,并将这些列向量堆叠成一个矩阵。展开后的矩阵
I
c
o
l
I_{col}
Icol:
[
1
2
5
6
2
3
6
7
3
4
7
8
5
6
9
10
6
7
10
11
7
8
11
12
9
10
13
14
10
11
14
15
11
12
15
16
]
\begin{bmatrix} 1 & 2 & 5 & 6 \\ 2 & 3 & 6 & 7 \\ 3 & 4 & 7 & 8 \\ 5 & 6 & 9 & 10 \\ 6 & 7 & 10 & 11 \\ 7 & 8 & 11 & 12 \\ 9 & 10 & 13 & 14 \\ 10 & 11 & 14 & 15 \\ 11 & 12 & 15 & 16 \\ \end{bmatrix}
1235679101123467810111256791011131415678101112141516
卷积核
K
K
K 展开为行向量
K
r
o
w
K_{row}
Krow:
[
1
0
0
−
1
]
\begin{bmatrix} 1 & 0 & 0 & -1 \end{bmatrix}
[100−1]
进行矩阵乘法
O
=
I
c
o
l
⋅
K
r
o
w
T
O = I_{col} \cdot K_{row}^T
O=Icol⋅KrowT
结果
O
O
O 将是一个列向量:
O
=
[
1
⋅
1
+
2
⋅
0
+
5
⋅
0
+
6
⋅
(
−
1
)
2
⋅
1
+
3
⋅
0
+
6
⋅
0
+
7
⋅
(
−
1
)
3
⋅
1
+
4
⋅
0
+
7
⋅
0
+
8
⋅
(
−
1
)
5
⋅
1
+
6
⋅
0
+
9
⋅
0
+
10
⋅
(
−
1
)
6
⋅
1
+
7
⋅
0
+
10
⋅
0
+
11
⋅
(
−
1
)
7
⋅
1
+
8
⋅
0
+
11
⋅
0
+
12
⋅
(
−
1
)
9
⋅
1
+
10
⋅
0
+
13
⋅
0
+
14
⋅
(
−
1
)
10
⋅
1
+
11
⋅
0
+
14
⋅
0
+
15
⋅
(
−
1
)
11
⋅
1
+
12
⋅
0
+
15
⋅
0
+
16
⋅
(
−
1
)
]
=
[
1
−
6
2
−
7
3
−
8
5
−
10
6
−
11
7
−
12
9
−
14
10
−
15
11
−
16
]
=
[
−
5
−
5
−
5
−
5
−
5
−
5
−
5
−
5
−
5
]
O = \begin{bmatrix} 1 \cdot 1 + 2 \cdot 0 + 5 \cdot 0 + 6 \cdot (-1) \\ 2 \cdot 1 + 3 \cdot 0 + 6 \cdot 0 + 7 \cdot (-1) \\ 3 \cdot 1 + 4 \cdot 0 + 7 \cdot 0 + 8 \cdot (-1) \\ 5 \cdot 1 + 6 \cdot 0 + 9 \cdot 0 + 10 \cdot (-1) \\ 6 \cdot 1 + 7 \cdot 0 + 10 \cdot 0 + 11 \cdot (-1) \\ 7 \cdot 1 + 8 \cdot 0 + 11 \cdot 0 + 12 \cdot (-1) \\ 9 \cdot 1 + 10 \cdot 0 + 13 \cdot 0 + 14 \cdot (-1) \\ 10 \cdot 1 + 11 \cdot 0 + 14 \cdot 0 + 15 \cdot (-1) \\ 11 \cdot 1 + 12 \cdot 0 + 15 \cdot 0 + 16 \cdot (-1) \\ \end{bmatrix} =\begin{bmatrix} 1 - 6 \\ 2 - 7 \\ 3 - 8 \\ 5 - 10 \\ 6 - 11 \\ 7 - 12 \\ 9 - 14 \\ 10 - 15 \\ 11 - 16 \\ \end{bmatrix} =\begin{bmatrix} -5 \\ -5 \\ -5 \\ -5 \\ -5 \\ -5 \\ -5 \\ -5 \\ -5 \\ \end{bmatrix}
O=
1⋅1+2⋅0+5⋅0+6⋅(−1)2⋅1+3⋅0+6⋅0+7⋅(−1)3⋅1+4⋅0+7⋅0+8⋅(−1)5⋅1+6⋅0+9⋅0+10⋅(−1)6⋅1+7⋅0+10⋅0+11⋅(−1)7⋅1+8⋅0+11⋅0+12⋅(−1)9⋅1+10⋅0+13⋅0+14⋅(−1)10⋅1+11⋅0+14⋅0+15⋅(−1)11⋅1+12⋅0+15⋅0+16⋅(−1)
=
1−62−73−85−106−117−129−1410−1511−16
=
−5−5−5−5−5−5−5−5−5