一文读懂图像卷积层

目录

什么是二维互相关?

一、直观理解:像 “盖章” 一样扫描输入

二、数学定义

三、步骤拆解(带实例)

1. 输入与卷积核

2. 滑动窗口计算(步长 = 1)

第 1 步:窗口左上角对齐 \(X[0,0]\)

第 2 步:窗口右移 1 格(对齐 )

第 3 步:窗口继续右移,直到一行结束,再下移 1 行重复计算

四、核心作用:提取局部特征

五、与卷积运算的区别

六、关键参数

七、总结

互相关运算为啥放在前向传播中?

1. 前向传播的本质是计算数据流

2. 互相关运算是卷积层的数学实现

3. 前向传播与反向传播的分工

完整代码

实验结果


什么是二维互相关?

二维互相关是卷积神经网络(CNN)中提取局部特征的核心操作:通过一个卷积核(Kernel,也称过滤器) 在输入矩阵上滑动,计算每个位置的局部加权和,生成输出特征图。

简单来说,它的作用是:用卷积核 “扫描” 输入矩阵,捕捉局部区域的特征(如边缘、纹理)

一、直观理解:像 “盖章” 一样扫描输入

想象你有一张图片(输入矩阵)和一个小印章(卷积核),互相关运算就是:

 
  1. 用 “印章” 在图片上从左到右、从上到下滑动(每次移动 1 步,步长可调整)。
  2. 每滑动到一个位置,就计算 “印章” 与图片对应区域的加权和,结果作为输出矩阵中对应位置的值。
 

整个过程类似用放大镜观察图片的局部细节,不同的 “印章”(卷积核)能捕捉不同的特征。

二、数学定义

三、步骤拆解(带实例)

用一个具体例子演示计算过程,直观理解每一步:

1. 输入与卷积核
  • 输入矩阵 X(5×5,模拟一张灰度图):
    [[1, 2, 3, 4, 5],
     [6, 7, 8, 9, 10],
     [11,12,13,14,15],
     [16,17,18,19,20],
     [21,22,23,24,25]]
    
  • 卷积核 K(3×3,模拟 “垂直边缘检测器”):
    [[1, 0, -1],
     [1, 0, -1],
     [1, 0, -1]]
    
2. 滑动窗口计算(步长 = 1)

卷积核像 “窗口” 一样在输入上滑动,每移动一次计算一个输出值:

