零基础-动手学深度学习-6.2 图像卷积

上节我们解析了卷积层的原理,现在我们看看它的实际应用。由于卷积神经网络的设计是用于探索图像数据,本节我们将以图像为例。

6.2.1. 互相关运算

核和偏置是可以学习的参数,核矩阵的大小是超参数:

不同的卷积核会带来不同的效果,第一个可以看到如果像素比较平滑,那么相乘后加和应该就为0,直到遇到不平滑的边缘才会显现出来。第二个则是一个锐化的效果,它不会把平滑的地方直接变为0,第三个则是模糊化。

这里说交叉相关核卷积之间的差别就是卷积在定义上需要反过来重叠一下,由于对称性在实际的使用中其实是没有区别的。

除了2维的交叉相关还有一维的文本、语言和时序序列的交叉相关,三维的则是视频、医学图像和气象地图等,总而言之卷积解决了输入过大的问题,具体的卷积函数操作代码如下:

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循环就很简单啦
        for j in range(Y.shape[1]):
            Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
    return Y

6.2.2. 卷积层

卷积层对输入和卷积核权重进行互相关运算,并在添加标量偏置之后产生输出。 所以,卷积层中的两个被训练的参数是卷积核权重和标量偏置。 就像我们之前随机初始化全连接层一样,在训练基于卷积层的模型时,我们也随机初始化卷积核权重。

基于上面定义的corr2d函数实现二维卷积层。在__init__构造函数中,将weightbias声明为两个模型参数。前向传播函数调用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

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

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

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

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

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

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

Y = corr2d(X, K)
Y

 这个只能检测垂直的边缘,我们将输入的二维图像转置,再进行如上的互相关运算结果则为0

6.2.4. 学习卷积核 

如果我们只需寻找黑白边缘,那么以上[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()#反向传播计算梯度
    # 迭代卷积核 比较重要的一步 对所有的权重减去lr乘以梯度
    conv2d.weight.data[:] -= lr * conv2d.weight.grad
    if (i + 1) % 2 == 0:#每两个batch输出一次loss
        print(f'epoch {i+1}, loss {l.sum():.3f}')

在10次迭代之后,误差已经降到足够低。现在我们来看看我们所学的卷积核的权重张量。

conv2d.weight.data.reshape((1, 2))

输出为:tensor([[ 1.0010, -0.9739]])

 6.2.5. 互相关和卷积

为了与深度学习文献中的标准术语保持一致,我们将继续把“互相关运算”称为卷积运算,尽管严格地说,它们略有不同。 此外,对于卷积核张量上的权重,我们称其为元素

6.2.6. 特征映射和感受野

输出的卷积层有时被称为特征映射(feature map),因为它可以被视为一个输入映射到下一层的空间维度的转换器。 在卷积神经网络中,对于某一层的任意元素,其感受野(receptive field)是指在前向传播期间可能影响计算的所有元素(来自所有先前层)。

请注意,感受野可能大于输入的实际大小。让我们用 图6.2.1为例来解释感受野: 给定2*2卷积核,阴影输出元素值19的感受野是输入阴影部分的四个元素。 假设之前输出为Y,其大小为2*2,现在我们在其后附加一个卷积层,该卷积层以Y为输入,输出单个元素z。 在这种情况下,Y上的z的感受野包括Y的所有四个元素,而输入的感受野包括最初所有九个输入元素。 因此,当一个特征图中的任意元素需要检测更广区域的输入特征时,我们可以构建一个更深的网络。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值