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參數有兩種創建方式:
  1. 我們可以直接將模型的成員變量(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.Paramterrequires_grad屬性值默認為True。另外上面例子給出了三種讀取parameter的方法,推薦使用后面兩種(這兩種的區別可參閱Pytorch: parameters(),children(),modules(),named_*區別),因為是以迭代生成器的方式來讀取,第一種方式是一股腦的把參數全丟給你,要是模型很大,估計你的電腦會吃不消。

另外需要介紹的是_parametersnn.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類型的參數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM