PyTorch 搭建卷积神经网络:常规写法 vs Sequential 写法详解

在 PyTorch 中搭建卷积神经网络(CNN),最常见的两种实现方式是常规逐层定义法 和 **nn.Sequential 堆叠法 **。本文将以 CIFAR10 分类任务的经典网络为例,详细讲解两种写法的实现、差异、优缺点及适用场景,附完整可运行代码与注释,方便直接复现与学习。


一、网络结构总览

本文实现的是一个 3 层卷积 + 池化 + 全连接的经典 CNN 结构,适配 CIFAR10 32×32 图像分类任务,图形、结构如下:

  1. 输入层:3 通道 32×32 图像(CIFAR10 标准格式)
  2. 卷积块 1 :Conv2d (3→32, 5×5, padding=2) → MaxPool2d (2×2),输出 32@16×16
  3. 卷积块 2 :Conv2d (32→32, 5×5, padding=2) → MaxPool2d (2×2),输出 32@8×8
  4. 卷积块 3 :Conv2d (32→64, 5×5, padding=2) → MaxPool2d (2×2),输出 64@4×4
  5. 展平层 :将 64@4×4 特征图转为一维向量 64×4×4=1024
  6. 全连接层:Linear (1024→64) → Linear (64→10),输出 10 类分类结果

二、方法一:常规逐层定义法

1. 完整代码(带详细注释)

python

运行

复制代码
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear

# 自定义卷积神经网络类,继承 nn.Module(所有 PyTorch 模型的基类)
class Tudui(nn.Module):
    def __init__(self):
        # 调用父类 nn.Module 的初始化方法,必须写,否则模型参数无法被正确注册
        super(Tudui, self).__init__()

        # -------------------------- 卷积+池化层定义 --------------------------
        # 第1层卷积:输入通道3(RGB图像),输出通道32,卷积核5×5,padding=2(保持输入输出尺寸不变)
        # 输入形状:[batch, 3, 32, 32] → 输出形状:[batch, 32, 32, 32]
        self.conv1 = Conv2d(
            in_channels=3, 
            out_channels=32, 
            kernel_size=5, 
            padding=2
        )
        # 第1层池化:2×2最大池化,步长默认2,特征图尺寸减半
        # 输入形状:[batch, 32, 32, 32] → 输出形状:[batch, 32, 16, 16]
        self.maxpool1 = MaxPool2d(kernel_size=2)

        # 第2层卷积:输入通道32,输出通道32,卷积核5×5,padding=2
        # 输入形状:[batch, 32, 16, 16] → 输出形状:[batch, 32, 16, 16]
        self.conv2 = Conv2d(
            in_channels=32, 
            out_channels=32, 
            kernel_size=5, 
            padding=2
        )
        # 第2层池化:2×2最大池化,尺寸减半
        # 输入形状:[batch, 32, 16, 16] → 输出形状:[batch, 32, 8, 8]
        self.maxpool2 = MaxPool2d(kernel_size=2)

        # 第3层卷积:输入通道32,输出通道64,卷积核5×5,padding=2
        # 输入形状:[batch, 32, 8, 8] → 输出形状:[batch, 64, 8, 8]
        self.conv3 = Conv2d(
            in_channels=32, 
            out_channels=64, 
            kernel_size=5, 
            padding=2
        )
        # 第3层池化:2×2最大池化,尺寸减半
        # 输入形状:[batch, 64, 8, 8] → 输出形状:[batch, 64, 4, 4]
        self.maxpool3 = MaxPool2d(kernel_size=2)

        # -------------------------- 展平层+全连接层定义 --------------------------
        # Flatten展平层:将 [batch, 64, 4, 4] 的特征图转为 [batch, 1024] 的一维向量
        # 64×4×4=1024,是单张图片经过卷积池化后的特征总数
        self.flatten = Flatten()

        # 全连接层1:输入1024维,输出64维隐藏层特征
        self.linear1 = Linear(in_features=1024, out_features=64)
        # 全连接层2:输入64维,输出10维,对应CIFAR10的10个分类
        self.linear2 = Linear(in_features=64, out_features=10)

    # 前向传播方法:定义数据在网络中的流动路径,必须实现
    def forward(self, x):
        # 第1个卷积+池化块
        x = self.conv1(x)       # 卷积计算
        x = self.maxpool1(x)    # 池化下采样

        # 第2个卷积+池化块
        x = self.conv2(x)
        x = self.maxpool2(x)

        # 第3个卷积+池化块
        x = self.conv3(x)
        x = self.maxpool3(x)

        # 展平特征图,转为一维向量
        x = self.flatten(x)

        # 全连接层计算,得到分类结果
        x = self.linear1(x)
        x = self.linear2(x)

        return x

# -------------------------- 模型测试 --------------------------
if __name__ == "__main__":
    # 实例化模型
    model = Tudui()
    print("模型结构:")
    print(model)

    # 模拟一批CIFAR10输入数据:batch_size=64,通道3,高宽32×32
    input = torch.ones((64, 3, 32, 32))
    # 前向传播,获取输出
    output = model(input)
    print("\n输入形状:", input.shape)
    print("输出形状:", output.shape)  # 输出应为 torch.Size([64, 10])

