自学内容网 自学内容网

通道 (卷积层里的多个输入/输出通道) + 代码 ——笔记2.8《动手学深度学习》

目录

0. 前言

1. 多输入通道

1.1 补充:通过debug深入学习

2. 多输出通道

3. 1×1 卷积层

4. 补充:二维卷积层

5. 小结


0. 前言

:label:sec_channels

虽然我们在 :numref:subsec_why-conv-channels中描述了构成每个图像的多个通道和多层卷积层。例如彩色图像具有标准的RGB通道来代表红、绿和蓝。 但是到目前为止,我们仅展示了单个输入和单个输出通道的简化例子。 这使得我们可以将输入、卷积核和输出看作二维张量。

当我们添加通道时,我们的输入和隐藏的表示都变成了三维张量。例如,每个RGB输入图像具有3×ℎ×𝑤的形状。我们将这个大小为3的轴称为通道(channel)维度。本节将更深入地研究具有多输入和多输出通道的卷积核。

fd0e3e0dd90a4ef3b0f9278c04a10846.png069ad71cef31410b92f01f42e8065cd5.png

1. 多输入通道

当输入包含多个通道时,需要构造一个与输入数据具有相同输入通道数的卷积核,以便与输入数据进行互相关运算。假设输入的通道数为𝑐𝑖,那么卷积核的输入通道数也需要为𝑐𝑖。如果卷积核的窗口形状是𝑘ℎ×𝑘𝑤,那么当𝑐𝑖=1时,我们可以把卷积核看作形状为𝑘ℎ×𝑘𝑤的二维张量。

然而,当𝑐𝑖>1时,我们卷积核的每个输入通道将包含形状为𝑘ℎ×𝑘𝑤的张量。将这些张量𝑐𝑖连结在一起可以得到形状为𝑐𝑖×𝑘ℎ×𝑘𝑤的卷积核。由于输入和卷积核都有𝑐𝑖个通道,我们可以对每个通道输入的二维张量和卷积核的二维张量进行互相关运算,再对通道求和(将𝑐𝑖的结果相加)得到二维张量。这是多通道输入和多输入通道卷积核之间进行二维互相关运算的结果。

在 :numref:fig_conv_multi_in中,我们演示了一个具有两个输入通道的二维互相关运算的示例。阴影部分是第一个输出元素以及用于计算这个输出的输入和核张量元素:

(1×1+2×2+4×3+5×4)+(0×0+1×1+3×2+4×3)=56

a48d285d2e3c46b6aaf76793f6ab6391.png

ecf0a65d364541779ffef95a21079ef9.png

:label:fig_conv_multi_in

为了加深理解,我们(实现一下多输入通道互相关运算)。 简而言之,我们所做的就是对每个通道执行互相关操作,然后将结果相加。

In [1]:

import torch

from d2l import torch as d2l

In [2]:

def corr2d_multi_in(X, K): # 输入X和K都是3D的

    # 先遍历“X”和“K”的第0个维度(输入通道维度);               # zip(X, K)将输入X,卷积核K按照第0维绑定起来
    return sum(d2l.corr2d(x, k) for x, k in zip(X, K))  # corr2d(x, k)是第“卷积层”定义的二维互相关
    # corr2d(x, k)传入的x是输入tensor.shape是(3,3), k是卷积核tensor.shape是(2,2), 都是2D的,不含通道维度

我们可以构造与 :numref:fig_conv_multi_in中的值相对应的输入张量X和核张量K,以(验证互相关运算的输出)。

In [3]:

X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]], # 创建tensor对象

               [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]]) # 输入有两个通道,都是3×3
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])  # 卷积核有两个通道,都是2×2
​
# 为了方便理解上一个代码块中zip(X, K),这里补充一些迭代器, for循环遍历的知识
Z = list(zip(X, K))
print(Z)  # 输出为列表; zip()将输入X,K按照第0维(输入通道维)绑定起来组合成迭代器
# 每次for x, k取出迭代器zip中绑定起来的两个tensor, 分别赋给x, k, 输入给corr2d(x, k)
# 用for取出x, k,放到3D维度上,可以理解为切片(把2×3×3的数据块X,切成两片3×3的数据片x,每次取出一片x)
​
# 可查看下方的具体调试 (pycharm的debug截图)来深入理解这里的代码
​
corr2d_multi_in(X, K)  # 这里复现了上图(多输入通道互相关)的计算过程
[(tensor([[0., 1., 2.],
        [3., 4., 5.],
        [6., 7., 8.]]), tensor([[0., 1.],
        [2., 3.]])), (tensor([[1., 2., 3.],
        [4., 5., 6.],
        [7., 8., 9.]]), tensor([[1., 2.],
        [3., 4.]]))]

Out[3]:

tensor([[ 56.,  72.],
        [104., 120.]])

1.1 补充:通过debug深入学习

  • 不去debug,就很难搞懂内部运算的逻辑
  • 咱这里主要关注 corr2d(x, k) 的计算过程细节
  • 主要关注输入变量(tensor对象)的形状(shape)
  • 还有输出,和变量的流转逻辑,why,how的问题

83304783b65448958a06e9a12a0577d8.png

