一.选择原因。

D-LinkNet(Densely Connected Link Network)是一种用于图像分割的深度学习模型。它是基于DenseNet模型的改进,通过引入链接模块和下采样模块来提高分割性能。

D-LinkNet的核心思想是在DenseNet的基础上引入链接模块和下采样模块。链接模块用于捕捉不同尺度的特征,它将不同层的特征图进行连接,从而增强了特征提取能力。下采样模块用于减小特征图的尺寸,同时保留更多的语义信息。这两个模块结合起来可以有效地提高分割性能。

它的训练过程包括两个阶段:首先,使用卷积神经网络对图像进行特征提取,然后使用反卷积操作将特征图恢复到原始图像大小,并进行像素级别的分类。在这个过程中,D-LinkNet使用了多个链接模块和下采样模块,以提高特征提取和分割性能。

它也在各种图像分割任务中取得了显著的成果,包括医学影像分割、道路场景分割等。它具有较高的准确率和鲁棒性,并且能够处理不同尺度和复杂场景下的图像分割问题。因此,D-LinkNet成为了图像分割领域的一个重要研究方向。

二.模型分析

下面是DlinkNet的模型结构

D-linkNet——一个小小的尝试_卷积

D-LinkNet使用在ImageNet数据集上预先训练的ResNet34作为编码器。ResNet34最初是为中分辨率图像大小为256×256的分类任务而设计的,但在这个挑战中,任务是从大小为1024×1024的高分辨率卫星图像分割道路。考虑到道路的狭窄性、连通性、复杂性和跨度大,在保留详细信息的同时,增加网络中心部分特征点的接受域是很重要的。利用池化层可以成倍增加特征点的接收域,但可能会降低中心特征图的分辨率,降低空间信息。一些最先进的深度学习模型[21,25,26,16]表明,膨胀卷积层是池化层的理想选择。D-LinkNet使用了一些膨胀卷积层,在中心部分跳跃连接。

膨胀卷积可以以级联方式堆叠。如果堆叠的膨胀卷积层的膨胀率分别为1、2、4、8、16,则每一层的接受域为3、7、15、31、63。编码器部分(RseNet34)有5个下采样层,如果一个大小为1024×1024的图像通过编码器部分,输出的feature map为大小为32×32。在这种情况下,D-LinkNet在中心部分使用膨胀的卷积层,膨胀率为1,2,4,8,因此最后中间的层上的特征点将在第一个中心特征图上看到31×31个点,覆盖第一个中心特征图的主要部分。但是,D-LinkNet利用了多分辨率的特性,D-LinkNet的中心部分可以看作是如图2所示的并行模式。

三.模型训练

预训练的编码器

迁移学习是一种有效的计算机视觉方法,特别是在训练图像数量有限的情况下。使用ImageNet预训练模型作为网络的编码器是语义分割领域广泛使用的一种方法。在DeepGlobe Road extraction Challenge中,我们发现迁移学习可以加速我们的网络收敛,使其在几乎不需要额外成本的情况下具有更好的性能。

核心代码