第 1 步:窗口左上角对齐 \(X[0,0]\)
  • 截取输入局部区域(3×3):
    [[1, 2, 3],
     [6, 7, 8],
     [11,12,13]]
    
  • 与卷积核对应元素相乘后求和:(1\times 1) + (2\times0) + (3\times-1) + (6\times1) + (7\times0) + (8\times-1) + (11\times1) + (12\times0) + (13\times-1)\)\(= 1 + 0 -3 +6 +0 -8 +11 +0 -13 = -6
  • 输出Y[0,0] = -6
第 2 步:窗口右移 1 格(对齐 X[0,1]
  • 截取输入局部区域:
    [[2, 3, 4],
     [7, 8, 9],
     [12,13,14]]
    
  • 计算得:\(Y[0,1] = -6\)
第 3 步:窗口继续右移,直到一行结束,再下移 1 行重复计算

最终得到 3×3 的输出矩阵 Y:

 
[[-6, -6, -6],
 [-18, -18, -18],
 [-30, -30, -30]]

四、核心作用:提取局部特征

卷积核的参数决定了它能提取的特征类型:

 
  • 例子中的卷积核 K 是 “垂直边缘检测器”:它对左侧元素赋正权重,右侧赋负权重,中间为 0。当输入中存在垂直边缘(左右像素差异大)时,输出值的绝对值会很大;而在平坦区域(左右像素差异小),输出值接近 0。
  • 训练过程中,卷积核的参数会自动学习(通过反向传播优化),以适应任务需求(如识别猫的 “耳朵特征”、车的 “轮子特征”)。

五、与卷积运算的区别

很多人会混淆 “互相关” 和 “卷积”,两者的唯一区别是:

  • 互相关:直接用原始卷积核滑动计算。
  • 卷积:先将卷积核旋转 180°,再滑动计算。

在深度学习中,两者效果几乎相同(因为卷积核参数会被训练优化),因此实际应用中常用互相关代替卷积(计算更简单)。

六、关键参数

  1. 卷积核大小(h, w):常见 3×3、5×5,越大能捕捉越复杂的特征,但计算量也越大。
  2. 步长(stride):每次滑动的距离(默认 1),步长越大,输出尺寸越小。
  3. 填充(padding):在输入边缘补 0,用于保持输出尺寸与输入一致(如 “same padding”)。

七、总结

二维互相关是 CNN 的 “眼睛”,通过滑动卷积核计算局部加权和,实现对输入数据的局部特征提取。它的优势在于:

  1. 参数共享:一个卷积核在输入的所有位置复用,大幅减少参数数量。
  2. 局部连接:每个输出只依赖输入的局部区域,符合 “局部特征决定整体” 的视觉规律(如识别物体先看边缘、纹理)。

互相关运算为啥放在前向传播中?

1. 前向传播的本质是计算数据流

  • 前向传播是神经网络从输入到输出的计算过程,负责将输入数据通过层层变换转换为预测结果。
  • 互相关运算是卷积层的核心操作,通过滑动卷积核对输入数据进行特征提取。将其放在前向传播中,正是为了实现这一特征提取的过程。

2. 互相关运算是卷积层的数学实现

  • 在深度学习中,卷积层的本质是通过互相关运算来实现的。虽然数学上的卷积需要对卷积核旋转 180°,但在实际应用中:
    • 由于卷积核是通过训练学习得到的参数,旋转与否不影响模型的表达能力。
    • 因此,直接使用互相关运算代替卷积,简化了计算过程。

3. 前向传播与反向传播的分工

  • 前向传播:负责计算预测值,使用当前参数(如卷积核权重、偏置)对输入进行变换。
  • 反向传播:负责计算梯度,根据预测误差调整参数。

将互相关运算放在前向传播中,而将梯度计算(基于链式法则)放在反向传播中,实现了计算过程的清晰分工。

完整代码

"""
文件名: 6.1
作者: 墨尘
日期: 2025/7/13
项目名: dl_env
备注: 
"""


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


def corr2d(X, K):  # @save
    """计算二维互相关运算"""
    # 获取卷积核K的高度h和宽度w
    h, w = K.shape
    # 计算输出特征图的尺寸
    Y_height = X.shape[0] - h + 1
    Y_width = X.shape[1] - w + 1
    # 初始化输出特征图Y为全零张量
    Y = torch.zeros((Y_height, Y_width)) #2*2
    # 遍历输出特征图的每个位置
    for i in range(Y_height):
        for j in range(Y_width):
            # 提取当前窗口区域
            window = X[i:i + h, j:j + w]
            # 窗口区域与卷积核逐元素相乘后求和
            Y[i, j] = (window * K).sum()
    return Y

# 卷积层
class Conv2D(nn.Module):
    def __init__(self, kernel_size):
        super().__init__()
        # 随机初始化卷积核权重
        self.weight = nn.Parameter(torch.rand(kernel_size))
        # 初始化偏置为0
        self.bias = nn.Parameter(torch.zeros(1))

    def forward(self, x):
        # 调用corr2d函数进行卷积运算,然后加上偏置  前向传播
        return corr2d(x, self.weight) + self.bias


if __name__ == '__main__':
    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))

    # 图像中目标的边缘检测
    """构造一个6*8像素的黑白图像。中间四列为黑色(0),其余像素为白色(1)"""
    X = torch.ones((6, 8))
    X[:, 2:6] = 0
    print(X)
    """构造一个高度为1、宽度为2的卷积核K"""
    K = torch.tensor([[1.0, -1.0]]) #两个元素值
    Y = corr2d(X, K)
    print(Y)

    # corr2d(X.t(), K)

    # -------------------------------------------------------------------------
    #     一个卷积核的训练过程
    # -------------------------------------------------------------------------

    # 创建中心化层实例
    # 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
    conv2d = nn.Conv2d(1, 1, kernel_size=(1, 2), bias=False)

    # 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),
    # 其中批量大小和通道数都为1
    """
    输入数据 X 的形状:(1, 1, 6, 8),表示 1 个样本,1 个通道,高度 6,宽度 8
    目标输出 Y 的形状:(1, 1, 6, 7),宽度 7 是因为 1x2 的卷积核在 8 宽度上滑动产生 7 个输出
    学习率:控制参数更新的步长"""
    X = X.reshape((1, 1, 6, 8))
    Y = Y.reshape((1, 1, 6, 7))
    lr = 3e-2  # 学习率

    # 前向传播:Y_hat = conv2d (X),计算模型预测值
    # 损失计算:l = (Y_hat - Y) ** 2,使用均方误差
    # 梯度清零:conv2d.zero_grad (),清除之前的梯度积累
    # 反向传播:l.sum ().backward (),计算损失对参数的梯度
    # 参数更新:手动实现梯度下降,更新卷积核权重
    # 打印损失:每 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}')

实验结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值