《零基础入门AI:深度学习中的视觉处理(卷积神经网络(CNN)学习)》

发布于:2025-08-14 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、概述

卷积神经网络是深度学习在计算机视觉领域的突破性成果。在计算机视觉领域, 往往我们输入的图像都很大,使用全连接网络的话,计算的代价较高。另外图像也很难保留原有的特征,导致图像处理的准确率不高。

​ 卷积神经网络(Convolutional Neural Network,CNN)是一种专门用于处理具有网格状结构数据的深度学习模型。最初,CNN主要应用于计算机视觉任务,但它的成功启发了在其他领域应用,如自然语言处理等。

​ 卷积神经网络(Convolutional Neural Network)是含有卷积层的神经网络. 卷积层的作用就是用来自动学习提取图像的特征。

​ CNN网络主要有三部分构成:卷积层、池化层和全连接层构成,其中卷积层负责提取图像中的局部特征;池化层用来大幅降低运算量并特征增强;全连接层类似神经网络的部分,用来输出想要的结果。

1. 使用场景

卷积神经网络(CNN)是专门用于处理网格状数据(如图像、音频、视频)的深度学习架构。它在计算机视觉领域有广泛应用:

  • 图像分类(识别图片中的物体)
  • 目标检测(定位并识别多个物体)
  • 图像分割(像素级分类)
  • 人脸识别
  • 医学影像分析

2. 与传统网络的区别

特性 全连接网络 卷积网络
连接方式 全连接 局部连接
权重使用 每个连接独立权重 权重共享
参数数量 巨大 大幅减少
空间信息 丢失 保留
适用领域 结构化数据 图像等网格数据

3. 全连接的局限性

假设处理一张256×256的RGB图像:

  • 输入节点数:256×256×3 = 196,608
  • 第一隐藏层节点数:1024
  • 参数量:196,608×1024 ≈ 2亿

这种参数量会导致:

  1. 训练困难(参数量巨大):需要海量数据和计算资源
  2. 过拟合风险高:模型容易记住训练数据
  3. 空间信息丢失(表达能力有限):像素位置关系被忽略,因为全连接网络无法捕捉图像的空间结构(如边缘、纹理),每个神经元与前一层所有像素相连,忽略了局部特征的重要性。

4. 卷积思想

卷:从左往右,从上往下

积:乘积,求和

作用:

CNN通过以下机制解决上述问题:

  • 局部感知:每个神经元只关注输入图像的一个局部区域(如3×3像素窗口)。
  • 权重共享:同一卷积核在图像不同位置重复使用,大幅减少参数量。
  • 层次化特征提取:通过多层卷积逐步从低级特征(边缘)到高级特征(物体轮廓)。

示例
假设图像尺寸为28×28,使用3×3卷积核,输出特征图尺寸为26×26。若使用10个卷积核,则参数量仅需10×(3×3×1 + 1) = 100(1为偏置项),远小于全连接网络。

数学表示

对于输入图像III和卷积核KKK
(I∗K)(i,j)=∑m∑nI(i+m,j+n)K(m,n)(I * K)(i,j) = \sum_{m}\sum_{n} I(i+m,j+n)K(m,n)(IK)(i,j)=mnI(i+m,j+n)K(m,n)

优势体现
  • 参数量大幅减少:一个3×3卷积核只有9个参数(共享)
  • 平移不变性:相同特征在不同位置被同等识别
  • 层次特征提取:底层→边缘,中层→形状,高层→物体

二、卷积层

1. 卷积核

  • 定义:一个小型矩阵(如3×3),用于检测特定特征(如水平边缘、垂直边缘)。

  • 参数:卷积核的值(权重)通过训练学习得到。

    卷积核(滤波器)是小的权重矩阵,在定义时需要考虑以下几方面的内容:

    • 卷积核的个数:卷积核(过滤器)的个数决定了其输出特征矩阵的通道数

    • 卷积核的值:卷积核的值是初始化好的,后续进行更新。

    • 卷积核的大小:常见的卷积核有1×1、3×3、5×5等,一般都是奇数 × 奇数。

