AI基本结构8、9-cnn实现图像识别

模型CNN定义

python 复制代码
class CNN(nn.Module):
    
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 20, (5, 5))
        self.pool1 = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(20, 40, (5, 5))
        self.pool2 = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(40 * 4 * 4, 120)
        self.fc2 = nn.Linear(120, 10)
        self.dropout = nn.Dropout(0.2)
        self.fc2 = nn.Linear(120, 10)

    def forward(self, x):
        '''
        向前传播
        参数
        ----
        x :torch.FloatTensor,形状为(B, 1, 28, 28)
        '''
        B = x.shape[0]                        # (B,  1, 28, 28)
        x = self.pool1(F.relu(self.conv1(x))) # (B, 20, 12, 12)
        x = self.pool2(F.relu(self.conv2(x))) # (B, 40,  4,  4)
        x = x.view(B, -1)                     # (B, 40 * 4 * 4)
        x = F.relu(self.fc1(x))               # (B, 120)
        x = self.dropout(x)
        x = self.fc2(x)                       # (B, 10)
        return x

model = CNN()

除模型定义外,与文章7中的代码一致

上述 CNN 模型由两个卷积层、两个池化层和两个全连接层组成,中间加入了 Dropout 正则化。输入为单通道 28×28 的灰度图像,经第一层卷积(1→20 通道,5×5 卷积核)和 2×2 最大池化后得到 20×12×12 的特征图;再经第二层卷积(20→40 通道)和池化后压缩为 40×4×4,随后展平送入全连接层,最终输出 10 个类别的预测分数。

卷积层

卷积层通过一组可学习的卷积核(滤波器)在输入图像上滑动,逐位置计算点积并叠加偏置,从而提取局部特征。本例中 nn.Conv2d(1, 20, (5, 5)) 表示输入通道数为 1(灰度图),输出通道数为 20(即 20 个 5×5 的卷积核),每个卷积核可捕捉一种特征模式(如边缘、纹理等)。卷积后紧跟 ReLU 激活函数引入非线性,再经过 MaxPool2d(2, 2) 以 2×2 窗口取最大值进行下采样,使特征图尺寸减半,同时保留最显著的特征响应。

池化层

池化层通过对局部区域进行聚合操作来降低特征图的空间分辨率,从而减少后续层的参数量和计算量,同时增强模型对微小位移的鲁棒性。本例使用 nn.MaxPool2d(2, 2) 进行最大池化,即以 2×2 的窗口滑动并在每个窗口内取最大值输出。最大池化有助于保留纹理特征和边缘响应,与平均池化相比更偏向凸显显著特征。每经过一次 2×2 池化,特征图的高和宽均缩小为原来的二分之一,有效压缩了信息密度。

添加残差层

残差块

python 复制代码
class ResidualBlock(nn.Module):
    
    def __init__(self, in_channel, out_channel, stride=1):
        '''
        定义残差块
        参数
        ----
        in_channel :int,输入通道
        out_channel :int,输出通道
        stride :int,步幅大小
        '''
        super().__init__()
        self.conv1 = nn.Conv2d(
            in_channel, out_channel, (3, 3), 
            stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channel)
        self.conv2 = nn.Conv2d(
            out_channel, out_channel, (3, 3),
            stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channel)
        self.downsample = None
        # 如果stride != 1或者in_channel != out_channel,那么输入的形状和输出的形状不一样
        # 使用下面的变换使得两个张量的形状一样
        if stride != 1 or in_channel != out_channel:
            # 下面两个卷积操作的输出形状是一样的
            # Conv2d(in_channel, out_channel, (3, 3), stride, padding=1)
            # Conv2d(in_channel, out_channel, (1, 1), stride, padding=0)
            self.downsample = nn.Sequential(
                nn.Conv2d(in_channel, out_channel, (1, 1), stride=stride, bias=False),
                nn.BatchNorm2d(out_channel))
            
    def forward(self, x):
        '''
        向前传播
        参数
        ----
        x :torch.FloatTensor,形状为(B, I, H, W)
        '''
        inputs = x
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        # 让输入(inputs)的形状和输出(out)的形状一样
        if self.downsample is not None:
            inputs = self.downsample(inputs)
        out += inputs
        out = F.relu(out)
        return out

