十、卷积神经网络知识和二维卷积层计算(3.7学习笔记)

卷积神经网络由来和概念

从全连接层到卷积

对于表格数据,寻找的模式可能涉及特征之间的交互,但是我们不能预先假设任何与特征交互相关的先验结构。 此时,多层感知机可能是最好的选择,然而对于高维感知数据,这种缺少结构的网络可能会变得不实用。尤其是对图像的处理,上万的像素意味着模型输入需要上万个维度。

因为图像中本就拥有丰富的结构,而这些结构可以被人类和机器学习模型使用,如rgb通道等特征。 卷积神经网络(convolutional neural networks,CNN)是机器学习利用自然图像中一些已知结构的创造性方法。

空间不变性

  1. 平移不变性(translation invariance)

不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应,即为“平移不变性”。

  1. 局部性(locality):

神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系,这就是“局部性”原则。最终,可以聚合这些局部特征,以在整个图像级别进行预测。

举个例子

如果要在一张图片里识别找到其中一只猪在哪里。那么模型学习到的一定是这个猪的模样、而不是出现的位置。比如图片上部一般是天空,猪很少出现在那里,但是如果真的猪出现在图片上部,我们也需要将它认出来。

卷积神经网络正是将空间不变性(spatial invariance)的这一概念系统化,从而基于这个模型使用较少的参数来学习有用的表示。

二维卷积层

二维互相关运算

互相关和卷积运算关系

我看书开始看的是李沐老师的,关于二维互相关运算和卷积运算的关系,下面这个博客介绍较好。

https://blog.csdn.net/qq_42589613/article/details/128297091

下面是结合几个相关资料的总结

1、大部分的深度学习教程中都把卷积定义为图像矩阵和卷积核的按位点乘。实际上,这种操作亦应该是互相关(cross-correlation)。而卷积需要把卷积核顺时针旋转180度(即将卷积核上下翻转再左右翻转)然后再做点乘。卷积运算和互相关运算虽然类似,但如果它们使用相同的核数组,对于同一个输入,输出往往并不相同。

2、在深度学习中核数组都是学出来的:卷积层无论使用互相关运算或卷积运算都不会影响模型预测时的输出。假设卷积层使用互相关运算学出某一核数组。设其他条件不变,使用卷积运算学出的核数组即为互相关核数组按上下、左右翻转。也就是说原始输入与学出的已翻转的核数组再做卷积运算时,依然得到的是同样输出。因此大多数深度学习中提到的卷积运算均指互相关运算。

互相关运算实现

import torch
from torch import nn
from d2l import torch as d2l
def corr2d(X, K): # 本函数已保存在d2lzh_pytorch包中⽅便以后使⽤
 """计算二维互相关运算"""
 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

二维卷积层简单应用:图像中目标的边缘检测

通过找到像素变化的位置,来(检测图像中不同颜色的边缘)

#首先构造一个6*8的像素矩阵
X = torch.ones((6, 8))
X[:, 2:6] = 0
#再构造一个高为1,宽为2的卷积核
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.]])

学习卷积核

很多时候,我们需要根据输入和输出来学习卷积核,来构建模型,使用刚刚的X和Y来学习卷积核W

# 构造一个二维卷积层,它具有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}')
conv2d.weight.data.reshape((1, 2))
tensor([[ 1.0486, -0.9313]])

可以看到经过10轮学习,学到的卷积核和此前预设基本一致。

特征映射和感受野

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

跨层感受野会相应变大。这个概念比较简单。

因此,当一个特征图中的任意元素需要检测更广区域的输入特征时,我们可以构建一个更深的网络。

小结

  • 二维卷积层的核心计算是二维互相关运算。最简单的形式是,对二维输入数据和卷积核执行互相关操作,然后添加一个偏置。

  • 我们可以设计一个卷积核来检测图像的边缘。

  • 我们可以从数据中学习卷积核的参数。

  • 学习卷积核时,无论用严格卷积运算或互相关运算,卷积层的输出不会受太大影响。

  • 当需要检测输入特征中更广区域时,我们可以构建一个更深的卷积网络。

作者:小常在学习原文地址:https://blog.csdn.net/Hongrui_Chang/article/details/129370897

%s 个评论

要回复文章请先登录注册