边缘检测核示例:
[-1, 0, 1]
[-1, 0, 1]
[-1, 0, 1]

2. 卷积计算

  1. 滑动窗口:卷积核在输入图像上滑动,每次与局部区域进行逐元素相乘后求和。

在这里插入图片描述

  • input 表示输入的图像

  • filter 表示卷积核, 也叫做滤波器

  • input 经过 filter 的得到输出为最右侧的图像,该图叫做特征图

​ 那么, 它是如何进行计算的呢?卷积运算本质上就是在滤波器和输入数据的局部区域间做点积。

在这里插入图片描述

​ 左上角的点计算方法:

在这里插入图片描述

​ 按照上面的计算方法可以得到最终的特征图为:

在这里插入图片描述

​ 卷积的重要性在于它可以将图像中的特征与卷积核进行卷积操作,从而提取出图像中的特征

​ 可以通过不断调整卷积核的大小、卷积核的值和卷积操作的步长,可以提取出不同尺度和位置的特征。

  1. 数学公式
    对于输入特征图 $ X $ 和卷积核 $ K $,输出特征图 $ Y $ 的某个位置 $ (i,j) $ 计算为:

Y[i,j]=∑m=0kh−1∑n=0kw−1X[i+m,j+n]⋅K[m,n]+b Y[i,j] = \sum_{m=0}^{k_h-1} \sum_{n=0}^{k_w-1} X[i+m, j+n] \cdot K[m,n] + b Y[i,j]=m=0kh1n=0kw1X[i+m,j+n]K[m,n]+b

其中 $ k_h, k_w $ 是卷积核的高和宽,$ b $ 是偏置项。

  1. 示例

    # 面向对象的模块化编程
    from matplotlib import pyplot as plt
    import os
    import torch
    import torch.nn as nn
    
    
    def test001():
        current_path = os.path.dirname(__file__)
        img_path = os.path.join(current_path, "data", "彩色.png")
        # 转换为相对路径
        img_path = os.path.relpath(img_path)
    
        # 使用plt读取图片
        img = plt.imread(img_path)
        print(img.shape)
        # 转换为张量:HWC  ---> CHW  ---> NCHW  链式调用
        img = torch.tensor(img).permute(2, 0, 1).unsqueeze(0)
        # 创建卷积核  (501, 500, 4)
        conv = nn.Conv2d(
            in_channels=4,  # 输入通道
            out_channels=32,  # 输出通道
            kernel_size=(5, 3),  # 卷积核大小
            stride=1,  # 步长
            padding=0,  # 填充
            bias=True
        )
        # 使用卷积核对图像进行卷积操作  [9999]  [[[[]]]]
        out = conv(img)
        
        # 输出128个特征图
        conv2 = nn.Conv2d(
            in_channels=32,  # 输入通道
            out_channels=128,  # 输出通道
            kernel_size=(5, 5),  # 卷积核大小
            stride=1,  # 步长
            padding=0,  # 填充
            bias=True
        )
        out = conv2(out)
        print(out)
        # 把图像显示出来
        print(out.shape)
        plt.imshow(out[0][10].detach().numpy(), cmap='gray')
        plt.show()
    
    
    # 作为主模块执行
    if __name__ == "__main__":
        test001()
    
底层实现

并不是水平和垂直方向的循环,实际计算通过im2col优化:

  1. 将图像局部块展开为列
  2. 将卷积核展开为行
  3. 进行矩阵乘法
  4. 结果重塑为特征图
输入图像
im2col转换
矩阵乘法
输出特征图

在这里插入图片描述

在这里插入图片描述

3. 边缘填充(Padding)

