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

相关推荐
ndsc_d1 小时前
前端实战复盘:用AI直接生成响应式官网落地页与React源码
前端·人工智能·react.js·ui·前端框架·aigc·paico
我没胡说八道1 小时前
论文排版避坑指南|按问题选工具,不踩坑,选好工具
人工智能·深度学习·考研·计算机视觉·自然语言处理·论文
天天讯通1 小时前
AI辅助如何打造呼叫中心的“金牌坐席”
人工智能
wanzehongsheng1 小时前
追日光伏花的双轴追踪机构与控制逻辑拆解
人工智能·光伏发电·光伏·绿色能源·低碳环保
带娃的IT创业者1 小时前
当推理速度突破物理极限:深度解析 MiMo-v2.5-Pro-UltraSpeed 的 1000 TPS 架构革命
人工智能·架构·大模型·架构优化·mimo·tps·推理速度
kiros_wang1 小时前
Osaurus 智能生成效果与能力边界实测
人工智能
Lottie20261 小时前
2026跨境铺货破局!1688/淘宝货源全自动采集对接技术
大数据·人工智能
大模型真好玩1 小时前
LangChain DeepAgents 速通指南(十一)—— DeepAgents Code 记忆与状态管理
人工智能·langchain·agent
weixin_397574091 小时前
企业级AI框架的五大核心能力
人工智能·ai
weipt2 小时前
MCP协议在企业中的实际应用
人工智能