Pytorch实战总结篇之模型构建

1. 写在前面

这段时间一直在持续学习Pytorch, 也大约整理了20篇左右的笔记, 主要包括系统学习Pytorch的10篇, 这里面主要是从原理的层次看Pytorch的运行机制, 一方面是可以大致上对学习Pytorch有一个整体的框架, 另一方面是能理解很多知识的背后原理, 然后是Pytorch的入门与实战8篇, 这里面是使用Pytorch进行一些实战任务, 从图像到语音, 大致上可以知道Pytorch在各个领域是怎么发挥作用的, 但是经过前面的这些文章, 可能依然无法把Pytorch运用起来, 第一个系列是偏底层原理和知识框架, 而第二个系列作为一个桥梁纽带, 更多的是介绍图像和语音方面的知识, 然后用Pytorch进行了一些任务的完成,如果换了任务, 可能依然不知道如何使用Pytorch, 所以这个终结系列的几篇文章从纯使用的角度来总结Pytorch,毕竟Pytorch还是作为一种工具, 下面我们就来看看真正的使用方法。

通过前面的学习, 使用Pytorch实现神经网络建模一般包括数据准备、模型建立、模型训练、模型评估使用和保存, 所以接下来会整理四篇文章对这几方面进行使用总结。 今天是第二篇, 模型的构建部分, 上一篇文章中已经在结构化, 图像, 文本和时序等四个方面介绍了如何准备数据, 今天就来看看有了数据之后, 如何构建模型以完成后面的任务。

Pytorch构建模型, 常用的方式有下面的三种: 继承nn.Module基类构建自定义模型, 使用nn.Sequential按层顺序构建模型, 继承nn.Module基类构建模型, 并辅助应用模型容器进行封装。 其中第一种方式最为常见, 第二种方式最简单, 第三种方式灵活但是复杂, 具体的可以根据实际任务而定。 这里整理一下, 方便以后参考查阅。

import torch
from torch import nn
from torchkeras import summary

2. 继承nn.Module基类构建自定义模型

下面是一个继承nn.Module基类构建自定义模型的范例。 模型中用到的层一般在__init__函数中定义, 然后再forward方法中定义模型的正向传播逻辑。下面的代码来自下面的链接。