作用:保持特征图尺寸不变,还更好的保护了图像边缘数据的特征。

  • Valid Padding:无填充,输出尺寸减小,为 $ \frac{n - k}{s} + 1 $。
  • Same Padding:填充使输出尺寸等于输入尺寸

填充尺寸计算:P=F−12P = \frac{F-1}{2}P=2F1(F为卷积核大小)

在这里插入图片描述

4. 步长(Stride)

卷积核移动的步长(根据实际情况而定)。步长越大,输出尺寸越小。

步长控制卷积核移动间隔:

  • 步长=1:每次移动1像素
  • 步长=2:输出尺寸减半

输出尺寸公式:
Wout=⌊Win+2P−FS⌋+1W_{\text{out}} = \left\lfloor \frac{W_{\text{in}} + 2P - F}{S} \right\rfloor + 1Wout=SWin+2PF+1

stride太小:重复计算较多,计算量大,训练效率降低;
stride太大:会造成信息遗漏,无法有效提炼数据背后的特征;

5. 多通道卷积计算

数字图像的标识

彩色图像表示为三维张量:[高度,宽度,通道][高度, 宽度, 通道][高度,宽度,通道]

  • RGB图像:3通道(红、绿、蓝)
多通道卷积计算

若输入有 $ C_{in} $ 个通道,卷积核需有相同的通道数。每个通道单独卷积后求和,再加偏置。

每个卷积核对应所有输入通道:
输出(x,y)=∑c=1Cin(Ic∗Kc)(x,y)+b \text{输出}(x,y) = \sum_{c=1}^{C_{\text{in}}} (I_c * K_c)(x,y) + b 输出(x,y)=c=1Cin(IcKc)(x,y)+b
其中,CinC_{\text{in}}Cin是输入通道数

在这里插入图片描述

计算方法如下:

  1. 当输入有多个通道(Channel), 例如RGB三通道, 此时要求卷积核需要有相同的通道数。
  2. 卷积核通道与对应的输入图像通道进行卷积。
  3. 将每个通道的卷积结果按位相加得到最终的特征图。

如下图所示:

在这里插入图片描述

6. 多卷积核卷积计算

实际对图像进行特征提取时, 我们需要使用多个卷积核进行特征提取。这个多个卷积核可以理解为从不同到的视角、不同的角度对图像特征进行提取。

每个卷积核提取一种特征:

  • 输入:[H,W,Cin][H, W, C_{\text{in}}][H,W,Cin]
  • 卷积核:[F,F,Cin,Cout][F, F, C_{in}, C_{out}][F,F,Cin,Cout]
  • 输出:[Hout,Wout,Cout][H_{out}, W_{out}, C_{out}][Hout,Wout,Cout]

在这里插入图片描述

7. 特征图大小

综合公式:
Hout=⌊Hin+2PH−FHSH⌋+1 H_{\text{out}} = \left\lfloor \frac{H_{\text{in}} + 2P_H - F_H}{S_H} \right\rfloor + 1 Hout=SHHin+2PHFH+1
Wout=⌊Win+2PW−FWSW⌋+1 W_{\text{out}} = \left\lfloor \frac{W_{\text{in}} + 2P_W - F_W}{S_W} \right\rfloor + 1 Wout=SWWin+2PWFW+1

其中, $ H_{in} $ 、$ W_{in} 是输入尺寸,是输入尺寸,是输入尺寸, P $ 是填充,$ F $ 是卷积核尺寸,$ S $ 是步长。

示例
输入尺寸 28×28,卷积核 3×3,步长 1,填充 1 → 输出尺寸 28×28。

8. 参数共享

  • 权重共享:同一卷积核在不同位置使用相同权重
  • 参数量(F×F×Cin+1)×Cout(F \times F \times C_{in} + 1) \times C_{out}(F×F×Cin+1)×Cout(+1为偏置)

9. 局部特征提取

卷积核学习不同层次特征:

  1. 浅层:边缘、纹理
  2. 中层:形状、部件
  3. 深层:物体、场景

