pytorch nn.Module()模块

nn.Module()

https://zhuanlan.zhihu.com/p/340453841

nn.Module()

nn.Module是nn中十分重要的类,包含网络各层的定义及forward方法

pytorch 里面一切自定义操作基本上都是继承nn.Module类来实现的。 简单的说 torch的核心是Module类,所有神经网络模块的基类。 模块也可以包含其他模块,从而可以将它们嵌套在树形结构中

如何定义自己的网络:

  • 我们在定义自已的网络的时候,需要继承nn.Module 类,
  • 重新实现构造函数__init__构造函数
  • 重新实现 forward这两个方法。

注意事项:

  • 一般把网络中具有可学习参数的层(如全连接层、卷积层等)(模块类的初始化) 放在构造函数__init__()

  • forward方法是必须要重写的,它是实现模型的功能,实现各个层之间的连接关系的核心

import torch.nn as nn
import torch.nn.functional as F
class Model(nn.Module):
    # nn.Module的子类函数必须在构造函数中执行父类的构造函数
    def __init__(self):
        super(Model, self).__init__()   # 等价与nn.Module.__init__()
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)
	def forward(self, x):
		x = F.relu(self.conv1(x))
		return F.relu(self.conv2(x))
    
   
model=Model()
print(model)

#Model(
#  (conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
#  (conv2): Conv2d(20, 20, kernel_size=(5, 5), stride=(1, 1))
# )

1、核心


forward(*input)			# 基函数中没有是实现需要在子函数中实现

apply(fn)				# 将Module及其所有的SubModule传进给定的fn函数操作
add_module(name,module)	# 将子模块加入当前的模块中,被添加的模块可以name来获取


apply(fn)

# 将net中的子模型Linear的 weight设置成 1,bias设置为0.  
# Example:    
def init_weights(m):
    # print(m)
    if type(m) == nn.Linear:
        m.weight.data.fill_(1.0)
        m.bias.data.fill_(0)
net = nn.Sequential(nn.Linear(2, 2))
net.apply(init_weights)

print(list(net.named_parameters()))
# >>>  tensor([[1., 1.], [1., 1.]], requires_grad=True)), ('0.bias', Parameter containing:tensor([0., 0.], requires_grad=True))] 

state_dict()

返回一个包含模块完整状态的字典

>>> net = torch.nn.Linear(2, 2)
>>> net.state_dict()

OrderedDict([('weight', tensor([[-0.3558,  0.2153],
        [-0.2785,  0.6982]])), ('bias', tensor([ 0.5771, -0.6232]))])
>>> net.state_dict().keys()
odict_keys(['weight', 'bias'])

add_module()

add_module(name,module)
# 添加子模块到当前模块中
# 该添加子模块能够使用给定的名字name来访问

"""输入参数
name(string)      子模块的名字
module (Module)   添加到该模块中的子模块
"""

2、查看

使用 nn.Module 可以对网络中的参数进行有效的管理

parameters()			# 返回一个 包含模型所有参数 的迭代器
buffers()			
children() 				# 返回当前模型 子模块的迭代器, 不递归  包含子模块
modules()				# 返回一个包含 当前模型 所有模块的迭代器,递归所有叶子module 

与之对应的四个
named_parameters()
named_buffers()
named_children()
named_modules()

net = nn.Sequential(
    nn.Linear(in_features=4, out_features=2),
    nn.Linear(in_features=2, out_features=2)
)

# 隐藏层的编号是从0开始的
list(net.parameters())[0] # [0]是layer0的w
list(net.parameters())[3].shape # [3]是layer1的b
dict(net.named_parameters()).items() # 返回所有层的参数
    
optimizer = optim.SGD(net.parameters(), lr=1e-3)

# 输出

torch.Size([2, 4])
torch.Size([2])
dict_items([('0.weight', Parameter containing:
tensor([[ 0.0195,  0.4698, -0.4913, -0.3336],
        [ 0.1422,  0.2908, -0.2469,  0.0583]], requires_grad=True)), ('0.bias', Parameter containing:
tensor([-0.4704, -0.1133], requires_grad=True)), ('1.weight', Parameter containing:
tensor([[-0.6511,  0.2442],
        [ 0.5658,  0.4419]], requires_grad=True)), ('1.bias', Parameter containing:
tensor([ 0.0114, -0.5664], requires_grad=True))])



3、设置

# 设置为训练或者测试模式
train()			# 将module设置为 training mode,只影响dropout和batchNorm
eval()   		# 将模型设置成evaluation模式,只影响dropout和batchNorm

requires_grad_() 	# 用于设置self.parameters()是否需要record梯度,默认情况下是True
zero_grad()			# 用于设置self.parameters()的gradients为零

to()  				# 它可以当成三种函数来使用

# 选择设备
cpu(device,id=None)   # 
cuda(device,id=None)
to() 			# 三种函数来使用  
"""
1、张量		to(tensor, non_blocking=False)
2、类型		to(dtype, non_blocking=False) 【cpu,cuda,type.float,double,half】
3、设备		to(device=None, dtype=None, non_blocking=False)
4、存储		to(memory_format=torch.channels_last)
"""

# 案例1
>>> linear = nn.Linear(2, 2)
>>> print(linear.weight)
Parameter containing:
tensor([[ 0.0822, -0.5305],
        [ 0.3486, -0.3749]], requires_grad=True)

>>> linear.to(torch.double)
>>> print(linear.weight)
Parameter containing:
tensor([[ 0.0822, -0.5305],
        [ 0.3486, -0.3749]], dtype=torch.float64, requires_grad=True)

