pytorch学习笔记:task05

Task05 pytorch模型定义的方式

  • 一 pytorch模型定义的方式
    • 1、三种定义方式
    • 2、pytorch定义方式——Sequential
      • (1)Sequential定义方式
      • (2)优缺点
    • 3、pytorch定义方式——ModuleList
      • (1)ModuleList 定义方式
    • 4、pytorch定义方式——ModuleDict
      • (1)ModuleDict定义方式
    • 5、三种方式比较
  • 二 利用模型块快速搭建复杂网络
    • 1、U-Net简介
    • 2、U-Net模型块分析&实现
    • 2、利用模型块组装U-Net

一 pytorch模型定义的方式

1、三种定义方式

基于nn.Module,Sequential,ModuleList和ModuleDict三种方式

2、pytorch定义方式——Sequential

借助nn.Sequential()

(1)Sequential定义方式

class MySequential(nn.Module):
    from collections import OrderedDict
    def __init__(self, *args):
        super(MySequential, self).__init__()
        if len(args) == 1 and isinstance(args[0], OrderedDict): # 如果传入的是一个OrderedDict
            for key, module in args[0].items():
                self.add_module(key, module)  # add_module方法会将module添加进self._modules(一个OrderedDict)
        else:  # 传入的是一些Module
            for idx, module in enumerate(args):
                self.add_module(str(idx), module)
    def forward(self, input):
        # self._modules返回一个 OrderedDict,保证会按照成员添加时的顺序遍历成
        for module in self._modules.values():
            input = module(input)
        return input
  • 直接排列定义模型
import torch.nn as nn
net = nn.Sequential(
        nn.Linear(784, 256),
        nn.ReLU(),
        nn.Linear(256, 10), 
        )
print(net)

pytorch学习笔记:task05_第1张图片

  • 借助OrderedDict定义
import collections
import torch.nn as nn
net2 = nn.Sequential(collections.OrderedDict([
          ('fc1', nn.Linear(784, 256)),
          ('relu1', nn.ReLU()),
          ('fc2', nn.Linear(256, 10))
          ]))
print(net2)

pytorch学习笔记:task05_第2张图片

(2)优缺点

使用Sequential定义模型的好处在于简单、易读,同时使用Sequential定义的模型不需要再写forward,因为顺序已经定义好了。
但使用Sequential也会使得模型定义丧失灵活性,比如需要在模型中间加入一个外部输入时就不适合用Sequential的方式实现。使用时需根据实际需求加以选择。

3、pytorch定义方式——ModuleList

nn.ModuleList()
ModuleList 接收一个子模块(或层,需属于nn.Module类)的列表作为输入,可以把任意 nn.Module 的子类 (比如 nn.Conv2d, nn.Linear 之类的) 加(append和extend操作)到这个 list 中。
不同的是,加入到 nn.ModuleList 里面的module会自动注册到整个网络上的,同时 module 的 parameters 也会自动添加到整个网络中。

(1)ModuleList 定义方式

nn.ModuleList 并没有定义一个网络,它只是将不同的模块储存在一起。ModuleList中元素的先后顺序并不代表其在网络中的真实位置顺序,需要经过forward函数指定各个层的先后顺序后才算完成了模型的定义。具体实现时用for循环即可完成,如下:

class model(nn.Module):
  def __init__(self, *args):
        super(MySequential, self).__init__()
        if len(args) == 1 and isinstance(args[0], OrderedDict): # 如果传入的是一个OrderedDict
            for key, module in args[0].items():
                self.add_module(key, module)  # add_module方法会将module添加进self._modules(一个OrderedDict)
        else:  # 传入的是一些Module
            for idx, module in enumerate(args):
                self.add_module(str(idx), module)
  def forward(self, x):
    for layer in self.modulelist:
      x = layer(x)
    return x

实例如下

net = nn.ModuleList([nn.Linear(784, 256), nn.ReLU()])
net.append(nn.Linear(256, 10)) # # 类似List的append操作
print(net[-1])  # 类似List的索引访问
print(net)

