深度学习-卷积神经网络CNN-卷积层

我们之前讨论的多层感知机十分适合处理表格数据,其中行对应样本,列对应特征。

对于表格数据,我们寻找的模式可能涉及特征之间的交互,但是我们不能预先假设任何与特征交互相关的先验结构。 此时,多层感知机可能是最好的选择,然而对于高维感知数据,这种缺少结构的网络可能会变得不实用。

下图所示,左图是传统的神经网络(NN),右图就是卷积神经网络(Convolutional Neural Network)(CNN)。

我们在这张图中可以明显地看出,左图看上去像二维的,右图好像是一个三维的图。

举个例子,比如在传统神经网络输入的一张图有784个像素点,所以输入层就有784个 神经元 ,但在我们的 CNN 中输入的就是原始的图像28*28*1(是三维的),它是一个三维的矩阵。我们可以看到右图中又定义三维名称'height*width*depth'简称'h*w*d',接下来我们就围绕着卷积层和深度到底怎么变换展开。

例如,在猫狗分类的例子中:

假设我们有一个足够充分的照片数据集,数据集中是拥有标注的照片,每张照片具有百万级像素,这意味着网络的每次输入都有一百万个维度。 即使将隐藏层维度降低到1000,这个全连接层也将有10^9 个参数。 想要训练这个模型将不可实现,因为需要有大量的GPU、分布式优化训练的经验和超乎常人的耐心。

1. 卷积神经网络概述

计算机视觉 神经网络 架构:

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

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

  • 图像的平移不变性使我们以相同的方式处理局部图像,而不在乎它的位置

  • 局部性意味着计算相应的隐藏表示只需一小部分局部图像 像素

  • 在图像处理中,卷积层通常比全连接层需要更少的参数,但依旧获得高效用的模型。

  • 卷积神经网络(CNN)是一类特殊的神经网络,它可以包含多个卷积层。

  • 多个输入和输出通道使模型在每个空间位置可以获取图像的多方面特征。

卷积层对输入和卷积核权重进行互相关运算,并在添加标量偏置之后产生输出。

所以,卷积层中的两个被训练的参数是卷积核权重W标量偏置b。就像我们之前随机初始化全连接层一样,在训练基于卷积层的模型时,我们也随机初始化卷积核权重。

|--------|--------------------------|-----------------|--------------|
| 维度 | 输入形状 (N,C,*spatial) | 典型库接口 | 例子 |
| 1D | (N, C, L) | nn.Conv1d | 文本、心电图、股票序列 |
| 2D | (N, C, H, W) | nn.Conv2d | 图像、特征图 |
| 3D | (N, C, D, H, W) | nn.Conv3d | 视频、CT/MRI 体素 |

下图卷积后的结果中,中间的高亮区域表示边缘。

2. 自主实现卷积

2.1 自主实现卷积计算

复制代码
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.]])

2.2 自主实现卷积层

复制代码
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

下面这个卷积核 K 只可以检测垂直边缘,无法检测水平边缘。

复制代码
构建黑白图像X:
X = torch.ones((6, 8))
X[:, 2:6] = 0

构造一个高度为1、宽度为2的卷积核K:
K = torch.tensor([[1.0, -1.0]])

计算结果Y:【由X的(6,8)变为Y的(6,7)】
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!

3. 调库实现卷积操作

下面使用的是 nn 库的Conv2d函数,不是自主实现的,我们只是自主实现了训练过程:

复制代码
# 构造一个二维卷积层,它具有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 6.422
epoch 4, loss 1.225
epoch 6, loss 0.266
epoch 8, loss 0.070
epoch 10, loss 0.022

查看参数:
conv2d.weight.data.reshape((1, 2))

参数结果如下,学习到的卷积核权重非常接近我们之前定义的卷积核K:
tensor([[ 1.0010, -0.9739]])

下篇带来卷积神经网络操作中的填充和步幅。

相关推荐
YuhsiHu1 小时前
【论文简读】LongSplat
人工智能·深度学习·计算机视觉·3d
2zcode1 小时前
基于Matlab图像处理的液晶显示器表面缺陷检测与分类研究
人工智能·计算机视觉
白杨SEO营销1 小时前
白杨SEO:百度搜索开放平台发布AI计划是什么?MCP网站红利来了?顺带说说其它
人工智能·百度
有Li1 小时前
探索医学领域多模态人工智能的发展图景:技术挑战与临床应用的范围综述|文献速递-医学影像算法文献分享
论文阅读·人工智能·医学生
陈大鱼头2 小时前
PromptPilot — AI 自动化任务的下一个环节
人工智能
小关会打代码2 小时前
机器学习第三课之逻辑回归(二)LogisticRegression
人工智能·机器学习·逻辑回归
天天找自己3 小时前
机器学习基石:深入解析线性回归
人工智能·机器学习·线性回归
Godspeed Zhao3 小时前
自动驾驶中的传感器技术12——Camera(3)
人工智能·机器学习·自动驾驶