从0开始深度学习(25)——多输入多输出通道
之前我们都只研究了一个通道的情况(二值图、灰度图),但实际情况中很多是彩色图像,即有标准的RGB三通道图片,本节将更深入地研究具有多输入和多输出通道的卷积核。
1 多输入通道
当输入包含多个通道时,需要构造一个与输入数据具有相同输入通道数的卷积核,以便与输入数据进行互相关运算。
当有多个通道时,我们可以对每个通道输入的二维张量和卷积核的二维张量进行互相关运算,再对通道求和得到二维张量,如下图所示:
下面是通过代码实现:
import torch
from d2l import torch as d2l
def corr2d_multi_in(X, K):
# 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起
return sum(d2l.corr2d(x, k) for x, k in zip(X, K))
X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]],
[[1.0, 2.0], [3.0, 4.0]]])
X = X.unsqueeze(0) # 增加一个维度,形状变为 (1, 2, 3, 3)
K = K.unsqueeze(0) # 增加一个维度,形状变为 (1, 2, 2, 2)
conv2d = nn.Conv2d(in_channels=2, out_channels=1, kernel_size=2, bias=False)
conv2d.weight.data =K
# 执行卷积操作
Y = conv2d(X)
# 输出卷积结果的形状
print("Output shape:", Y.shape)
# 如果需要查看具体的卷积结果,可以打印出来
print("Output tensor:")
print(Y)
运行结果
2 多输出通道
到目前为止,我们还只有一个输出通道。在最流行的神经网络架构中,随着神经网络层数的加深,我们常会增加输出通道的维数,通过减少空间分辨率以获得更大的通道深度。直观地说,我们可以将每个通道看作对不同特征的响应。
代码如下:
import torch
from d2l import torch as d2l
def corr2d_multi_in(X, K):
# 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起
return sum(d2l.corr2d(x, k) for x, k in zip(X, K))
X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = torch.tensor([
[[[0.0, 1.0], [2.0, 3.0]],
[[1.0, 2.0], [3.0, 4.0]]], # K
[[[1.0, 2.0], [3.0, 4.0]],
[[2.0, 3.0], [4.0, 5.0]]], # K + 1
[[[2.0, 3.0], [4.0, 5.0]],
[[3.0, 4.0], [5.0, 6.0]]] # K + 2
])
X = X.unsqueeze(0) # 增加一个维度,形状变为 (1, 2, 3, 3)
conv2d = nn.Conv2d(in_channels=2, out_channels=2, kernel_size=2, bias=False)
conv2d.weight.data =K
# 执行卷积操作
Y = conv2d(X)
# 输出卷积核的形状,第一个是参数输出通道,第二个参数是输入通道,后面两个是卷积的高度和宽度
print("kernle shape:", K.shape)
# 如果需要查看具体的卷积结果,可以打印出来
print("Output tensor:")
print(Y)
运行结果
3 1 × \times × 1 卷积核
1 × 1 1\times1 1×1的卷积核看似没有作用,实则可以起到一些对输入图像的通道进行一些操作
3.1 通道变换
使用 1x1 卷积核将通道数从 64 变为 128。
import torch
from torch import nn
# 输入特征图形状为 (1, 64, 28, 28)
x = torch.randn(1, 64, 28, 28)
# 使用 1x1 卷积核将通道数从 64 变为 128
conv1x1 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=1)
# 执行卷积操作
y = conv1x1(x)
# 输出形状为 (1, 128, 28, 28)
print(y.shape) # torch.Size([1, 128, 28, 28])
3.2 特征融合
1x1 卷积核可以用来融合不同通道的特征,使用 1x1 卷积核将通道数从 32 变为 16。
# 输入特征图形状为 (1, 32, 32, 32)
x = torch.randn(1, 32, 32, 32)
# 使用 1x1 卷积核将通道数从 32 变为 16
conv1x1 = nn.Conv2d(in_channels=32, out_channels=16, kernel_size=1)
# 执行卷积操作
y = conv1x1(x)
# 输出形状为 (1, 16, 32, 32)
print(y.shape) # torch.Size([1, 16, 32, 32])
3.3 提升计算效率
使得 1x1 卷积核在深度网络中可以高效地减少参数数量和计算成本,假设我们有一个大通道数的特征图,使用 1x1 卷积核可以显著减少后续层的计算负担,最后在进行升维。
# 输入特征图形状为 (1, 256, 14, 14)
x = torch.randn(1, 256, 14, 14)
# 使用 1x1 卷积核将通道数从 256 减少到 64
conv1x1_reduce = nn.Conv2d(in_channels=256, out_channels=64, kernel_size=1)
x = conv1x1_reduce(x)
# 应用 3x3 卷积核
conv3x3 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1)
x = conv3x3(x)
# 使用 1x1 卷积核将通道数从 64 恢复到 256
conv1x1_expand = nn.Conv2d(in_channels=64, out_channels=256, kernel_size=1)
x = conv1x1_expand(x)
# 输出形状为 (1, 256, 14, 14)
print(x.shape) # torch.Size([1, 256, 14, 14])