FA_CNN卷积神经网络二十六---从全连接到卷积PyTorch

FA :Formula and Algorithm,CNN:Convolution Netural Network

1. 引言

卷积神经网络(CNN)是深度学习中处理图像、语音等网格结构数据的核心模型。理解 CNN,关键在于搞清楚一个问题:卷积层到底是从全连接层"进化"而来的哪一步?

本文从全连接层出发,逐步推导出卷积层的两大核心特性------局部性 与平移不变性,再通过 PyTorch 手写互相关运算与卷积层,把抽象概念落到可运行的代码上。

2. 从全连接到卷积

2.1 全连接层的表示方式

先回顾全连接层是如何处理一张图片的。

  • 设 kkk、lll 分别表示图片的第 kkk 行、第 lll 列个像素,它相当于全连接层的输入 xix_ixi。xix_ixi 乘以一个权重值,就得到全连接层中某个神经元的一条输入线的值。
  • 权重 WWW 是一个四维张量,里面包含很多个 www。例如前图的全连接层有 100 个 www,用 iii、jjj 表示这个 www 的坐标,遍历所有 iii、jjj 恰好合为 100 个。每个 www 本身又是一个矩阵,连接所有像素,对应矩阵的宽高为 kkk 和 lll。因此下图中的 hi,jh_{i,j}hi,j 就是全连接层中一个神经元的输出。
  • 原来的 kkk、lll 是基于图片的绝对位置 ,而 aaa、bbb 是根据 iii、jjj 的相对位置。这里可以省略绝对位置,只需要一个原点加上相对原点的相对位置,就能表示位置信息。

2.2 平移不变性:权重不随位置变化

从全连接层走向卷积层,第一个关键假设是平移不变性。

  • 当在图片中形成一个识别器后,在一定像素大小的范围内,它都有自己的权重;当这个识别器在图片上换位置之后,它的权重应该保持不变。
  • 可以理解成:用同一张卷积核遍历整张图片,卷积核不会随着位置变化而变化。
  • 权重就是特征提取器,不应该随位置而发生变化。
  • 简而言之,卷积核就是个"框",在图片上不断扫描,无论扫在图上的哪个位置,卷积核都是不变的。
  • 对于一张图片应该有多个卷积核,但每个卷积核要识别的东西不同,一个卷积核就是一个分类器。
  • 卷积确实是 weight shared,但不是全连接,每个神经元只对应卷积核大小个输入。
  • 一句话总结:卷积就是 weight shared 的全连接。

2.3 局部性:只看一小块

第二个关键假设是局部性。

  • 指取一个不那么大的框,即每个输出神经元只与输入的一个局部窗口相连,而不是与全部像素相连。

正是"局部性 + 平移不变性"这两个假设,把全连接层中数量爆炸的权重,压缩成了一个小小可复用的卷积核。

3. 卷积层

3.1 卷积核在做什么

  • 卷积核遇到和自己相似的区域,响应会极度膨胀;遇到和自己不一样的区域,响应会极度缩小。
  • 卷积层用于提取图像特征,且卷积层越深,提取的越是语义层面的特征。

3.2 为什么卷积能省参数

假如就是要看一个 3×33 \times 33×3 的局部信息,那么卷积核就是一个 3×33 \times 33×3 的矩阵。它不会因为输入图片变得特别大,就导致权重数量变得特别大------这正是卷积相对全连接层的最大优势。

4. 卷积层操作(使用自定义实现)

下面用 PyTorch 从零实现二维互相关运算,并逐步构建一个卷积层。

4.1 互相关运算

python 复制代码
import torch
from torch import nn
from d2l import torch as d2l


def corr2d(X, K):  # X 为输入,K 为核矩阵
    """计算二维互相关运算"""
    h, w = K.shape  # 核矩阵的行数和列数
    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))  # X.shape[0] 为输入高
    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

验证上述二维互相关运算的输出:

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

输出:

