1.定义网络模型
import torch
from torch import nn
net = nn.Sequential(nn.Linear(4,8),nn.ReLU(),nn.Linear(8,1))
X = torch.rand(size=(2,4))
print(net)
输出结果:
Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=1, bias=True)
)
2.查看每一层网络权重参数:
从已有模型中访问参数。 当通过Sequential类定义模型时, 我们可以通过下标索引来访问模型的任意层。 这就像模型是一个列表一样,列表里面元素即代表一个层,每个层包含这个层的权重参数信息。 如下所示,我们可以检查第三个层的参数
#当前网络有三层,两个Linear层和一个ReLU层
net[2].state_dict()
输出结果:
OrderedDict([('weight',
tensor([[-0.3038, 0.3059, -0.2047, 0.2368, 0.1714, 0.2864, 0.3057, -0.1771]])),
('bias', tensor([-0.1689]))])
注意:输出结果表明这个全连接层包含两个参数, 并且结果是一个有序的字典类型,里面包含权重参数weight和偏差bias
3.获取目标层权重weight和偏差bias
print("bias.type = ",type(net[2].bias))#输出参数类型
print("bias = ",net[2].bias)#获取第三层bias参数,类型为Parameter
print("bias.data = ",net[2].bias.data)#类型为tensor格式
print("bias.data.item() = ",net[2].bias.data.item())#获取tensor里面元素值需要调用item()函数
print("weight = ",net[2].weight)#获取第三层weight参数,类型为Parameter
输出结果:
bias.type = <class 'torch.nn.parameter.Parameter'>
bias = Parameter containing:
tensor([-0.1689], requires_grad=True)
bias.data = tensor([-0.1689])
bias.data.item() = -0.16890332102775574
weight = Parameter containing:
tensor([[-0.3038, 0.3059, -0.2047, 0.2368, 0.1714, 0.2864, 0.3057, -0.1771]],
requires_grad=True)
3.查看网络每一层的梯度:
参数是复合的对象,包含
值、梯度和额外信息
。 这就是我们需要显式参数值的原因。 除了值之外,我们还可以访问每个参数的梯度
net[2].weight.grad #查看第三层网络的梯度
print("查看计算梯度前的grad : ")
print(net[2].weight.grad == None)#刚开始网络还没有调用反向传播,因此参数的梯度处于初始状态,为None
y = torch.rand(2,1)
loss = nn.MSELoss()
l = loss(net(X),y.reshape(2,-1))
l.backward()
print("查看计算梯度后的grad : ")
print(net[2].weight.grad)
输出结果:
查看计算梯度前的grad :
True
查看计算梯度后的grad :
tensor([[ 0.0091, -0.0960, -0.0732, 0.0000, -0.1485, 0.0000, 0.0010, 0.0000]])
4.一次性查看网络所有参数:
print("第一种访问所有参数的方式:")
print(*[(name, param.shape) for name, param in net[0].named_parameters()])
print(*[(name, param.shape) for name, param in net.named_parameters()])
print("=====另外一种访问网络所有参数:=====")
print(net.state_dict())#类型为有序键值对dict
print("======打印第三层bias:======")
print(net.state_dict()['2.bias'])
输出结果:
=====访问网络所有参数:=====
OrderedDict([('0.weight', tensor([[-0.0043, -0.4935, -0.2520, -0.2440],
[-0.2112, 0.2671, -0.3323, -0.1353],
[-0.0596, 0.4947, -0.3870, -0.2382],
[ 0.2701, -0.3518, 0.1539, -0.0712],
[ 0.2908, -0.1608, 0.4329, 0.3725],
[ 0.4162, -0.4409, -0.4309, -0.3289],
[ 0.3932, -0.0667, -0.4542, -0.3637],
[-0.3167, -0.1757, -0.4067, 0.2949]])), ('0.bias', tensor([ 0.4910, 0.4605, 0.2857, -0.3043, 0.1845, 0.0351, 0.2598, -0.0989])), ('2.weight', tensor([[ 0.1251, 0.1157, -0.0849, 0.0575, 0.0276, -0.0441, 0.0468, 0.1986]])), ('2.bias', tensor([0.2353]))])
======打印第三层bias:======
tensor([0.2353])
5.从嵌套块查看参数:
因为层是分层嵌套的,所以我们也可以像通过嵌套列表索引一样访问它们。 例如下面,我们访问第一个主要的块中、第二个子块的第一层的偏置项。
def block1():
return nn.Sequential(nn.Linear(4,8),nn.ReLU(),nn.Linear(8,4),nn.ReLU())
def block2():
net = nn.Sequential()
for i in range(4):
net.add_module(f"block{i}",block1())#block{i}是对添加的模块的名字标识,如果名字相同则会进行覆盖
return net
rgnet = nn.Sequential(block2(),block1())
print("打印rgnet网络结构:")
print(rgnet)
print(rgnet(X))
print("查看第一个主块中第二个子块中第一个层的bias值:")
print(rgnet[0][1][0].bias.data)#跟嵌套列表访问下标方式一样
输出结果:
打印rgnet网络结构:
Sequential(
(0): Sequential(
(block0): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block1): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block2): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block3): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
)
(1): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
)
tensor([[0.0000, 0.3053, 0.0000, 0.0000],
[0.0000, 0.3054, 0.0000, 0.0000]], grad_fn=<ReluBackward0>)
查看第一个主块中第二个子块中第一个层的bias值:
tensor([ 0.0419, 0.0318, -0.3740, -0.1464, 0.0328, -0.4305, 0.4983, -0.1561])
6.模型参数初始化:
深度学习框架提供默认随机初始化, 也允许我们创建自定义初始化方法, 满足我们通过其他规则实现初始化权重。
默认情况下,PyTorch会根据一个范围均匀地初始化权重和偏置矩阵, 这个范围是根据输入和输出维度计算出的。 PyTorch的nn.init模块提供了多种预置初始化方法。
6.1内置初始化:
#内置初始化
print("网络手动初始化前的参数:")
print(net[0].weight.data)#第一层的weight
print(net[0].bias.data)#第一层的bias
def init_normal(m):
if type(m) ==nn.Linear:
nn.init.normal_(m.weight,mean=0,std=1)#权重参数初始化为均值为0,方差为1的高斯随机变量,正态分布
nn.init.zeros_(m.bias)#将网络bias初始化为0
net.apply(init_normal)#将网络所有层递归调用init_normal()函数,如果当前层为线性层,则将其weight,bias参数进行初始化
print("网络手动初始化后的参数:")
print(net[0].weight.data)#第一层的weight
print(net[0].bias.data)#第一层的bias
输出结果:
网络手动初始化前的参数:
tensor([[ 0.1578, -0.1795, 0.4302, 0.3081],
[-0.2809, -0.2261, -0.1772, -0.3692],
[ 0.3474, 0.1296, -0.1798, -0.2431],
[-0.3672, -0.1137, -0.4514, -0.4026],
[ 0.4464, -0.4416, -0.0856, -0.1792],
[-0.2553, -0.2837, -0.1460, -0.3735],
[-0.3935, -0.4341, -0.3780, -0.4967],
[-0.1182, -0.0212, 0.4872, 0.4944]])
tensor([-0.4315, 0.2152, 0.3420, -0.3812, -0.3065, 0.2562, 0.3562, 0.4229])
网络手动初始化后的参数:
tensor([[-0.2072, -0.1545, 0.3582, 0.6092],
[ 1.6172, 0.0604, -0.0331, -1.7334],
[-0.7343, 1.5221, -0.3575, -1.4513],
[-1.2600, -0.5106, 1.2613, 0.3267],
[ 2.0954, -0.5164, 0.5844, -0.1147],
[-1.4974, -0.3811, -1.6760, 0.9083],
[-0.2349, -2.1486, 0.1563, -0.9675],
[ 0.8971, 1.3023, 1.2888, 2.3419]])
tensor([0., 0., 0., 0., 0., 0., 0., 0.])
#将线性网络所有参数初始化为给定的常数,比如初始化为1
print("网络手动初始化前的参数:")
print(net[0].weight.data)#第一层的weight
print(net[0].bias.data)#第一层的bias
def init_constant(m):
if type(m) ==nn.Linear:
nn.init.constant_(m.weight,1)#权重参数初始化为1
nn.init.zeros_(m.bias)#将网络bias初始化为0
net.apply(init_normal)#将网络所有层递归调用init_normal()函数,如果当前层为线性层,则将其weight,bias参数进行初始化
print("网络手动初始化后的参数:")
print(net[0].weight.data)#第一层的weight
print(net[0].bias.data)#第一层的bias
输出结果:
网络手动初始化前的参数:
tensor([[ 1.8708e-01, 1.5800e-01, -2.1730e-01, -4.2157e-01],
[ 3.1356e-01, -2.7407e-01, 3.4003e-01, -1.7509e-02],
[ 4.1263e-01, 4.7126e-02, -9.5766e-02, 2.4069e-01],
[-4.1355e-01, 4.9330e-01, 2.8744e-01, -5.1064e-02],
[-1.9057e-01, 6.0020e-02, -3.1255e-02, 3.4372e-01],
[ 6.1105e-02, 2.5677e-01, 7.3035e-02, 3.0843e-01],
[-7.8338e-02, -3.6490e-04, -4.3265e-02, 9.1942e-02],
[ 3.4140e-01, -6.1874e-03, 3.3147e-01, 3.8987e-02]])
tensor([ 0.0695, -0.2131, 0.2754, 0.3682, 0.3665, -0.2256, 0.4950, 0.4537])
网络手动初始化后的参数:
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
tensor([0., 0., 0., 0., 0., 0., 0., 0.])
#对不同层应用不同的初始化方法,比如:使用Xavier初始化方法初始化第一个神经网络层, 然后将第三个神经网络层初始化为常量值42
def init_xavier(m):
if type(m) == nn.Linear:
nn.init.xavier_uniform_(m.weight)
def init_constant_42(m):
if type(m) == nn.Linear:
nn.init.constant_(m.weight,42)
net[0].apply(init_xavier)#对第一层使用xavier初始化参数
net[2].apply(init_constant_42)#对第三层使用常量初始化参数
print(net[0].weight.data)
print(net[2].weight.data)
输出结果:
tensor([[-0.1172, -0.0877, -0.2024, -0.6203],
[-0.1364, -0.0685, -0.1863, -0.5243],
[-0.5010, 0.2677, 0.1483, 0.0910],
[ 0.2132, 0.1636, 0.4678, -0.0672],
[ 0.0275, -0.6788, 0.1568, 0.1017],
[ 0.6513, -0.5893, 0.4852, -0.4098],
[ 0.3675, -0.3404, 0.1597, 0.4320],
[-0.4341, 0.4389, 0.1168, -0.1841]])
tensor([[42., 42., 42., 42., 42., 42., 42., 42.]])
6.2 自定义初始化参数:
有时,深度学习框架没有提供我们需要的初始化方法。 在下面的例子中,使用以下的分布为任意权重参数 𝑤 定义初始化方法,使用均匀分布初始后再判断初始化的参数绝对值是否大于5,是则保留,不是则变为0:
w
∼
{
U
(
5
,
10
)
可能性
1
4
0
可能性
1
2
U
(
−
10
,
−
5
)
可能性
1
4
\begin{aligned} w \sim \begin{cases} U(5, 10) & \text{ 可能性 } \frac{1}{4} \\ 0 & \text{ 可能性 } \frac{1}{2} \\ U(-10, -5) & \text{ 可能性 } \frac{1}{4} \end{cases} \end{aligned}
w
∼
⎩
⎪
⎨
⎪
⎧
U
(
5
,
1
0
)
0
U
(
−
1
0
,
−
5
)
可能性
4
1
可能性
2
1
可能性
4
1
def my_init(m):
if type(m) == nn.Linear:
print("init",*[(name,parameters.shape) for name,parameters in m.named_parameters()][0])
nn.init.uniform(m.weight,-10,10)
m.weight.data *= m.weight.data.abs()>=5
net.apply(my_init)
net[0].weight[:2]
输出结果:
init weight torch.Size([8, 4])
init weight torch.Size([1, 8])
tensor([[-9.0073, 0.0000, -0.0000, -0.0000],
[-5.1906, -0.0000, -0.0000, -6.4784]], grad_fn=<SliceBackward0>)
注意:我们可以直接设置参数:
net[0].weight.data[:] += 1
net[0].weight.data[0, 0] = 42
net[0].weight.data[0]
#输出结果:tensor([42.0000, 9.0813, 1.0000, -5.7370])
7.共享(绑定)网络层参数:在多个层共享参数(绑定参数)
#共享层
shared_layer = nn.Linear(8,8)
#模型第三层和第五层都是同一个层shared_layer,都共享shared_layer层参数,指向(引用)shared_layer,当shared_layer参数发生改变时,该模型第三层和第五层参数也会跟着改变
net = nn.Sequential(nn.Linear(4,8),nn.ReLU(),shared_layer,nn.ReLU(),shared_layer,nn.ReLU(),nn.Linear(8,1))
net(X)
print(net[2].weight.data == net[4].weight.data)
net[2].weight.data[0,0] = 100 #当改变第三层参数时,第五层参数也会跟着改变
print(net[2].weight.data == net[4].weight.data)
输出结果:
tensor([[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True]])
tensor([[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True]])
表明第三个和第五个神经网络层的参数是绑定的,即使改变第三层参数,第五层参数也会相应改变
。 它们不仅值相等,而且由相同的张量表示。 因此,如果我们改变其中一个参数,另一个参数也会改变。
思考:当参数绑定时,梯度会发生什么情况?
答案是由于模型参数包含梯度,因此在反向传播期间第二个隐藏层 (即第三个神经网络层)和第三个隐藏层(即第五个神经网络层)的梯度会加在一起。
8.全部源代码:
import torch
from torch import nn
#1.定义网络模型
net = nn.Sequential(nn.Linear(4,8),nn.ReLU(),nn.Linear(8,1))
X = torch.rand(size=(2,4))
y = torch.rand(2,1)
print("y = ",y)
print(net)
print(net[2].state_dict())
# 2.查看每一层网络权重参数:
print("bias.type = ",type(net[2].bias))#输出参数类型
print("bias = ",net[2].bias)#类型为Parameter
print("bias.data = ",net[2].bias.data)#类型为tensor格式
print("bias.data.item() = ",net[2].bias.data.item())#获取tensor里面元素值需要调用item()函数
print("weight = ",net[2].weight)
# 3.查看网络每一层的梯度:
print("查看计算梯度前的grad : ")
print(net[2].weight.grad == None)
y = torch.rand(2,1)
loss = nn.MSELoss()
l = loss(net(X),y.reshape(2,-1))
l.backward()
print("查看计算梯度后的grad : ")
print(net[2].weight.grad)
# 4.一次性查看网络所有参数:
print(*[(name,parameters.shape) for name,parameters in net[0].named_parameters()])
print(*[(name,parameters.shape) for name,parameters in net.named_parameters()])
print("=====访问网络所有参数:=====")
print(net.state_dict())#类型为有序键值对dict
print("======打印第三层bias:======")
print(net.state_dict()['2.bias'])
# 5.从嵌套块查看参数:
def block1():
return nn.Sequential(nn.Linear(4,8),nn.ReLU(),nn.Linear(8,4),nn.ReLU())
def block2():
net = nn.Sequential()
for i in range(4):
net.add_module(f"block{i}",block1())#block{i}是对添加的模块的名字标识,如果名字相同则会进行覆盖
return net
rgnet = nn.Sequential(block2(),block1())
print("打印rgnet网络结构:")
print(rgnet)
print(rgnet(X))
print("查看第一个主块中第二个子块中第一个层的bias值:")
print(rgnet[0][1][0].bias.data)
# 6.模型参数初始化:
## 6.1内置初始化:
print("网络手动初始化前的参数:")
print(net[0].weight.data)#第一层的weight
print(net[0].bias.data)#第一层的bias
def init_normal(m):
if type(m) ==nn.Linear:
nn.init.normal_(m.weight,mean=0,std=1)#权重参数初始化为均值为0,方差为1的高斯随机变量,正态分布
nn.init.zeros_(m.bias)#将网络bias初始化为0
net.apply(init_normal)#将网络所有层递归调用init_normal()函数,如果当前层为线性层,则将其weight,bias参数进行初始化
print("网络手动初始化后的参数:")
print(net[0].weight.data)#第一层的weight
print(net[0].bias.data)#第一层的bias
#将线性网络所有参数初始化为给定的常数,比如初始化为1
print("网络手动初始化前的参数:")
print(net[0].weight.data)#第一层的weight
print(net[0].bias.data)#第一层的bias
def init_constant(m):
if type(m) ==nn.Linear:
nn.init.constant_(m.weight,1)#权重参数初始化为1
nn.init.zeros_(m.bias)#将网络bias初始化为0
net.apply(init_normal)#将网络所有层递归调用init_normal()函数,如果当前层为线性层,则将其weight,bias参数进行初始化
print("网络手动初始化后的参数:")
print(net[0].weight.data)#第一层的weight
print(net[0].bias.data)#第一层的bias
#对不同层应用不同的初始化方法,比如:使用Xavier初始化方法初始化第一个神经网络层, 然后将第三个神经网络层初始化为常量值42
def init_xavier(m):
if type(m) == nn.Linear:
nn.init.xavier_uniform_(m.weight)
def init_constant_42(m):
if type(m) == nn.Linear:
nn.init.constant_(m.weight,42)
net[0].apply(init_xavier)#对第一层使用xavier初始化参数
net[2].apply(init_constant_42)#对第三层使用常量初始化参数
print(net[0].weight.data)
print(net[2].weight.data)
## 6.2 自定义初始化参数:
def my_init(m):
if type(m) == nn.Linear:
print("init",*[(name,parameters.shape) for name,parameters in m.named_parameters()][0])
nn.init.uniform(m.weight,-10,10)
m.weight.data *= m.weight.data.abs()>=5
net.apply(my_init)
#直接对参数进行初始化
net[0].weight[:2]
net[0].weight.data[:]+=1
net[0].weight.data[0,0]=42
net[0].weight.data[0]
# 7.共享(绑定)网络层参数:在多个层共享参数(绑定参数)
shared_layer = nn.Linear(8,8)
#模型第三层和第五层都是同一个层shared_layer,都共享shared_layer层参数,指向(引用)shared_layer,当shared_layer参数发生改变时,该模型第三层和第五层参数也会跟着改变
net = nn.Sequential(nn.Linear(4,8),nn.ReLU(),shared_layer,nn.ReLU(),shared_layer,nn.ReLU(),nn.Linear(8,1))
net(X)
print(net[2].weight.data == net[4].weight.data)
net[2].weight.data[0,0] = 100 #当改变第三层参数时,第五层参数也会跟着改变
print(net[2].weight.data == net[4].weight.data)