pytorch模型容器Containers nn.ModuleDict、nn.moduleList、nn.Sequential

模型容器Containers之nn.ModuleDict、nn.moduleList、nn.Sequential


pytorch模型容器Containers nn.ModuleDict、nn.moduleList、nn.Sequential_第1张图片

nn.Sequential()对象

建立nn.Sequential()对象,必须小心确保一个块的输出大小与下一个块的输入大小匹配。基本上,它的行为就像一个nn.Module。

模型建立方式

第一种写法:
nn.Sequential()对象.add_module(层名,层class的实例)

net1 = nn.Sequential()
net1.add_module('conv', nn.Conv2d(3, 3, 3))
net1.add_module('batchnorm', nn.BatchNorm2d(3))
net1.add_module('activation_layer', nn.ReLU())

第二种写法:
nn.Sequential(*多个层class的实例)

net2 = nn.Sequential(
        nn.Conv2d(3, 3, 3),
        nn.BatchNorm2d(3),
        nn.ReLU()
        )

第三种写法:
nn.Sequential(OrderedDict([*多个(层名,层class的实例)]))

from collections import OrderedDict
net3= nn.Sequential(OrderedDict([
          ('conv', nn.Conv2d(3, 3, 3)),
          ('batchnorm', nn.BatchNorm2d(3)),
          ('activation_layer', nn.ReLU())
        ]))

输出

print('net1:', net1)
print('net2:', net2)
print('net3:', net3)
/*
net1: Sequential(
  (conv): Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1))
  (batchnorm): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True)
  (activation_layer): ReLU()
)
net2: Sequential(
  (0): Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1))
  (1): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True)
  (2): ReLU()
)
net3: Sequential(
  (conv): Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1))
  (batchnorm): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True)
  (activation_layer): ReLU()
)*/

取值

# 可根据名字或序号取出子module
net1.conv, net2[0], net3.conv
/**
(Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)),
 Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)),
 Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)))
**/

nn.ModuleList()对象

为什么有他?
写一个module然后就写foreword函数很麻烦,所以就有了这两个。它被设计用来存储任意数量的nn. module。

什么时候用?
如果在构造函数__init__中用到list、tuple、dict等对象时,一定要思考是否应该用ModuleList或ParameterList代替。
如果你想设计一个神经网络的层数作为输入传递。

和list的区别?
ModuleList是Module的子类,当在Module中使用它的时候,就能自动识别为子module。

当添加 nn.ModuleList 作为 nn.Module 对象的一个成员时(即当我们添加模块到我们的网络时),所有 nn.ModuleList 内部的 nn.Module 的 parameter 也被添加作为 我们的网络的 parameter。


class MyModule(nn.Module):
    def __init__(self):
        super(MyModule, self).__init__()
        self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])
    def forward(self, x):
        # ModuleList can act as an iterable, or be indexed         using ints
        for i, l in enumerate(self.linears):
            x = self.linears[i // 2](x) + l(x)
        return x

extend和append方法

nn.moduleList定义对象后,有extend和append方法,用法和python中一样,extend是添加另一个modulelist append是添加另一个module

class LinearNet(nn.Module):
  def __init__(self, input_size, num_layers, layers_size, output_size):
     super(LinearNet, self).__init__()
 
     self.linears = nn.ModuleList([nn.Linear(input_size, layers_size)])
     self.linears.extend([nn.Linear(layers_size, layers_size) for i in range(1, self.num_layers-1)])
     self.linears.append(nn.Linear(layers_size, output_size)

建立以及使用方法

modellist = nn.ModuleList([nn.Linear(3,4), nn.ReLU(), nn.Linear(4,2)])
input = V(t.randn(1, 3))
for model in modellist:
    input = model(input)
# 下面会报错,因为modellist没有实现forward方法
# output = modelist(input)

和普通list不一样,它和torch的其他机制结合紧密,继承了nn.Module的网络模型class可以使用nn.ModuleList并识别其中的parameters,当然这只是个list,不会自动实现forward方法,

class MyModule(nn.Module):
    def __init__(self):
        super(MyModule, self).__init__()
        self.list = [nn.Linear(3, 4), nn.ReLU()]
        self.module_list = nn.ModuleList([nn.Conv2d(3, 3, 3), nn.ReLU()])
    def forward(self):
        pass
model = MyModule()
print(model)
/**
MyModule(
  (module_list): ModuleList(
    (0): Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1))
    (1): ReLU()
  )
)
**/

for name, param in model.named_parameters():
    print(name, param.size())
/**
('module_list.0.weight', torch.Size([3, 3, 3, 3]))
('module_list.0.bias', torch.Size([3]))
**/

可见,普通list中的子module并不能被主module所识别,而ModuleList中的子module能够被主module所识别。这意味着如果用list保存子module,将无法调整其参数,因其未加入到主module的参数中。

除ModuleList之外还有ParameterList,其是一个可以包含多个parameter的类list对象。在实际应用中,使用方式与ModuleList类似。

nn.ModuleDict

主要方法:
clear(): 清空ModuleDict
items(): 返回可迭代的键值对(key-value pairs)
keys(): 返回字典的键(key)
values(): 返回字典的值(value)
pop(): 返回一对键值,并从字典中删除

class ModuleDict(nn.Module):
    def __init__(self):
        super(ModuleDict, self).__init__()
        self.choices = nn.ModuleDict({
            'conv': nn.Conv2d(10, 10, 3),
            'pool': nn.MaxPool2d(3)
        })

        self.activations = nn.ModuleDict({
            'relu': nn.ReLU(),
            'prelu': nn.PReLU()
        })

    def forward(self, x, choice, act):
        x = self.choices[choice](x)
        x = self.activations[act](x)
        return x

net = ModuleDict()
fake_img = torch.randn((4, 10, 32, 32))
output = net(fake_img, 'conv', 'relu')
print(output)

从上面的代码我们可以看到在forward处我们传入两个参数,第一个参数选择使用conv还是pool,第二个参数选择使用relu还是prelu。

参考

https://blog.csdn.net/Zero_run/article/details/108478846
https://blog.csdn.net/e01528/article/details/84397174

你可能感兴趣的:(PyTorch框架,pytorch,深度学习)