2. 核心特点解析

  • 逐层定义 :在 __init__ 中为每一层(卷积、池化、全连接)单独定义实例,每个层都有独立的变量名(如 self.conv1self.maxpool1)。
  • 前向调用 :在 forward 方法中,按数据流动顺序逐行调用各层,手动控制数据流向。
  • 灵活性强 :可以在 forward 中任意插入自定义操作,比如打印中间层形状、添加分支结构、条件判断等。

三、方法二:nn.Sequential 堆叠法

1. 完整代码(带详细注释)

python

运行

复制代码
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential

# 自定义卷积神经网络类,继承 nn.Module
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()

        # 使用 nn.Sequential 按顺序堆叠所有层,一次性定义整个网络结构
        # Sequential 会自动按列表顺序执行层,无需在 forward 中逐行调用
        self.model = Sequential(
            # -------------------------- 卷积+池化块 --------------------------
            # 第1个卷积+池化块:Conv2d → MaxPool2d
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),

            # 第2个卷积+池化块:Conv2d → MaxPool2d
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),

            # 第3个卷积+池化块:Conv2d → MaxPool2d
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),

            # -------------------------- 展平层+全连接层 --------------------------
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    # 前向传播方法:直接将数据喂给 Sequential 实例,一行代码完成所有计算
    def forward(self, x):
        x = self.model(x)
        return x

# -------------------------- 模型测试 --------------------------
if __name__ == "__main__":
    # 实例化模型
    model = Tudui()
    print("模型结构:")
    print(model)

    # 模拟一批CIFAR10输入数据
    input = torch.ones((64, 3, 32, 32))
    # 前向传播,获取输出
    output = model(input)
    print("\n输入形状:", input.shape)
    print("输出形状:", output.shape)  # 输出应为 torch.Size([64, 10])

2. 核心特点解析

  • 一次性堆叠 :在 __init__ 中使用 nn.Sequential,将所有层按数据流动顺序放入列表,一次性完成网络定义。
  • 极简前向forward 方法只需一行代码 x = self.model(x),无需手动调用每一层。
  • 线性执行 :层的执行顺序完全由 Sequential 中的列表顺序决定,无法插入分支或自定义操作,适合线性结构的网络。

四、两种方法对比总结

表格

对比维度 常规逐层定义法 nn.Sequential 堆叠法
代码结构 层定义分散,forward 逐行调用,代码行数多 层定义集中在 Sequential 中,forward 极简,代码更紧凑
灵活性 极高,支持自定义操作、分支结构、条件判断、中间层调试 较低,仅支持线性堆叠,无法插入额外逻辑
可读性 对新手友好,每一层的调用顺序清晰可见 结构一目了然,适合快速搭建简单网络
调试难度 调试方便,可在 forward 中任意位置打印中间层输出 调试不便,需拆分 Sequential 或手动获取中间层输出
适用场景 复杂网络、多分支结构、需要调试中间层的场景 简单线性网络、快速搭建原型、结构固定的场景

五、关键注意事项

  1. 效果完全等价:两种写法实现的网络结构、计算流程、参数数量完全一致,训练效果无差异,只是代码组织方式不同。
  2. Sequential 线性执行:层的执行顺序严格按照列表顺序,无法调整或分支,因此不适合 ResNet 等带残差连接的复杂网络。
  3. 中间层调试 :如果需要查看中间层输出形状,常规写法更方便;使用 Sequential 时,可通过拆分多个 Sequential 或手动获取中间层输出实现。
  4. 推荐使用场景 :简单 CNN 优先使用 nn.Sequential 简化代码;复杂多分支网络优先使用常规写法保证灵活性。

六、运行结果说明

两种写法运行后,模型输出形状均为 torch.Size([64, 10]),表示:

  • 批次大小 64:一批处理 64 张图片
  • 输出维度 10:对应 CIFAR10 的 10 个分类结果

你可以基于这两种写法,为网络添加 nn.ReLU 激活函数、nn.Dropout 层,或调整卷积核数量、池化方式,进一步优化模型性能。

相关推荐
沐籽李1 小时前
Agent入门第三课:上下文不是越长越好,记忆也不是越多越聪明
人工智能·agent·aidd·基础术语·入门概念
zhengfei6111 小时前
第7章 Agent 求职面试准备与行业实践
人工智能
老饼讲解-BP神经网络1 小时前
BP神经网络用什么训练算法(traingd、traingdm、trainlm)
人工智能·神经网络·算法
xiezhr1 小时前
Hermes官方桌面版发布了
人工智能·ai·agent·codex·hermes
Gavynlee1 小时前
ubuntu22.04 配置cluade code & 硅基流动API
人工智能
jinxindeep1 小时前
Bi-Adapt:基于语义对应实现跨类别双臂操作的高效泛化
人工智能·机器人
Godspeed Zhao2 小时前
Level 4自动驾驶系统设计2——功能与场景2
人工智能·机器学习·自动驾驶
Jerry.张蒙2 小时前
AI工具Opencode助力SAP提质增效实践
大数据·运维·服务器·人工智能·运维开发
老徐聊GEO2 小时前
AI搜索流量转化率实测分享:我的案例与复盘
人工智能·python