6.1.1. 不变性

  1. 平移不变性(translation invariance):
    不管检测对象出现在图像中的哪个位置,
    神经⽹络的前⾯⼏层应该对相同的图像区域具有相似的反应,即为“平移不变性”。
    2. 局部性(locality):
    神经⽹络的前⾯⼏层应该只探索输⼊图像中的局部区域,
    而不过度在意图像中相隔较远区域的关系,
    这就是“局部性”原则。
    最终,在后续神经⽹络,整个图像级别上可以集成这些局部特征⽤于预测。

    代码

    1. !pip install -U d2l
    1. from d2l import torch as d2l
    2. import torch
    3. import os
    1. def corr2d(X, K):
    2. """计算二维互相关运算。"""
    3. h, w = K.shape
    4. Y = d2l.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
    5. for i in range(Y.shape[0]):
    6. for j in range(Y.shape[1]):
    7. #求点积
    8. Y[i, j] = d2l.reduce_sum((X[i: i + h, j: j + w] * K))
    9. return Y
    1. X = d2l.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
    2. K = d2l.tensor([[0.0, 1.0], [2.0, 3.0]])
    3. corr2d(X, K)
    输出:
    1. tensor([[19., 25.],
    2. [37., 43.]])
    就是下面这个意思
    image.png

输出大小等于输入大小视频19 卷积层 - 图2减去卷积核大小视频19 卷积层 - 图3,即:
视频19 卷积层 - 图4

6.2.3. 图像中目标的边缘检测

如下是卷积层的一个简单应用:通过找到像素变化的位置,来检测图像中不同颜色的边缘。 首先,我们构造一个 6×8 像素的黑白图像。中间四列为黑色(0),其余像素为白色(1)。

  1. X = torch.ones((6, 8))
  2. X[:, 2:6] = 0
  3. X

输出:

  1. tensor([[1., 1., 0., 0., 0., 0., 1., 1.],
  2. [1., 1., 0., 0., 0., 0., 1., 1.],
  3. [1., 1., 0., 0., 0., 0., 1., 1.],
  4. [1., 1., 0., 0., 0., 0., 1., 1.],
  5. [1., 1., 0., 0., 0., 0., 1., 1.],
  6. [1., 1., 0., 0., 0., 0., 1., 1.]])

接下来,我们构造一个高度为 1、宽度为 2 的卷积核 K 。当进行互相关运算时,如果水平相邻的两元素相同,则输出为零,否则输出为非零。

  1. K = torch.tensor([[1.0, -1.0]])

现在,我们对参数 X (输入)和 K (卷积核)执行互相关运算。 如下所示,输出Y中的1代表从白色到黑色的边缘,-1代表从黑色到白色的边缘,其他情况的输出为 0。

  1. Y = corr2d(X, K)
  2. Y

输出:

  1. tensor([[ 0., 1., 0., 0., 0., -1., 0.],
  2. [ 0., 1., 0., 0., 0., -1., 0.],
  3. [ 0., 1., 0., 0., 0., -1., 0.],
  4. [ 0., 1., 0., 0., 0., -1., 0.],
  5. [ 0., 1., 0., 0., 0., -1., 0.],
  6. [ 0., 1., 0., 0., 0., -1., 0.]])

现在我们将输入的二维图像转置,再进行如上的互相关运算。
其输出如下,之前检测到的垂直边缘消失了。
不出所料,这个卷积核K只可以检测垂直边缘,无法检测水平边缘。

  1. corr2d(X.t(), K)

输出:

  1. tensor([[0., 0., 0., 0., 0.],
  2. [0., 0., 0., 0., 0.],
  3. [0., 0., 0., 0., 0.],
  4. [0., 0., 0., 0., 0.],
  5. [0., 0., 0., 0., 0.],
  6. [0., 0., 0., 0., 0.],
  7. [0., 0., 0., 0., 0.],
  8. [0., 0., 0., 0., 0.]])

6.2.4. 学习卷积核

  1. # 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
  2. conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)
  3. # 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),
  4. # 其中批量大小和通道数都为1
  5. X = X.reshape((1, 1, 6, 8))
  6. Y = Y.reshape((1, 1, 6, 7))
  7. #梯度下降
  8. for i in range(10):
  9. Y_hat = conv2d(X)
  10. l = (Y_hat - Y) ** 2
  11. conv2d.zero_grad()
  12. l.sum().backward()
  13. # 迭代卷积核
  14. conv2d.weight.data[:] -= 3e-2 * conv2d.weight.grad
  15. if (i + 1) % 2 == 0:
  16. print(f'batch {i+1}, loss {l.sum():.3f}')

输出

  1. batch 2, loss 6.239
  2. batch 4, loss 1.047
  3. batch 6, loss 0.176
  4. batch 8, loss 0.030
  5. batch 10, loss 0.005
  1. conv2d.weight.data.reshape((1, 2))

输出:

  1. tensor([[ 0.9862, -0.9878]])