深度学习---------------卷积层

从全连接到卷积

分类猫和狗的图片

    使用一个还不错的相机采集图片(12M像素=120万像素)
RGB图片有36M元素(360万像素)
    使用100大小的单隐藏层MLP,模型有3.6B元素(3.6B是指的参数数量而不是像素数量,即3.6Billion=36亿像素)

    远多于世界上所有猫和狗的总数(900M狗,600M猫)

在这里插入图片描述

3,600,000,000 * 4字节 = 14,400,000,000字节 ≈ 14.4GB

在这里插入图片描述
在这里插入图片描述

①k、l表示图片的第k行、第l列个像素,它相当于全连接层的 x i x_i xi, x i x_i xi乘以一个权重值,得到全连接层中一层神经元中一个神经元的一条线的值。

②有一个四维的W,里面有很多个w,例如前图的全连接层有100个w。i,j表示此w的坐标,即遍历所有的i、j合为100。每个w又是一个矩阵,每个w连接所有像素,对应矩阵宽高为k和l。因此下图中的 h i , j h_{i,j} hi,j为全连接层中一个神经元的输出。

③原来的k,l是基于图片的绝对位置,ab是根据ij的相对位置,这里就省略绝对位置,只需要一个原点加相对原点的相对位置,就可以表示位置信息。




重新考察全连接层

将输入和输出变形为矩阵(宽度,高度)
将权重变形为4维张量(h,w)到(h’,w’)

在这里插入图片描述

原本权重为二维:输入输出为一维向量,可以理解为从输入和输出分别选一个节点。
现在权重为四维:输入输出为二维矩阵,可以理解成从输入和输出分别选一个包含高宽的节点。

i、j表示选的w的坐标(即许多个卷积核,或者说滤波器、检测器),k、l表示图片像素的位置。
(可以理解为:权重w是输入的第i行j列这个元素到输出的k行l列的变换系数,所以w是4维的才能把输入和输出的每一个元素都连接上。)

第i行第j列的卷积核中的第(k,l)个元素。
(可以理解为:有一个4维的W,里面有很多个w。i、j表示此w的坐标,每个w又是一个矩阵,矩阵宽高为k、l。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
重新对w进行索引,对w一些元素重新排列一下做到一个v上面。把原来的w写成变换后的w的话,那么需要对 对应的X进行变换。




原则1------平移不变性

在这里插入图片描述
这就是2维交叉相关。




原则2------局部性

在这里插入图片描述
意思是:在 X i , j X_{i,j} Xi,j附近区域,区域🔺很小。
在这里插入图片描述




总结

    对全连接层使用平移不变性局部性得到卷积层。

在这里插入图片描述




卷积层

在这里插入图片描述
① 卷积核遇到和自己相似的,会极度膨胀,遇到和自己不一样的,会极度缩小。

② 提取图像特征,卷积层越深,提取的是语义的特征。




二维交叉相关

在这里插入图片描述

在这里插入图片描述




二维卷积层

在这里插入图片描述
    输出大小略小于输入大小。这是因为卷积核的宽度和高度大于1,而卷积核只与图像中每个大小完全适合的位置进行互相关运算。所以,输出大小等于输入大小 n k n_k nk× n k n_k nk减去卷积核大小 k h k_h kh× k h k_h kh,即:

在这里插入图片描述


在这里插入图片描述




交叉相关 vs 卷积

在这里插入图片描述


一维和三维交叉相关

在这里插入图片描述


总结

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

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

    核矩阵的大小是超参数。




图像卷积

二维互相关运算

在这里插入图片描述

import torch


# K是核矩阵
def corr2d(X, K):  # @save
    """计算二维互相关运算"""
    h, w = K.shape  # 获取卷积核的长&宽
    # 输入 X 的高度减去卷积核的高度加一,输入 X 的宽度减去卷积核的宽度加一
    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
    # 使用两层嵌套循环遍历输出Y的每一个位置(i, j)  
    for i in range(Y.shape[0]): # 外层循环遍历Y的行
        for j in range(Y.shape[1]): # 内层循环遍历Y的列
            # 这里的K相当于W 的一个子集
            # 通过切片操作 X[i:i + h, j:j + w] 从输入 X 中提取一个与卷积核 K 大小相同的子区域。
            # 将这个子区域与卷积核 K 进行逐元素相乘
            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]])
print(corr2d(X, K))

输出:
在这里插入图片描述



实现二维卷积层

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

    corr2d函数实现二维卷积层。在__init__构造函数中,将weight和bias声明为两个模型参数。前向传播函数调用corr2d函数并添加偏置。

    高度和宽度分别为 h 和 w 的卷积核可以被称为 h × w 卷积或 h × w 卷积核。 我们也将带有 h × w 卷积核的卷积层称为 h × w 卷积层

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



