一、神经网络核心组件

组件 定义与作用
神经网络的基本结构单元,核心功能是将输入张量通过数据变换(如卷积、线性变换)转换为输出张量
模型 由多个 “层” 按特定逻辑组合而成的完整网络,实现从输入数据到预测结果的端到端映射
损失函数 参数学习的目标函数,用于量化模型预测值(Y')与真实值(Y)的差异,训练中需最小化该函数
优化器 用于最小化损失函数的工具,通过调整模型参数(如权重),逐步降低损失,实现模型优化

二、构建神经网络的主要工具

2.1 两大工具的核心特性

  • nn.Module

    1. 本质:可继承的基类,继承后能自动提取模型中可学习的参数(如权重 weight、偏置 bias),无需手动管理。
    2. 适用场景:适用于构建含可学习参数的层,如卷积层(nn.Conv2d)、全连接层(nn.Linear)、dropout 层(nn.Dropout) 等。
    3. 写法示例:self.linear1 = nn.Linear(in_dim, n_hidden_1)(实例化后调用)。
  • nn.functional

    1. 本质:纯函数集合,无参数自动管理功能,仅实现数据变换逻辑。
    2. 适用场景:适用于无参数的操作,如激活函数(nn.functional.relu)、池化层(nn.functional.max_pool2d) 等。
    3. 写法示例:x = F.relu(self.bn1(x))(直接调用函数并传参)。

2.2 两大工具的核心区别

对比维度 nn.Module nn.functional
参数管理 自动定义、管理 weight/bias 需手动定义、传入 weight/bias
模型容器适配 可与 nn.Sequential 等容器结合使用 无法与 nn.Sequential 结合使用
dropout 状态转换 调用 model.eval () 后自动切换状态 需手动控制训练 / 测试状态
代码复用性 高(实例化后可重复调用) 低(每次调用需重复传参)

三、模型构建的三种方法

3.1 方法 1:直接继承 nn.Module 基类

  • 核心逻辑:通过继承nn.Module,在__init__中定义网络层,在forward中实现正向传播流程。
  • 关键参数示例:输入维度28×28=784,第一隐藏层300维,第二隐藏层100维,输出层10维(适配 10 分类任务)。
  • 代码要点
    1. 需调用super(Model_Seq, self).__init__()初始化父类。
    2. 层定义:如self.flatten = nn.Flatten()(展平 28×28 张量为 784 维向量)、self.linear1 = nn.Linear(784, 300)
    3. 正向传播:x = F.relu(self.bn1(self.linear1(self.flatten(x)))),最终通过F.softmax(x, dim=1)输出概率。
  • 运行结果特点:打印模型时可清晰看到各层名称及参数(如(linear1): Linear(in_features=784, out_features=300, bias=True))。

3.2 方法 2:使用 nn.Sequential 按层顺序构建

  • 三种实现方式
    实现方式 特点 代码示例片段
    可变参数 无法指定层名称,按顺序排列 nn.Sequential(nn.Flatten(), nn.Linear(784, 300), ...)
    add_module 方法 可通过add_module("名称", 层)指定名称 Seq_module.add_module("flatten", nn.Flatten())
    OrderedDict 以有序字典传入,层名称与层绑定 from collections import OrderedDict; nn.Sequential(OrderedDict([("flatten", nn.Flatten()), ...]))
  • 优势:代码简洁,无需手动定义forward方法,层按顺序自动执行。

3.3 方法 3:继承 nn.Module + 模型容器

  • 核心逻辑:继承nn.Module基类,在__init__中用nn.Sequential/nn.ModuleList/nn.ModuleDict封装层,兼顾灵活性与组织性。
  • 三种容器示例
    1. nn.Sequential 容器:将多个层组合为一个子模块,如self.layer1 = nn.Sequential(nn.Linear(784, 300), nn.BatchNorm1d(300)),正向传播时直接调用self.layer1(x)
    2. nn.ModuleList 容器:以列表形式管理层,需在forward中循环调用,如self.layers = nn.ModuleList([nn.Flatten(), nn.Linear(784, 300), ...]),正向传播:for layer in self.layers: x = layer(x)
    3. nn.ModuleDict 容器:以字典形式管理层(键为层名称,值为层),需指定层执行顺序,如self.layers_dict = nn.ModuleDict({"flatten": nn.Flatten(), ...}),正向传播:按列表["flatten", "linear1", ...]循环调用。

四、自定义网络模块(以 ResNet 为例)

4.1 两种残差块设计

残差块类型 设计目的 核心结构差异
RestNetBasicBlock 输入与输出张量形状一致时,实现 “恒等映射” 仅含 2 个 3×3 卷积层 + 批量规范化层,直接将输入与输出相加(x + output
RestNetDownBlock 输入与输出形状不一致(如通道数 / 分辨率变化),需调整维度以实现相加 在 2 个 3×3 卷积层基础上,增加 1×1 卷积层(extra),用于调整输入通道数与分辨率,最终相加extra_x + out

4.2 ResNet18 网络结构

  • 层结构详情
    1. 初始层:conv1(3 输入通道→64 输出通道,7×7 卷积核, stride=2,padding=3)→bn1(批量规范化)→maxpool(3×3 池化核,stride=2,padding=1)。
    2. 残差块组:
      • layer1:2 个RestNetBasicBlock(64 通道→64 通道,stride=1)。
      • layer2:1 个RestNetDownBlock(64→128 通道,stride=[2,1])+1 个RestNetBasicBlock(128→128 通道)。
      • layer3:1 个RestNetDownBlock(128→256 通道,stride=[2,1])+1 个RestNetBasicBlock(256→256 通道)。
      • layer4:1 个RestNetDownBlock(256→512 通道,stride=[2,1])+1 个RestNetBasicBlock(512→512 通道)。
    3. 输出层:avgpool(自适应平均池化,输出 1×1 特征图)→展平(512 维)→fc(512→10 输出通道,适配 10 分类)。

五、模型训练流程

  1. 加载预处理数据集:导入已预处理的数据集(如 MNIST,28×28 手写数字),准备训练集与测试集。
  2. 定义损失函数:选择适配任务的损失函数(如分类任务用交叉熵损失,文档中未指定具体函数,但明确其为 “参数学习的目标函数”)。
  3. 定义优化方法:选择优化器(如 SGD、Adam),用于最小化损失函数以更新模型参数。
  4. 循环训练模型:迭代训练集,执行正向传播(计算预测值)、反向传播(计算梯度,loss.backward())、参数更新(optimizer.step())。
  5. 循环测试或验证模型:迭代测试集 / 验证集,关闭梯度计算(torch.no_grad()),评估模型性能(如准确率)。
  6. 可视化结果:通过图表(如损失曲线、准确率曲线)展示训练与测试过程的结果。

4. 关键问题

问题 1:nn.Module 与 nn.functional 在 PyTorch 构建神经网络中的核心区别是什么?

答案

二者核心区别体现在 3 个维度:

  1. 参数管理:nn.Module 可自动定义、管理 weight/bias 等可学习参数(如 nn.Linear 实例化后无需手动传入参数);nn.functional 需手动定义参数,且每次调用需传入(如nn.functional.linear(x, weight, bias)),不利于代码复用。
  2. 模型容器适配:nn.Module 可与 nn.Sequential、nn.ModuleList 等模型容器结合使用,便于层的组织;nn.functional 无法适配 nn.Sequential,仅能单独调用。
  3. dropout 状态转换:使用 nn.Module 的 nn.Dropout 时,调用model.eval()后会自动切换为测试状态(关闭 dropout);nn.functional.dropout 需手动通过training参数控制状态(如nn.functional.dropout(x, training=self.training))。

问题 2:PyTorch 中,继承 nn.Module 基类构建模型与使用 nn.Sequential 构建模型的适用场景有何差异?

答案

  1. 继承 nn.Module 基类的适用场景:适用于复杂网络结构(如含分支、多输入 / 多输出、自定义正向传播逻辑的网络)。例如 ResNet 的残差块需将输入与卷积输出相加,需在forward中自定义该逻辑,无法通过 nn.Sequential 的线性流程实现;此外,需灵活调整层执行顺序或插入条件判断时,也需使用该方式。
  2. 使用 nn.Sequential 的适用场景:适用于层结构简单、按线性顺序执行的网络(如基础全连接网络、简单卷积网络)。例如文档中 28×28 输入→展平→线性层→批量规范化→激活函数→输出的分类网络,层按固定顺序执行,无需自定义逻辑,此时 nn.Sequential 可简化代码(无需手动定义forward),提升开发效率。

问题 3:ResNet 中的两种残差块(RestNetBasicBlock 与 RestNetDownBlock)的设计目的及结构差异是什么?

答案

  1. 设计目的差异

    • RestNetBasicBlock:用于输入与输出张量形状一致(通道数、分辨率相同)的场景,实现 “恒等映射”,确保残差连接(输入直接加到输出)时维度匹配,避免梯度消失。
    • RestNetDownBlock:用于输入与输出形状不一致(如通道数增加、分辨率降低)的场景,需通过额外结构调整输入维度,使残差连接可实现(输入经调整后与输出维度匹配)。
  2. 结构差异

    • RestNetBasicBlock:仅包含 2 个 3×3 卷积层 + 对应批量规范化层,正向传播为 “卷积 1→批量规范化→ReLU→卷积 2→批量规范化→输入 + 输出→ReLU”,无额外维度调整层。
    • RestNetDownBlock:在 2 个 3×3 卷积层 + 批量规范化层的基础上,增加 1 个1×1 卷积层 + 批量规范化层(命名为extra),正向传播时先将输入通过extra调整通道数与分辨率(extra_x = self.extra(x)),再与卷积输出相加(extra_x + out),最终经 ReLU 输出。
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