[pytorch]model.children(), model.modules()和parameters()的区别和用法

本文详细介绍了PyTorch中children, modules和parameters三种方法的使用区别与场景。通过具体示例,展示了如何利用这些方法来遍历神经网络结构,并解释了它们在模型初始化、参数加载等方面的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[pytorch]children,modules和parameters的区别和用法

children

children只获取最浅层的网络结构,相应的named_children则返回tuple的数据,tuple[0]是该层的名称,tuple[1]是相应的结构:

class Net4(torch.nn.Module):
    def __init__(self):
        super(Net4, self).__init__()
        self.conv = torch.nn.Sequential(
            OrderedDict(
                [
                    ("conv1", torch.nn.Conv2d(3, 32, 3, 1, 1)),
                    ("relu1", torch.nn.ReLU()),
                    ("pool1", torch.nn.MaxPool2d(2))
                ]
            ))

        self.dense = torch.nn.Sequential(
            OrderedDict([
                ("dense1", torch.nn.Linear(32 * 3 * 3, 128)),
                ("relu2", torch.nn.ReLU()),
                ("dense2", torch.nn.Linear(128, 10))
            ])
        )

    def forward(self, x):
        conv_out = self.conv1(x)
        res = conv_out.view(conv_out.size(0), -1)
        out = self.dense(res)
        return out


print("Method 4:")
model4 = Net4()
# children()方法其实就是获取模型的属性,可看到构造函数有两个属性
for i in model4.children():
    print(i)
    print(type(i))
print('==============================')
# named_children()方法也是获取模型的属性,同时获取属性的名字
for i in model4.named_children():
    print(i)
    print(type(i))

输出的结果是:

'''
Sequential(
  (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (relu1): ReLU()
  (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
<class 'torch.nn.modules.container.Sequential'>
Sequential(
  (dense1): Linear(in_features=288, out_features=128, bias=True)
  (relu2): ReLU()
  (dense2): Linear(in_features=128, out_features=10, bias=True)
)
<class 'torch.nn.modules.container.Sequential'>

==============================
('conv', Sequential(
  (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (relu1): ReLU()
  (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
))
<class 'tuple'>
('dense', Sequential(
  (dense1): Linear(in_features=288, out_features=128, bias=True)
  (relu2): ReLU()
  (dense2): Linear(in_features=128, out_features=10, bias=True)
))
<class 'tuple'>
'''

module

model的modules()方法和named_modules()方法都会将整个模型的所有构成(包括包装层、单独的层、自定义层等)由浅入深依次遍历出来, 直到最深处的单层,只不过modules()返回的每一个元素是直接返回的层对象本身,而named_modules()返回的每一个元素是一个元组,第一个元素是名称,第二个元素才是层对象本身。

class Net4(torch.nn.Module):
    def __init__(self):
        super(Net4, self).__init__()
        self.conv = torch.nn.Sequential(
            OrderedDict(
                [
                    ("conv1", torch.nn.Conv2d(3, 32, 3, 1, 1)),
                    ("relu1", torch.nn.ReLU()),
                    ("pool1", torch.nn.MaxPool2d(2))
                ]
            ))

        self.dense = torch.nn.Sequential(
            OrderedDict([
                ("dense1", torch.nn.Linear(32 * 3 * 3, 128)),
                ("relu2", torch.nn.ReLU()),
                ("dense2", torch.nn.Linear(128, 10))
            ])
        )

    def forward(self, x):
        conv_out = self.conv1(x)
        res = conv_out.view(conv_out.size(0), -1)
        out = self.dense(res)
        return out


print("Method 4:")
model4 = Net4()
# modules方法将整个模型的所有构成(包括包装层Sequential、单独的层、自定义层等)由浅入深依次遍历出来,直到最深处的单层
for i in model4.modules():
    print(i)
    print('==============================')
print('=============华丽分割线=================')
# named_modules()同上,但是返回的每一个元素是一个元组,第一个元素是名称,第二个元素才是层对象本身。
for i in model4.named_modules():
    print(i)
    print('==============================')

输出结果是:

'''
离模型最近即最浅处,就是模型本身
Net4(
  (conv): Sequential(
    (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu1): ReLU()
    (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (dense): Sequential(
    (dense1): Linear(in_features=288, out_features=128, bias=True)
    (relu2): ReLU()
    (dense2): Linear(in_features=128, out_features=10, bias=True)
  )
)
==============================
由浅入深,到模型的属性
Sequential(
  (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (relu1): ReLU()
  (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
==============================
由浅入深,再到模型的属性的内部
Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
==============================
由浅入深,再到模型的属性的内部,依次将这个属性遍历结束
ReLU()
==============================
MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
由浅入深,再到模型的属性的内部,依次将这个属性遍历结束,再遍历另个属性
==============================
Sequential(
  (dense1): Linear(in_features=288, out_features=128, bias=True)
  (relu2): ReLU()
  (dense2): Linear(in_features=128, out_features=10, bias=True)
)
==============================
Linear(in_features=288, out_features=128, bias=True)
==============================
ReLU()
==============================
Linear(in_features=128, out_features=10, bias=True)
==============================
=============华丽分割线=================
('', Net4(
  (conv): Sequential(
    (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (relu1): ReLU()
    (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (dense): Sequential(
    (dense1): Linear(in_features=288, out_features=128, bias=True)
    (relu2): ReLU()
    (dense2): Linear(in_features=128, out_features=10, bias=True)
  )
))
==============================
('conv', Sequential(
  (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (relu1): ReLU()
  (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
))
==============================
('conv.conv1', Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)))
==============================
('conv.relu1', ReLU())
==============================
('conv.pool1', MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))
==============================
('dense', Sequential(
  (dense1): Linear(in_features=288, out_features=128, bias=True)
  (relu2): ReLU()
  (dense2): Linear(in_features=128, out_features=10, bias=True)
))
==============================
('dense.dense1', Linear(in_features=288, out_features=128, bias=True))
==============================
('dense.relu2', ReLU())
==============================
('dense.dense2', Linear(in_features=128, out_features=10, bias=True))
==============================
'''

parameters

上述的两种一般都还是用在可视化网络的结构的,parameters主要作用是需要获取模型的每个层的对象时使用,比如模型初始化,模型加载参数等。

for k,v in resnet_backbone.named_parameters():
    if 'bn' in k:
        v.requires_grad = False


for k,v in resnet_backbone.named_parameters():
    print('{}: {}'.format(k, v.requires_grad))

输出结果是:

convnet_1.0.weight: True
convnet_1.1.weight: True
convnet_1.1.bias: True
convnet_2.0.0.conv1.weight: True
convnet_2.0.0.bn1.weight: False
convnet_2.0.0.bn1.bias: False
convnet_2.0.0.conv2.weight: True
convnet_2.0.0.bn2.weight: False
convnet_2.0.0.bn2.bias: False
convnet_2.0.0.conv3.weight: True
convnet_2.0.0.bn3.weight: False
convnet_2.0.0.bn3.bias: False
convnet_2.0.0.downsample.0.weight: True
convnet_2.0.0.downsample.1.weight: True
convnet_2.0.0.downsample.1.bias: True
<think>好的,我现在要回答用户关于PyTorch中解冻模型层数的问题。首先,用户问的是为什么在代码中使用`for param in model.layer4.parameters(): param.requires_grad = True`就能解冻部分层,以及如何查看代码中的层数。我需要逐步分析,确保解释清楚这两个问题。 首先,理解解冻层的概念。在PyTorch中,每个参数(param)都有一个`requires_grad`属性,这个属性默认为True,表示在训练过程中需要计算梯度以便进行反向传播参数更新。如果设置为False,则该参数在训练时不会更新,也就是被“冻结”了。所以,当用户设置`param.requires_grad = True`时,实际上是让这些参数在训练过程中重新参与梯度计算更新,即“解冻”了这些层。 接下来,用户代码中的`model.layer4`可能指的是模型的某个特定层组。例如,在ResNet中,模型通常分为多个层组(如layer1到layer4),每个层组包含多个残差块。解冻layer4的参数意味着允许这些层的权重在训练过程中被调整,而其他层(如layer1到layer3)可能保持冻结状态。这样做的好处是,可以利用预训练模型在较低层次学到的通用特征(如边缘、纹理等),同时针对目标任务调整更高层次的抽象特征。 然后,关于如何查看代码中的层数。用户可能需要知道如何确定模型的结构,以了解各个层的名称层级关系。在PyTorch中,可以通过打印模型的结构来查看层数。例如,使用`print(model)`会输出模型的各层及其名称。此外,还可以通过遍历模型的命名参数来查看每一层的名称,如使用`model.named_parameters()`或者`model.named_children()`来获取层的详细信息。 需要注意的是,不同的预训练模型结构可能不同,因此层数的名称分组方式也会有所差异。例如,ResNet中的layer4通常指最后一个残差层组,而VGG可能按特征层分类器层分组。用户需要根据具体的模型文档或结构输出来确定各个层的名称。 此外,用户可能还想了解为什么选择解冻特定层(如layer4)。通常,较低层次的层学习的是通用特征,而较高层次的层更偏向于特定任务的特征。因此,在迁移学习中,通常冻结底层,解冻顶层以适应新任务。但这也取决于目标任务源任务的相似性。如果目标任务与源任务差异较大,可能需要解冻更多层;反之,如果相似,可能只需要调整顶层。 在解释过程中,还需要注意术语的准确性,例如“解冻”即允许参数更新,“冻结”即固定参数。同时,要确保示例代码的准确性,比如使用ResNet的例子是否正确,以及如何替换顶层分类器等。 最后,总结这两个问题的回答:解冻层的机制是通过设置`requires_grad`为True,允许参数更新;查看层数的方法包括打印模型结构使用命名参数/子模块。需要根据具体模型结构来确定层的名称分组方式,从而选择解冻哪些层。</think>--- ### **1. 为什么 `param.requires_grad = True` 是解冻部分层?** 在PyTorch中,模型的参数(权重偏置)默认需要计算梯度(`requires_grad=True`),以便通过反向传播进行更新。**冻结层**的本质是**固定这些参数的值**,使其在训练过程中不更新。具体原理如下: - **冻结(Freeze)**:设置 `param.requires_grad = False`,表示不计算梯度,参数不更新。 - **解冻(Unfreeze)**:设置 `param.requires_grad = True`,允许梯度计算参数更新。 在代码中: ```python for param in model.layer4.parameters(): param.requires_grad = True # 解冻layer4的所有参数 ``` 这意味着: - `model.layer4` 是模型的某一层(如ResNet的最后一个残差块组)。 - 解冻这一层的参数后,这些参数会在训练过程中被优化器更新,从而适配目标任务。 --- ### **2. 如何查看模型中的层数?** #### **方法1:直接打印模型结构** 通过 `print(model)` 输出模型各层的名称层级关系。 **示例(ResNet-18)**: ```python import torchvision.models as models model = models.resnet18(pretrained=True) print(model) # 输出模型的层结构 ``` **输出片段**: ``` ... (avgpool): AdaptiveAvgPool2d(output_size=(1, 1)) (fc): Linear(in_features=512, out_features=1000, bias=True) (layer1): Sequential(...) # 第1层组 (layer2): Sequential(...) # 第2层组 (layer3): Sequential(...) # 第3层组 (layer4): Sequential(...) # 第4层组 ... ``` #### **方法2:遍历模型的命名子模块** 使用 `named_children()` 或 `named_modules()` 查看各层的名称层级。 **示例**: ```python # 遍历所有子模块的层级名称 for name, module in model.named_children(): print(f"Layer group: {name}") # 可进一步递归查看子模块的内部结构 # 输出示例: # Layer group: conv1 # Layer group: bn1 # Layer group: relu # Layer group: maxpool # Layer group: layer1 # Layer group: layer2 # Layer group: layer3 # Layer group: layer4 # Layer group: avgpool # Layer group: fc ``` #### **方法3:查看参数名称** 通过 `named_parameters()` 查看具体参数所属的层。 **示例**: ```python for name, param in model.named_parameters(): print(f"参数名称: {name}, 是否计算梯度: {param.requires_grad}") ``` **输出示例**: ``` 参数名称: conv1.weight, 是否计算梯度: False 参数名称: layer4.0.conv1.weight, 是否计算梯度: True ... ``` --- ### **3. 如何选择解冻哪些层?** - **通用规则**: - **底层(如layer1~layer3)**:通常学习通用特征(如边缘、纹理),可保持冻结。 - **高层(如layer4、fc层)**:学习任务相关特征(如类别语义),需解冻并微调。 - **具体策略**: - 目标任务数据少 → 仅解冻最后1-2层。 - 目标任务数据多 → 解冻更多层。 - 领域差异大 → 可能需要解冻全部层。 --- ### **4. 代码示例:分阶段解冻** ```python # 冻结所有层 for param in model.parameters(): param.requires_grad = False # 解冻layer4fc层 for param in model.layer4.parameters(): param.requires_grad = True for param in model.fc.parameters(): param.requires_grad = True ``` --- ### **总结** - **解冻层**:通过设置 `param.requires_grad = True` 允许参数更新。 - **查看层数**:使用 `print(model)`、`named_children()` 或 `named_parameters()` 分析模型结构。 - **实践建议**:根据任务需求数据量,灵活选择解冻的层数范围。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值