Pytorch基础九、(模型参数访问、初始化和共享)

图像处理直播MySQL

本次将分享模型参数如何访问、初始化以及共享,首先我们再来定义一个MLP

1、导入库


          
import torch
          
from torch import nn
          
from torch.nn import init
          

          
print(torch.__version__)
      

1.0.1.post2

2、定义MLP模型


          
net = nn.Sequential(nn.Linear(4, 3), nn.ReLU(), nn.Linear(3, 1))  # pytorch已进行默认初始化
          

          
print(net)
          
X = torch.rand(2, 4)
          
Y = net(X).sum()
      

Sequential(
(0): Linear(in_features=4, out_features=3, bias=True)
(1): ReLU()
(2): Linear(in_features=3, out_features=1, bias=True)
)

3、访问模型参数

    对于 Sequential 实例中含模型参数的层,我们可以通过 Module 类的 parameters() 或者 named\_parameters ⽅法来访问所有参数 (以迭代器的形式返回),后者除了返回参数 Tensor 外还会返回其名字。下面,访问多层感知机 net 的所有参数:

          
print(type(net.named_parameters()))
          
for name, param in net.named_parameters():
          
    print(name, param.size())
      

<class 'generator'>
0.weight torch.Size([3, 4])
0.bias torch.Size([3])
2.weight torch.Size([1, 3])
2.bias torch.Size([1])

    可见返回的名字⾃动加上了层数的索引作为前缀。我们再来访问 net 中单层的参数。对于使用 Sequential 类构造的神经⽹络,我们可以通过方括号 [] 来访问⽹络的任一层。索引0表示隐藏层 为 Sequential 实例最先添加的层。

          
for name, param in net[0].named_parameters():
          
    print(name, param.size(), type(param))
      

weight torch.Size([3, 4]) <class 'torch.nn.parameter.Parameter'>
bias torch.Size([3]) <class 'torch.nn.parameter.Parameter'>

   因为这里是单层的所以没有了层数索引的前缀 。另外返回的 param 的类 型为 torch.nn.parameter.Parameter ,其实这是 Tensor 的⼦类,和 Tensor 不同的是如果一 个 Tensor 是 Parameter ,那么它会⾃动被添加到模型的参数列表里,来看下⾯这个例子。

          
class MyModel(nn.Module):
          
    def __init__(self, **kwargs):
          
        super(MyModel, self).__init__(**kwargs)
          
        self.weight1 = nn.Parameter(torch.rand(20, 20))
          
        self.weight2 = torch.rand(20, 20)
          
    def forward(self, x):
          
        pass
          
    
          
n = MyModel()
          
for name, param in n.named_parameters():
          
    print(name)
      

weight1

   上⾯的代码中 weight1 在参数列表中但是 weight2 却没在参数列表中。

因为 Parameter 是 Tensor ,即 Tensor 拥有的属性它都有,⽐如可以根据 data 来访问参数值, ⽤grad 来访问参数梯度


          
weight_0 = list(net[0].parameters())[0]
          
print(weight_0.data)
          
print(weight_0.grad)
          
Y.backward()
          
print(weight_0.grad)
      

tensor([[ 0.1113, -0.3047, -0.4044, -0.0759],
[ 0.3811, -0.1577, -0.1052, -0.4404],
[-0.3102, -0.1009, 0.3620, 0.2666]])
None
tensor([[0.0000, 0.0000, 0.0000, 0.0000],
[0.0069, 0.0008, 0.0123, 0.0061],
[0.0300, 0.0500, 0.0778, 0.0806]])

4、初始化模型参数

    PyTorch的 init 模块⾥提供了多种预设的初始化方法。在下面的例子中, 我们将权重参数初始化成均值为0、标准差为0.01的正态分布随机数,并依然将偏差参数清零。

          
for name, param in net.named_parameters():
          
    if 'weight' in name:
          
        init.normal_(param, mean=0, std=0.01)
          
        print(name, param.data)
      

0.weight tensor([[ 0.0217, -0.0044, -0.0019, -0.0016],
[-0.0080, -0.0026, 0.0184, 0.0134],
[ 0.0004, 0.0022, -0.0319, 0.0015]])
2.weight tensor([[0.0111, 0.0065, 0.0038]])

下⾯使⽤常数来初始化权重参数。


          
for name, param in net.named_parameters():
          
    if 'bias' in name:
          
        init.constant_(param, val=0)
          
        print(name, param.data)
      

0.bias tensor([0., 0., 0.])
2.bias tensor([0.])

如果只想对某个特定参数进⾏初始化,我们可以调用 Parameter 类的 initialize 函数,它与 Block 类提供的 initialize 函数的使⽤方法⼀致

5、自定义模型参数

     有时候我们需要的初始化⽅法并没有在 init 模块中提供,这就需要我们自定义初始化函数,在这之前我们先来看看PyTorch是怎么实现这些初始化⽅法的,例如 torch.nn.init.normal\_

          
def init_weight_(tensor):
          
    with torch.no_grad():
          
        tensor.uniform_(-10, 10)
          
        tensor *= (tensor.abs() >= 5).float()
      

     可以看到这就是一个inplace改变 Tensor 值的函数,⽽且这个过程是不记录梯度的。类似的我们来实现⼀个⾃定义的初始化方法。在下面的例子里,我们令权重有⼀半概率初始化为0,有另一半概率初始化为[-10,-5][5,10]两个区间⾥均匀分布的随机数

          
for name, param in net.named_parameters():
          
    if 'weight' in name:
          
        init_weight_(param)
          
        print(name, param.data)
      

0.weight tensor([[-8.3413, -6.6506, 0.0000, 0.0000],
[-0.0000, -0.0000, 0.0000, -7.8356],
[-0.0000, -9.9840, 8.4777, -9.9372]])
2.weight tensor([[-0.0000, 7.0348, -0.0000]])

6、参数共享

    在有些情况下,我们希望在多个层之间共享模型参数。Module 类的 forward 函数⾥多次调⽤同⼀个层。此外,如果我们传入 Sequential 的模块是同一个 Module 实例的话参数也是共享的,下⾯来看⼀个例子:

          
linear = nn.Linear(1, 1, bias=False)
          
net = nn.Sequential(linear, linear) 
          
print(net)
          
for name, param in net.named_parameters():
          
    init.constant_(param, val=3)
          
    print(name, param.data)
      

Sequential(
(0): Linear(in_features=1, out_features=1, bias=False)
(1): Linear(in_features=1, out_features=1, bias=False)
)
0.weight tensor([[3.]])

在内存中,这两个线性层其实⼀一个对象:


          
print(id(net[0]) == id(net[1]))
          
print(id(net[0].weight) == id(net[1].weight))
      

True
True

    因为模型参数⾥包含了梯度,所以在反向传播计算时,这些共享的参数的梯度是累加的:

          
x = torch.ones(1, 1)
          
y = net(x).sum()
          
print(y)
          
y.backward()
          
print(net[0].weight.grad)
      

tensor(9., grad_fn=)
tensor([[6.]])

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论