神经网络的层与块

什么是层?什么是块?

在深度学习中,层(Layer)块(Block) 是构建神经网络的核心概念,尤其在 PyTorch、TensorFlow 等框架中,二者既紧密关联又有明确分工。理解它们的定义、关系和用法,是掌握神经网络设计的基础。

一、核心定义

1. 层(Layer)

层是神经网络中最基本的计算单元,实现特定的数学操作(如线性变换、卷积、激活函数等)。

  • 功能单一:通常只完成一种特定计算(如nn.Linear实现线性变换y = Wx + bnn.ReLU实现激活函数y = max(0, x))。
  • 可复用性低:单个层一般不单独使用,需与其他层组合才能完成复杂任务。
2. 块(Block)

块是由多个层(或其他块)组合而成的复杂单元,封装了一组相关的计算逻辑。

  • 功能复合:可以包含多个层(如 "卷积层 + 激活函数 + 池化层" 组成的卷积块),甚至嵌套其他块(如 ResNet 中的残差块包含多个卷积块)。
  • 可复用性高:块可以被看作 "超级层",在网络中重复使用(如 Transformer 中的 Encoder 块被重复堆叠)。

二、本质关系:层是块的 "原子",块是层的 "组合"

在 PyTorch 中,所有层和块都继承自nn.Module ,因此它们在接口上保持一致(都有__init__初始化方法和forward前向传播方法)。

  • 层是 "最小化的块":单个层(如nn.Linear)可以视为只包含一个计算步骤的特殊块。
  • 块是 "结构化的层集合":块通过组合多个层(或块),实现更复杂的功能(如特征提取、残差连接等)。

三、具体区别与联系

维度 层(Layer) 块(Block)
组成 单一计算单元(如矩阵乘法、卷积) 多个层 / 块的组合(如 "线性层 + 激活函数 + dropout")
功能 实现基础操作(如线性变换、非线性激活) 实现复杂功能(如特征提取、残差连接、注意力机制)
复用性 低(通常作为块的组成部分) 高(可作为模块重复嵌入到不同网络中)
示例 nn.Linearnn.Conv2dnn.ReLU nn.Sequential、ResNet 的残差块、Transformer 的 Encoder 块

为什么需要自定义 Sequential?

虽然 PyTorch 已有nn.Sequential,但自定义版本有以下用途:

  1. 学习原理:理解 PyTorch 如何管理模块和参数。
  2. 扩展功能:例如,添加日志记录、中间输出缓存等功能。
  3. 简化接口:在特定场景下提供更简洁的 API。

MySequentialnn.Sequential功能基本相同,有那些细微差异?