10. PyTorch卷积层API

import torch.nn as nn

# 创建卷积层
conv_layer = nn.Conv2d(
    in_channels=3,    # 输入通道数
    out_channels=64,  # 输出通道数(卷积核数量)
    kernel_size=3,    # 卷积核大小
    stride=1,         # 步长
    padding=1,        # 填充
    bias=True         # 是否使用偏置
)

示例:

import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import os


def showimg(img):
    plt.imshow(img)
    # 隐藏刻度
    plt.axis("off")
    plt.show()


def test001():
    dir = os.path.dirname(__file__)
    img = plt.imread(os.path.join(dir, "彩色.png"))
    # 创建卷积核
    # in_channels:输入数据的通道数
    # out_channels:输出特征图数,和filter数一直
    conv = nn.Conv2d(in_channels=4, out_channels=1, kernel_size=3, stride=1, padding=1)
    # 注意:卷积层对输入的数据有形状要求 [batch, channel, height, width]
    # 需要进行形状转换  H, W, C -> C, H, W
    img = torch.tensor(img, dtype=torch.float).permute(2, 0, 1)
    print(img.shape)
    # 接着变形:CHW -> BCHW
    newimg = img.unsqueeze(0)
    print(newimg.shape)
    # 送入卷积核运算一下
    newimg = conv(newimg)
    print(newimg.shape)

    # 蒋NCHW->HWC
    newimg = newimg.squeeze(0).permute(1, 2, 0)
    showimg(newimg.detach().numpy())


# 多卷积核
def test002():
    dir = os.path.dirname(__file__)
    img = plt.imread(os.path.join(dir, "彩色.png"))
    # 定义一个多特征图输出的卷积核
    conv = nn.Conv2d(in_channels=4, out_channels=3, kernel_size=3, stride=1, padding=1)

    # 图形要进行变形处理
    img = torch.tensor(img).permute(2, 0, 1).unsqueeze(0)
    
    # 使用卷积核对图片进行卷积计算
    outimg = conv(img)
    print(outimg.shape)
    # 把图形形状转换回来以方便显示
    outimg = outimg.squeeze(0).permute(1, 2, 0)
    print(outimg.shape)
    # showimg(outimg)
    # 显示这些特征图
    for idx in range(outimg.shape[2]):
        showimg(outimg[:, :, idx].squeeze(-1).detach())


if __name__ == "__main__":
    test002()

11. 知识点扩展

  • 空洞卷积:扩大感受野(间隔采样)
  • 深度可分离卷积:减少计算量(分离空间和通道卷积)
  • 转置卷积:用于上采样(反卷积)

三、池化层:特征降维与不变性

1. 概述

池化层(Pooling)用于降低特征图空间尺寸:

  • 减少计算量
  • 增加平移不变性
  • 防止过拟合

2. 池化层计算

最大池化(Max Pooling)

取局部区域最大值:
输出(i,j)=max⁡m,n∈Rij输入(m,n) \text{输出}(i,j) = \max_{m,n \in R_{ij}} \text{输入}(m,n) 输出(i,j)=m,nRijmax输入(m,n)

平均池化(Average Pooling)

取局部区域平均值:
输出(i,j)=1∣Rij∣∑m,n∈Rij输入(m,n) \text{输出}(i,j) = \frac{1}{|R_{ij}|} \sum_{m,n \in R_{ij}} \text{输入}(m,n) 输出(i,j)=Rij1m,nRij输入(m,n)

在这里插入图片描述

3. 步长(Stride)

通常等于池化窗口大小:

  • 2×2池化 → 步长=2
  • 输出尺寸减半

4. 边缘填充(Padding)

池化层较少使用填充,因为目的是降维

5. 多通道池化

池化独立应用于每个通道:

  • 输入通道数 = 输出通道数
  • 空间尺寸减小

