之前我们用到的输入和输出都是二维数组,但真实数据的维度经常更高。
比如彩色图像就有(RGB)三个通道。
(一)多输入通道
下图数两个输入通道的卷积过程:
过程大概就是在每个通道上卷积核与输入进行互相关运算后,把得到的输出数组相加,这样得到的输出数组其实是只有一个通道的。
通道数继续增加过程类似。
代码过程:
import d2lzh as d2l
from mxnet import nd
def corr2d_multi_in(X, K):
# mxnet.ndarray.add_n(*args, **kwargs)实现的功能是;add_n(a1,a2,...,an)=a1+a2+...+an
return nd.add_n(*[d2l.corr2d(x, k) for x, k in zip(X, K)])
#首先利用之前的corr2d函数对每一个通道的x和k进行互相关运算,然后利用add_n函数进行相加
输出为:
(二)多输出通道
我们知道上面的方法得到的输出通道只有一个,那么为了得到多输出通道,设卷积核输入通道数和输出通道数分别为ci和co,高和宽分别为kh和kw。如果希望得到含多个通道的输出,我们可以为每个输出通道分别创建形状为ci×kh×kw的核数组。将它们在输出通道维上连结,卷积核的形状即co×ci×kh×kw。
在这个过程中需要用到nd的stack函数,先补一下stack函数的功能:
def corr2d_multi_in_out(X, K):
return nd.stack(*[corr2d_multi_in(X, k) for k in K])
# 对于K的第一个维度co进行遍历,所以就是用每一个维度为ci×kh×kw的卷积核与X卷积,这个过程与上面的多输入通道一致。
# 得到了co个单通道输出数组后利用stack进行整合。
K = nd.stack(K, K + 1, K + 2)
#由于之前假设的2×2×2的形状,所以为了得到3通道的输出数组,把卷积核的第一维数变成3
#K+1是K的每个元素加1
K.shape #得到的K的形状现在应该是3×2×2×2
corr2d_multi_in_out(X, K)
(三)1×1卷积层
卷积层的长和宽均为1×1时称为1×1卷积层。假设将通道维当作特征维,将高和宽维度上的元素当成数据样本,那么1×1卷积层的作用与全连接层等价。一般用1×1卷积层来进行信道压缩,信道降维。
怎么理解这个与全连接层等价呢?
从这张图上理解,输入有三个维度,输出有两个维度,所以卷积核为2×3×长宽。
浅蓝色的输出是由输入的浅蓝色方块与该维度的浅蓝色核相乘后相加得到的,形如
x1×w1+x2×w2+x3×w3。
从代码上理解:
先对X和K进行定义:
X = nd.random.uniform(shape=(3, 3, 3))
K = nd.random.uniform(shape=(2, 3, 1, 1))
X:
K:
再来看看计算过程:
def corr2d_multi_in_out_1x1(X, K):
c_i, h, w = X.shape # c_i,h,w分别获得X的输入维度,长,宽
c_o = K.shape[0] # c_o获得输出维度
X = X.reshape((c_i, h * w)) #对X和K进行整形,便于dot运算,整形结果如下图
K = K.reshape((c_o, c_i))
Y = nd.dot(K, X) # 全连接层的矩阵乘法
return Y.reshape((c_o, h, w))
对Y整形后:
在图中看到X和K整形后的结果,我们来看一下dot运算和图解是否一致:
在dot运算中,
X中每一行第一个元素(黑色框)(其实就是未整形X的每一维的第一行第一个元素)与Y第一行相乘后相加得到的是Y中第一行第一个元素(其实也是Y的每一维的第一行第一个元素)
X中每一行第四个元素(红色框)(其实就是未整形X的每一维的第二行第一个元素)与Y第一行相乘后相加得到的是Y中第一行第四个元素(其实也是Y的每一维的第二行第一个元素)
X中每一行第七个元素(紫色框)(其实就是未整形X的每一维的第三行第一个元素)与Y第一行相乘后相加得到的是Y中第一行第七个元素(其实也是Y的每一维的第三行第一个元素)
可以看出这个算法和上面的图解以及全连接层的运算方法一致。