首先回顾一下之前用Sequential创建MLP模型的方法。
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)
)
在创建的过程中,并没有可见的进行参数初始化的过程,因为这里使用了默认的方式进行了初始化参数。
使用Module
类的parameters()
或named_parameters()
方法生成迭代器来访问所有参数。其中named_parameters()
还可以返回参数的名字。下面用实例说明这一过程。
#访问所有参数
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)
通过使用层数索引和[],可以访问网络中任一层的参数。
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)
参数的类型为torch.nn.parameter.Parameter
,这个类型本质是Tensor
的子类。如果一个Tensor
是torch.nn.parameter.Parameter
类型的,那么它会被自动添加到模型的参数列表。下面是实例说明
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
使用data
可以访问参数数值,使用grad
可以访问参数梯度。
#以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
PyTorch默认根据不同类型的layer
采取不同的初始化方法。但同样也可以不使用默认的初始化方法,PyTorch中的init
模块提供了多种初始化方法。下面介绍两种较为常用的方法。
#以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.])
当需要使用init
模块中没有的初始化方法时,我们就需要自定义初始化方法。自定义初始化方法需要注意这个过程是不记录梯度的。下面以初始化权重有一半概率初始化为0,有另一半概率初始化为[-10,-5]和[5,10]两个区间里均匀分布的随机数。
#以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
方法获取参数值并进行操作。
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])
当同一个Module
实例(layer
或net
)被传入Sequential
多次,他妈的参数也是共享的。
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
其实是同一个对象。在计算反向传播计算时,这些共享参数的梯度是累加的。