最简单的端到端结构:基于编码器-解码器范式,将原始图像直接输入到深度学习模型,不需要手工设计特征,而是让深度学习模型自动学习图像的特征。
否定空间信息:本文否定了空间信息的重要性,因此在设计网络结构时编码器将图像数据降维成(512,1,1)的格式,丧失了空间信息。
采用UNet结构:基于编码器-解码器范式,编码过程由卷积操作和池化操作实现,解码过程由卷积操作与反卷积操作组成。同时还添加了skip connection操作。
skip connection:下采样部分会逐渐丢失特征与空间信息,而上采样会逐渐恢复图像尺寸,在上采样操作添加skip connection操作,更充分地融合浅层特征和深层特征,将丢失的空间信息重新插入到特征中,也会更加充分利用空间信息。
迁移学习:迁移学习是一种机器学习的方法,指的是一个预训练的模型被重新用在另一个任务中。
盐数据有两种不同的模拟数据,在训练时先利用数据量较大的模拟速度模型正演得到模拟地震数据,并投入网络得到Pre-trained model。之后使用数据量较小的数据做同样的操作,得到Re-trained model。
由三个基本部分封装成一个卷积操作:
批归一化:在网络中的每个隐藏层对输入进行归一化处理,使得每个神经元的输入分布更稳定,从而有助于网络的收敛和训练效果。
LeakyReLU:在Leaky ReLU中,当输入为负值时,它会提供一个小的负斜率,从而允许一些负值通过,避免了梯度消失的问题。因此,Leaky ReLU在处理一些具有稀疏数据的任务时表现更好。
代码实现:
class ConvBlock(nn.Module):
def __init__(self, in_fea, out_fea, kernel_size=3, stride=1, padding=1, norm='bn', relu_slop=0.2, dropout=None):
super(ConvBlock,self).__init__()
layers = [nn.Conv2d(in_channels=in_fea, out_channels=out_fea, kernel_size=kernel_size, stride=stride, padding=padding)]
if norm in NORM_LAYERS:
layers.append(NORM_LAYERS[norm](out_fea))
layers.append(nn.LeakyReLU(relu_slop, inplace=True))
if dropout:
layers.append(nn.Dropout2d(0.8))
self.layers = nn.Sequential(*layers)
def forward(self, x):
return self.layers(x)
类似于卷积操作,由反卷积层、 批归一化和LeakyReLU三部分组成。反卷积可以看作卷积的逆过程,可以用于上采样。
class DeconvBlock(nn.Module):
def __init__(self, in_fea, out_fea, kernel_size=2, stride=2, padding=0, output_padding=0, norm='bn'):
super(DeconvBlock, self).__init__()
layers = [nn.ConvTranspose2d(in_channels=in_fea, out_channels=out_fea, kernel_size=kernel_size, stride=stride, padding=padding, output_padding=output_padding)]
if norm in NORM_LAYERS:
layers.append(NORM_LAYERS[norm](out_fea))
layers.append(nn.LeakyReLU(0.2, inplace=True))
self.layers = nn.Sequential(*layers)
def forward(self, x):
return self.layers(x)
原数据的尺寸是(6,1000,7),首先在时间维度上降维,将图像下采样为方形(128,32,32),接着两个方向同时降维,变成(512,1,1)。
每次下采样,需要两个卷积操作,第一个改变了图像尺寸,第二个没有改变(存疑)。
self.convblock2_1 = ConvBlock(dim1, dim2, kernel_size=(3, 1), stride=(2, 1), padding=(1, 0))
self.convblock2_2 = ConvBlock(dim2, dim2, kernel_size=(3, 1), padding=(1, 0))
将(512,1,1)的高维向量解码,使用反卷积上采样,两个方向同时升维,最后通过一个Tanh激活函数的卷积操作得到最终的结果。
self.deconv2_1 = DeconvBlock(dim5, dim4, kernel_size=4, stride=2, padding=1)
self.deconv2_2 = ConvBlock(dim4, dim4)
每次上采样,先后需要一个反卷积操作和卷积操作,第一个改变了图像尺寸,第二个没有。
该网络的下采样由两个部分组成:
class unetConv2(nn.Module):
def __init__(self, in_size, out_size, is_batchnorm):
'''
Convolution with two basic operations
:param in_size: Number of channels of input
:param out_size: Number of channels of output
:param is_batchnorm: Whether to use BN
'''
super(unetConv2, self).__init__()
if is_batchnorm:
self.conv1 = nn.Sequential(nn.Conv2d(in_size, out_size, 3, 1, 1),
nn.BatchNorm2d(out_size),
nn.ReLU(inplace=True), )
self.conv2 = nn.Sequential(nn.Conv2d(out_size, out_size, 3, 1, 1),
nn.BatchNorm2d(out_size),
nn.ReLU(inplace=True), )
else:
self.conv1 = nn.Sequential(nn.Conv2d(in_size, out_size, 3, 1, 1),
nn.ReLU(inplace=True), )
self.conv2 = nn.Sequential(nn.Conv2d(out_size, out_size, 3, 1, 1),
nn.ReLU(inplace=True), )
def forward(self, inputs):
'''
:param inputs: Input Image
:return:
'''
outputs = self.conv1(inputs)
outputs = self.conv2(outputs)
return outputs
self.down = nn.MaxPool2d(2, 2, ceil_mode=True)
上采样有input1、input2和output1、output2,最后返回output1、output2连接后并卷积两次的数据
class unetUp(nn.Module):
def __init__(self, in_size, out_size, is_deconv):
'''
Upsampling Unit
[Affiliated with FCNVMB]
:param in_size: Number of channels of input
:param out_size: Number of channels of output
:param is_deconv: Whether to use deconvolution
'''
super(unetUp, self).__init__()
self.conv = unetConv2(in_size, out_size, True)
# Transposed convolution
if is_deconv:
self.up = nn.ConvTranspose2d(in_size, out_size, kernel_size=2, stride=2)
else:
self.up = nn.UpsamplingBilinear2d(scale_factor=2)
def forward(self, inputs1, inputs2):
'''
:param inputs1: Layer of the selected coding area via skip connection
:param inputs2: Current network layer based on network flows
:return:
'''
outputs2 = self.up(inputs2)
offset1 = (outputs2.size()[2] - inputs1.size()[2])
offset2 = (outputs2.size()[3] - inputs1.size()[3])
padding = [offset2 // 2, (offset2 + 1) // 2, offset1 // 2, (offset1 + 1) // 2]
# Skip and concatenate
outputs1 = F.pad(inputs1, padding)
return self.conv(torch.cat([outputs1, outputs2], 1))