核心内容来自博客链接1博客连接2希望大家多多支持作者
本文记录用,防止遗忘
0
×
0
+
1
×
1
+
3
×
2
+
4
×
3
=
19
1
×
0
+
2
×
1
+
4
×
2
+
5
×
3
=
25
3
×
0
+
4
×
1
+
6
×
2
+
7
×
3
=
37
4
×
0
+
5
×
1
+
7
×
2
+
8
×
3
=
43
0 × 0 + 1 × 1 + 3 × 2 + 4 × 3 = 19\\ 1 × 0 + 2 × 1 + 4 × 2 + 5 × 3 = 25\\ 3 × 0 + 4 × 1 + 6 × 2 + 7 × 3 = 37\\ 4 × 0 + 5 × 1 + 7 × 2 + 8 × 3 = 43\\
0×0+1×1+3×2+4×3=191×0+2×1+4×2+5×3=253×0+4×1+6×2+7×3=374×0+5×1+7×2+8×3=43
输入
X
:
n
h
×
n
w
\mathbf{X}:n_h \times n_w
X:nh×nw
核
W
:
k
h
×
k
w
\mathbf{W}:k_h \times k_w
W:kh×kw
偏差
b
∈
R
b\in\mathbb{R}
b∈R
输出
Y
:
(
n
h
−
k
h
+
1
)
×
(
n
w
−
k
w
+
1
)
\mathbf{Y}:(n_h-k_h+1)\times(n_w-k_w+1)
Y:(nh−kh+1)×(nw−kw+1)
Y
=
X
∗
W
+
b
\mathbf{Y=X * W}+b
Y=X∗W+b
其中,
W
和
b
\mathbf{W}和b
W和b是可以学习的参数
原图如下:
二维交叉相关:
y
i
,
j
=
∑
a
=
1
h
∑
b
=
1
w
w
a
,
b
x
i
+
a
,
j
+
b
y_{i,j}=\sum_{a=1}^h\sum_{b=1}^ww_{a,b}x_{i+a,j+b}
yi,j=a=1∑hb=1∑wwa,bxi+a,j+b
二维卷积
y
i
,
j
=
∑
a
=
1
h
∑
b
=
1
w
w
−
a
,
−
b
x
i
+
a
,
j
+
b
y_{i,j}=\sum_{a=1}^h\sum_{b=1}^ww_{-a,-b}x_{i+a,j+b}
yi,j=a=1∑hb=1∑ww−a,−bxi+a,j+b
由于对称性,在实际使用中没有区别
y
i
=
∑
a
=
1
h
w
a
x
i
+
a
y_i=\sum_{a=1}^hw_ax_{i+a}
yi=∑a=1hwaxi+a
文本、语言、时间序列
y
i
,
j
,
k
=
∑
a
=
1
h
∑
b
=
1
w
∑
c
=
1
d
w
a
,
b
,
c
x
i
+
a
,
j
+
b
,
k
+
c
y_{i,j,k}=\sum_{a=1}^h \sum_{b=1}^w \sum_{c=1}^dw_{a,b,c}x_{i+a,j+b,k+c}
yi,j,k=∑a=1h∑b=1w∑c=1dwa,b,cxi+a,j+b,k+c
视频、医学图像、气象地图
1、卷积层将输入和核矩阵进行交叉相关,加上偏移后得到输出
2、核矩阵和偏移是可学习的参数
3、核矩阵的大小是超参数
严格来说,卷积层是个错误的叫法,因为它所表达的运算其实是互相关运算(cross-correlation),而不是卷积运算。 根据上一节中的描述,在卷积层中,输入张量和核张量通过互相关运算产生输出张量。
虽然卷积层得名于卷积(convolution)运算,但我们通常在卷积层中使用更加直观的互相关(cross-correlation)运算。在二维卷积层中,一个二维输入数组和一个二维核(kernel)数组通过互相关运算输出一个二维数组。
首先,我们暂时忽略通道(第三维)这一情况,看看如何处理二维图像数据和隐藏表示。在下图中,输入是高度为3、宽度为3的二维张量(即形状为
3
×
3
3 \times 3
3×3)。卷积核的高度和宽度都是2,而卷积核窗口(或卷积窗口)的形状由内核的高度和宽度决定(即
2
×
2
2 \times 2
2×2)。
0
×
0
+
1
×
1
+
3
×
2
+
4
×
3
=
19
0\times0+1\times1+3\times2+4\times3=19
0×0+1×1+3×2+4×3=19
在二维互相关运算中,卷积窗口从输入张量的左上角开始,从左到右、从上到下滑动。 当卷积窗口滑动到新一个位置时,包含在该窗口中的部分张量与卷积核张量进行按元素相乘,得到的张量再求和得到一个单一的标量值,由此我们得出了这一位置的输出张量值。 在如上例子中,输出张量的四个元素由二维互相关运算得到,这个输出高度为2、宽度为2。
注意,输出大小略小于输入大小。这是因为卷积核的宽度和高度大于1, 而卷积核只与图像中每个大小完全适合的位置进行互相关运算。 所以,输出大小等于输入大小
n
h
×
n
w
n_h \times n_w
nh×nw减去卷积核大小
k
h
×
k
w
k_h \times k_w
kh×kw,即:
(
n
h
−
k
h
+
1
)
×
(
n
w
−
k
w
+
1
)
.
(n_h-k_h+1) \times (n_w-k_w+1).
(nh−kh+1)×(nw−kw+1).
这是因为我们需要足够的空间在图像上“移动”卷积核。稍后,我们将看到如何通过在图像边界周围填充零来保证有足够的空间移动卷积核,从而保持输出大小不变。 接下来,我们在corr2d
函数中实现如上过程,该函数接受输入张量X和卷积核张量K,并返回输出张量Y。
import torch
from torch import nn
from d2l import torch as d2l
def corr2d(X, K): #@save
"""计算二维互相关运算"""
h, w = K.shape
Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
return Y
我们来验证上述二维互相关运算的输出
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
corr2d(X, K)
输出:
tensor([[19., 25.],
[37., 43.]])
卷积层对输入和卷积核权重进行互相关运算,并在添加标量偏置之后产生输出。 所以,卷积层中的两个被训练的参数是卷积核权重和标量偏置。 就像我们之前随机初始化全连接层一样,在训练基于卷积层的模型时,我们也随机初始化卷积核权重。
基于上面定义的corr2d
函数实现二维卷积层。在__init__
构造函数中,将weight
和bias
声明为两个模型参数。前向传播函数调用corr2d
函数并添加偏置。
class Conv2D(nn.Module):
def __init__(self, kernel_size):
super().__init__()
self.weight = nn.Parameter(torch.rand(kernel_size))
self.bias = nn.Parameter(torch.zeros(1))
def forward(self, x):
return corr2d(x, self.weight) + self.bias
高度和宽度分别为h和w的卷积核可以被称为 h × w h \times w h×w卷积或 h × w h \times w h×w卷积核。 我们也将带有 h × w h \times w h×w卷积核的卷积层称为 h × w h \times w h×w卷积层。
如下是卷积层的一个简单应用:通过找到像素变化的位置,来检测图像中不同颜色的边缘。 首先,我们构造一个像素的黑白图像。中间四列为黑色(0),其余像素为白色(1)。
X = torch.ones((6, 8))
X[:, 2:6] = 0
X
输出:
tensor([[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.]])
接下来,我们构造一个高度为1、宽度为2的卷积核K。当进行互相关运算时,如果水平相邻的两元素相同,则输出为零,否则输出为非零。
K = torch.tensor([[1.0, -1.0]])
现在,我们对参数X(输入)和K(卷积核)执行互相关运算。 如下所示,输出Y中的1代表从白色到黑色的边缘,-1代表从黑色到白色的边缘,其他情况的输出为0。
Y = corr2d(X, K)
Y
输出:
tensor([[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.]])
现在我们将输入的二维图像转置,再进行如上的互相关运算。 其输出如下,之前检测到的垂直边缘消失了。 不出所料,这个卷积核K只可以检测垂直边缘,无法检测水平边缘。
corr2d(X.t(), K)
输出:
tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
如果我们只需寻找黑白边缘,那么以上[1, -1]的边缘检测器足以。然而,当有了更复杂数值的卷积核,或者连续的卷积层时,我们不可能手动设计滤波器。那么我们是否可以学习由X生成Y的卷积核呢?
现在让我们看看是否可以通过仅查看“输入-输出”对来学习由X生成Y的卷积核。 我们先构造一个卷积层,并将其卷积核初始化为随机张量。接下来,在每次迭代中,我们比较Y与卷积层输出的平方误差,然后计算梯度来更新卷积核。为了简单起见,我们在此使用内置的二维卷积层,并忽略偏置。
# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)
# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),
# 其中批量大小和通道数都为1
X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))
lr = 3e-2 # 学习率
for i in range(10):
Y_hat = conv2d(X)
l = (Y_hat - Y) ** 2
conv2d.zero_grad()
l.sum().backward()
# 迭代卷积核
conv2d.weight.data[:] -= lr * conv2d.weight.grad
if (i + 1) % 2 == 0:
print(f'epoch {i+1}, loss {l.sum():.3f}')
输出:
epoch 2, loss 4.735
epoch 4, loss 0.874
epoch 6, loss 0.179
epoch 8, loss 0.043
epoch 10, loss 0.013
在次迭代之后,误差已经降到足够低。现在我们来看看我们所学的卷积核的权重张量。
conv2d.weight.data.reshape((1, 2))
输出:
tensor([[ 0.9990, -0.9791]])
回想一下我们在之前观察到的互相关和卷积运算之间的对应关系。 为了得到正式的卷积运算输出,我们需要执行严格卷积运算,而不是互相关运算。 幸运的是,它们差别不大,我们只需水平和垂直翻转二维卷积核张量,然后对输入张量执行互相关运算。
值得注意的是,由于卷积核是从数据中学习到的,因此无论这些层执行严格的卷积运算还是互相关运算,卷积层的输出都不会受到影响。
为了与深度学习文献中的标准术语保持一致,我们将继续把“互相关运算”称为卷积运算,尽管严格地说,它们略有不同。 此外,对于卷积核张量上的权重,我们称其为元素。
输出的卷积层有时被称为特征映射(feature map),因为它可以被视为一个输入映射到下一层的空间维度的转换器。 在卷积神经网络中,对于某一层的任意元素x,其感受野(receptive field)是指在前向传播期间可能影响计算x的所有元素(来自所有先前层)。
请注意,感受野可能大于输入的实际大小。 给定 2 × 2 2 \times 2 2×2卷积核,阴影输出元素值19的感受野是输入阴影部分的四个元素。 假设之前输出为 Y \mathbf{Y} Y,其大小为 2 × 2 2 \times 2 2×2,现在我们在其后附加一个卷积层,该卷积层以 Y \mathbf{Y} Y为输入,输出单个元素 z z z。 在这种情况下, Y \mathbf{Y} Y上的 z z z的感受野包括 Y \mathbf{Y} Y的所有四个元素,而输入的感受野包括最初所有九个输入元素。 因此,当一个特征图中的任意元素需要检测更广区域的输入特征时,我们可以构建一个更深的网络。
1、二维卷积层的核心计算是二维互相关运算。最简单的形式是,对二维输入数据和卷积核执行互相关操作,然后添加一个偏置。
2、我们可以设计一个卷积核来检测图像的边缘。
3、我们可以从数据中学习卷积核的参数。
4、学习卷积核时,无论用严格卷积运算或互相关运算,卷积层的输出不会受太大影响。
5、当需要检测输入特征中更广区域时,我们可以构建一个更深的卷积网络。