6. 池化层的作用

  1. 降维:减少后续层计算量
  2. 平移不变性:微小位移不影响输出
  3. 特征抽象:保留最显著特征
  4. 防止过拟合:减少参数复杂度

7. PyTorch池化层API

# 最大池化
max_pool = nn.MaxPool2d(
    kernel_size=2,  # 池化窗口大小
    stride=2       # 步长(默认等于kernel_size)
)

# 平均池化
avg_pool = nn.AvgPool2d(
    kernel_size=2,
    stride=2
)

示例:

import torch
import torch.nn as nn


# 1. API 基本使用
def test01():

    inputs = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]]).float()
    inputs = inputs.unsqueeze(0).unsqueeze(0)

    # 1. 最大池化
    # 输入形状: (N, C, H, W)
    polling = nn.MaxPool2d(kernel_size=2, stride=1, padding=0)
    output = polling(inputs)
    print(output)

    # 2. 平均池化
    polling = nn.AvgPool2d(kernel_size=2, stride=1, padding=0)
    output = polling(inputs)
    print(output)


# 2. stride 步长
def test02():

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

    # 1. 最大池化
    polling = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
    output = polling(inputs)
    print(output)

    # 2. 平均池化
    polling = nn.AvgPool2d(kernel_size=2, stride=2, padding=0)
    output = polling(inputs)
    print(output)


# 3. padding 填充
def test03():

    inputs = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]]).float()
    inputs = inputs.unsqueeze(0).unsqueeze(0)

    # 1. 最大池化
    polling = nn.MaxPool2d(kernel_size=2, stride=1, padding=1)
    output = polling(inputs)
    print(output)

    # 2. 平均池化
    polling = nn.AvgPool2d(kernel_size=2, stride=1, padding=1)
    output = polling(inputs)
    print(output)


# 4. 多通道池化
def test04():

    inputs = torch.tensor([[[0, 1, 2], [3, 4, 5], [6, 7, 8]],
                           [[10, 20, 30], [40, 50, 60], [70, 80, 90]],
                           [[11, 22, 33], [44, 55, 66], [77, 88, 99]]]).float()

    inputs = inputs.unsqueeze(0)

    # 最大池化
    polling = nn.MaxPool2d(kernel_size=2, stride=1, padding=0)
    output = polling(inputs)
    print(output)


if __name__ == '__main__':
    test04()

8. 知识点扩展

  • 全局平均池化:将特征图降维为1×1(用于分类)
  • 重叠池化:步长小于窗口大小(保留更多信息)
  • 随机池化:按值大小概率采样(正则化效果)

四、整体结构:特征图变化

CNN典型架构

输入图像
卷积层1
激活函数
池化层1
卷积层2
激活函数
池化层2
全连接层
输出

特征图变化示例

以224×224 RGB图像为例:

层类型 参数 输出尺寸 说明
输入 - 224×224×3 原始图像
卷积层 64个7×7核,步长2 112×112×64 空间尺寸减半
最大池化 3×3,步长2 56×56×64 尺寸再减半
卷积层 128个3×3核 56×56×128 通道数增加
卷积层 128个3×3核 56×56×128 特征深化
最大池化 2×2,步长2 28×28×128 尺寸减半
全局平均池化 - 1×1×512 空间信息聚合
全连接层 - 1000 分类输出

特征可视化

随着网络加深:

  1. 浅层:响应简单边缘和纹理
  2. 中层:响应复杂纹理和形状
  3. 深层:响应高级语义特征(如物体部件)

关键概念总结

  1. 局部连接 vs 全连接:CNN的核心突破,极大减少参数量
  2. 权重共享:使模型能够检测平移不变特征
  3. 层次特征提取:从简单到复杂的特征抽象过程
  4. 空间不变性:通过池化实现的位置不变性
浅层
中层
深层
原始图像
卷积特征提取
非线性激活
池化降维
深度
简单特征
复杂特征
语义特征
分类/检测