【动手学深度学习】卷积层

学习资料:

20 卷积层里的填充和步幅【动手学深度学习v2】_哔哩哔哩_bilibili

19 卷积层【动手学深度学习v2】_哔哩哔哩_bilibili

卷积神经网络原理 - 11 - 通道的概念_哔哩哔哩_bilibili

神经网络基础原理 - 04 前馈神经网络_哔哩哔哩_bilibili

神经网络基础原理 - 06 全连接前馈神经网络的数据模型_哔哩哔哩_bilibili

卷积神经网络原理 - 12 - 有趣的1x1卷积核_哔哩哔哩_bilibili

1 理论基础

前馈神经网络

【动手学深度学习】卷积层_第1张图片

全连接前馈神经网络的数据模型

【动手学深度学习】卷积层_第2张图片

1.1 一维卷积

【动手学深度学习】卷积层_第3张图片

1.2 二维卷积(交叉相关)

【动手学深度学习】卷积层_第4张图片

1.3 卷积层的填充和步幅

1.3.1 填充

【动手学深度学习】卷积层_第5张图片

1.3.2 步幅

【动手学深度学习】卷积层_第6张图片

1.4 多输入输出通道

【动手学深度学习】卷积层_第7张图片

一张彩色的图片拥有红色、绿色和蓝色三个通道。每个通道都有一个卷积核,卷积的结果是所有卷积结果之和

无论有多少输入通道 到目前为止我们只用到单输出通道

若要得到多输出通道,我们可以设定多个三维卷积核,每个核生成一个输出通道

【动手学深度学习】卷积层_第8张图片

1.5 1×1卷积层

【动手学深度学习】卷积层_第9张图片

总结

  • 核矩阵的大小、填充、步幅、输出通道书都是卷积层的超参数

  • 对全连接层使用平移不变性和局部性得到卷积层,故卷积层是特殊的全连接层

  • 卷积层将输入和核矩阵进行交叉相关,加上偏移后得到输出

  • 核矩阵和偏移是可学习的参数

  • 每个输入通道有独立的二维卷积核,所有通道结果相加得到一个输出通道结果

  • 每个输出通道有独立的三维卷积核

2 代码

2.1手动实现一个互相关运算

import torch
from torch import nn
from d2l import torch as d2l

def corr2d(X, K):  #K是核矩阵
    """计算二维互相关运算"""
    h, w = K.shape# 取出K的行数和列数
    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)

2.2实现二维卷积层

class Conv2D(nn.Module):
    def __init__(self, kernel_size):   #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

卷积层的一个简单应用:检测图像中不同颜色的边缘

X = torch.ones((6, 8))
X[:, 2:6] = 0
X    #定义输入
【动手学深度学习】卷积层_第10张图片

K = torch.tensor([[1.0, -1.0]])    # 核矩阵定义

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

Y = corr2d(X, K)
Y
【动手学深度学习】卷积层_第11张图片

卷积核K只可以检测垂直边缘

corr2d(X.t(), K)
【动手学深度学习】卷积层_第12张图片

2.3 学习由X生成Y的卷积核

# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)
# 直接调用pytorch.nn.Con2d的定义:第一个参数表示输入的通道数(单通道表示为黑白图片),第二个参数表示输出...

# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),
# 其中批量大小和通道数都为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}')
【动手学深度学习】卷积层_第13张图片

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

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

2.4 填充(padding)

import torch
from torch import nn


# 为了方便起见,我们定义了一个计算卷积层的函数。
# 此函数初始化卷积层权重,并对输入和输出提高和缩减相应的维数
def comp_conv2d(conv2d, X):
    X = X.reshape((1, 1) + X.shape)
 # 这里的(1,1)表示批量大小和通道数都是1
    Y = conv2d(X)
    # 省略前两个维度:批量大小和通道
    return Y.reshape(Y.shape[2:])

# 请注意,这里每边都填充了1行或1列,因此总共添加了2行或2列
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)
# 前两个参数表示输入和输出的通道数都为1
# padding=1表示上下左右都填充1行
X = torch.rand(size=(8, 8))
comp_conv2d(conv2d, X).shape   #可以验证,做了对应填充后 输出的大小与输入相同

下面举一个稍微复杂的例子(不对称情况)

conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))
# padding=(2,1)表示上下填充2行,左右填充1列
comp_conv2d(conv2d, X).shape  #可以验证,输入和输出还是一样的大小

2.5 步幅(stride)

conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2) #将高度和宽度的步幅设置为2 
comp_conv2d(conv2d, X).shape

一个稍微复杂的例子

conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
# 将高度步幅设为3,宽度步幅设为4
comp_conv2d(conv2d, X).shape

  • 在框架中使用填充和步幅,只用传入padding 和stride两个超参数即可

2.6 多输入多输出通道

2.6.1 多输入通道

import torch
from d2l import torch as d2l

def corr2d_multi_in(X, K):
    # 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起
    return sum(d2l.corr2d(x, k) for x, k in zip(X, K))

验证互相关运算的输出

X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
               [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])

corr2d_multi_in(X, K)

2.6.2 多输出通道

def corr2d_multi_in_out(X, K):#X是3维,K是4维(最外面的维是输出通道)
    # 迭代“K”的第0个维度,每次都对输入“X”执行互相关运算。
    # 最后将所有结果都叠加在一起
    return torch.stack([corr2d_multi_in(X, k) for k in K], 0)
#corr2d_multi_in(X, k)计算得到2×2的矩阵,把所有的计算结果在0的维度上叠加起来
K = torch.stack((K, K + 1, K + 2), 0)
K.shape  #K的输出通道为3,输入是2个2×2矩阵?
corr2d_multi_in_out(X, K)
【动手学深度学习】卷积层_第14张图片

2.6.3 1×1卷积层

验证:1×1的卷积层等价于一个全连接层

def corr2d_multi_in_out_1x1(X, K):  #我们定义一个特殊的用全连接来实现多输入多输出通道的互相关操作
    c_i, h, w = X.shape
    c_o = K.shape[0]
    X = X.reshape((c_i, h * w))  #h*w表示把高和宽拉成一个向量
    K = K.reshape((c_o, c_i))
    # 全连接层中的矩阵乘法
    Y = torch.matmul(K, X)
    return Y.reshape((c_o, h, w))

我们来验证一下

X = torch.normal(0, 1, (3, 3, 3))  #X的shape是3×3×3
K = torch.normal(0, 1, (2, 3, 1, 1))   #(2,3,1,1)表示输出通道为2 输入通道为3 核矩阵形为1×1

Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(torch.abs(Y1 - Y2).sum()) < 1e-6

2.6.4 小结

  • 多输入多输出通道可以用来扩展卷积层的模型

  • 当以每像素为基础应用时,1×1卷积层相当于全连接层

  • 1×1卷积层通常用于调整网络层的通道数量和控制模型复杂性

来的都是惊喜 没来的也不是遗憾

你可能感兴趣的:(深度学习,深度学习)