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,残差结构使梯度传导更顺畅,有效避免了梯度消失和性能退化,从而能用更深的网络获得更好的泛化能力。

相关推荐
江畔柳前堤1 天前
roLabelImg 详细安装教程
开发语言·人工智能·后端·云原生
阿里云大数据AI技术1 天前
分链路差异化设计的DSP准实时数仓|钛动科技基于阿里云实时计算 Flink 版 + DLF Paimon + EMR Serverless StarRocks 的实践
人工智能·flink
陕西企来客1 天前
2026年7月AI智能搜索曝光趋势研判
大数据·人工智能·机器学习·ai智能搜索曝光
阿里云大数据AI技术1 天前
从算力到智能体,面向 Agentic AI 的基础设施演进
人工智能·agent
hangyuekejiGEO1 天前
GEO技术服务选型指南
大数据·人工智能·python
阿里云大数据AI技术1 天前
EMR Serverless Spark AI Function 的双维降本实践
人工智能·sql·spark
维基框架1 天前
GitHub源码处理提速 一趟扫描反而更慢
人工智能·github
冬奇Lab1 天前
代码库知识库系列(05):向量检索 vs 知识图谱——加了调用图并没有变更好
人工智能
AKAMAI1 天前
你的源服务器可能是你做出的最昂贵决定
运维·人工智能·云计算
冬奇Lab1 天前
【无标题】
人工智能·开源