Pytorch学习记录(四):参数初始化

参数初始化(Weight Initialization)

只需要学会如何对模型的参数进行初始化的赋值即可。

使用 NumPy 来初始化

举例

import torch
import numpy as np
from torch import nn

net1 = nn.Sequentisl(
    nn.Linear(30,40),
    nn.ReLU(),
    nn.Linear(40,50),
    nn.ReLU(),
    nn.Linear(50,10)
)

# 访问第一层的参数
w1 = net1[0].weight
b1 = net1[0].bias
print(w1)
Parameter containing:
 0.1236 -0.1731 -0.0479  ...   0.0031  0.0784  0.1239
 0.0713  0.1615  0.0500  ...  -0.1757 -0.1274 -0.1625
 0.0638 -0.1543 -0.0362  ...   0.0316 -0.1774 -0.1242
          ......          
 0.1551  0.1772  0.1537  ...   0.0730  0.0950  0.0627
 0.0495  0.0896  0.0243  ...  -0.1302 -0.0256 -0.0326
-0.1193 -0.0989 -0.1795  ...   0.0939  0.0774 -0.0751
[torch.FloatTensor of size 40x30]

注意,这是一个 Parameter,也就是一个特殊的 Variable,我们可以访问其 .data属性得到其中的数据,然后直接定义一个新的 Tensor 对其进行替换。
我们可以使用 PyTorch 中的一些随机数据生成的方式,比如 torch.randn,如果要使用更多PyTorch 中没有的随机化方式,可以使用 numpy
使用torch.from_numpy(np.random.uniform(min,max,(size))

定义一个 Tensor 直接对其进行替换

# 定义一个 Tensor 直接对其进行替换
net1[0].weight.data = torch.from_numpy(np.random.uniform(3, 5, size=(40, 30)))

再次打印

print(net1[0].weight)
Parameter containing:
 4.5768  3.6175  3.3098  ...   4.7374  4.0164  3.3037
 4.1809  3.5624  3.1452  ...   3.0305  4.4444  4.1058
 3.5277  4.3712  3.7859  ...   3.5760  4.8559  4.3252
          ......          
 4.8983  3.9855  3.2842  ...   4.7683  4.7590  3.3498
 4.9168  4.5723  3.5870  ...   3.2032  3.9842  3.2484
 4.2532  4.6352  4.4857  ...   3.7543  3.9885  4.4211
[torch.DoubleTensor of size 40x30]

进行循环访问

for layer in net1:
    if isinstance(layer, nn.Linear): # 判断是否是线性层
        param_shape = layer.weight.shape
        layer.weight.data = torch.from_numpy(np.random.normal(0, 0.5, size=param_shape)) 
        # 定义为均值为 0,方差为 0.5 的正态分布

对于 Module 的参数初始化,可以直接像Sequential 一样对其 Tensor 进行重新定义,其唯一不同的地方在于,如果要用循环的方式访问,需要介绍两个属性,children 和 modules.

class sim_net(nn.Module):
    def __init__(self):
        super(sim_net, self).__init__()
        self.l1 = nn.Sequential(
            nn.Linear(30, 40),
            nn.ReLU()
        )
        
        self.l1[0].weight.data = torch.randn(40, 30) # 直接对某一层初始化
        
        self.l2 = nn.Sequential(
            nn.Linear(40, 50),
            nn.ReLU()
        )
        
        self.l3 = nn.Sequential(
            nn.Linear(50, 10),
            nn.ReLU()
        )
    
    def forward(self, x):
        x = self.l1(x)
        x =self.l2(x)
        x = self.l3(x)
        return x
net2 = sim_net()

访问 children

# 访问 children
for i in net2.children():
    print(i)

Sequential(
  (0): Linear(in_features=30, out_features=40)
  (1): ReLU()
)
Sequential(
  (0): Linear(in_features=40, out_features=50)
  (1): ReLU()
)
Sequential(
  (0): Linear(in_features=50, out_features=10)
  (1): ReLU()
)

访问 modules

# 访问 modules
for i in net2.modules():
    print(i)

# 访问 modules
for i in net2.modules():
    print(i)
# 访问 modules
for i in net2.modules():
    print(i)
sim_net(
  (l1): Sequential(
    (0): Linear(in_features=30, out_features=40)
    (1): ReLU()
  )
  (l2): Sequential(
    (0): Linear(in_features=40, out_features=50)
    (1): ReLU()
  )
  (l3): Sequential(
    (0): Linear(in_features=50, out_features=10)
    (1): ReLU()
  )
)
Sequential(
  (0): Linear(in_features=30, out_features=40)
  (1): ReLU()
)
Linear(in_features=30, out_features=40)
ReLU()
Sequential(
  (0): Linear(in_features=40, out_features=50)
  (1): ReLU()
)
Linear(in_features=40, out_features=50)
ReLU()
Sequential(
  (0): Linear(in_features=50, out_features=10)
  (1): ReLU()
)
Linear(in_features=50, out_features=10)
ReLU()

children 只会访问到模型定义中的第一层,因为上面的模型中定义了三个 Sequential,所以只会访问到三个 Sequential,而 modules 会访问到最后的结构,比如上面的例子,modules 不仅访问到了 Sequential,也访问到了 Sequential 里面.
循环访问进行初始化

for layer in net2.modules():
    if isinstance(layer, nn.Linear):
        param_shape = layer.weight.shape
        layer.weight.data = torch.from_numpy(np.random.normal(0, 0.5, size=param_shape)) 

torch.nn.init

torch.nn.init:其操作层面仍然在 Tensor 上

init.xavier_uniform
from torch.nn import init
init.xavier_uniform(net1[0].weight) # 这就是上面我们讲过的 Xavier 初始化方法,PyTorch 直接内置了其实现

常见初始化方法

1.Xavier Initialization

Xavier初始化的基本思想是保持输入和输出的方差一致,这样就避免了所有输出值都趋向于0。这是通用的方法,适用于任何激活函数。
nn.init.xavier_uniform_(m.weight)

for m in model.modules():
    if isinstance(m, (nn.Conv2d, nn.Linear)):
        nn.init.xavier_uniform_(m.weight)
2.He et. al Initialization

He initialization的思想是:在ReLU网络中,假定每一层有一半的神经元被激活,另一半为0。推荐在ReLU网络中使用。

torch.nn.init.kaiming_uniform_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu')
2.正交初始化(Orthogonal Initialization)

以解决深度网络下的梯度消失、梯度爆炸问题,在RNN中经常使用的参数初始化方法。
nn.init.orthogonal(m.weight)

for m in model.modules():
    if isinstance(m, (nn.Conv2d, nn.Linear)):
        nn.init.orthogonal(m.weight)

3.Batchnorm Initialization

在非线性激活函数之前,我们想让输出值有比较好的分布(例如高斯分布),以便于计算梯度和更新参数。Batch Normalization 将输出值强行做一次 Gaussian Normalization 和线性变换:
nn.init.constant(m.weight, 1)nn.init.constant(m.bias, 0)

for m in model:
    if isinstance(m, nn.BatchNorm2d):
        nn.init.constant(m.weight, 1)
        nn.init.constant(m.bias, 0)

单层初始化

conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)
nn.init.xavier_uniform(conv1.weight)
nn.init.constant(conv1.bias, 0.1)

模型初始化

def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv2d') != -1:
        nn.init.xavier_normal_(m.weight.data)
        nn.init.constant_(m.bias.data, 0.0)
    elif classname.find('Linear') != -1:
        nn.init.xavier_normal_(m.weight)
        nn.init.constant_(m.bias, 0.0)
net = Net()
net.apply(weights_init) #apply函数会递归地搜索网络内的所有module并把参数表示的函数应用到所有的module上。

学习链接:
https://blog.csdn.net/ys1305/article/details/94332007

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值