PyTorch编程基础04--定义模型结构的常用方法

1.Module类的使用方法

Module类是所有模型的基类。

1.1Module类的add_module()方法

Module类也可以包含其他Modules对象,允许使用树的结构进行嵌入。

import torch.nn as nn
class LogicNet(nn.Module):  # 继承nn.Module类,构建模型
    def __init__(self, inputdim, hiddendim, outputdim):  # 初始化网络结构
        super(LogicNet, self).__init__()
        # self.Linear1 = nn.Linear(inputdim, hiddendim)  # 定义全连接层
        # self.Linear2 = nn.Linear(hiddendim, outputdim)  # 定义全连接层
        self.add_module("Linear1", nn.Linear(inputdim, hiddendim))
        self.add_module("Linear2", nn.Linear(hiddendim, outputdim))
        self.criterion = nn.CrossEntropyLoss()  # 定义交叉熵函数
...

注意:在搭建模型时,还有更高级的ModuleList()方法,该方法可以将网络层以列表形式组合起来进行搭建。


1.2Module类的children()方法

所有通过Module类定义的模型,都可以从其实例化对象中通过children()方法取得各层的信息。

...
model = LogicNet(inputdim=2, hiddendim=3, outputdim=2)  # 实例化模型
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)  # 定义优化器
for sub_module in model.children():  # 调用模型的children()方法获取各层信息
	print(sub_module)

输出:
Linear(in_features=2, out_features=3, bias=True)
Linear(in_features=3, out_features=2, bias=True)
CrossEnrtopyLoss()


1.3Module类的named_children()方法

通过Module类定义的模型,还可以从其实例化对象中通过named_children()方法取得模型中各层的名字及结构信息。

for name, nodule in model.named_children():  # 调用模型的children()方法取得模型中各层的名字及结构信息
	print(name,"is:",module)

输出:
Linear1 is:Linear(in_features=2, out_features=3, bias=True)
Linear2 is:Linear(in_features=3, out_features=2, bias=True)
criterion is:CrossEnrtopyLoss()


1.4Module类的modules()方法

通过Module类定义的模型,还可以从其实例化对象中通过modules()方法取得整个网络的结构信息。

for module in model.modules():  # 调用模型的modules()方法取得整个网络的结构信息
	print(module)

输出:
LogicNet(
(Linear1):Linear(in_features=2, out_features=3, bias=True)
(Linear2):Linear(in_features=3, out_features=2, bias=True)
(criterion):CrossEnrtopyLoss()
)
Linear(in_features=2, out_features=3, bias=True)
Linear(in_features=3, out_features=2, bias=True)
CrossEnrtopyLoss()
注意:还可以使用print()函数直接将模型打印出来,也可以使用模型的eval()方法来输出模型结构。

2.模型中的参数(Parameters变量)

Parameters类时Variable的子类,代表模型参数。它是模型的重要组成部分之一。

2.1模型与参数的关系

深度学习中的模型可以被抽象成一堆参数按照固定的运算规则所组成的公式。模型中的每个参数都是具体的数字,运算规则就是模型的网络结构。
在训练过程中,模型通过反复将公式的计算结果与目标值比较,并利用二者的差距来对每个参数进行调整。经过多次调整后的参数,可以使公式最终的输出结果高度地接近目标值,得到可用的模型。


2.2Paramenter与Variable的区别

通过Paramenters类实例化的Paramenter变量本质上也是一个变量对象,但它与Variable类型的变量具有不同的属性:

  • 在将Paramenter变量赋值给Module的属性时,Paramenter变量会被自动加到Module的参数列表中(即会出现在arameters()迭代器中)。
  • 在将Variable变量赋值给Module的属性时,Variable变量不会被加到Module的参数列表中。

3.为模型添加参数

在像模型中添加网络层时,系统会根据该网络层的定义在模型中创建相应的参数。这些参数就是在模型训练过程中所要调整的对象。
除通过定义网络层的方法向模型中添加参数外,还可以直接调用Module类的方法向模型中添加参数。具体如下。

3.1为模型添加参数

调用函数register_parameter(),可以向模型添加参数(parameter变量)。该函数的定义是register_parameter(name, param)。在取值时,可以通过注册时的name来读取模型参数。
注意:在搭建模型时,还有更高级的ParameterList()方法,该方法可以将模型参数以列表的形式组合起来进行搭建。

3.2为模型添加状态参数

在神经网络搭建过程中,有时需要保存一个状态,但是这个状态又不能被看作模型参数(例如批量正则化中的均值和方差变量)。这时可以使用register_buffer()函数为模型添加状态参数。
该函数的定义如下:
register_buffer(name, tensor)
函数register_buffer()返回的结果(状态参数)并不是模型的权重参数,它只是在模型运行中用来保存的临时变量。这种状态参数也被称为Buffers。
在Buffers被保存之后,可以通过该状态参数注册时所使用的名字(name)进行取值。


4.从模型中获取参数

4.1用parameters()方法获取模型的参数