能看到tensor.shape分别是:

  • 输入x:(3,3)
  • 卷积核k:(2,2)
  • debug调试代码,是为了深入理解代码,透彻了解代码后才能进一步改进代码
  • debug调试代码是基础,无论是做学术,还是工业界工作,调试代码都是必修课
  • 在开发过程中,调试(debug)花的时间一般会比开发的时间还要长

2. 多输出通道

到目前为止,不论有多少输入通道,我们还只有一个输出通道。然而,正如我们在 :numref:subsec_why-conv-channels中所讨论的,每一层有多个输出通道是至关重要的。在最流行的神经网络架构中,随着神经网络层数的加深,我们常会增加输出通道的维数,通过减少空间分辨率以获得更大的通道深度。直观地说,我们可以将每个通道看作对不同特征的响应。而现实可能更为复杂一些,因为每个通道不是独立学习的,而是为了共同使用而优化的。因此,多输出通道并不仅是学习多个单通道的检测器。

28b116bcb5bf4dc4821bca9dfbcdb31a.png

4900335827eb4148807b3bde436f549b.png

如下所示,我们实现一个[计算多个通道的输出的互相关函数]。

In [4]:

def corr2d_multi_in_out(X, K): # 多通道输出, 在多通道输入上更进一(小)步; X还是3D的,为了多个通道输出,K是4D的

    # 迭代“K”的第0个维度(输出通道维),每次都对输入“X”执行互相关运算
    # 最后将所有结果都叠加在一起
    return torch.stack([corr2d_multi_in(X, k) for k in K], 0)  # for每次迭代,都是沿着最外层0维拿出来k

通过将核张量KK+1K中每个元素加1)和K+2连接起来,构造了一个具有3个输出通道的卷积核。

In [5]:

K = torch.stack((K, K + 1, K + 2), 0) # 补充stack()的拼接用法(输入的K是3D的,为了演示方便,构造出三个3D张量)

K.shape  # 数字0表示位置索引; 数字0表示会在第0维(最外层的维度)上拼接三个3D张量(K、K+1、K+2)

Out[5]:

torch.Size([3, 2, 2, 2])

下面,我们对输入张量X与卷积核张量K执行互相关运算。现在的输出包含3个通道,第一个通道的结果与先前输入张量X和多输入单输出通道的结果一致。

In [6]:

corr2d_multi_in_out(X, K)

Out[6]:

tensor([[[ 56.,  72.],
         [104., 120.]],

        [[ 76., 100.],
         [148., 172.]],

        [[ 96., 128.],
         [192., 224.]]])

3. 1×1 卷积层

[1x1卷积]

cfba256885ec446fbedafd52b0f81d02.png

b74aac2bd8a843208f633d3f014fc9c2.png

:label:fig_conv_1x1

下面,我们使用全连接层实现1×1卷积。 请注意,我们需要对输入和输出的数据形状进行调整。

In [7]:

def corr2d_multi_in_out_1x1(X, K):  # 1×1卷积层,用全连接实现,可以理解为用来通道融合
    c_i, h, w = X.shape
    c_o = K.shape[0]  # c_o:输出通道数
    X = X.reshape((c_i, h * w))  # 将每个输入通道的2D张量,拉成1维, 再按照输入通道数c_i, 排成c_i排
    K = K.reshape((c_o, c_i))  # 因为每个通道1×1, 最内层的两个维度可以reshape掉
    # 全连接层中的矩阵乘法 (对比卷积, 卷积层的每个输出是点乘→求和)
    Y = torch.matmul(K, X)  # torch.matmul是矩阵乘法, 和点乘(torch.mul或者*)不一样, 需要满足K的列数=X的行数
    return Y.reshape((c_o, h, w))

当执行1×1卷积运算时,上述函数相当于先前实现的互相关函数corr2d_multi_in_out。让我们用一些样本数据来验证这一点。

In [8]:

# 均值为0,方差为1的初始化
X = torch.normal(0, 1, (3, 3, 3))  # 3个输入通道, 每个通道3×3; 实现上X是3×3×3的张量
K = torch.normal(0, 1, (2, 3, 1, 1))  # 2个输出通道,3个输入通道, 每个通道1×1; 实现上K是2×3的张量
​
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(torch.abs(Y1 - Y2).sum()) < 1e-6  # assert断言语句,这里没有报错,小于1e-6,可以认为Y1和Y2相等

4. 补充:二维卷积层

b1b1350cadac4e80b11649e777920574.png

5. 小结

  • 多输入多输出通道可以用来扩展卷积层的模型。
  • 当以每像素为基础应用时,1×1卷积层相当于全连接层。
  • 1×1卷积层通常用于调整网络层的通道数量和控制模型复杂性。
  • 输出通道数是卷积层的超参数。
  • 每个输入通道有独立的二维卷积核,所有通道结果相加得到一个输出通道结果。
  • 每个输出通道有独立的三维卷积核,具体的代码实现是把4D的卷积核K切片成3D的k(每个输出通道的卷积核),做和多通道输入一样的运算。


原文地址:https://blog.csdn.net/weixin_57972634/article/details/143052776

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!