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)])