通过Module类定义的模型,可以从其实例化对象中通过parameters()方法取得该模型中的参数(Parameter变量)

for param in model.parameters()  # 调用模型的parameters()方法获取整个网络结构中的参数
	print(type(param.data), param.size())

输出:
<class ‘torch.Tensor’> torch.Size([3, 2])
<class ‘torch.Tensor’> torch.Size([3])
<class ‘torch.Tensor’> torch.Size([2, 3])
<class ‘torch.Tensor’> torch.Size([2])
输出了四条信息,前两条是Linear1层的参数权重;后两条是Linear2层的参数权重。在模型的反向优化中,通常通过模型的parameters()方法获取模型参数,进行参数的更新。


4.2用name_parameters()方法获取模型中的参数和参数名字

通过Module类定义的模型,可以从其实例化对象中通过name_parameters()方法取得该模型中的参数和参数名字(Parameter变量)。

for param in model.named_parameters()  # 调用模型的named_parameters()方法获取整个网络结构中的参数和参数名字
	print(type(param.data), param.size(), name)

输出:
<class ‘torch.Tensor’> torch.Size([3, 2]) Linear1.weight
<class ‘torch.Tensor’> torch.Size([3]) Linear1.bias
<class ‘torch.Tensor’> torch.Size([2, 3]) Linear2.weight
<class ‘torch.Tensor’> torch.Size([2]) Linear2.bias


4.3用state_dict()方法获取模型的全部参数

通过Module类的state_dict()方法可以获取模型的全部参数,包括模型参数(Parameter)和状态参数(Buffers)。
下面将网络层、Variable变量、Parameter变量与buffer参数一同定义在ModelPar类中,并用state_dict()方法对它们取值。具体代码如下:

import torch
form torch.autograd import Variable
import torch.nn as nn
class ModelPar(nn.Module):
	def __init__(self):
	suprt(ModelPar, self).__init__()
	self.Line1 = nn.Linear(1, 2)  # 定义全连接层
	self.vari = Variable(torch.rand([1]))  # 定义Variable变量
	self.par = nn.Parameter(torch.rand([1]))  # 定义Parameter变量
	self.register_buffer("buffer", torch.randn([2, 3]))  # 定义buffer参数

model = ModelPar()  # 实例化ModelPar对象
for par in model.state_dict():
	print(par, ":", model.state_dict()[par])

输出:
par : tensor([0.0701])
buffer : tensor([[0.6635, 0.9307, 0.4224], [1.4274, 0.4996, 0.0608]])
Line1.weight : tensor([[0.7248], [-0.5080]])
Line1.bias : tensor([-0.9028, -0.5151])
从输出结果可以看出,state_dict()方法可以将模型中的Parameter和buffer参数取出,但不能将Variable变量取出。


4.4为模型中的参数指定名称,并查看权重

在深度网络模型中,如果想查看某一层的权重,则可以通过为其指定名称的方式,对该层权重进行快速提取。

import torch
import torch.nn as nn
from collections import OrderDict  # 定义一个网络
model = nn.Sequential(OrderDict([  # 为每个网络层指定名称
('conv1', nn.Conv2d(1, 20, 5)),
('relu1', nn.ReLU()),
('conv2', nn.Conv2d(20, 64, 5)),
('relu2', nn.ReLU())]))
print(model)  # 打印网络结构

输出:
Sequential(
(conv1) : Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(relu1) : ReLU()
(conv2) : Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
(relu2) : ReLU()
)
接下来,可以根据网络层的名称来直接提取对应的权重

params = model.state_dict()
print(params['conv1.weight'])
print(params['conv1.bias'])

5.激活模型接口

在PyTorch中,每个激活函数都有两种形式。

  • 类形式:在torch.nn模块中定义。需要先对其实例化才能进行使用
  • 函数形式:在torch.nn.functional模块中定义。可以直接以函数调用的方式进行使用。

以激活函数Tanh()为例,以下写法都是正确的。

  1. 以类成员对象的形式使用
    在模型类的init()方法中定义激活函数
self.tanh = torch.nn.Tanh()  # 对tanh类进行实例化

接着便可以在模型类的forward()方法中添加激活函数的应用

output= self.tanh(input)
  1. 以临时类对象的形式使用
    在模型类的forward()方法中
output= torch.nn.Tanh()(input)
  1. 以函数的形式使用
    在模型类的forward()方法中
output= torch.nn.functional.tanh(input)

6.L2正则化接口

在PyTorch中进行L2正则化,可以直接用优化器自带的weight_decay参数来指定权重值的衰减率,它相当于L2正则化公式中的λ。
注意:权值衰减参数weight_decay默认会对模型中的所有参数进行L2正则处理,即包括权重w和偏置b。在实际情况中,有时只需要对权重w进行正则化(如果对b进行L2正则化处理,则有可能会引起模型发生欠拟合问题),此时可以用优化器预置参数(Pre-parameter)的方式实现。
优化器预置参数的方式是指:在构建优化器时,以字典的方式对每一个实例化参数进行特别的指定,以满足更为细致的要求。具体要求如下:

