我们知道 Conv1×1 可以使数据升维和降维,如何做到这一点呢?其实很简单。
首先,随机创建一个torch张量,其有两个通道,每个通道都是一组 2×2 的数据。即:
import torch
input = torch.randint(0, 3, (2, 2, 2)).float()
print('input:\n', input)
输出:
input:
tensor([[[0., 1.],
[1., 2.]],
[[1., 0.],
[0., 0.]]])
接下来创建一个 1×1的卷积操作。
import torch.nn as nn
conv = nn.Conv2d(in_channels=2, out_channels=3, kernel_size=1, bias=False)
# print weight size
print(conv.weight.data.size())
# print value of weight
print(conv.weight.data)
输出:
torch.Size([3, 2, 1, 1])
tensor([[[[0.2874]],
[[0.3226]]],
[[[0.6905]],
[[0.0917]]],
[[[0.5057]],
[[0.3510]]]])
可以看到一共有3组卷积核,每组有2个 1×1 卷积核。它们究竟是怎么做到使数据从2个通道升维到3个通道的呢?接下来我们进行卷积运算看一看运算结果:
output = conv(input)
print(output)
输出:
tensor([[[0.3226, 0.2874],
[0.2874, 0.5747]],
[[0.0917, 0.6905],
[0.6905, 1.3809]],
[[0.3510, 0.5057],
[0.5057, 1.0114]]], grad_fn=<SqueezeBackward1>)
这时注意到:
0.3226 = 0×0.2874 + 1×0.3226
0.2874 = 1×0.2874 + 0×0.3226
0.2874 = 1×0.2874 + 0×0.3226
0.5747 = 2×0.2874 + 0×0.3226
……
在其余的通道也可以发现同样的规律。这意味着什么?
1. 原始数据形状是[2,2,2], 即两个通道,每个通道都是一个2×2矩阵
2. 指定的输出通道是3, 所以产生了3组卷积核,每组包含2个 1×1 形状的卷积核
3. 现在来看第一组卷积核都干了什么。0.2874 这个卷积核在输入数据的第一个通道(2×2)上进行卷积操作之后获得了一个形状为 2×2 的 feature map:
4. 0.3226 在输入数据的第二个通道(2×2)进行卷积操作后也得到了一个形状为 2×2 的 feature map:
5. 接下来把0.2874 和 0.3226 产出的两个 feature map 直接相加,得到一个形状为 2×2 的结果:, 这就是我们输出的第1个通道的值。
6. 第2个通道即是用第2组卷积核分别在原数据上进行卷积操作,然后把结果相加,etc.
我们发现,只要改变卷积核的数量,我们就可以改变输出的通道数。