Pytorch—模型参数与自定义网络层

Pytorch—模型参数与自定义网络层

1 模型参数相关

1.1 模型参数访问

在之前的文章中,我们介绍了在pytorch中关于模型的定义以及在init方法中定义模型的相关参数。在这一个小节中,我们来介绍对于模型参数的访问。

首先,我们来定义一个简单的网络结构:

net = nn.Sequential(nn.Linear(4,3),nn.ReLU(),nn.Linear(3,1))
X = torch.rand(2,4)
Y = net(X).sum()

回顾在之前的文章中提到的,Sequential是Module的子类,那么对于Sequential中的参数,可以通过Module类的的parameter()方法和named_parameters方法来访问所有的参数,两种方法返回的都是迭代器,但是第二种方法会返回参数的名称。

    for item in net.parameters():
        print(item)
    for name,item in net.named_parameters():
        print("the name is ",name)
        print("the parameters is ",item)

参数的名称是用[层数.变量名] 构成的。对于Sequential中的参数,我们也可以单独访问每一个层的参数:

	for item in net[0].parameters():
		print(item)
	for name,item in net[0].named_parameters():
		print(name,item)

这种方法方式和上面的访问的本质是一样的,本质上,Sequential是一个容器类,我们可以容器内部也是由各个类组成的,这些类都是Module类的子类,那么就都可以调用Module类的参数访问方法。

除了这种Module类自定义的子类中的参数,我们一般会自己定义一个神经网络结构,在这个结构中会自定义一些参数,例如:

class model(nn.Module):
	def __init__(self):
		super(model,self)
		self.weight1 = nn.Parameter(torch.rand(2,2))
		self.weight2 = torch.rand(2,2)
	def forward(self,X):
		return 1

在这个部分中,我们自定义了一个模型,在模型中定义了两个类内部的变量,这里值得注意的是,weight1是模型的参数,而weight2不是模型的参数。nn.Parameters()就是为模型声明参数的方法。而自定义的张量不能算作是模型的参数。同时nn.Parameter()将参数与我们自定义的模型绑定到一起,可以让参数通过梯度进行更新,也可以被优化器进行优化。

一般来说,我们对于参数的访问主要是pytorch中的已经定义好的类的参数,否则就是我们自定义类的参数。上面已经介绍了方法。

上述访问的结果一般是通过张量进行返回的,进一步,我们可以通过data来访问参数值的。在进行反向传播的时候,可以使用grad来访问梯度值。

1.2 模型参数初始化

从数学的角度来看,模型参数初始化的方式有很多种,常见的有均匀分布,正态分布等等方式。在torch定义好的类中,一般封装好了该类的初始化方式,我们下面主要关注的是如何初始化自己定义的模型参数。在pytorch的init包中,封装了很多初始化的方式,下面我们来介绍几种常见的初始化方式:

import torch.nn.init as init # 引入初始化模块 

最为常见的是我们对于偏置项的初始化,相对来说,偏置项一般是一个标量的形式存在,在init中存在方法constant_方法来进行常量初始化。

net = nn.Sequential(nn.Linear(4,3),nn.ReLU(),nn.Linear(3,1))
for name,param in net.Parameters():
	if 'bias' in param:
		init.constant_(param,val=0)

在constant_方法中的参数包括要进行初始化的参数名称和对应初始化的参数值。

对于参数矩阵,我们对于其初始化的方式一般为正太或者均匀的随机初始方式:

for name,param in net.Parameters():
	if 'weight' in param:
		init.normal_(param,mean=0,std=0.1)

上面是正太分布的初始化方式,参数包括需要初始化的参数名称,均值,标准差。

for name,param in net.Parameters():
	if 'weight' in param:
		init.uniform_(param,0,1)

上面是均匀分布的初始化方式,参数包括需要进行初始化的参数名称,均匀分布的下界和上界。

在这一小节的最后,我们来关注如何自定义参数的初始化方式,在很多情况下,torch中没有我们想要的参数初始化方式,需要我们自定义一个参数的初始化方法。首先,我们来看定义好的方式是如何进行初始化的

def normal_(tensor,mean=0,std=1):
	with torch.no_grad():
		return tensor.normal_(mean,std)

在这其中,tensor.normal_(mean,std) 是将当前的tensor转换成均值为mean,标准差为std的方法。下面,依照这种方式来自定义初始化方法

def init_weight(tensor):
    with torch.no_grad():
        tensor.uniform_(-1,1)
        tensor *= (tensor.abs() >= 5).float()
        return tensor

1.3 共享模型参数

在某些情况向,我们定义了很多的网络层,但是却希望这些网络层能够使用相同的参数。这里我们从两个方向进行介绍,首先如果使用的是类似于Sequential的容器类,我们可以使用相同的类多次传入到容器中来达到使用相同参数的目的。

liear = nn.Linear(2,1)
net = nn.Sequential(linear,linear)

如果是我们自己定义的参数,那么这种方式更加的简单,我们只需要使用自己定义的参数进行多次的做运算即可。

2 自定义神经网络层

在接下来的一节中,我们重点介绍的是如何自己定义网络层。随着深度学习的不断的发展,神经网络的结构变得越来越复杂,在这其中,模型自带的层的结果往往不能够满足我们的需求,这个时候我们需要自己定义网络中某一个层的结构。在pytorch中,所有的模型都是在继承了nn.Module的子类。说白了,对于复杂网络中的某一个层级,我们也可以将其视为一个子网络的结构,这种结构也是nn.Module的一个子类。既然是子类,其就需要继承nn.Module这个父类。同时,我们自定义这个子类中的参数和结构情况即可。我们下面来举一个例子

import torch
import torch.nn as nn

class OneLayer(nn.Module):
	def __init__(self):
		super(OneLayer,self).__init__()
		self.one = nn.Linear(5,4)
		self.two = nn.Linear(4,1)
	def forward(self,X):
		X = self.one(X)
		X = self.two(X)
		return X
class Model(nn.Module):
	def __init__(self):
		super(Model,self).__init__()
		self.one = OneLayer()
		self.two = nn.Linear(1,1)
	def forward(self,X):
		X = self.one(X)
		X = self.two(X)
		return X 

这里,我们简单定义一个自定义的网络层OneLayer,同时在自定义的Model中将OneLayer作为一个网络层添加到了网络模型中。进一步,我们也可以将我们自己定义的网络层添加到Sequential或ModelDict等容器类中作为一层。

3 参考

  1. 动手学习深度学习—pytorch版
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值