optimizer = torch.optim.Adam([{'params': weight_p, 'weight_decay': 0.001},
{'params': bias_p, 'weight_decay': 0}], lr=0.01)

其中,字典重的params指的是模型中的权重。将具体的权重张量放入优化器,再为参数weight_dacay赋值指定权值衰减率,之后便可以对参数进行正则化处理了。
那么字典中的权重张量weight_p和bias_p是怎么得到的呢?可以通过实例化后的模型对象得到。具体代码如下:

weight_p, bias_p = [], []
for name, p in model.named_parameters():  # 获取模型中所有的参数及参数名字
	if 'bias' in name:
		bias_p += [p]
	else:
		weight_p += [p]

7.Dropout接口

再PyTorch中,按照不同的维度实现了3种Dropout的封装。

  • Dropout:对1维数据进行Dropout处理。输入形状是[N,D](N表示批次数,D表示数据的个数)
  • Dropout2D:对2维数据进行Dropout处理。输入形状是[N,C,H,W](N表示批次数,C表示通道数,H代表高度,W代表宽度),系统将随机选取部分通道,并将其权重设为0
  • Dropout3D:对3维数据进行Dropout处理。输入形状是[N,C,D,H,W](N表示批次数,C表示通道数,D代表深度,H代表高度,W代表宽度),系统将整个通道设为0。

每种维度的Dropout处理都有两种使用方式:基于类的使用、基于函数的使用。下面以1维数据的Dropout方式为例,其基于函数形式的定义如下:

torch.nn.functional.dropout(input, p=0.5, training=False, inplace=False)

其中的参数意义如下:

  • input:输入的模型节点
  • p:丢弃率。如果为1,则全部丢弃;默认0.5
  • training:该函数当前的使用状态。如果是False,则表明不在训练状态使用,这时不丢弃任何节点
  • inplace:是否改变输入值,默认是False

注意:Dropout技术的本质是对神经网络的网络结构进行了修改,它仅属于训练时的方法,所以一般在进行测试时要将函数dropout()的training参数变为False,代表不需要进行丢弃,否则会影响模型的正常输出。
另外,在使用类的方式进行Dropout时没有training参数,因为Dropout实例化对象会利用模型本身的调用方式来自动调节training参数


8.批量归一化接口

在PyTorch中,按照不同的维度实现了3种批量归一(BN)处理,具体如下:

  • BatchNorm1d:对2维或3维的数据进行BN处理。输入形状是[N,D]或者[N,D,L](N表示批次数,D代表数据的个数,L代表数据的长度)
  • BatchNorm2d:对4维数据进行BN处理。输入形状是[N,C,H,W](N表示批次数,C表示通道数,H表示高度,W表示宽度)
  • BatchNorm3d:对5维数据进行BN处理。输入形状是[N,C,D,H,W](N表示批次数,C表示通道数,H表示高度,W表示宽度)

BN=γ*[(x-ų)/ɗ ]+β
其中,每种维度的BN处理都被封装成类的形式供使用。以BatchNorm1d为例:

torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

其中的参数意义如下:

  • num_features:待处理的输入数据的特征数。该值需要手动计算,如果输入数据的形状是[N,D],该值为D。如果在BatchNorm2d中,该参数为通道数
  • eps:默认值为1e-5。是为了保证数值的稳定性(分母不能取0),而给分母加上的值。
  • momentum:动态均值和动态方差所使用的动量,默认值0.1
  • affine:是否使用自适应模式。如果是True,系统将自动对BN中的γ,β参数进行优化学习。如果是False,则去掉γ,β参数
  • track_running_stats:是否跟踪当前批次数据的统计特性。在训练过程中,如果是False,则系统只使用当前批次数据的均值和方差。如果为True,则系统将跟踪每批次输入到数据,并实时更新整个数据集的均值和方差,一般是同True

注意:在训练过程中,跟踪计算均值和方差的更新方式如下:
running_mean = momentum * running_mean + (1.0-momentum) * batch_mean
running_var = momentum * running_var + (1.0-momentum) * batch_var
在上面公式中,running_mean和running_var分别代表所要跟踪更新的均值和方差,momentum代表动量参数,batch_mean与batch_var代表当前批次所计算出来的均值和方差。

torch.nn.BatchNorm1d类继承于nn.Module类,nn.Module类会有一个统一的属性training,该属性用于指定当前的调用是“训练”状态还是“使用”状态。
PyTorch还提供了一个相对底层的函数式使用方式,该函数的定义如下:

torch.nn.functioanl.batch_norm(input, running_mean, running_var, weight=None, bias=None, training=Flase, momentun=0.1, eps=1e-05)

其中running_mean,running_var分别表示均值和方差,weight和bias分别表示自适应参数γ,β,参数training用于指定训练模式。
在实际开发中,通常使用类的方式实现BN。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值