前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【动手学深度学习笔记】之对模型参数的访问、初始化和共享

【动手学深度学习笔记】之对模型参数的访问、初始化和共享

作者头像
树枝990
发布2020-08-19 22:24:47
7600
发布2020-08-19 22:24:47
举报
文章被收录于专栏:拇指笔记拇指笔记

1. 模型参数

首先回顾一下之前用Sequential创建MLP模型的方法。

代码语言:javascript
复制
net = nn.Sequential(
	nn.Linear(4,3),
	nn.ReLU(),
	nn.Lineaar(3,1),
	)

print(net)

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

在创建的过程中,并没有可见的进行参数初始化的过程,因为这里使用了默认的方式进行了初始化参数。

1.1 访问模型参数

使用Module类的parameters()named_parameters()方法生成迭代器来访问所有参数。其中named_parameters()还可以返回参数的名字。下面用实例说明这一过程。

代码语言:javascript
复制
#访问所有参数
for name,param in net.named_parameters():
	print(name,param)
    
Out[1]:
#返回的名字以层数作为前缀
0.weight Parameter containing:
tensor([[-0.4466, -0.1915,  0.3137,  0.0990],
        [ 0.3665,  0.2707, -0.4795,  0.4625],
        [ 0.3726, -0.3353, -0.1634,  0.1339]], requires_grad=True)
0.bias Parameter containing:
tensor([-0.3975,  0.1861,  0.4645], requires_grad=True)
2.weight Parameter containing:
tensor([[-0.2939,  0.1900,  0.1157]], requires_grad=True)
2.bias Parameter containing:
tensor([0.0854], requires_grad=True)

通过使用层数索引和[],可以访问网络中任一层的参数。

代码语言:javascript
复制
for name,param in net[0].named_parameters():
    print(name,param)
    
Out[1]:
#访问单层的参数没有层数索引前缀
weight Parameter containing:
tensor([[-0.1242,  0.4956, -0.0380, -0.0223],
        [-0.4294,  0.2217,  0.4595, -0.3092],
        [ 0.0738, -0.2290,  0.1326, -0.4832]], requires_grad=True)
bias Parameter containing:
tensor([ 0.2073,  0.0474, -0.4783], requires_grad=True)

1.2 参数的数据类型

参数的类型为torch.nn.parameter.Parameter,这个类型本质是Tensor的子类。如果一个Tensortorch.nn.parameter.Parameter类型的,那么它会被自动添加到模型的参数列表。下面是实例说明

代码语言:javascript
复制
class Test(nn.Module):
    def __init__(self):
        super(Test,self).__init__()
        self.weight1 = nn.Parameter(torch.rand(20,20))
        self.weight2 = torch.rand(20,20)
    def forward(self,x):
        pass

#下面看一下哪些参数被添加到了参数列表。
net1 = Test()
for name,param in net1.named_parameters():
    print(name)
    
Out[1]:
#可以看到只有类型为Parameter的weight1被添加到了参数列表。
weight1

1.3 访问参数的数值和梯度

使用data可以访问参数数值,使用grad可以访问参数梯度。

代码语言:javascript
复制
#以net1网络为例
for name,param in net1.named_parameters():
    print(param.data)
    print(param.grad)
    
Out[1]:
tensor([[0.3835, 0.5893, 0.2007, 0.6323, 0.9700, 0.6664, 0.7636, 0.7858, 0.6420,
         0.2491, 0.8562, 0.9641, 0.7165, 0.0363, 0.4048, 0.2425, 0.9410, 0.2126,
         0.9872, 0.0461],
		......
        [0.1776, 0.3865, 0.3770, 0.3702, 0.4588, 0.3745, 0.6851, 0.4005, 0.1305,
         0.4921, 0.6546, 0.9982, 0.0174, 0.2351, 0.7404, 0.2557, 0.4719, 0.7664,
         0.3595, 0.7744]])
None

1.4 初始化模型参数

PyTorch默认根据不同类型的layer采取不同的初始化方法。但同样也可以不使用默认的初始化方法,PyTorch中的init模块提供了多种初始化方法。下面介绍两种较为常用的方法。

代码语言:javascript
复制
#以net网络为例
#将权重参数初始化为均值为0,标准差为0.01的正态分布(使用normal_方法)
for name,param in net.named_parameters()
	if 'weight' in name:
		init.normal_(param,mean=0,std=0.01)
        print(name,param.data)

#将偏差参数初始化为常数(0)(使用constant_方法)
for name,param in net.named_parameters()
	if 'bias' in name:
		init.constant_(param,val=0)
        print(name,param.data)


Out[1]:
0.weight tensor([[ 0.0030,  0.0094,  0.0070, -0.0010],
        [ 0.0001,  0.0039,  0.0105, -0.0126],
        [ 0.0105, -0.0135, -0.0047, -0.0006]])
2.weight tensor([[-0.0074,  0.0051,  0.0066]])

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

1.5 自定义初始化方法

当需要使用init模块中没有的初始化方法时,我们就需要自定义初始化方法。自定义初始化方法需要注意这个过程是不记录梯度的。下面以初始化权重有一半概率初始化为0,有另一半概率初始化为[-10,-5]和[5,10]两个区间里均匀分布的随机数。

代码语言:javascript
复制
#以net网络为例
def init_w(tensor):
    #不记录梯度
    with torch.no_grad():
        tensor.uniform(-10,10)	#-10到10的均匀分布
        tensor *= (tensor.abs() >= 5).float()	
        #绝对值小于5的数清零,大于5的数置1,然后与tensor相乘
        
for name, param in net.named_parameters():
    if 'weight' in name:
        init_weight_(param)
        print(name, param.data)
        
Out[1]:
0.weight tensor([[ 0.0000, -0.0000,  0.0000,  0.0000],
        [-0.0000,  5.4846,  8.1156,  7.4273],
        [-5.7105, -0.0000,  6.6128, -9.7673]])
2.weight tensor([[ 0.0000, -7.2850,  6.9467]])

第二种初始化方法,使用data方法获取参数值并进行操作。

代码语言:javascript
复制
for name, param in net.named_parameters():
    if 'bias' in name:
        param.data += 1
        print(name, param.data)
        
Out[1]:
0.bias tensor([0.9058, 0.8452, 0.8508])
2.bias tensor([1.1459])

1.6 共享模型参数

当同一个Module实例(layernet)被传入Sequential多次,他们的参数也是共享的。

代码语言:javascript
复制
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)
    
Out[1]:
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.]])

另外,在内存中,这两个layer其实是同一个对象。在计算反向传播计算时,这些共享参数的梯度是累加的。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 拇指笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 模型参数
    • 1.1 访问模型参数
      • 1.2 参数的数据类型
        • 1.3 访问参数的数值和梯度
          • 1.4 初始化模型参数
            • 1.5 自定义初始化方法
              • 1.6 共享模型参数
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档