pytorch学习笔记:task05_第3张图片

4、pytorch定义方式——ModuleDict

nn.ModuleDict()。
和ModuleList的作用类似,但能更方便地为神经网络的层添加名称。

(1)ModuleDict定义方式

案例

net = nn.ModuleDict({
    'linear': nn.Linear(784, 256),
    'act': nn.ReLU(),
})
net['output'] = nn.Linear(256, 10) 
print(net['linear']) 
print(net.output)
print(net)

pytorch学习笔记:task05_第4张图片

5、三种方式比较

  • Sequential适用于快速验证结果,因为已经明确了要用哪些层,直接写一下就好了,不需要同时写__init__和forward;

  • ModuleList和ModuleDict在某个完全相同的层需要重复出现多次时,非常方便实现,可以”一行顶多行“;

当我们需要之前层的信息的时候,比如 ResNets 中的 残差计算,当前层的结果需要和之前层中的结果进行融合,一般使用 ModuleList/ModuleDict 比较方便。

二 利用模型块快速搭建复杂网络

在实例中,需要借助模型块来搭建复杂的网络,而不是才取前面提到的三种定义方式。
大部分模型如ResNet、DenseNet等,虽然结构有很多层,但是较多时重复出现的。因此可以将此部分定义为“模块”,从而构建模型。具体样例如下:

1、U-Net简介

U-Net 是比较早的使用全卷积网络进行语义分割的算法之一,使用包含压缩路径和扩展路径的对称U形结构在当时非常具有创新性,且一定程度上影响了后面若干个分割网络的设计。模型结构如下:
pytorch学习笔记:task05_第5张图片
U-Net 通过残差连接结构解决了模型学习中的退化问题,使得神经网络的深度能够不断扩展

2、U-Net模型块分析&实现

组成主要包括:
(1)每个子块内部的两次卷积(Double Convolution)

class DoubleConv(nn.Module):
    """(convolution => [BN] => ReLU) * 2"""

    def __init__(self, in_channels, out_channels, mid_channels=None):
        super().__init__()
        if not mid_channels:
            mid_channels = out_channels
        self.double_conv = nn.Sequential(
            nn.Conv2d(in_channels, mid_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(mid_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        return self.double_conv(x)

(2)左侧模型块之间的下采样连接,通过Max pooling来实现

class Down(nn.Module):
    """Downscaling with maxpool then double conv"""

    def __init__(self, in_channels, out_channels):
        super().__init__()
        self.maxpool_conv = nn.Sequential(
            nn.MaxPool2d(2),
            DoubleConv(in_channels, out_channels)
        )

    def forward(self, x):
        return self.maxpool_conv(x)

(3)右侧模型块之间的上采样连接(Up sampling)

class Up(nn.Module):
    """Upscaling then double conv"""

    def __init__(self, in_channels, out_channels, bilinear=True):
        super().__init__()

        # if bilinear, use the normal convolutions to reduce the number of channels
        if bilinear:
            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
            self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)
        else:
            self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)
            self.conv = DoubleConv(in_channels, out_channels)

    def forward(self, x1, x2):
        x1 = self.up(x1)
        # input is CHW
        diffY = x2.size()[2] - x1.size()[2]
        diffX = x2.size()[3] - x1.size()[3]

        x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2,
                        diffY // 2, diffY - diffY // 2])
        # if you have padding issues, see
        # https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a
        # https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd
        x = torch.cat([x2, x1], dim=1)
        return self.conv(x)

(4)输出层的处理
除此之外,还包括:模型块之间的横向连接,输入和U-Net底部的连接等计算,可以通过forward函数来实现

class OutConv(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(OutConv, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)

    def forward(self, x):
        return self.conv(x)

2、利用模型块组装U-Net

参考内容:DataWhale pytorch进阶
https://github.com/milesial/Pytorch-UNet

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