text 复制代码
tensor([[19., 25.],
        [37., 43.]])

4.2 实现二维卷积层

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

4.3 应用:检测图片中的边缘

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

python 复制代码
X = torch.ones((6, 8))
X[:, 2:6] = 0  # 把中间四列设置为 0
print(X)  # 0 与 1 之间进行过渡,表示边缘

K = torch.tensor([[1.0, -1.0]])  # 左右原值相等时,乘 1 和 -1 相加为 0,则不是边缘
Y = corr2d(X, K)
print(Y)

print(corr2d(X.t(), K))  # X.t() 为 X 的转置,而 K 卷积核只能检测垂直边缘

输出:

text 复制代码
tensor([[1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.]])
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.]])
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.]])

可以看到,KKK 只能检测垂直边缘,对转置后的水平边缘毫无响应。

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

更进一步,我们不必手工设计卷积核,而是让模型自己学出来。

python 复制代码
conv2d = nn.Conv2d(1, 1, kernel_size=(1, 2), bias=False)
# 输入通道为 1(黑白图片通道为 1,彩色图片通道为 3),输出通道为 1

X = X.reshape((1, 1, 6, 8))  # 通道维:通道数;批量维:样本数
Y = Y.reshape((1, 1, 6, 7))

for i in range(10):
    Y_hat = conv2d(X)
    l = (Y_hat - Y) ** 2
    conv2d.zero_grad()
    l.sum().backward()
    conv2d.weight.data[:] -= 3e-2 * conv2d.weight.grad  # 3e-2 是学习率
    if (i + 1) % 2 == 0:
        print(f'batch {i + 1}, loss {l.sum():.3f}')

输出:

text 复制代码
batch 2, loss 9.914
batch 4, loss 3.107
batch 6, loss 1.113
batch 8, loss 0.429
batch 10, loss 0.171

查看学到的卷积核权重:

python 复制代码
print(conv2d.weight.data.reshape((1, 2)))

输出:

text 复制代码
tensor([[ 0.9473, -1.0320]])

学到的权重非常接近手工设计的 [[1.0, -1.0]],说明卷积核确实可以从数据中自动学习得到。

5. 总结

  • 卷积层可以看作带权重共享约束的全连接层:局部性限制了每个神经元的感受野,平移不变性让同一组权重在整张图上复用。
  • 卷积核本质上是一个可学习的特征提取器,多个卷积核负责识别不同的模式。
  • 卷积层越深,提取的特征越偏向语义层面。
  • 通过 corr2d 与 nn.Conv2d 的对比实验可以看到,卷积核既能手工设计,也能通过梯度下降自动学习。

理解"从全连接到卷积"的推导过程,是掌握 CNN 设计思想的第一步。

相关推荐
艺杯羹16 分钟前
MCP本地服务集成避坑:从Stdio管道僵死到双向JSON-RPC稳健通信
人工智能·python·ai·系统架构·大语言模型·ai编程·mcp
武子康31 分钟前
h3.c 的两个权重槽,怎样把内存压力换成 SSD 等待
c语言·人工智能·agent
大模型真好玩35 分钟前
豆包真人分身操作技巧:这次让我来做主演
人工智能·agent·deepseek
xyzzklk1 小时前
在Open-WebUI中提升向量化速度
网络·人工智能·爬虫·ai·语言模型·自然语言处理·安卓
QXWZ_IA1 小时前
矿山安全监测从感知到预警的全链路算法
人工智能·安全
日日大王学编程1 小时前
AI Agent 技术发展历程、演进逻辑与技术范式综述
人工智能·python
Yuzhiyuxia2 小时前
【AI Agent面试系列】上下文管理1
人工智能·面试·职场和发展
王中阳Go2 小时前
预算给够了,时间花错了地方:三跳链路给了 27 秒,实际只用掉 9 秒
人工智能·后端·性能优化
方方洛2 小时前
ray教程-25-附录E-端到端实战案例
人工智能·分布式·机器学习