练习:

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


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是核矩阵
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]):
            # 这里的K相当于W
            Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
    return Y


X = torch.tensor([[0, 1, 2, 3],
                  [4, 5, 6, 7],
                  [8, 9, 10, 11],
                  [12, 13, 14, 15]], dtype=torch.float32)

# 创建 Conv2D 实例
conv = Conv2D(kernel_size=(2, 2))
# 进行卷积操作
Y = conv(X)
print(Y)

输出:
在这里插入图片描述


图像中目标的边缘检测

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

import torch

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

输出:

在这里插入图片描述


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

在这里插入图片描述

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



输出Y中的1代表从白色到黑色的边缘,-1代表从黑色到白色的边缘。 其他情况的输出为0。

Y = corr2d(X, K)
print(Y)

输出:
在这里插入图片描述


该部分总代码

import torch


# K是核矩阵
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]):
            # 这里的K相当于W
            Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
    return Y


X = torch.ones((6, 8))
X[:, 2:6] = 0
K = torch.tensor([[1.0, -1.0]])
Y = corr2d(X, K)
print(Y)



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

corr2d(X.t(), K)

输出:
在这里插入图片描述
为什么全零?因为x转置后的结果如下图:

在这里插入图片描述

答:水平相邻的两元素相同,则输出为零

那么如何解决呢?
答:将K也转置

在这里插入图片描述
效果展示:
在这里插入图片描述



该部分总代码

import torch


# K是核矩阵
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]):
            # 这里的K相当于W
            Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
    return Y


X = torch.ones((6, 8))
X[:, 2:6] = 0
K = torch.tensor([[1.0, -1.0]])
Y = corr2d(X.t(), K)
print(Y)



    如果我们只需寻找黑白边缘,那么以上[1, -1]的边缘检测器足以。然而,当有了更复杂数值的卷积核,或者连续的卷积层时,我们不可能手动设计滤波器。那么我们是否可以学习由X生成Y的卷积核呢?

    仅查看“输入-输出”对 来学习由X生成Y的卷积核。先构造一个卷积层,并将其卷积核初始化为随机张量。接下来,在每次迭代中,我们比较Y卷积层输出平方误差,然后计算梯度来更新卷积核。为了简单起见,我们在此使用内置的二维卷积层,并忽略偏置

# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
# 第一个参数:输入通道数为1,第二个参数:输出通道数为1
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
    Y_hat = conv2d(X)
    l = (Y_hat - Y) ** 2
    conv2d.zero_grad()
    l.sum().backward()
    # 迭代卷积核,使用权重数据的data属性进行原地更新,学习率与梯度相乘得到了权重应该更新的方向和大小。
    conv2d.weight.data[:] -= lr * conv2d.weight.grad
    if (i + 1) % 2 == 0:
        print(f'epoch {i+1}, loss {l.sum():.3f}')



该部分总代码

import torch
from torch import nn


# K是核矩阵
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]):
            # 这里的K相当于W
            Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
    return Y


X = torch.ones((6, 8))
X[:, 2:6] = 0
K = torch.tensor([[1.0, -1.0]])
Y = corr2d(X, K)
# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
# 第一个参数:输入通道数为1,第二个参数:输出通道数为1
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
    Y_hat = conv2d(X)
    l = (Y_hat - Y) ** 2
    conv2d.zero_grad()
    l.sum().backward()
    # 迭代卷积核,使用权重数据的data属性进行原地更新,学习率与梯度相乘得到了权重应该更新的方向和大小。
    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))

输出:
在这里插入图片描述




问题

①为什么要权重变形?为什么要重新索引?
解释全连接层和卷积层之间的关系,即怎么样对全连接层做变换、做限制能得到卷积层。

②为什么不应该看那么远?感受野不是越大越好吗?
类似于全连接层为什么隐藏层不是越大越好。(可以做一个很浅的很宽的全连接层,但没一个深一点短一点的全连接层好。)卷积神经网络是一样的。对一个卷积层比较少的(一层或者两层的)但是每个盒比较大的不如把每一层盒变小一点、做深一点。通常小一点的主要用3×3,5×5(即:2D卷积核一般shape = (3,3),最多(5,5)。大shape的卷积核不如更深层的小shape的卷积核)。

③二维卷积层,有没有可能同时使用两个不同尺寸的Kernel进行计算,然后再计算出一个更合适的Kernel,从而提升特征提取的性能。
有的

④为什么100的MLP太大放不下,100的全连接层能放下?
全连接层最大的问题:权重w的高取决于输入的宽。当输入一个1200万像素的图片,输入的维度就变成1200万的维度,所以炸掉了。100的全连接层是不大的,假设给定一个很大的图片那么全连接层就不行了,而卷积没有这个问题,因为盒是固定的。

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值