学习模型构造

顺序块(下面也自定义了顺序快):

三种方式写 自定义块(需要继承nn.Module) 重点

不建议第二种 一般第一种简单

python 复制代码
import torch.nn as nn
import torch.nn.functional as F

# 第一种方式:直接定义
class MLP1(nn.Module):
    def __init__(self):
        super().__init__()
        # 直接创建层
        self.hidden = nn.Linear(20, 256)
        self.relu = nn.ReLU()
        self.out = nn.Linear(256, 10)
    
    def forward(self, x):
        x = self.hidden(x)
        x = self.relu(x)
        x = self.out(x)
        return x

# 第二种方式:通过元组传入
class MLP2(nn.Module):
    def __init__(self, layers_tuple):
        super().__init__()
        # layers_tuple 是 (nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
        # 我们需要在 __init__ 中处理这个元组
        self.layers = nn.ModuleList(layers_tuple)  # 使用ModuleList保存
        
    def forward(self, x):
        for layer in self.layers:
            x = layer(x)
        return x

# 第三种方式:自定义Sequential(更灵活)
class MySequential(nn.Module):
    def __init__(self, *args):  # 使用*args接收任意数量的参数
        super().__init__()
        # args 是一个元组,包含传入的所有层
# 这里,module是Module子类的一个实例。我们把它保存在'Module'类的成员
            # 变量_modules中。_module的类型是OrderedDict
        for idx, layer in enumerate(args):
            # 将每一层添加到 _modules 字典中
            self.add_module(str(idx), layer)
            # 或者:self._modules[str(idx)] = layer
    
    def forward(self, x):
        # 按顺序执行所有层 # OrderedDict保证了按照成员添加的顺序遍历它们
        for layer in self._modules.values():
            x = layer(x)
        return x

__init__函数将每个模块逐个添加到有序字典_modules 中。 读者可能会好奇为什么每个Module都有一个_modules属性? 以及为什么我们使用它而不是自己定义一个Python列表?

简而言之,_modules的主要优点是: 在模块的参数初始化过程中, 系统知道在_modules字典中查找需要初始化参数的子块。

第三种方式调用:

X = torch.rand(2, 20)

创建一个形状为 (2, 20) 的张量,其中每个元素都是从均匀分布 U[0, 1) 中随机生成的浮点数

why要自定义块:

Sequential类使模型构造变得简单, 允许我们组合新的架构,而不必定义自己的类。

然而,并不是所有的架构都是简单的顺序架构。 当需要更强的灵活性时,我们需要定义自己的块。 例如,我们可能希望在前向传播函数中执行Python的控制流。 此外,我们可能希望执行任意的数学运算, 而不是简单地依赖预定义的神经网络层。

参数共享:

from torch.nn import functional as F

python 复制代码
# 对比两种写法:

# 写法A:参数共享(原始代码)
class SharedModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(20, 20)  # 只有一组参数
        self.rand_weight = torch.rand((20, 20), requires_grad=False)
    
    def forward(self, x):
        x = self.linear(x)  # 第一次使用
        x = F.relu(torch.mm(x, self.rand_weight) + 1)
        x = self.linear(x)  # 第二次使用相同的参数
        return x

# 写法B:不共享参数
class SeparateModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear1 = nn.Linear(20, 20)  # 第一组参数
        self.linear2 = nn.Linear(20, 20)  # 第二组参数(不同的参数)
        self.rand_weight = torch.rand((20, 20), requires_grad=False)
    
    def forward(self, x):
        x = self.linear1(x)  # 使用linear1
        x = F.relu(torch.mm(x, self.rand_weight) + 1)
        x = self.linear2(x)  # 使用linear2
        return x

# 参数量对比
shared_model = SharedModel()
separate_model = SeparateModel()

print(f"参数共享模型参数量: {sum(p.numel() for p in shared_model.parameters())}")
print(f"独立参数模型参数量: {sum(p.numel() for p in separate_model.parameters())}")
# 输出:
# 参数共享模型参数量: 420  (20 * 20 + 20)
# 独立参数模型参数量: 840  (2*(20 * 20 + 20))
参数量计算:
线性层默认的偏置(bias)不是0,而是随机初始化的

验证:

上面例子的复用线性层的可训练参数计算:

步骤1:识别可训练参数

  1. self.rand_weightrequires_grad=False,所以不算可训练参数

  2. self.linear:这是可训练的参数

    • 权重:20 × 20 = 400

    • 偏置:20

    • 小计:400 + 20 = 420

总可训练参数:420

步骤2:计算总参数(包括固定参数)

  • 可训练参数:420

  • 固定参数(self.rand_weight):20 × 20 = 400

  • 总参数420 + 400 = 820

组合块:

相关推荐
天天进步20151 小时前
Python全栈项目--基于Python的数据库管理工具
开发语言·数据库·python
阿提说说1 小时前
我的 NVIDIA 考试攻略
python·大模型·agent
LaughingZhu1 小时前
Product Hunt 每日热榜 | 2026-06-09
人工智能·经验分享·深度学习·神经网络·产品运营
一锅炖出任易仙1 小时前
创梦汤锅学习日记day29
学习·ai·ue5·游戏引擎
xyz_CDragon2 小时前
OpenClaw 局域网调用 Ollama 本地大模型:完整配置与踩坑指南
python·ai编程·集成学习·ollama·deepseek·openclaw
MartinYeung52 小时前
[论文学习]无资料选择性遗忘:透过模型反演实现 LLM 的资料免隐私保护(DFSU)
学习
极光代码工作室2 小时前
基于NLP的论文关键词提取系统
python·深度学习·自然语言处理·nlp
Wang ruoxi2 小时前
Pygame 小游戏——数独
开发语言·python·pygame
吠品2 小时前
处理 Python 类继承中那些变来变去的初始化参数
linux·前端·python