m = ResidualBlock(3, 4, 2)
print(m(torch.randn(1, 3, 28, 28)).size())

残差块通过引入恒等映射(identity mapping)缓解深层网络的退化问题,使模型学习残差映射而非直接拟合目标。每个残差块包含两条通路:主路径依次执行 Conv2d → BatchNorm → ReLU → Conv2d → BatchNorm,卷积核均为 3×3、padding=1 以保持空间尺寸;跳跃连接则将输入直接传递到主路径末端,二者按元素相加。若输入与输出通道数不一致或步长不为 1,代码通过 self.downsample(1×1 卷积 + BatchNorm)调整输入维度,使两条路径维度对齐后方可相加。最后经过 ReLU 激活得到输出。这种设计使梯度能够沿跳跃连接直接回传,大幅降低优化难度,使训练数十甚至数百层的网络成为可能。

残差神经网络

python 复制代码
class ResNet(nn.Module):
    
    def __init__(self):
        super().__init__()
        self.block1 = ResidualBlock(1, 20)
        self.block2 = ResidualBlock(20, 40, stride=2)
        self.block3 = ResidualBlock(40, 60, stride=2)
        self.block4 = ResidualBlock(60, 80, stride=2)
        self.block5 = ResidualBlock(80, 100, stride=2)
        self.block6 = ResidualBlock(100, 120, stride=2)
        self.lm = nn.Linear(120, 10)

    def forward(self, x):
        '''
        向前传播
        参数
        ----
        x :torch.FloatTensor,形状为(B, 1, 28, 28)
        '''
        x = self.block1(x) # (B,  20, 28, 28)
        x = self.block2(x) # (B,  40, 14, 14)
        x = self.block3(x) # (B,  60,  7,  7)
        x = self.block4(x) # (B,  80,  4,  4)
        x = self.block5(x) # (B, 100,  2,  2)
        x = self.block6(x) # (B, 120,  1,  1)
        out = self.lm(x.view(x.shape[0], -1)) # (B, 10)
        return out

model = ResNet()

残差神经网络(ResNet)通过堆叠多个残差块逐步提取高层特征。本实现使用了 6 个 ResidualBlock:通道数从 1 逐步增长到 20、40、60、80、100、120,同时通过将后续块的 stride 设为 2 实现空间下采样------特征图尺寸从 28×28 逐级降至 1×1。每个块内部都遵循残差连接的范式,且通过 downsample 自动处理维度匹配。最后将 120×1×1 的特征图展平,经一个线性层 nn.Linear(120, 10) 输出 10 个类别的预测分数。相比基础 CNN,残差结构使梯度传导更顺畅,有效避免了梯度消失和性能退化,从而能用更深的网络获得更好的泛化能力。

相关推荐
云浪2 小时前
如何让大模型操作 MySQL 数据库?
javascript·人工智能·后端
小睿科技2 小时前
建筑AI睿兔大脑 | 工程造价AI化的技术路线:谁在真正解决算量痛点
人工智能
luckystar513~2 小时前
LLM那些事(二):LLM训练三阶段——从会接龙,到会听话,到会思考
人工智能
AI工具测评家2 小时前
论文AI率和重复率双降怎么做?拆解AIGC降重底层技术与实操方法
人工智能·aigc·降重·ai检测·查重·降ai
故七月2 小时前
基于最新Geo数据的测评行业服务商分析——万域智瞰深度洞察
大数据·人工智能
weixin_469163692 小时前
从提示词到驾驭工程:金融 AI 需求工程的三段式演进路径
大数据·人工智能·金融
安之眼Angleyes3 小时前
运动相机的“盲拍”困局与AX10的解法:AR实时预览+AI双引擎,让所见即所得
人工智能·数码相机·ar
前沿在线3 小时前
黑芝麻智能亮相2026世界机器人大会, SesameX全矩阵产品展现具身智能硬核实力
人工智能·ai·大模型
Zzj_tju3 小时前
复现一个 Transformer Encoder:从论文公式到最小 PyTorch
人工智能·pytorch·深度学习·语言模型