class Net(nn.Module):
    
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=3,out_channels=32,kernel_size = 3)
        self.pool1 = nn.MaxPool2d(kernel_size = 2,stride = 2)
        self.conv2 = nn.Conv2d(in_channels=32,out_channels=64,kernel_size = 5)
        self.pool2 = nn.MaxPool2d(kernel_size = 2,stride = 2)
        self.dropout = nn.Dropout2d(p = 0.1)
        self.adaptive_pool = nn.AdaptiveMaxPool2d((1,1))
        self.flatten = nn.Flatten()
        self.linear1 = nn.Linear(64,32)
        self.relu = nn.ReLU()
        self.linear2 = nn.Linear(32,1)
        self.sigmoid = nn.Sigmoid()
        
    def forward(self,x):
        x = self.conv1(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.pool2(x)
        x = self.dropout(x)
        x = self.adaptive_pool(x)
        x = self.flatten(x)
        x = self.linear1(x)
        x = self.relu(x)
        x = self.linear2(x)
        y = self.sigmoid(x)
        return y
        
net = Net()
print(net)

summary(net, input_shape=(3, 32, 32))

通过最后这行代码就可以看到我们建立的模型的结构了。
在这里插入图片描述
这种方法是比较常见的一种方式。这个就类似于keras里面的Model方法。

3. 使用nn.Sequential按层顺序构建模型

使用nn.Sequential按层顺序构建模型无需定义forward方法。仅仅适合于简单的模型。这个就类似于keras里面的sequence方法。

下面是使用nn.Sequential搭建模型的一些等价方法。

  1. 利用add_module方法

    net = nn.Sequential()
    net.add_module("conv1",nn.Conv2d(in_channels=3,out_channels=32,kernel_size = 3))
    net.add_module("pool1",nn.MaxPool2d(kernel_size = 2,stride = 2))
    net.add_module("conv2",nn.Conv2d(in_channels=32,out_channels=64,kernel_size = 5))
    net.add_module("pool2",nn.MaxPool2d(kernel_size = 2,stride = 2))
    net.add_module("dropout",nn.Dropout2d(p = 0.1))
    net.add_module("adaptive_pool",nn.AdaptiveMaxPool2d((1,1)))
    net.add_module("flatten",nn.Flatten())
    net.add_module("linear1",nn.Linear(64,32))
    net.add_module("relu",nn.ReLU())
    net.add_module("linear2",nn.Linear(32,1))
    net.add_module("sigmoid",nn.Sigmoid())
    
    print(net)
    
  2. 利用变长参数
    这种方式构建时不能给每个层指定名称

    net = nn.Sequential(
        nn.Conv2d(in_channels=3,out_channels=32,kernel_size = 3),
        nn.MaxPool2d(kernel_size = 2,stride = 2),
        nn.Conv2d(in_channels=32,out_channels=64,kernel_size = 5),
        nn.MaxPool2d(kernel_size = 2,stride = 2),
        nn.Dropout2d(p = 0.1),
        nn.AdaptiveMaxPool2d((1,1)),
        nn.Flatten(),
        nn.Linear(64,32),
        nn.ReLU(),
        nn.Linear(32,1),
        nn.Sigmoid()
    )
    
    print(net)
    
  3. 利用OrderedDict

    from collections import OrderedDict
    
    net = nn.Sequential(OrderedDict(
              [("conv1",nn.Conv2d(in_channels=3,out_channels=32,kernel_size = 3)),
                ("pool1",nn.MaxPool2d(kernel_size = 2,stride = 2)),
                ("conv2",nn.Conv2d(in_channels=32,out_channels=64,kernel_size = 5)),
                ("pool2",nn.MaxPool2d(kernel_size = 2,stride = 2)),
                ("dropout",nn.Dropout2d(p = 0.1)),
                ("adaptive_pool",nn.AdaptiveMaxPool2d((1,1))),
                ("flatten",nn.Flatten()),
                ("linear1",nn.Linear(64,32)),
                ("relu",nn.ReLU()),
                ("linear2",nn.Linear(32,1)),
                ("sigmoid",nn.Sigmoid())
              ])
            )
    print(net)
    

3. 继承nn.Module基类构建模型并辅助应用模型容器进行封装

当模型的结构比较复杂的时候, 我们可以应用模型容器(nn.Sequential, nn.ModuleList, nn.ModuleDict)对模型的部分结构进行封装, 这样做会让模型整体更加有层次感, 有时候也能减少代码量。关于这几个模块的介绍, 可以参考下面的第二个链接。

下面演示一下这些容器的使用, 注意,在下面的范例中我们每次仅仅使用一种模型容器,但实际上这些模型容器的使用是非常灵活的,可以在一个模型中任意组合任意嵌套使用。

  1. nn.Sequential 作为模型容器

    class Net(nn.Module):
        
        def __init__(self):
            super(Net, self).__init__()
            self.conv = nn.Sequential(
                nn.Conv2d(in_channels=3,out_channels=32,kernel_size = 3),
                nn.MaxPool2d(kernel_size = 2,stride = 2),
                nn.Conv2d(in_channels=32,out_channels=64,kernel_size = 5),
                nn.MaxPool2d(kernel_size = 2,stride = 2),
                nn.Dropout2d(p = 0.1),
                nn.AdaptiveMaxPool2d((1,1))
            )
            self.dense = nn.Sequential(
                nn.Flatten(),
                nn.Linear(64,32),
                nn.ReLU(),
                nn.Linear(32,1),
                nn.Sigmoid()
            )
        def forward(self,x):
            x = self.conv(x)
            y = self.dense(x)
            return y 
        
    net = Net()
    print(net)
    
  2. nn.ModuleList作为模型容器
    注意, 下面中的ModuleList不能用python中的列表代替。

    class Net(nn.Module):
        
        def __init__(self):
            super(Net, self).__init__()
            self.layers = nn.ModuleList([
                nn.Conv2d(in_channels=3,out_channels=32,kernel_size = 3),
                nn.MaxPool2d(kernel_size = 2,stride = 2),
                nn.Conv2d(in_channels=32,out_channels=64,kernel_size = 5),
                nn.MaxPool2d(kernel_size = 2,stride = 2),
                nn.Dropout2d(p = 0.1),
                nn.AdaptiveMaxPool2d((1,1)),
                nn.Flatten(),
                nn.Linear(64,32),
                nn.ReLU(),
                nn.Linear(32,1),
                nn.Sigmoid()]
            )
        def forward(self,x):
            for layer in self.layers:
                x = layer(x)
            return x
    net = Net()
    print(net)
    
  3. nn.ModuleDict作为模型容器
    注意下面中的ModuleDict不能用Python中的字典代替。

    class Net(nn.Module):
        
        def __init__(self):
            super(Net, self).__init__()
            self.layers_dict = nn.ModuleDict({
          
          "conv1":nn.Conv2d(in_channels=3,out_channels=32,kernel_size = 3),
                   "pool": nn.MaxPool2d(kernel_size = 2,stride = 2),
                   "conv2":nn.Conv2d(in_channels=32,out_channels=64,kernel_size = 5),
                   "dropout": nn.Dropout2d(p = 0.1),
                   "adaptive":nn.AdaptiveMaxPool2d((1,1)),
                   "flatten": nn.Flatten(),
                   "linear1": nn.Linear(64,32),
                   "relu":nn.ReLU(),
                   "linear2": nn.Linear(32,1),
                   "sigmoid": nn.Sigmoid()
                  })
        def forward(self,x):
            layers = ["conv1","pool","conv2","pool","dropout","adaptive",
                      "flatten","linear1","relu","linear2","sigmoid"]
            for layer in layers:
                x = self.layers_dict[layer](x)
            return x
    net = Net()
    print(net)
    

参考

猜你喜欢

转载自blog.csdn.net/wuzhongqiang/article/details/108880199