首页人工智能Pytorch【深度学习(PyTorch...

【深度学习(PyTorch篇)】43.nn.Module属性

本系列文章配套代码获取有以下两种途径:

  • 通过百度网盘获取:
链接:https://pan.baidu.com/s/1XuxKa9_G00NznvSK0cr5qw?pwd=mnsj 提取码:mnsj
  • 前往GitHub获取
https://github.com/returu/PyTorch





nn.Module 基类的构造函数中包含几个重要的属性,这些属性用于管理模块的状态、子模块、参数、缓冲区和钩子函数。
def __init__(self):
    self._parameters = OrderedDict()
    self._modules = OrderedDict()
    self._buffers = OrderedDict()
    self._backward_hooks = OrderedDict()
    self._forward_hooks = OrderedDict()
    self.training = True
其中:
  • _parameters: 一个有序字典,用于存储用户直接设置的Parameter这些参数在训练过程中会被优化。例如,self.weight = nn.Parameter(torch.Tensor(5, 10)),构造函数会在字典中加入一个keyweightvalue为对应Parameteritem。而self.submodel = nn.Linear(5,4)中的Parameter不会被保存在该字典中。
  • _modules: 一个有序字典,用于 存储模型的所有子模块,这些子模块本身也是 nn.Module 的实例,可以嵌套包含其他模块,形成模块树结构。例如,self.submodel = nn.Linear(5,4)指定的子module会被保存在字典中。
  • _buffers: 一个有序字典,用于存储存储模型的缓冲区变量,这些变量在训练和评估阶段可能会有不同的值,但不会被优化算法更新。例如,批量归一化层中的运行均值和运行方差。
  • _backward_hooks_forward_hooks: 这两个有序字典允许用户注册钩子函数,这些函数会在模块的前向或反向传播过程中被调用。这可以用于调试、监控和修改模型等操作。_backward_hooks在反向传播过程中执行的钩子,通常用于自定义梯度计算。_forward_hooks在前向传播过程中执行的钩子,可以用于检查输入输出,或者修改模型的行为。
  • training: 一个布尔值,用于指示模型当前是否处于训练模式。当 self.trainingTrue 时,某些层(如 Dropout BatchNorm)会表现得与评估模式下不同。例如,Dropout 层在训练模式下会随机丢弃一些神经元的输出,而在评估模式下则不会。

在上述几个属性中,通过self.key可以获得_parameters_modules_buffers三个字典中的键值。等价于self._parameters[‘key’]

本次以下述网络为例进行演示操作:

class CustomLinearLayer(nn.Module):  
    def __init__(self, input_features, output_features):  
        super().__init__()  
        self.weight = nn.Parameter(torch.Tensor(output_features, input_features))  
        self.bias = nn.Parameter(torch.Tensor(output_features))  

        self.submodel_1 = nn.Linear(5,4)
        self.submodel_2 = nn.Linear(4,2)
        self.submodel_3 = nn.Sequential(nn.Linear(2,3),nn.Linear(3,5))

    def forward(self, x):  
        out = torch.mm(x, self.weight.t()) + self.bias  
        out = self.submodel_1(out)
        out = self.submodel_2(out)
        out = self.submodel_3(out)

        return out 

01

_parameters


nn.Module类中,_parameters是一个私有属性,它是一个 OrderedDict,用于存储模块中定义的参数。可以通过该私有属性访问模型参数。但是通常,不推荐直接操作 _parameters,而是通过高级 API,如 model.parameters()model.named_parameters() 来访问这些参数。

其中,named_parameters() PyTorchnn.Module 类的一个方法,它返回模块中所有参数的迭代器,其中每个参数都是一个具名元组 (name, parameter),使用该方法可以很容易地识别每个参数属于模型的哪个部分。

# 不推荐使用
custom_linear._parameters

# 推荐以下两种方式
#遍历模型的参数
for parameter in custom_linear.parameters():
    print(parameter.shape)

# 遍历模型的命名参数
for name , parameter in custom_linear.named_parameters():
    print(name , parameter.shape)
要访问具体的参数,可以通过self.key的方式获取_parameters字典中的键值,该方式等价于self._parameters[‘key’]
# 等价于 custom_linear._parameters['bias'] 和 getattr(custom_linear , 'bias')
custom_linear.bias 

02

_modules


在实际使用中nn.Module一般都是层层嵌套的,即一个module可能包含若干个子module,每一个子module也可能包含更多的子module

因此,为了方便用户访问各个子modulenn.Module实现了很多方法,比如,使用children函数可以查看所有直接子module,使用_modules函数可以查看所有子module(包含当前module)。另外,与之对应的named_childrennamed_modules函数能在返回module列表的同时返回对应的module名称。

# 获取当前module和它的子module
custom_linear._modules

# 获取所有直接子module
custom_linear.children

# 用于获取模型中所有子模块的迭代器,并且每个子模块都会附带其名称
for name , submodel in custom_linear.named_children():
    print(name , submodel)

# 用于获取模型中所有模块的迭代器,每个模块都由其名称和模块对象本身组成
# 与 named_children() 不同的是,named_modules() 不仅会返回直接的子模块,还会递归地返回所有嵌套的子模块
for name , submodel in custom_linear.named_modules():
    print(name , submodel)

同样的,要访问具体的module,可以通过self.key的方式获取_modules字典中的键值,该方式等价于self._modules[‘key’]

# 等价于custom_linear._modules['submodel_1'] 和 getattr(custom_linear , 'submodel_1')
custom_linear.submodel_1

03

_buffers


_buffers 是用于存储缓冲数据的字典。这些缓冲数据通常不是模型学习的参数,但在模型的前向和反向传播中可能会被使用。与模型的参数parameters不同,缓冲区不会被优化器更新,因此它们不包含梯度。

例如,可以通过以下方式访问BatchNorm内部的缓冲区。对于nn.BatchNorm1d来说,这些缓冲数据主要包括running_meanrunning_var,它们分别用于在训练过程中跟踪每个特征的均值和方差。这些统计数据在评估(或测试)模式下用于归一化,而不是使用当前批次的统计量。

bn = nn.BatchNorm1d(3)

input = torch.rand(4,3)
output = bn(input)

# 直接访问BatchNorm1d层内部的缓冲区
bn._buffers

需要注意的是,通常不建议直接访问_buffers这样的内部属性,因为它们主要是为PyTorch的内部实现而设计的。如果你需要访问这些缓冲数据,可以使用公共方法,如bn.state_dict(),它会返回一个包含模块参数和缓冲数据的字典。

# 返回一个包含模块参数和缓冲数据的字典
bn.state_dict()
# 返回结果:
# OrderedDict([('weight', tensor([1., 1., 1.])),
#              ('bias', tensor([0., 0., 0.])),
#              ('running_mean', tensor([0.0534, 0.0582, 0.0693])),
#              ('running_var', tensor([0.9206, 0.9125, 0.9038])),
#              ('num_batches_tracked', tensor(1))])


04

training


training属性用于指示模型当前是否处于训练模式。

例如,Dropout 层在训练模式下会随机丢弃一些神经元的输出,而在评估模式下则不会。

input = torch.arange(0,12).reshape(3,4).float()

model = nn.Dropout()
# 在训练模式下会随机丢弃一些神经元的输出
model(input)
# 返回结果:
# tensor([[ 0.,  0.,  4.,  0.],
#         [ 0., 10., 12., 14.],
#         [ 0.,  0.,  0., 22.]])

# 在评估模式下不会丢弃任何输入,以确保评估时的一致性
model.training = False
model(input)
# 返回结果:
# tensor([[ 0.,  1.,  2.,  3.],
#         [ 4.,  5.,  6.,  7.],
#         [ 8.,  9., 10., 11.]])

但是,通过直接设置training属性将子module划分为train模式或者eval模式的方式较为繁琐。如果一个模型包含多个Dropout层,此时就需要为每个Dropout层都指定training属性。

推荐做法是调用model.train()函数将当前module及其子module中所有training属性都设置为True。相应的,调用model.eval()函数会将所有的training属性都设置为False

# 等价于 getattr(custom_linear , 'training') , getattr(custom_linear.submodel_1 , 'training')......
custom_linear.training , custom_linear.submodel_1.training , custom_linear.submodel_2.training , custom_linear.submodel_3.training
# 返回结果:(TrueTrueTrueTrue)

eval()将所有的training属性都设置为False
custom_linear.eval()

custom_linear.training , custom_linear.submodel_1.training , custom_linear.submodel_2.training , custom_linear.submodel_3.training
# 返回结果:(FalseFalseFalseFalse)


05

钩子函数


钩子函数(hook function)是一种在特定事件或条件触发时自动调用的函数。PyTorch中,钩子函数通常用于在模型的某个层或模块的前向或反向传播过程中插入自定义的操作,比如记录中间结果、进行调试、可视化特征图等。

PyTorchnn.Module中,可以使用以下两种钩子:
  • 前向钩子(Forward Hook):
在模块的前向传播过程中被调用。可以使用register_forward_hook方法来注册一个前向钩子函数。这个函数会在模块的前向计算之后立即被调用,并且可以接受模块的输入和输出作为参数。
前向传播的钩子函数形式如下
hook(module , input , output) -> None
  • 反向钩子(Backward Hook):
在模块的反向传播过程中被调用。使用register_backward_hook方法来注册一个反向钩子函数。这个函数会在模块的梯度计算之后被调用,并且可以接受模块的输入梯度和输出梯度作为参数。

反向传播的钩子函数形式如下:

hook(module , grad_input , grad_output) -> Tensor or None

钩子函数不应该修改模型的输入和输出,在使用后应该及时删除,避免每次都运行钩子函数增加运行负载。

下面是一个使用前向钩子的简单示例:

使用register_forward_hook方法,在给定的网络中的submodel_2处注册一个钩子函数。该方法允许你注册一个函数,该函数会在模块的前向传播函数forward被调用后立即执行,并可以访问模块的输入和输出。

# 创建模型实例
model = CustomLinearLayer(input_features=10, output_features=5)  

# 定义钩子函数
features = torch.Tensor()
def hook(module , input , output):
    # 把这一层的输出复制到features中
    global features
    features = output.data.clone()
    print("Input shape:", input[0].shape)
    print("Output shape:", output.shape)
    print("Output data:", output.data)

# 注册钩子到submodel_2
handle = model.submodel_2.register_forward_hook(hook)

# 测试前向传播
# 假设输入是一个3x10的张量,即3个样本,每个样本10个特征  
input_tensor = torch.randn(3, 10)
output_tensor = model(input_tensor)  

# 使用完钩子函数后将其删除
handle.remove()

输出结果如下:

Input shapetorch.Size([3, 4])
Output shapetorch.Size([3, 2])
Output datatensor([[-0.5211,  0.3788],
        [-0.5211,  0.3788],
        [-0.5211,  0.3788]])


更多内容可以前往官网查看

https://pytorch.org/


本篇文章来源于微信公众号: 码农设计师

RELATED ARTICLES

欢迎留下您的宝贵建议

Please enter your comment!
Please enter your name here

- Advertisment -

Most Popular

Recent Comments