PyTorch中的nn.ModuleList和nn.Sequential

nn.Sequential

nn.Sequential里面的所构造的模块是要按照顺序进行排列的,必须确保前一个模块的输出大小和下一个模块的输入大小是一致的。

import torch
import torch.nn as nn
import torch.nn.functional as F
class net_seq(nn.Module):
    def __init__(self):
        super(net2, self).__init__()
        self.seq = nn.Sequential(
                        nn.Conv2d(1,20,5),
                         nn.ReLU(),
                          nn.Conv2d(20,64,5),
                       nn.ReLU()
                       )      
    def forward(self, x):
        return self.seq(x)
net_seq = net_seq()
print(net_seq)
#net_seq(
#  (seq): 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()
#  )
#)

nn.Sequential也可以使用OrderedDict来指定每个module的名字,而不是采用默认的命名方式(按序号 0,1,2,3…)。

from collections import OrderedDict

class net_seq(nn.Module):
    def __init__(self):
        super(net_seq, self).__init__()
        self.seq = nn.Sequential(OrderedDict([
                        ('conv1', nn.Conv2d(1,20,5)),
                         ('relu1', nn.ReLU()),
                          ('conv2', nn.Conv2d(20,64,5)),
                       ('relu2', nn.ReLU())
                       ]))
    def forward(self, x):
        return self.seq(x)
net_seq = net_seq()
print(net_seq)
#net_seq(
#  (seq): 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()
#  )
#)

nn.Sequential

nn.ModuleList,它是一个储存不同 module,并自动将每个 module 的 parameters 添加到网络之中的容器。你可以把任意 nn.Module 的子类 (比如 nn.Conv2d, nn.Linear 之类的) 加到这个 list 里面,方法和 Python 自带的 list 一样,无非是 extend,append 等操作。但不同于一般的 list,加入到 nn.ModuleList 里面的 module 是会自动注册到整个网络上的,同时 module 的 parameters 也会自动添加到整个网络中。若使用python的list,则会出问题。

class net_modlist(nn.Module):
    def __init__(self):
        super(net_modlist, self).__init__()
        self.modlist = nn.ModuleList([
                       nn.Conv2d(1, 20, 5),
                       nn.ReLU(),
                        nn.Conv2d(20, 64, 5),
                        nn.ReLU()
                        ])

    def forward(self, x):
        for m in self.modlist:
            x = m(x)
        return x

net_modlist = net_modlist()
print(net_modlist)
#net_modlist(
#  (modlist): ModuleList(
#    (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()
#  )
#)
for param in net_modlist.parameters():
    print(type(param.data), param.size())
#<class 'torch.Tensor'> torch.Size([20, 1, 5, 5])
#<class 'torch.Tensor'> torch.Size([20])
#<class 'torch.Tensor'> torch.Size([64, 20, 5, 5])
#<class 'torch.Tensor'> torch.Size([64])

可以看到,这个网络权重 (weithgs) 和偏置 (bias) 都在这个网络之内。接下来看看另一个作为对比的网络,它使用 Python 自带的 list:

class net_modlist(nn.Module):
    def __init__(self):
        super(net_modlist, self).__init__()
        self.modlist = [
                       nn.Conv2d(1, 20, 5),
                       nn.ReLU(),
                        nn.Conv2d(20, 64, 5),
                        nn.ReLU()
                        ]

    def forward(self, x):
        for m in self.modlist:
            x = m(x)
        return x

net_modlist = net_modlist()
print(net_modlist)
#net_modlist()
for param in net_modlist.parameters():
    print(type(param.data), param.size())
#None

显然,使用 Python 的 list 添加的卷积层和它们的 parameters 并没有自动注册到我们的网络中。当然,我们还是可以使用 forward 来计算输出结果。但是如果用其实例化的网络进行训练的时候,因为这些层的parameters不在整个网络之中,所以其网络参数也不会被更新,也就是无法训练。

nn.Sequential与nn.ModuleList的区别

不同点1:
nn.Sequential内部实现了forward函数,因此可以不用写forward函数。而nn.ModuleList则没有实现内部forward函数。如果完全直接用 nn.Sequential,确实是可以的,但这么做的代价就是失去了部分灵活性,不能自己去定制 forward 函数里面的内容了。一般情况下 nn.Sequential 的用法是来组成卷积块 (block),然后像拼积木一样把不同的 block 拼成整个网络,让代码更简洁,更加结构化。

不同点2:
nn.Sequential可以使用OrderedDict对每层进行命名,上面已经阐述过了;

不同点3:
nn.Sequential里面的模块按照顺序进行排列的,所以必须确保前一个模块的输出大小和下一个模块的输入大小是一致的。而nn.ModuleList 并没有定义一个网络,它只是将不同的模块储存在一起,这些模块之间并没有什么先后顺序可言。网络的执行顺序是根据 forward 函数来决定的。若将forward函数中几行代码互换,使输入输出之间的大小不一致,则程序会报错。此外,为了使代码具有更高的可读性,最好把ModuleList和forward中的顺序保持一致。

不同点4:
有的时候网络中有很多相似或者重复的层,我们一般会考虑用 for 循环来创建它们,而不是一行一行地写,比如:

layers = [nn.Linear(10, 10) for i in range(5)]

那么这里我们使用ModuleList:

class net4(nn.Module):
    def __init__(self):
        super(net4, self).__init__()
        layers = [nn.Linear(10, 10) for i in range(5)]
        self.linears = nn.ModuleList(layers)

    def forward(self, x):
        for layer in self.linears:
            x = layer(x)
        return x

net = net4()
print(net)
# net4(
#   (linears): ModuleList(
#     (0): Linear(in_features=10, out_features=10, bias=True)
#     (1): Linear(in_features=10, out_features=10, bias=True)
#     (2): Linear(in_features=10, out_features=10, bias=True)
#   )
# )

参考选自: 知乎.

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pytorch 是一个高效的深度学习框架,其nn.modulelistnn.sequential是常用的模块。这两种模块都可以用于创建深度学习网络,并且能够实现自动求导。nn.sequential 是一个有序的容器,其每个模块按照传入的顺序依次进行计算。nn.modulelist 是一个无序的容器,其每个模块都可以以列表的形式存储,且没有特定的计算顺序。 nn.sequential 模块的优点是简单易用,并且可以通过一行代码构建和训练网络。例如,要创建一个简单的两层全连接神经网络,可以如下代码实现: ``` model = nn.Sequential(nn.Linear(784, 64), nn.ReLU(), nn.Linear(64, 10), nn.Softmax(dim=1)) ``` 这会定义一个两个全连接层网络以及 ReLU 和softmax 激活函数,输入大小为 784(MNIST 图像大小) ,输出大小为 10(10 个数字)。 nn.modulelist 是一个更加灵活的容器,可以在其添加任意的子模块。要使用 nn.modulelist,需要先创建一个空的 nn.modulelist,然后手动向其添加子模块。例如,可以这样创建一个相同的两层全连接网络: ``` model = nn.ModuleList([ nn.Linear(784, 64), nn.ReLU(), nn.Linear(64, 10), nn.Softmax(dim=1) ]) ``` 需要注意的是,nn.modulelist 的子模块顺序可能会影响计算结果,因为没有特定的训练顺序。因此,在使用 nn.modulelist 时应该尽量保证顺序的准确性。 综上所述,nn.sequentialnn.modulelist 都是常用的容器,用于组织神经网络的子模块,它们在不同场景下具有各自的优势。在简单的前向计算nn.sequential 更加容易使用;在需要更好的灵活性时,nn.modulelist 可以更好地实现目标。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值