深入浅出PytorchTask03打卡

Pytorch 模型定义
image

首先先引入一些包:
image

Pytorch 定义模型的方式有三种:

  1. Sequential
  2. ModuleList
  3. ModuleDict

用sequential定义模型的方式如下:
image
当然sequential出了上述的direct list这种按顺序走下来,一目了然的方式之外,还有ordered dict 方式,也就是“按顺序的字典”
image

由以下代码可以证明,用这两种方式构建的sequential模型是一样的:
image

当然,sequential构建模型比较简单,接下来是另一种构建模型的方式:ModuleList

Sequential和modulelist有什么差别呢,首先sequential是按顺序排列,一层层的走下去,如果模型内部内容很多那只能一条条的写下去。如果有很多层的操作是重复的情况下,sequential的模型比较冗杂且重复。
而把它写成字典的形式,可以方便进行操作,比如append, pop之类的操作
image

但是注意的是,modulelist并没有定义一个网络,而是将不同的模块储存到了一起,并没有一个前向传播的顺序在里面,所以不能够out3 = net3(a)这样直接使用。
而解决的方案就是定义一个类,在初始化中将modulelist放进去:
image

在类中定义了forward函数,这样程序就知道了操作的顺序,所以也就能够运行成功了。

接下来就是ModuleDict构建模型了:
用字典指定每个层的名称,然后运用名称来对每一层进行操作:
image

但是moduledict并没有定义顺序,也只是将不同的模块进行组合到了一起。
需要和modulelist一样进行class操作,进行forward的定义。

所以这三种方式进行模块定义都各有利弊,sequential的方式不需要定义传递方式,但是需要一层层的写下去,而modulelist对于重复性较大的模块可以进行快速的复用,moduledict处理较为灵活且直观,但是modulelist和moduledict都需要定义各个模块的传递方式。

讲完了每个模块定义的方式之后,要讲解如何用模块快速的搭建复杂的网络,以U-Net模型为例,它大体包括了:

  1. 卷积
  2. Max pooling
  3. Up sampling
  4. Output
    四个模块,模块之间横向链接。那接下来就是构建这个U-Net模型。

    如上图所示,首先是两次卷积的模块定义,
    需要的输入是in_channels, middle_channels, out_channels,其中in和out是卷积的输入和输出,而middle则是因为两次卷积,中间会出现一个中间值(第一次卷积的输出,第二次卷积的输入)。

可以看到定义的方式是sequential,所以不需要定义forward,直接返回self.double_conv这个结果就OK了。

然后是定义降采样的操作的模块。
image
同样,也不需要定义传递方式。

上采样模块的构建方式就如下:


上采样是需要插值的,bilinear就是一种插值的方式,如果使用了bilinear插值的方式,就使用torch内部的upsample函数进行上采样,然后再使用两次卷积(之前定义过的模块)。

比较注意的是up类中,forward函数的写法,它有一个torch.cat([x2,x1], dim = 1),它这个操作把之前的数据也会和现在的采样部分进行叠加,x2就是之前的数据流(及下图红框中的白色部分),x1为流程中走下来的数据流(即流程中的蓝色部分)。
image

最后,就是我们的输出部分:
image

主要是再做一次卷积,这个out_channels主要取决于分类的类别,是二分类那就是2,5分类那就是5。

有了模型块了,接下来就是将他们组装起来:


可以看出来这个下采样的时候通道数是连续的64-128-256-512这样,通道数不断的增加,上采样的时候通道数不断的减少。

运行的顺序也是写成数据流的形式,非常的明了。
这个类的输入也只有in_channel和out_channel,两个通道数,如果你是RGB的图像,那就是in_channel = 3。

接下来就是修改模型。比如要修改output的channel,或者添加额外的输入、输出等等。
首先,尝试修改特定的层数。
image
复制一下之前的unet,看一下它的输出层:
image

然而如何修改它输出的channel数呢?
image
用上述方法,将原本的1通道的out_unet1,就修改成了5通道的out_unet1。(如果原本是设置1通道,而现在想判断5种类别,就可以用这种方式修改),得到的结果如下:
image
可以看到已经从1通道变成了5通道。

如何添加输入呢?

就是需要再forward中添加添加一个add_variable,并且再中间设置好添加的位置。

增加输出也是比较简单,


就是在return的时候增加一个return 值就可以,只需要注意实例化的时候,多一个参数来接住return值。

然后是模型的保存和读取
PyTorch存储模型主要采用pkl,pt,pth三种格式。保存有两种模式:单卡和多卡,也有两种类型:保存整个模型和保存模型的权重。
首先看一下模型的权重(以字典的方式,将模型每一层的权重存起来)在哪里
image
结果如下:
image

首先是CPU模式或者单卡模式下保存模型的方式,
image
然后是用CPU或者单卡模式保存模型权重的方式,
image

在多卡保存的时候,不建议存储整个模型,因为保存模型时会保存模型的GPU_id等信息,这些在读取后可能会因为训练环境的不同而产生差异(比如之前是cuda1,2训练,现在用cuda2,3训练;或者多卡保存单卡加载等等情况)。

浙ICP备19012682号