DL-FWI内训Day4
发布于 2023717|遵循 CC BY-NC-SA 4.0 许可

今日内训主要研习理解FCNVMB,一种基于FCN(fully convolutional networks)的VMB(velocity-model build)方法。

FCNVMB

FCN指的是全卷积网络,其具体定义可参考StackExchange的解释。与传统CNN有所不同的是,FCN的所有操作均为卷积计算,而前者的全连接层中并不包含卷积操作。该网络在图像语义分割中有着广泛应用。

FCN

虽然这篇文章的作者强调该算法属于FCN引导的VMB,但在代码层面上该算法更类似于UNet结构——一种FCN改进后的变体。

UNet

网络分析

如前文所提,FCNVMB可以理解为UNet网络架构下的FWI。与UNet相同,FCNVMB会分别经历图像下采样(编码器)和上采样(解码器)两个步骤。

下采样主要用于提取图像的深层特征;而上采样过程中,通过与对应的浅层特征图像拼接,更好地融合了图像的浅层特征和深层特征两个方面。其中,浅层特征图像更倾向于表达例如点、线、边缘轮廓等基本特征单元,蕴含的空间信息更多;而深层特征图更倾向于表达图像的语义信息,蕴含的空间信息更少,语义特征更多。

FCNVMB网络结构

编码器

下采样过程中,每一轮都会经历两轮红色箭头操作,不改变图像尺寸;并在最后经历一次紫色箭头操作,使得图像尺寸缩小为原来的一半。

红色箭头包括了三样操作:kernal size = 3 * 3的卷积计算、批归一化操作BN以及ReLU激活函数。

紫色箭头是一次size = 2 * 2的最大池化操作,用于缩小图像尺寸,挖掘图像深层语义信息。

解码器

红色箭头同上所述,不改变特征图像尺寸的同时,对特征进行重整合。

黄色箭头为kernel size = 2 * 2的反卷积操作,使得图像尺寸扩大为原本的两倍。

在每次黄色箭头操作后,都需要将图像对应的浅层特征图像拼接到深层特征图像后,融合特征信息,增加图像的通道数。

代码实现

函数封装

分析了FCNVMB的算法原理后,我们可将图像处理过程封装为3个函数:

  1. 卷积组合操作:

    python
    复制代码
    1class unetConv2(nn.Module): 2 def __init__(self, in_size, out_size, is_batchnorm): # in/out_size分别为输入输出的通道数 3 ''' 4 Convolution with two basic operations 5 [Affiliated with FCNVMB] 6 7 :param in_size: Number of channels of input 8 :param out_size: Number of channels of output 9 :param is_batchnorm: Whether to use BN 10 ''' 11 super(unetConv2, self).__init__() 12 if is_batchnorm: 13 self.conv1 = nn.Sequential(nn.Conv2d(in_size, out_size, 3, 1, 1), 14 nn.BatchNorm2d(out_size), 15 nn.ReLU(inplace=True), ) 16 self.conv2 = nn.Sequential(nn.Conv2d(out_size, out_size, 3, 1, 1), 17 nn.BatchNorm2d(out_size), 18 nn.ReLU(inplace=True), ) 19 else: 20 self.conv1 = nn.Sequential(nn.Conv2d(in_size, out_size, 3, 1, 1), 21 nn.ReLU(inplace=True), ) 22 self.conv2 = nn.Sequential(nn.Conv2d(out_size, out_size, 3, 1, 1), 23 nn.ReLU(inplace=True), ) 24 25 def forward(self, inputs): 26 ''' 27 28 :param inputs: Input Image 29 :return: 30 ''' 31 outputs = self.conv1(inputs) 32 outputs = self.conv2(outputs) 33 return outputs
  2. 下采样操作:

    python
    复制代码
    1class unetDown(nn.Module): 2 def __init__(self, in_size, out_size, is_batchnorm): 3 ''' 4 Downsampling Unit 5 [Affiliated with FCNVMB] 6 7 :param in_size: Number of channels of input 8 :param out_size: Number of channels of output 9 :param is_batchnorm: Whether to use BN 10 ''' 11 super(unetDown, self).__init__() 12 self.conv = unetConv2(in_size, out_size, is_batchnorm) 13 self.down = nn.MaxPool2d(2, 2, ceil_mode=True) 14 15 def forward(self, inputs): 16 ''' 17 18 :param inputs: Input Image 19 :return: 20 ''' 21 outputs = self.conv(inputs) 22 outputs = self.down(outputs) 23 return outputs
  3. 上采样操作:

    python
    复制代码
    1class unetUp(nn.Module): 2 def __init__(self, in_size, out_size, is_deconv): 3 ''' 4 Upsampling Unit 5 [Affiliated with FCNVMB] 6 7 :param in_size: Number of channels of input 8 :param out_size: Number of channels of output 9 :param is_deconv: Whether to use deconvolution 10 ''' 11 super(unetUp, self).__init__() 12 self.conv = unetConv2(in_size, out_size, True) 13 # Transposed convolution 14 if is_deconv: 15 self.up = nn.ConvTranspose2d(in_size, out_size, kernel_size=2, stride=2) 16 else: 17 self.up = nn.UpsamplingBilinear2d(scale_factor=2) 18 19 def forward(self, inputs1, inputs2): 20 ''' 21 22 :param inputs1: Layer of the selected coding area via skip connection 23 :param inputs2: Current network layer based on network flows 24 :return: 25 ''' 26 outputs2 = self.up(inputs2) 27 offset1 = (outputs2.size()[2] - inputs1.size()[2]) 28 offset2 = (outputs2.size()[3] - inputs1.size()[3]) 29 padding = [offset2 // 2, (offset2 + 1) // 2, offset1 // 2, (offset1 + 1) // 2] 30 31 # Skip and concatenate 32 outputs1 = F.pad(inputs1, padding) 33 return self.conv(torch.cat([outputs1, outputs2], 1))

匹配尺寸

上述的封装函数unetUp中,在拼接浅层特征和深层特征图像时两者的尺寸并不一致,而是相差一倍。为此,需要对inputs1(浅层特征图像)进行pad操作进行图像尺寸匹配。

网络类

经过函数封装后,FCNVMB类可以简写如下:

python
复制代码
1class FCNVMB(nn.Module): 2 def __init__(self, n_classes, in_channels, is_deconv, is_batchnorm): 3 ''' 4 Network architecture of FCNVMB 5 6 :param n_classes: Number of channels of output (any single decoder) 7 :param in_channels: Number of channels of network input 8 :param is_deconv: Whether to use deconvolution 9 :param is_batchnorm: Whether to use BN 10 ''' 11 super(FCNVMB, self).__init__() 12 self.is_deconv = is_deconv 13 self.in_channels = in_channels 14 self.is_batchnorm = is_batchnorm 15 self.n_classes = n_classes 16 17 filters = [64, 128, 256, 512, 1024] 18 19 self.down1 = unetDown(self.in_channels, filters[0], self.is_batchnorm) 20 21 self.down2 = unetDown(filters[0], filters[1], self.is_batchnorm) 22 self.down3 = unetDown(filters[1], filters[2], self.is_batchnorm) 23 self.down4 = unetDown(filters[2], filters[3], self.is_batchnorm) 24 self.center = unetConv2(filters[3], filters[4], self.is_batchnorm) 25 self.up4 = unetUp(filters[4], filters[3], self.is_deconv) 26 self.up3 = unetUp(filters[3], filters[2], self.is_deconv) 27 self.up2 = unetUp(filters[2], filters[1], self.is_deconv) 28 self.up1 = unetUp(filters[1], filters[0], self.is_deconv) 29 self.final = nn.Conv2d(filters[0], self.n_classes, 1) 30 31 def forward(self, inputs, label_dsp_dim): 32 ''' 33 34 :param inputs: Input Image 35 :param label_dsp_dim: Size of the network output image (velocity model size) 36 :return: 37 ''' 38 down1 = self.down1(inputs) 39 down2 = self.down2(down1) 40 down3 = self.down3(down2) 41 down4 = self.down4(down3) 42 center = self.center(down4) 43 up4 = self.up4(down4, center) 44 up3 = self.up3(down3, up4) 45 up2 = self.up2(down2, up3) 46 up1 = self.up1(down1, up2) 47 up1 = up1[:, :, 1:1 + label_dsp_dim[0], 1:1 + label_dsp_dim[1]].contiguous() 48 49 return self.final(up1)

异同比较

主要对比Day 3介绍的InversionNet。

相同点

  1. 无论是FCNVMB还是InversionNet,它们都是单一的端到端深度网络并没有利用更多的物理含义。
  2. 都采用了编码器-解码器的架构。
  3. 都是利用叠前多炮数据的不同炮集直接投入训练,并未处理。

不同点

  1. InversionNet在编码的过程中最终将图像压缩为完全的一维向量,抛弃了空间关联性;而FCNVMB在压缩后仍保留了25 * 19的空间尺寸关联。
  2. FCNVMB面向SEG数据,InversionNet面向部分OpenFWI的数据。因为OpenFWI数据的特点,InversionNet有非常明显的高度降维部分。
  3. FCNVMB使用了迁移学习的训练手段,后者InversionNet是单一的训练思想。
  4. FCNVMB采用了包含skip connection的UNet的架构,而InversionNet是单一的CNN架构。
Comments