import torch
import torch.nn as nn
import torch.optim as optim
#
定义
dlink-net
神经网络模型
class DLinkNet(nn.Module):
    def __init__(self):
        super(DLinkNet, self).__init__()
        #定义网络结构、层数和激活函数等
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
        self.relu1 = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
        self.relu2 = nn.ReLU(inplace=True)
        self.conv3 = nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)
        self.relu3 = nn.ReLU(inplace=True)
        self.conv4 = nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)
        self.relu4 = nn.ReLU(inplace=True)
        self.conv5 = nn.Conv2d(512, 1024, kernel_size=3, stride=1, padding=1)
        self.relu5 = nn.ReLU(inplace=True)
        self.conv6 = nn.Conv2d(1024, 2048, kernel_size=3, stride=1, padding=1)
        self.relu6 = nn.ReLU(inplace=True)
        self.upconv6 = nn.ConvTranspose2d(2048, 1024, kernel_size=4, stride=2, padding=1)
        self.deconv6 = nn.Conv2d(2048, 1024, kernel_size=3, stride=1, padding=1)
        self.upconv5 = nn.ConvTranspose2d(1024, 512, kernel_size=4, stride=2, padding=1)

        self.deconv5 = nn.Conv2d(1024, 512, kernel_size=3, stride=1, padding=1)
        self.upconv4 = nn.ConvTranspose2d(512, 256, kernel_size=4, stride=2, padding=1)
        self.deconv4 = nn.Conv2d(512, 256, kernel_size=3, stride=1, padding=1)
        self.upconv3 = nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1)
        self.deconv3 = nn.Conv2d(256, 128, kernel_size=3, stride=1, padding=1)
        self.upconv2 = nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1)
        self.deconv2 = nn.Conv2d(128, 64, kernel_size=3, stride=1, padding=1)
        self.upconv1 = nn.ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1)
        self.deconv1 = nn.Conv2d(64, 32, kernel_size=3, stride=1, padding=1)
        self.conv7 = nn.Conv2d(32, 1, kernel_size=3, stride=1, padding=1)

    def forward(self,x):
        #前向传播过程
        conv1_out = self.conv1(x)
        relu1_out = self.relu1(conv1_out)
        conv2_out = self.conv2(relu1_out)
        relu2_out = self.relu2(conv2_out)
        conv3_out = self.conv3(relu2_out)
        relu3_out = self.relu3(conv3_out)
        conv4_out = self.conv4(relu3_out)
        relu4_out = self.relu4(conv4_out)
        conv5_out = self.conv5(relu4_out)
        relu5_out = self.relu5(conv5_out)
        conv6_out = self.conv6(relu5_out)
        relu6_out = self.relu6(conv6_out)

        upconv6_out = self.upconv6(relu6_out)
        deconv6_out = torch.cat((upconv6_out,self.deconv6(conv5_out)),dim=1)

        upconv5_out = self.upconv5(deconv6_out)
        deconv5_out = torch.cat((upconv5_out,self.deconv5(conv4_out)),dim=1)

        upconv4_out = self.upconv4(deconv5_out)
        deconv4_out = torch.cat((upconv4_out,self.deconv4(conv3_out)),dim=1)

        upconv3_out = self.upconv3(deconv4_out)
        deconv3_out = torch.cat((upconv3_out,self.deconv3(conv2_out)),dim=1)

        upconv2_out = self.upconv2(deconv3_out)
        deconv2_out = torch.cat((upconv2_out,self.deconv2(conv1_out)),dim=1)

        upconv1_out = self.upconv1(deconv2_out)
        deconv1_out = torch.cat((upconv1_out,x),dim=1)

        conv7_out = self.conv7(deconv1_out)
        return conv7_out
#准备数据集
train_data = ...
train_labels = ...
test_data = ...
test_labels = ...

#定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(dlink_net.parameters(), lr=0.001)

#训练模型
for epoch in range(num_epochs):
    running_loss = 0.0
    for i in range(num_batches):
    	#获取数据和标签
        inputs = train_data[i*batch_size:(i+1)*batch_size]
        labels = train_labels[i*batch_size:(i+1)*batch_size]
        
        #前向传播
        outputs = dlink_net(inputs)
        
        #计算损失并反向传播
        loss = criterion(outputs, labels)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        #统计损失
        running_loss += loss.item()
    
    #输出训练结果
    print('Epoch [%d/%d], Loss: %.4f' % (epoch+1,num_epochs,running_loss/num_batches))
#测试模型
with torch.no_grad():
    correct = 0
    total = 0
    for i in range(num_test_batches):
        #获取数据和标签
        inputs = test_data[i*batch_size:(i+1)*batch_size]
        labels = test_labels[i*batch_size:(i+1)*batch_size]
        #前向传播
        outputs = dlink_net(inputs)
        #计算准确率
        predicted = outputs.round()
        total += labels.size(0) * labels.size(1) * labels.size(2) * labels.size(3)
        correct += (predicted == labels).sum().item()
    #输出测试结果
    print('Accuracy of the network on the test images: %d %%' % (100 * correct / total))

四.模型测试

我在Cityscapes数据集上测试了将数据集表示为二值分割问题,其中道路标记为前景。

其他物体标记为背景。

测试效果如下:

D-linkNet——一个小小的尝试_卷积_02

五.总结收获

在此次比赛中,我通过不断更新模型结构,调整数据集的处理方式,最后还通过使用Intel提供的模型训练平台和分析工具,得到了比较好的分割效果。非常感谢intel举办此次比赛,能够让我学到了很多东西,尤其是Intel架构的学习。我在使用Intel VTune Profiler等工具对Dlink模型进行性能分析和调优,找出性能瓶颈并进行优化。最终才取得了更好的结果。