特性 MySequential nn.Sequential
模块命名 自动生成索引(如 "0", "1") 可自定义名称(如nn.Sequential(relu=nn.ReLU())
初始化方式 接收任意数量的模块 接收多个模块或有序字典
实现复杂度 约 20 行代码 更复杂(支持更多特性)

完整代码

python 复制代码
"""
文件名: 5.1
作者: 墨尘
日期: 2025/7/13
项目名: dl_env
备注:  输出结果不一样,是因为Linear权值是随机初始化的
"""
import torch
from torch import nn
from torch.nn import functional as F

"""多层感知机,使用自定义块实现"""
class MLP(nn.Module):
    # 用模型参数声明层。这里,我们声明两个全连接的层
    def __init__(self):
        # 调用MLP的父类Module的构造函数来执行必要的初始化。
        # 这样,在类实例化时也可以指定其他函数参数,例如模型参数params(稍后将介绍)
        super().__init__()
        self.hidden = nn.Linear(20, 256)  # 隐藏层
        self.out = nn.Linear(256, 10)  # 输出层

    # 定义模型的前向传播,即如何根据输入X返回所需的模型输出
    def forward(self, X):
        # 注意,这里我们使用ReLU的函数版本,其在nn.functional模块中定义。
        return self.out(F.relu(self.hidden(X)))


"""自定义顺序块,按传入顺序连接多个模块"""


# MySequential的核心目标是:将多个层按传入的顺序连接起来,前一层的输出作为后一层的输入
class MySequential(nn.Module):
    def __init__(self, *args):
        """
        初始化顺序块,接收任意数量的PyTorch模块
        参数:
            *args: 任意数量的nn.Module子类实例(如nn.Linear, nn.ReLU等)
        """
        # 调用父类nn.Module的构造函数,完成必要的初始化
        super().__init__()
        # 遍历所有传入的模块
        for idx, module in enumerate(args):
            # 将模块添加到PyTorch内置的有序字典_modules中
            # 键: 模块的索引(字符串形式)
            # 值: 具体的模块实例
            # _modules是nn.Module的特殊属性,PyTorch会自动管理其中的所有模块
            # 包括参数初始化、设备同步、序列化等
            self._modules[str(idx)] = module
    def forward(self, X):
        """
        定义前向传播逻辑,按顺序依次调用所有模块
        参数:
            X: 输入张量
        返回:
            经过所有模块处理后的输出张量
        """
        # 按_modules中保存的顺序遍历所有模块
        # OrderedDict保证了遍历时模块的顺序与添加时一致
        for block in self._modules.values():
            # 将输入数据依次通过每个模块
            # 前一个模块的输出直接作为下一个模块的输入
            X = block(X)
        # 返回最终输出
        return X

"""在前向传播函数中执行代码"""
class FixedHiddenMLP(nn.Module):
    def __init__(self):
        """
        自定义神经网络模块,展示PyTorch中的特殊用法:
        1. 使用固定权重(训练期间不更新)
        2. 层参数共享
        3. 前向传播中的控制流
        """
        super().__init__()
        # 创建固定权重矩阵(随机初始化,但不参与训练)
        # requires_grad=False:禁用梯度计算,训练时权重不会更新
        self.rand_weight = torch.rand((20, 20), requires_grad=False)
        # 定义可训练的线性层
        self.linear = nn.Linear(20, 20)
    def forward(self, X):
        """
        定义前向传播逻辑,包含非常规操作:
        1. 使用固定权重矩阵进行矩阵乘法
        2. 复用同一个线性层(参数共享)
        3. 使用while循环控制输出规模
        """
        # 第一层:可训练的线性变换
        X = self.linear(X)
        # 第二层:使用固定随机权重进行矩阵乘法,添加偏置1,再通过ReLU激活
        # torch.mm:矩阵乘法
        # self.rand_weight在训练过程中保持不变
        X = F.relu(torch.mm(X, self.rand_weight) + 1)
        # 第三层:复用第一个线性层(参数共享)
        # 相当于两个不同层共享同一组参数
        X = self.linear(X)
        # 控制流:如果张量X的绝对值之和大于1,则不断将X除以2
        # 这是一个自定义的输出规范化策略
        while X.abs().sum() > 1:
            X /= 2
        # 返回标量值:所有元素的和
        return X.sum()


if __name__ == '__main__':
    """多层感知机
    构建一个包含
    输入层→隐藏层→输出层
    的全连接神经网络,对随机生成的输入数据进行计算并输出结果。"""
    # 线性变换负责特征的线性映射,激活函数负责注入非线性,两者交替使用才能让网络有能力学习复杂数据。
    # 传播过程拆解:
    # 输入X(形状(2,20))→ 第 1 层线性变换 → 输出X1(形状(2,256))
    # X1→ 第 2 层 ReLU 激活 → 输出X2(形状(2,256),所有元素非负)
    # X2→ 第 3 层线性变换 → 输出Y(形状(2,10))
    # 1. 定义神经网络
    # 这个前向传播函数非常简单: 它将列表中的每个块连接在一起,将每个块的输出作为下一个块的输入。
    net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
    # 2. 生成输入数据
    X = torch.rand(2, 20)
    # 3. 前向传播并打印输出
    print(net(X))


    """自定义块"""
    net = MLP()
    print(net(X))

    """顺序块"""
    net = MySequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
    print(net(X))


    """在前向传播函数中执行代码"""
    net = FixedHiddenMLP()
    print(net(X))

实验结果

相关推荐
ATM00616 分钟前
人机协作系列(四)AI编程的下一个范式革命——看Factory AI如何重构软件工程?
人工智能·大模型·agent·人机协作·人机协同
读创商闻1 小时前
极狐GitLab CEO 柳钢——极狐 GitLab 打造中国企业专属 AI 编程平台,引领编程新潮流
人工智能·gitlab
kailp1 小时前
语言模型玩转3D生成:LLaMA-Mesh开源项目
人工智能·3d·ai·语言模型·llama·gpu算力
marteker1 小时前
弗兰肯斯坦式的人工智能与GTM策略的崩溃
人工智能·搜索引擎
无心水1 小时前
大语言模型零样本情感分析实战:无需机器学习训练,96%准确率实现指南
人工智能·机器学习·语言模型
来自于狂人1 小时前
AI大模型训练的云原生实践:如何用Kubernetes指挥千卡集群?
人工智能·云原生·kubernetes
橡晟7 小时前
深度学习入门:让神经网络变得“深不可测“⚡(二)
人工智能·python·深度学习·机器学习·计算机视觉
Leah01057 小时前
什么是神经网络,常用的神经网络,如何训练一个神经网络
人工智能·深度学习·神经网络·ai
倔强青铜37 小时前
苦练Python第18天:Python异常处理锦囊
开发语言·python