(pytorch-深度学习系列)模型参数的初始化与访问操作-学习笔记

模型参数的初始化与访问操作

学习 如何初始化以及访问模型参数,以及如何在多层之间共享模型参数
首先定义一个含有单个隐藏层的多层感知机,使用默认方式初始化该模型的参数,并且进行一次前向计算:

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

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)
)

接着尝试访问模型参数
由于Sequentail类与Module类的继承关系,可以通过Module类的parameters()函数或者named_parameters()来访问所有的参数:

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

输出:
named_parameters()返回参数的名称以及其该参数值(tensor变量)

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

其中“0“、”2“为层数索引,我们可以通过下面的方法访问网络的任意一层:

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'>

由于我们采用了下标索引访问网络的某一层,所以结果中没有了层数索引,返回的参数类型为:“torch.nn.parameter.Parameter”,这是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,这说明参数weight1在参数列表中,而参数weight2不在参数列表中

初始化模型参数

将权重参数初始化成均值为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)

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

我们查看torch.nn.init.normal_函数,观察torch默认初始化的方式:

def normal_(tensor, mean=0, std=1):
    with torch.no_grad():
        return tensor.normal_(mean, std)

这里使用一个inplace改变Tensor的值,这个过程不记录梯度
根据相同的实现方式,我们来实现一个自定义的初始化方法。在下面的例子里,我们令权重有一半概率初始化为0,有另一半概率初始化为 [ − 10 , − 5 ] [-10,-5] [10,5] [ 5 , 10 ] [5,10] [5,10]两个区间里均匀分布的随机数。

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

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

我们还可以使用在之前的blog中提到的改变Parameter的data属性,来改写模型参数的同时不改变参数的梯度:

for name, param in net.named_parameters():
    if 'bias' in name:
        param.data += 1
        print(name, param.data)

这里通过param.data改变模型参数,但是不会影响param的梯度。

共享模型的参数

如何实现在多层之间共享模型的参数
(1)对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.]])

根据输出结果可以发现,网络有两层,但是weight参数只有一个,这时候,这两个层共享参数。

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

x = torch.ones(1, 1)
y = net(x).sum()
print(y)
y.backward()
print(net[0].weight.grad) # 单次梯度是3,两次所以就是6,weight的值在上面输出了,是3

输出:

tensor(9., grad_fn=<SumBackward0>)
tensor([[6.]])
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值