pytorch--nn.module里的parameters和buffer的区别与定义
定义:
在模型中有两种需要被保存下来的参数:parameter和buffer
- 一种是反向传播需要被optimizer更新的,称之为 parameter(如权重等)
- 一种是反向传播不需要被optimizer更新,称之为 buffer(一些阈值之类的)
注册:torch.nn.register_parameter()用于注册Parameter实例到当前Module中(一般可以用torch.nn.Parameter()代替);torch.nn.register_buffer()用于注册Buffer实例到当前Module中。此外,Module中的parameters()函数会返回当前Module中所注册的所有Parameter的迭代器;而_all_buffers()函数会返回当前Module中所注册的所有Buffer的迭代器,(所以优化器不会计算Buffer的梯度,自然不会对其更新)。此外,Module中的state_dict()会返回包含当前Module中所注册的所有Parameter和Buffer(所以模型中未注册成Parameter或Buffer的参数无法被保存)。
创建:
- parameter参数有两种创建方式:
- 我们可以直接将模型的成员变量(self.xxx)通过nn.Parameter()创建,会自动注册到parameters中,可以通过model.parameters()返回,并且这样创建的参数会自动保存到OrderedDict中去。
class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() self.my_param = nn.Parameter(torch.randn(3, 3)) # 模型的成员变量 def forward(self, x): # 可以通过 self.my_param 和 self.my_buffer 访问 pass model = MyModel() for param in model.parameters(): print(param) print("----------------") print(model.state_dict()) 输出: Parameter containing: tensor([[-0.5421, 2.9562, 0.3447], [ 0.0869, -0.3464, 1.1299], [ 0.8644, -0.1384, -0.6338]]) ---------------- OrderedDict([('param', tensor([[-0.5421, 2.9562, 0.3447], [ 0.0869, -0.3464, 1.1299], [ 0.8644, -0.1384, -0.6338]]))])
2.通过nn.Parameter()创建普通的Parameter对象,不作为模型的成员变量,然后将Parameter对象通过register_parameter()进行注册,可以通过model.parameters()返回,注册后的参数也是会自动保存到OrderedDict中去。
import torch import torch.nn as nn class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() param = nn.Parameter(torch.randn(3, 3)) # 普通 Parameter 对象 self.register_parameter("my_param", param) def forward(self, x): # 可以通过 self.my_param 和 self.my_buffer 访问 pass model = MyModel() for param in model.parameters(): print(param) print("----------------") print(model.state_dict()) 输出: Parameter containing: tensor([[-0.2313, -0.1490, -1.3148], [-1.2862, -2.2740, 1.0558], [-0.6559, 0.4552, 0.5993]]) ---------------- OrderedDict([('my_param', tensor([[-0.2313, -0.1490, -1.3148], [-1.2862, -2.2740, 1.0558], [-0.6559, 0.4552, 0.5993]]))])
- buffer参数的创建方式
这种参数的创建需要先创建tensor,然后将tensor通过register_buffer()进行注册,可以通过model._all_buffers()返回,注册完成后参数也会自动保存到OrderedDict中去。
class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() buffer = torch.randn(2, 3) # tensor self.register_buffer('my_buffer', buffer) def forward(self, x): # 可以通过 self.param 和 self.my_buffer 访问 pass model = MyModel() for buffer in model._all_buffers(): print(buffer) print("----------------") print(model.state_dict()) 输出: tensor([[-0.2191, 0.1378, -1.5544], [-0.4343, 0.1329, -0.3834]]) ---------------- OrderedDict([('my_buffer', tensor([[-0.2191, 0.1378, -1.5544], [-0.4343, 0.1329, -0.3834]]))])
模型保存:
pytorch一般情况下,是将网络中的参数保存成OrderedDict。
那么现在的问题是这两种参数如何创建呢,创建好了如何保存到OrderDict呢?
第一种参数有两种方式:
我们可以直接将模型的成员变量(http://self.xxx) 通过nn.Parameter() 创建,会自动注册到parameters中,可以通过model.parameters() 返回,并且这样创建的参数会自动保存到OrderDict中去;
通过nn.Parameter() 创建普通Parameter对象,不作为模型的成员变量,然后将Parameter对象通过register_parameter()进行注册,可以通model.parameters() 返回,注册后的参数也会自动保存到OrderDict中去;
第二种参数我们需要创建tensor, 然后将tensor通过register_buffer()进行注册,可以通model.buffers() 返回,注册完后参数也会自动保存到OrderDict中去。
原文链接:https://blog.csdn.net/m0_37400316/article/details/106669378
读取:
import torch.nn as nn fc = nn.Linear(2,2) # 读取参数的方式一 fc._parameters >>> OrderedDict([('weight', Parameter containing: tensor([[0.4142, 0.0424], [0.3940, 0.0796]], requires_grad=True)), ('bias', Parameter containing: tensor([-0.2885, 0.5825], requires_grad=True))]) # 读取参数的方式二(推荐这种) for n, p in fc.named_parameters(): print(n,p) >>>weight Parameter containing: tensor([[0.4142, 0.0424], [0.3940, 0.0796]], requires_grad=True) bias Parameter containing: tensor([-0.2885, 0.5825], requires_grad=True) # 读取参数的方式三 for p in fc.parameters(): print(p) >>>Parameter containing: tensor([[0.4142, 0.0424], [0.3940, 0.0796]], requires_grad=True) Parameter containing: tensor([-0.2885, 0.5825], requires_grad=True)
通过上面的例子可以看到,nn.parameter.Paramter
的requires_grad
属性值默认为True。另外上面例子给出了三种读取parameter的方法,推荐使用后面两种(这两种的区别可参阅Pytorch: parameters(),children(),modules(),named_*区别),因为是以迭代生成器的方式来读取,第一种方式是一股脑的把参数全丢给你,要是模型很大,估计你的电脑会吃不消。
另外需要介绍的是_parameters
是nn.Module
在__init__()
函数中就定义了的一个OrderDict
类,这个可以通过看下面给出的部分源码看到,可以看到还初始化了很多其他东西,其实原理都大同小异,你理解了这个之后,其他的也是同样的道理
class Module(object): ... def __init__(self): self._backend = thnn_backend self._parameters = OrderedDict() self._buffers = OrderedDict() self._backward_hooks = OrderedDict() self._forward_hooks = OrderedDict() self._forward_pre_hooks = OrderedDict() self._state_dict_hooks = OrderedDict() self._load_state_dict_pre_hooks = OrderedDict() self._modules = OrderedDict() self.training = True
每当我们给一个成员变量定义一个nn.parameter.Paramter
的时候,都会自动注册到_parameters
,具体的步骤如下:
import torch.nn as nn class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() # 下面两种定义方式均可 self.p1 = nn.paramter.Paramter(torch.tensor(1.0)) print(self._parameters) self.p2 = nn.Paramter(torch.tensor(2.0)) print(self._parameters)
- 首先运行
super(MyModel, self).__init__()
,这样MyModel就初始化了_paramters
等一系列的OrderDict
,此时所有变量还都是空的。 self.p1 = nn.paramter.Paramter(torch.tensor(1.0))
: 这行代码会触发nn.Module
预定义好的__setattr__
函数,该函数部分源码如下,:-
def __setattr__(self, name, value): ... params = self.__dict__.get('_parameters') if isinstance(value, Parameter): if params is None: raise AttributeError( "cannot assign parameters before Module.__init__() call") remove_from(self.__dict__, self._buffers, self._modules) self.register_parameter(name, value) ...
__setattr__
函数作用简单理解就是判断你定义的参数是否正确,如果正确就继续调用register_parameter
函数进行注册,这个函数简单概括就是做了下面这件事 -
def register_parameter(self,name,param): ... self._parameters[name]=param
下面我们实例化这个模型看结果怎样
-
model = MyModel() >>>OrderedDict([('p1', Parameter containing: tensor(1., requires_grad=True))]) OrderedDict([('p1', Parameter containing: tensor(1., requires_grad=True)), ('p2', Parameter containing: tensor(2., requires_grad=True))])
原文链接:https://www.cnblogs.com/marsggbo/p/12075244.html
示例:
class myModel(nn.Module): def __init__(self, kernel_size=3): super(Depth_guided1, self).__init__() self.kernel_size = kernel_size self.net = torch.nn.Sequential( torch.nn.Conv2d(3, 32, 3, padding=1), torch.nn.ReLU(True), torch.nn.Conv2d(3, 64, 3, padding=1), torch.nn.ReLU(True), ) mybuffer = np.arange(1,10,1) self.mybuffer_tmp = np.randn((len(mybuffer), 1, 1, 10), dtype='float32') self.mybuffer_tmp = torch.from_numpy(self.mybuffer_tmp) # register preset variables as buffer # So that, in testing , we can use buffer variables. self.register_buffer('mybuffer', self.mybuffer_tmp) # Learnable weights self.conv_weights = nn.Parameter(torch.FloatTensor(64, 10).normal_(mean=0, std=0.01)) # Other code def forward(self): ... # 这里使用 self.mybuffer!
注意:
I. 定义Parameter和buffer都只需要传入 Tensor即可。也不需要将其转成gpu。这是因为,当网络进行 .cuda()时候,会自动将里面的层的参数,buffer等转换成相应的GPU上。
II. self.register_buffer可以将tensor注册成buffer,在forward中使用self.mybuffer, 而不是self.mybuffer_tmp.
III. 网络存储时也会将buffer存下,当网络load模型时,会将存储的模型的buffer也进行赋值。
IV. buffer的更新在forward中,optim.step只能更新nn.Parameter类型的参数。