# 案例2
>>> gpu1 = torch.device("cuda:0")
>>> linear.to(gpu1, dtype=torch.half, non_blocking=True)
>>> print(linear.weight)
Parameter containing:
tensor([[ 0.0822, -0.5303],
        [ 0.3486, -0.3750]], device='cuda:0', dtype=torch.float16,requires_grad=True)

# 案例3
>>> cpu = torch.device("cpu")
>>> linear.to(cpu,dtype=torch.double)
>>> print(linear.weight)
Parameter containing:
tensor([[ 0.0822, -0.5303],
        [ 0.3486, -0.3750]], dtype=torch.float64, requires_grad=True)

4、注册

register_parameter				# 向self._parameters注册新元素
register_buffer					# 向self._buffers注册新元素 

register_backward_hook			# 向self._backward_hooks注册新元素
register_forward_pre_hook		# 向self._forward_pre_hooks注册新元素
register_forward_hook			# 向self._forward_hooks注册新元素

5、转换

可以很方便的将所有运算都转入到 GPU 上去,使用.device() 函数

to()		 # 
type()		 # type函数是将所有parameters和buffers都转成指定的目标类型dst_type
double()	 # 将parameters和buffers的数据类型转换成double
float()      # 将parameters和buffers的数据类型转换成float
half()		 # 将parameters和buffers的数据类型转换成half 

6、加载

可以很方便的进行 save 和 load,以防止突然发生的断点和系统崩溃现象

load_state_dict(state_dict, strict=True)
# 将state_dict中的参数和缓冲区复制到此模块及其后代中。如果strict为真,则state_dict的键必须与该模块的state_dict()函数返回的键完全匹配。

"""
state_dict (dict) – 保存parameters和persistent buffers的字典。
将state_dict中的parameters和buffers复制到此module和它的后代中。

state_dict中的key必须和 model.state_dict()返回的key一致。
"""


如何将模型连接起来

nn.Sequential()

nn.Sequential(*args)

Sequential 本质上是一个容器,只是继承了 nn.Module() ,时序容器,Modules 会以他们传入的顺序被添加到容器中

  • 继承了 nn.Module()
class Sequential(Module): # 继承Module
    def __init__(self, *args):  # 重写了构造函数
    def _get_item_by_idx(self, iterator, idx):
    def __getitem__(self, idx):
    def __setitem__(self, idx, module):
    def __delitem__(self, idx):
    def __len__(self):
    def __dir__(self):
    def forward(self, input):  # 重写关键方法forward

当你使用Sequential时,Modules会以传入的顺序来添加layer到容器中,也可以传入一个OrderedDict

添加模块1

# Example of using Sequential
# 三种写法

model = nn.Sequential(
          nn.Conv2d(1,20,5),
          nn.ReLU(),
          nn.Conv2d(20,64,5),
          nn.ReLU()
        )
# 采用第一种方式,默认命名方式为  [0,1,2,3,4,...]
print(model)
print(model[2]) # 通过索引获取第几个层
'''运行结果为:
Sequential(
  (0): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
  (1): ReLU()
  (2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
  (3): ReLU()
	)
	
Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
'''


添加模块2


model = nn.Sequential(OrderedDict([
          ('conv1', nn.Conv2d(1,20,5)),
          ('relu1', nn.ReLU()),
          ('conv2', nn.Conv2d(20,64,5)),
          ('relu2', nn.ReLU())
        ]))
print(model)
print(model[2]) # 通过索引获取第几个层
'''运行结果为:
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()
	)
	
Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
'''


# model[2] 是正确的
# model["conv2"] 是错误的
# 这其实是由它的定义实现的,看上面的Sequenrial定义可知,只支持index访问。


添加模块3

# 继承 nn.Module 的方法,可以自定义添加模块
# add_module(name, module)

import torch.nn as nn
from collections import OrderedDict
model = nn.Sequential()

##  实际上使用的是  nn.module() 的属性

model.add_module("conv1",nn.Conv2d(1,20,5))
model.add_module('relu1', nn.ReLU())
model.add_module('conv2', nn.Conv2d(20,64,5))
model.add_module('relu2', nn.ReLU())
 
print(model)
print(model[2]) # 通过索引获取第几个层

'''运行结果为:
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()
	)
Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
'''

nn.ModuleList()

class torch.nn.ModuleList(modules=None)

将你的模型保存在一个list中,可以像python list一样被索引, moduleList 中包含的modules已经被正确的注册,对所有module method可见

ModuleList 具有和List 相似的用法,实际上可以把它视作是 Module 和 list 的结合。

# 输入参数  modules (list, optional) – 将要被添加到MuduleList中的 modules 列表

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.layers=nn.ModuleList([
            nn.Linear(1,10), nn.ReLU(),
            nn.Linear(10,1)])
    def forward(self,x):
        out = x
        for layer in self.layers:
            out = layer(out)
        return out
    
model = Model()
print(model)

Model(
  (layers): ModuleList(
    (0): Linear(in_features=1, out_features=10, bias=True)
    (1): ReLU()
    (2): Linear(in_features=10, out_features=1, bias=True)
  )
)

append(module)

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.layers=nn.ModuleList([
            nn.Linear(1,10), nn.ReLU(),
            nn.Linear(10,1)])
        self.layers.append(nn.Linear(1, 5))
    def forward(self,x):
        out = x
        for layer in self.layers:
            out = layer(out)
        return out

extend(modules)

extend(),必须也为一个list

self.layers.extend([nn.Linear(size1, size2) for i in range(1, num_layers)])
posted @ 2021-09-27 09:39  贝壳里的星海  阅读(4000)  评论(0编辑  收藏  举报