赞
踩
目录
通常来说,我们会用到彩色图片,彩色图像一般是由RGB三个通道组成的。彩色图片一般会有更加丰富的信息。
但是转换为灰度会丢失信息,所以在图片的表示中通道数应该是3。我们之前都是只用了一个通道,简单图片对于单通道来说还是ok的,但是对于复杂图像就不行了。
假设图片的大小为200x200,那么图像的张量表示应该是200x200x3,不仅仅是一个简单的矩阵了。
当输入有了多个通道之后,假设有2个通道,Input中,前面的是通道0,后面是通道1。那么每个通道就会需要一个卷积核,比如针对通道0的卷积核对通道0做卷积,针对通道1的卷积核对通道1做卷积。再按元素相加,得到我们最终的结果。
① 核的通道数与输入的通道数一样。如果有多个通道,每一个通道都有一个卷积核,结果是所有通道卷积结果的和。
我们假设:
卷积核也会对应的变成三维的矩阵,但是输出是一个单通道,因为不管输入是多少通道,输出是把结果相加之后产生的。也就是说对每一个通道,把它对应的输入和对应的核做卷积,再按元素相加起来,得到输出。
无论有多少通道的输入,到目前为止不论有多少输入通道,我们只会得到单输出通道。
如果我们希望输出是多维的,得到多输出通道该怎么办呢?
做法是对每一个输出都有一个自己的三维的卷积核,总共设置多个三维的卷积核,每一个卷积核计算出来的结果作为一个通道,把每一个通道一一做运算,再把它们concat起来得到我们的输出。
相比于之前单输出通道多了一个参数Co。输出通道数,即卷积核的个数是卷积层的另一个超参数。
输出里面的第i个通道,其实就是完整的输入X与对应第i个核,做多输入的卷积,然后对所有的i做遍历。
这样就得到了多输出通道的结果。
那为什么要这么做呢?
我们可以认为每一个通道识别出来的都是一些特殊的模式,这是输出通道干的事情。
多输入通道干什么呢?假设我把这6个通道丢给下一层,下一层要把这每个模式识别出来并组合起来,得到一个组合的模式序列。
当然, 每一层有多个输出通道时至关重要的。
在最流行的神经网络架构中,随着神经网络层数的加深,我们常会增加输出通道的维数,通过减少空间分别率以获得更大的通道深度。
比如说,我们可以先识别猫的胡须,耳朵,再往上走的话,把这些纹理组合起来,在上层的一些卷积层可能就是识别的猫头。
直观地说,我们可以将每个通道看作是对不同特征的响应。而现实可能更为复杂一些,因为每个通道不是独立学习的,而是为了共同使用而优化的。
因此,多输出通道并不仅是学习多个单通道的检测器。
(1,1)的卷积核是一个常用的卷积核,它并不能识别空间信息,它的作用是融合通道。
因为1x1卷积层每次只识别一个像素,而不查看该像素与周围像素的关系,所以它并不识别空间信息。
等价于把整个NhxNw的输入拉成一个长为NhNw的向量,也就是说空间信息没有了,然后通道数拉成特征数Ci。将卷积核重新写成CoxCi,等价于输入为NhxNwxCi,权重为CoxCi的全连接层。
模型存储挺小的,但是计算量不一定小。
(1)实现一下多输入通道互相关运算:
假设X和K是3D的,将X和K zip起来,每次zip就会对输入通道的维数进行遍历,即每次for拿出对应的输入通道里的那个小矩阵,然后把它做互运算,对所有通道求和。
- import torch
- from d2l import torch as d2l
-
- def corr2d_multi_in(X, K):
- return sum(d2l.corr2d(x, k) for x,k in zip(X,K))
(2)验证互相关运算的输出:
- # 这是多通道输入(2,3,3),单通道输出(2,2),两个核(2,2)
- # 一个核对应一个通道
- 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]]])
-
- corr2d_multi_in(X, K)
(3)计算多个通道的输出的互相关函数:
假设X是3D的,K是4D的,最外层是输出通道数。
对每一个输出通道的K拿出它对应的3D核K。
现在一个核有多个通道,每一个核的形状是(2, 2, 2),共有三个核(3,2,2,2)。
一个卷积核经过一次操作后会得到单通道的输出(2,2),下面这个操作就是让每一个卷积核得到的一个通道的输出,堆叠起来----------(3,2,2)。
- # 计算多个通道的输入输出
- def corr2d_multi_in_out(X,K):
- # 现在一个核有多个通道,每一个核的形状是(2, 2, 2),共有三个核(3,2,2,2)
- # 一个卷积核经过一次操作后会得到单通道的输出(2,2)
- # 下面这个操作就是让每一个卷积核得到的一个通道的输出,堆叠起来。(3,2,2)
- return torch.stack([corr2d_multi_in(X,k) for k in K])
-
- # 创建三个卷积核
- K = torch.stack((K, K + 1, K + 2), 0)
- print(type(K))
- # print(K.shape)
-
- corr2d_multi_in_out(X,K)
help(torch.stack)
验证1X1卷积等价于一个全连接?
- # 1*1的卷积核
- def corr2d_multi_in_out_1x1(X, K):
- c_i,h,w = X.shape
- c_o = K.shape[0] # K的第一个shape
- # 这里就是将一张图片的高和宽展平成一条直线(3, 3, 3) => (3,9)
- # 剩下(channel_num,input)
- X = X.reshape((c_i,h*w)) # c_i是通道数
- # 把卷积核的高和宽展平成一条直线(2, 3, 1, 1) => (2,3),剩下(kernel_num, kernnel),就变成了一个矩阵
- K = K.reshape((c_o,c_i)) # 这样,X和K都是一个矩阵了
- Y = torch.matmul(K,X) # 那么直接调用矩阵乘法,将K*X
- print(X.shape,K.shape,Y.shape)
- return Y.reshape((c_o, h, w)) # 最后的Y
-
- X = torch.normal(0, 1, (3, 3, 3))
- K = torch.normal(0, 1, (2, 3, 1, 1)) # 输出通道为2,输入通道为3,核为1x1
-
- Y1 = corr2d_multi_in_out_1x1(X, K)
- Y2 = corr2d_multi_in_out(X, K)
- assert float(torch.abs(Y1 - Y2).sum()) < 1e-6 # 如果小于1e-6,可以认为几乎完全一样
- def comp_conv2d(conv2d, X): # conv2d 作为传参传进去,在内部使用
- X = X.reshape((1,1)+X.shape) # 在维度前面加入一个通道数和批量大小数
- Y = conv2d(X) # 卷积处理是一个四维的矩阵
- return Y.reshape(Y.shape[2:]) # 将前面两个维度拿掉
-
- X = torch.rand(size=(8,8))
- conv2d = nn.Conv2d(1,1,kernel_size=3,padding=1,stride=2) # Pytorch里面卷积函数的第一个参数为输出通道,第二个参数为输入通道
- print(comp_conv2d(conv2d,X).shape)
-
- conv2d = nn.Conv2d(1,1,kernel_size=(3,5),padding=(0,1),stride=(3,4)) # 一个稍微复杂的例子
- print(comp_conv2d(conv2d,X).shape)
输入的高宽都减半的情况下,通常会把输出的通道数加一倍。(空间信息压缩了,把提取出来的信息保存在更多的通道里)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。