憨批的语义分割重制版11——Keras 搭建自己的HRNetV2语义分割平台

憨批的语义分割重制版11——Keras 搭建自己的HRNetV2语义分割平台

  • 学习前言
  • 什么是HRNetV2模型
  • 代码下载
  • HRNetV2实现思路
    • 一、预测部分
      • 1、主干网络介绍
        • a、Section-1
        • b、Section-2
        • c、Section-3
        • d、Section-4
      • 2、特征整合部分
      • 3、利用特征获得预测结果
    • 二、训练部分
      • 1、训练文件详解
      • 2、LOSS解析
  • 训练自己的HRNetV2模型
    • 一、数据集的准备
    • 二、数据集的处理
    • 三、开始网络训练
    • 四、训练结果预测

学习前言

最近学了一下HRnet,代码真的好难看懂,还只有Pytorch版本的,Keras复现很有难度,作者写的逻辑很厉害,只能慢慢攻破啦!
在这里插入图片描述

什么是HRNetV2模型

传统的卷积神经网络模型是自上而下不断进行特征提取的,如VGG、Mobilenet、Resnet系列等,VGG网络中存在5个步长为(2, 2)最大池化,Mobilenet网络中存在5个步长为(2, 2)的逐层卷积,Resnet网络中存在5个步长为(2, 2)的普通卷积。

这些网络常常存在多个下采样的过程,输入进来的图片利用卷积或者最大池化进行高和宽的压缩。在特征提取的过程中,我们会获得不同形状的特征层。传统的卷积神经网络模型仅仅会有自上而下的特征提取,而HRNet则改变了传统卷积神经网络模型的工作模式,使得网络在工作时刻保持高分辨率特征图。通过下采样以及上采样,在网络进行特征提取时融合不同形状的特征。

最开始的HRNet被用于人体的姿态检测,该论文发表于2019年的CVPR上。在原来HRNet的基础上,官方修改出HRNetV2并且让他可以适应不同任务的需要,语义分割就是其中之一。HRNet提取出来的特征极其丰富,包含各种的分辨率,理论上可以适应不同的CV需求(目标检测、语义分割、实例分割等),本文以HRNetv2做分割为例,进行介绍。
在这里插入图片描述
整个HRNetV2由三部分组成,分别是主干部分特征整合部分预测头部分。如上图所示,灰色方框的左半部分就是主干部分,灰色部分与右半部分就是分割头部分。

在主干部分,HRNetV2会在特征提取的过程中同时进行下采样与上采样,在这个过程里会获得不同形状特征图并进行特征融合

在特征整合部分,HRNetV2会将获取到的所有特征图进行特征融合,将宽高较小的特征图网上进行上采样,然后与最大的特征图进行特征融合,然后再利用1个卷积+标准化+激活函数进行特征整合。

在预测头部分,HRNetV2会利用一个卷积核为(1, 1)的卷积调整通道数为num_classes,利用resize进行上采样使得最终输出层,宽高和输入图片一样。。

代码下载

Github源码下载地址为:
https://github.com/bubbliiiing/hrnet-keras

复制该路径到地址栏跳转。

HRNetV2实现思路

一、预测部分

1、主干网络介绍

在这里插入图片描述
上图是官方给出的HRNetV2用于分割的示意图,但其实这个图里面遗漏了一些细节。遗漏的细节容易引起误会。

上图将整个HRNetV2的主干分为四个部分,为了方便理解,我们将其命名为Section-1,Section-2,Section-3,Section-4.

a、Section-1

在这里插入图片描述
Section-1是第一部分,用于进行初步的特征提取,在图中对应了最左边的四个方块。在实际代码中,图片处理的过程不仅仅包含这四个方块。

如果将该部分进行详细绘制,其本质上应该是这样的。
在这里插入图片描述
对于输入进来的图片,HRNetV2还会使用两次步长为(2, 2)、卷积核大小为(3, 3)、通道为64的卷积对输入图片进行高和宽的压缩与特征提取。

假设输入进来的图片是[480, 480, 3]的,在经过两次步长为(2, 2)、卷积核大小为(3, 3)、通道为64的卷积后,获得的特征层为[120, 120, 64]。

此时再进行四次bottleneck_Block。这里用到的bottleneck_Block,其实就是Resnet系列里面用到的残差卷积,主要用在Resnet50、Resnet101里面。

bottleneck_Block的结构如下图所示,bottleneck_Block可以分为两个部分,左边部分为主干部分,存在两次卷积、标准化、激活函数和一次卷积、标准化;右边部分为残差边部分,不经过处理或者经过少量处理直接与输出相接
在这里插入图片描述
由图可知,结构分为两个情况:
如果不发生高宽以及通道的变化,则bottleneck_Block如左图所示,此时残差边不经过任何处理便直接与主干部分相接,输入特征层和输出特征层的shape是相同的。

如果发生高宽以及通道的变化,则bottleneck_Block如右图所示,此时残差边经过少量处理便直接和主干相接,可以改变输出特征层的宽高和通道数。

在经过四次bottleneck_Block后,我们获得的特征层的shape为[120, 120, 256]。

总体上,Section-1的构建代码如下:

def bottleneck_Block(input, out_filters, strides=(1, 1), with_conv_shortcut=False, name=""):
    expansion = 4
    de_filters = int(out_filters / expansion)

    x = Conv2D(de_filters, 1, use_bias=False, kernel_initializer='he_normal', name=name+'.conv1')(input)
    x = BatchNormalization(epsilon=1e-5, name=name+'.bn1')(x)
    x = Activation('relu')(x)

    x = Conv2D(de_filters, 3, strides=strides, padding='same', use_bias=False, kernel_initializer='he_normal', name=name+'.conv2')(x)
    x = BatchNormalization(epsilon=1e-5, name=name+'.bn2')(x)
    x = Activation('relu')(x)

    x = Conv2D(out_filters, 1, use_bias=False, kernel_initializer='he_normal', name=name+'.conv3')(x)
    x = BatchNormalization(epsilon=1e-5, name=name+'.bn3')(x)

    if with_conv_shortcut:
        residual = Conv2D(out_filters, 1, strides=strides, use_bias=False, kernel_initializer='he_normal', name=name+'.downsample.0')(input)
        residual = BatchNormalization(epsilon=1e-5, name=name+'.downsample.1')(residual)
        x = add([x, residual])
    else:
        x = add([x, input])

    x = Activation('relu')(x)
    return x

def stem_net(input):
    x = ZeroPadding2D(((1, 1),(1, 1)))(input)
    x = Conv2D(64, 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="conv1")(x)
    x = BatchNormalization(epsilon=1e-5, name="bn1")(x)
    x = Activation('relu')(x)

    x = ZeroPadding2D(((1, 1),(1, 1)))(x)
    x = Conv2D(64, 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="conv2")(x)
    x = BatchNormalization(epsilon=1e-5, name="bn2")(x)
    x = Activation('relu')(x)

    x = bottleneck_Block(x, 256, with_conv_shortcut=True, name="layer1.0")
    x = bottleneck_Block(x, 256, with_conv_shortcut=False, name="layer1.1")
    x = bottleneck_Block(x, 256, with_conv_shortcut=False, name="layer1.2")
    x = bottleneck_Block(x, 256, with_conv_shortcut=False, name="layer1.3")
    return x

b、Section-2

在这里插入图片描述
Section-2是第二部分,用于进行进一步的特征提取,对应图中中间靠左的12个方块。如果将该部分进行详细绘制,其本质上应该是这样的。
在这里插入图片描述
对于输入进来的特征层,HRNetV2分别利用两个卷积进行处理,一个卷积的步长为(1, 1)、通道为32,一个卷积的步长为(2, 2)、通道为64。此时,我们获得一个[120,120, 32]的特征层和一个[60, 60, 64]的特征层。

之后对这两个特征层,分别进行四次basic_Block的处理。这里用到的basic_Block,也是Resnet系列里面用到的残差卷积,主要用在Resnet18、Resnet32里面。

basic_Block的结构如下图所示,basic_Block可以分为两个部分,左边部分为主干部分,存在一次卷积、标准化、激活函数和一次卷积、标准化;右边部分为残差边部分,不经过处理或者经过少量处理直接与输出相接
在这里插入图片描述
由图可知,结构分为两个情况:
如果不发生高宽以及通道的变化,则basic_Block如左图所示,此时残差边不经过任何处理便直接与主干部分相接,输入特征层和输出特征层的shape是相同的。

如果发生高宽以及通道的变化,则basic_Block如右图所示,此时残差边经过少量处理便直接和主干相接,可以改变输出特征层的宽高和通道数。

在分别经过进行四次basic_Block的处理后,我们依然获得一个[120,120, 32]的特征层和一个[60, 60, 64]的特征层。

之后对[120,120, 32]的特征层进行下采样,与[60, 60, 64]的特征层相加;对[60, 60, 64]的特征层进行上采样,与[120,120, 32]相加。

总体上,Section-2的构建代码如下:

def transition_layer1(x, out_filters_list=[32, 64]):
    x0 = Conv2D(out_filters_list[0], 3, padding='same', use_bias=False, kernel_initializer='he_normal', name="transition1.0.0")(x)
    x0 = BatchNormalization(epsilon=1e-5, name="transition1.0.1")(x0)
    x0 = Activation('relu')(x0)

    x1 = ZeroPadding2D(((1, 1),(1, 1)))(x)
    x1 = Conv2D(out_filters_list[1], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name = "transition1.1.0.0")(x1)
    x1 = BatchNormalization(epsilon=1e-5, name="transition1.1.0.1")(x1)
    x1 = Activation('relu')(x1)
    return [x0, x1]

def make_stage2(x_list, out_filters_list=[32, 64]):
    x0, x1 = x_list

    x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage2.0.branches.0.0")
    x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage2.0.branches.0.1")
    x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage2.0.branches.0.2")
    x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage2.0.branches.0.3")

    x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage2.0.branches.1.0")
    x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage2.0.branches.1.1")
    x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage2.0.branches.1.2")
    x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage2.0.branches.1.3")

    x0_0 = x0
    x0_1 = Conv2D(out_filters_list[0], 1, use_bias=False, kernel_initializer='he_normal', name="stage2.0.fuse_layers.0.1.0")(x1)
    x0_1 = BatchNormalization(epsilon=1e-5, name="stage2.0.fuse_layers.0.1.1")(x0_1)
    x0_1 = UpsampleLike(name="Upsample1")([x0_1, x0_0])
    x0_out = add([x0_0, x0_1])
    x0_out = Activation('relu')(x0_out)

    x1_0 = ZeroPadding2D(((1, 1),(1, 1)))(x0)
    x1_0 = Conv2D(out_filters_list[1], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage2.0.fuse_layers.1.0.0.0")(x1_0)
    x1_0 = BatchNormalization(epsilon=1e-5, name="stage2.0.fuse_layers.1.0.0.1")(x1_0)
    x1_1 = x1
    x1_out = add([x1_0, x1_1])
    x1_out = Activation('relu')(x1_out)
    
    return x0_out, x1_out

c、Section-3

在这里插入图片描述
Section-3是第三部分,用于进行进一步的特征提取,对应图中中间靠右的18个方块。如果将该部分进行详细绘制,其本质上应该是这样的。
在这里插入图片描述
对于输入进来的特征层,HRNetV2利用一个步长为(2, 2)、通道为128的卷积对[60, 60, 64]进行处理。此时,我们获得一个[30, 30, 128]的特征层。加上初始的两个特征层,我们存在三个特征层。

之后对这三个特征层,分别进行四次basic_Block的处理。这里用到的basic_Block,与Stage-2里面一样。然后利用[128, 128, 32]、[60, 60, 64]、[30, 30, 128]三个特征层,利用上采样和下采样建立密集连接,完成高度的特征融合。这个过程需要重复四次。

总体上,Section-3的构建代码如下:

def transition_layer2(x, out_filters_list=[32, 64, 128]):
    x2 = ZeroPadding2D(((1, 1),(1, 1)))(x[1])
    x2 = Conv2D(out_filters_list[2], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="transition2.2.0.0")(x2)
    x2 = BatchNormalization(epsilon=1e-5, name="transition2.2.0.1")(x2)
    x2 = Activation('relu')(x2)
    return [x[0], x[1], x2]

def make_stage3(x_list, num_modules, out_filters_list=[32, 64, 128]):
    for i in range(num_modules):
        x0, x1, x2 = x_list
        
        x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.0.0")
        x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.0.1")
        x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.0.2")
        x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.0.3")

        x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.1.0")
        x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.1.1")
        x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.1.2")
        x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.1.3")

        x2 = basic_Block(x2, out_filters_list[2], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.2.0")
        x2 = basic_Block(x2, out_filters_list[2], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.2.1")
        x2 = basic_Block(x2, out_filters_list[2], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.2.2")
        x2 = basic_Block(x2, out_filters_list[2], with_conv_shortcut=False, name="stage3." + str(i) + ".branches.2.3")

        x0_0 = x0
        x0_1 = Conv2D(out_filters_list[0], 1, use_bias=False, kernel_initializer='he_normal', name="stage3." + str(i) + ".fuse_layers.0.1.0")(x1)
        x0_1 = BatchNormalization(epsilon=1e-5, name="stage3." + str(i) + ".fuse_layers.0.1.1")(x0_1)
        x0_1 = UpsampleLike(name="Upsample." + str(i) + ".2")([x0_1, x0_0])
        x0_2 = Conv2D(out_filters_list[0], 1, use_bias=False, kernel_initializer='he_normal', name="stage3." + str(i) + ".fuse_layers.0.2.0")(x2)
        x0_2 = BatchNormalization(epsilon=1e-5, name="stage3." + str(i) + ".fuse_layers.0.2.1")(x0_2)
        x0_2 = UpsampleLike(name="Upsample." + str(i) + ".3")([x0_2, x0_0])
        x0_out = add([x0_0, x0_1, x0_2])
        x0_out = Activation('relu')(x0_out)

        
        x1_0 = ZeroPadding2D(((1, 1),(1, 1)))(x0)
        x1_0 = Conv2D(out_filters_list[1], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage3." + str(i) + ".fuse_layers.1.0.0.0")(x1_0)
        x1_0 = BatchNormalization(epsilon=1e-5, name="stage3." + str(i) + ".fuse_layers.1.0.0.1")(x1_0)
        x1_1 = x1
        x1_2 = Conv2D(out_filters_list[1], 1, use_bias=False, kernel_initializer='he_normal', name="stage3." + str(i) + ".fuse_layers.1.2.0")(x2)
        x1_2 = BatchNormalization(epsilon=1e-5, name="stage3." + str(i) + ".fuse_layers.1.2.1")(x1_2)
        x1_2 = UpsampleLike(name="Upsample." + str(i) + ".4")([x1_2, x1_1])
        x1_out = add([x1_0, x1_1, x1_2])
        x1_out = Activation('relu')(x1_out)

        x2_0 = ZeroPadding2D(((1, 1),(1, 1)))(x0)
        x2_0 = Conv2D(out_filters_list[0], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage3." + str(i) + ".fuse_layers.2.0.0.0")(x2_0)
        x2_0 = BatchNormalization(epsilon=1e-5, name="stage3." + str(i) + ".fuse_layers.2.0.0.1")(x2_0)
        x2_0 = Activation('relu')(x2_0)
        x2_0 = ZeroPadding2D(((1, 1),(1, 1)))(x2_0)
        x2_0 = Conv2D(out_filters_list[2], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage3." + str(i) + ".fuse_layers.2.0.1.0")(x2_0)
        x2_0 = BatchNormalization(epsilon=1e-5, name="stage3." + str(i) + ".fuse_layers.2.0.1.1")(x2_0)
        x2_1 = ZeroPadding2D(((1, 1),(1, 1)))(x1)
        x2_1 = Conv2D(out_filters_list[2], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage3." + str(i) + ".fuse_layers.2.1.0.0")(x2_1)
        x2_1 = BatchNormalization(epsilon=1e-5, name="stage3." + str(i) + ".fuse_layers.2.1.0.1")(x2_1)
        x2_2 = x2
        x2_out = add([x2_0, x2_1, x2_2])
        x2_out = Activation('relu')(x2_out)
        
        x_list = [x0_out, x1_out, x2_out]

    return x_list

d、Section-4

在这里插入图片描述
Section-4是第四部分,用于进行进一步的特征提取,对应图中中间靠右的24个方块。如果将该部分进行详细绘制,其本质上应该是这样的。
在这里插入图片描述
对于输入进来的特征层,HRNetV2利用一个步长为(2, 2)、通道为256的卷积对[30, 30, 128]进行处理。此时,我们获得一个[15, 15, 256]的特征层。加上初始的三个特征层,我们存在四个特征层。

之后对这四个特征层,分别进行四次basic_Block的处理。这里用到的basic_Block,与Stage-2里面一样。然后利用[128, 128, 32]、[60, 60, 64]、[30, 30, 128]、[15, 15, 256]四个特征层,利用上采样和下采样建立密集连接,完成高度的特征融合。这个过程需要重复三次。

总体上,Section-3的构建代码如下:

def transition_layer3(x, out_filters_list=[32, 64, 128, 256]):
    x3 = ZeroPadding2D(((1, 1),(1, 1)))(x[2])
    x3 = Conv2D(out_filters_list[3], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="transition3.3.0.0")(x3)
    x3 = BatchNormalization(epsilon=1e-5, name="transition3.3.0.1")(x3)
    x3 = Activation('relu')(x3)

    return [x[0], x[1], x[2], x3]

def make_stage4(x_list, num_modules, out_filters_list=[32, 64, 128, 256]):
    for i in range(num_modules):
        x0, x1, x2, x3 = x_list
        
        x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.0.0")
        x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.0.1")
        x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.0.2")
        x0 = basic_Block(x0, out_filters_list[0], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.0.3")

        x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.1.0")
        x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.1.1")
        x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.1.2")
        x1 = basic_Block(x1, out_filters_list[1], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.1.3")

        x2 = basic_Block(x2, out_filters_list[2], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.2.0")
        x2 = basic_Block(x2, out_filters_list[2], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.2.1")
        x2 = basic_Block(x2, out_filters_list[2], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.2.2")
        x2 = basic_Block(x2, out_filters_list[2], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.2.3")

        x3 = basic_Block(x3, out_filters_list[3], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.3.0")
        x3 = basic_Block(x3, out_filters_list[3], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.3.1")
        x3 = basic_Block(x3, out_filters_list[3], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.3.2")
        x3 = basic_Block(x3, out_filters_list[3], with_conv_shortcut=False, name="stage4." + str(i) + ".branches.3.3")

        x0_0 = x0
        x0_1 = Conv2D(out_filters_list[0], 1, use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.0.1.0")(x1)
        x0_1 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.0.1.1")(x0_1)
        x0_1 = UpsampleLike(name="Upsample." + str(i) + ".5")([x0_1, x0_0])
        x0_2 = Conv2D(out_filters_list[0], 1, use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.0.2.0")(x2)
        x0_2 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.0.2.1")(x0_2)
        x0_2 = UpsampleLike(name="Upsample." + str(i) + ".6")([x0_2, x0_0])
        x0_3 = Conv2D(out_filters_list[0], 1, use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.0.3.0")(x3)
        x0_3 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.0.3.1")(x0_3)
        x0_3 = UpsampleLike(name="Upsample." + str(i) + ".7")([x0_3, x0_0])
        x0_out = add([x0_0, x0_1, x0_2, x0_3])
        x0_out = Activation('relu')(x0_out)

        x1_0 = ZeroPadding2D(((1, 1),(1, 1)))(x0)
        x1_0 = Conv2D(out_filters_list[1], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.1.0.0.0")(x1_0)
        x1_0 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.1.0.0.1")(x1_0)
        x1_1 = x1
        x1_2 = Conv2D(out_filters_list[1], 1, use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.1.2.0")(x2)
        x1_2 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.1.2.1")(x1_2)
        x1_2 = UpsampleLike(name="Upsample." + str(i) + ".8")([x1_2, x1_1])
        x1_3 = Conv2D(out_filters_list[1], 1, use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.1.3.0")(x3)
        x1_3 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.1.3.1")(x1_3)
        x1_3 = UpsampleLike(name="Upsample." + str(i) + ".9")([x1_3, x1_1])
        x1_out = add([x1_0, x1_1, x1_2, x1_3])
        x1_out = Activation('relu')(x1_out)

        x2_0 = ZeroPadding2D(((1, 1),(1, 1)))(x0)
        x2_0 = Conv2D(out_filters_list[0], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.2.0.0.0")(x2_0)
        x2_0 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.2.0.0.1")(x2_0)
        x2_0 = Activation('relu')(x2_0)
        x2_0 = ZeroPadding2D(((1, 1),(1, 1)))(x2_0)
        x2_0 = Conv2D(out_filters_list[2], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.2.0.1.0")(x2_0)
        x2_0 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.2.0.1.1")(x2_0)
        x2_1 = ZeroPadding2D(((1, 1),(1, 1)))(x1)
        x2_1 = Conv2D(out_filters_list[2], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.2.1.0.0")(x2_1)
        x2_1 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.2.1.0.1")(x2_1)
        x2_2 = x2
        x2_3 = Conv2D(out_filters_list[2], 1, use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.2.3.0")(x3)
        x2_3 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.2.3.1")(x2_3)
        x2_3 = UpsampleLike(name="Upsample." + str(i) + ".10")([x2_3, x2_2])
        x2_out = add([x2_0, x2_1, x2_2, x2_3])
        x2_out = Activation('relu')(x2_out)
        
        x3_0 = ZeroPadding2D(((1, 1),(1, 1)))(x0)
        x3_0 = Conv2D(out_filters_list[0], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.3.0.0.0")(x3_0)
        x3_0 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.3.0.0.1")(x3_0)
        x3_0 = Activation('relu')(x3_0)
        x3_0 = ZeroPadding2D(((1, 1),(1, 1)))(x3_0)
        x3_0 = Conv2D(out_filters_list[0], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.3.0.1.0")(x3_0)
        x3_0 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.3.0.1.1")(x3_0)
        x3_0 = Activation('relu')(x3_0)
        x3_0 = ZeroPadding2D(((1, 1),(1, 1)))(x3_0)
        x3_0 = Conv2D(out_filters_list[3], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.3.0.2.0")(x3_0)
        x3_0 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.3.0.2.1")(x3_0)
        x3_1 = ZeroPadding2D(((1, 1),(1, 1)))(x1)
        x3_1 = Conv2D(out_filters_list[1], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.3.1.0.0")(x3_1)
        x3_1 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.3.1.0.1")(x3_1)
        x3_1 = Activation('relu')(x3_1)
        x3_1 = ZeroPadding2D(((1, 1),(1, 1)))(x3_1)
        x3_1 = Conv2D(out_filters_list[3], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.3.1.1.0")(x3_1)
        x3_1 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.3.1.1.1")(x3_1)
        x3_2 = ZeroPadding2D(((1, 1),(1, 1)))(x2)
        x3_2 = Conv2D(out_filters_list[3], 3, strides=(2, 2), padding='valid', use_bias=False, kernel_initializer='he_normal', name="stage4." + str(i) + ".fuse_layers.3.2.0.0")(x3_2)
        x3_2 = BatchNormalization(epsilon=1e-5, name="stage4." + str(i) + ".fuse_layers.3.2.0.1")(x3_2)
        x3_3 = x3
        x3_out = add([x3_0, x3_1, x3_2, x3_3])
        x3_out = Activation('relu')(x3_out)
        x_list = [x0_out, x1_out, x2_out, x3_out]

    return x_list

2、特征整合部分

在这里插入图片描述
通过主干部分的构建,我们最终会在Section-4之后获得4个有效特征层。四个有效特征层的形状为:
[128, 128, 32]、[60, 60, 64]、[30, 30, 128]、[15, 15, 256]

在该部分,我们会对四个有效特征层进行特征融合。首先对[60, 60, 64]、[30, 30, 128]、[15, 15, 256]三个有效特征层进行上采样,调整成高宽为128x128。然后将调整后的特征层进行堆叠。

即将:[128, 128, 32]、[128, 128, 64]、[128, 128, 128]、[128, 128, 256]四个特征层进行堆叠。之后再利用一个卷积+标准化+激活函数进行特征整合。

inputs = Input(shape=input_shape)
x, num_filters = HRnet_Backbone(inputs, backbone)

x0_0 = x[0]
x0_1 = UpsampleLike()([x[1], x[0]])
x0_2 = UpsampleLike()([x[2], x[0]])
x0_3 = UpsampleLike()([x[3], x[0]])

x = Concatenate(axis=-1)([x0_0, x0_1, x0_2, x0_3])

x = Conv2D(np.sum(num_filters), 1, strides=(1, 1))(x)
x = BatchNormalization(epsilon=1e-5)(x)
x = Activation("relu")(x)

3、利用特征获得预测结果

利用1、2步,我们可以获取输入进来的图片的特征,此时,我们需要利用特征获得预测结果。

利用特征获得预测结果的过程可以分为2步:
1、利用一个1x1卷积进行通道调整,调整成Num_Classes。
2、利用resize进行上采样使得最终输出层,宽高和输入图片一样。

x = Conv2D(num_classes, 1, strides=(1, 1))(x)

shape = tf.keras.backend.int_shape(inputs)
x = Lambda(lambda xx : tf.image.resize_images(xx, shape[1:3], align_corners=True))(x)
x = Softmax()(x)
model = Model(inputs, x, name="HRnet")
return model

二、训练部分

1、训练文件详解

我们使用的训练文件采用VOC的格式。
语义分割模型训练的文件分为两部分。
第一部分是原图,像这样:
在这里插入图片描述
第二部分标签,像这样:
在这里插入图片描述
原图就是普通的RGB图像,标签就是灰度图或者8位彩色图。

原图的shape为[height, width, 3],标签的shape就是[height, width],对于标签而言,每个像素点的内容是一个数字,比如0、1、2、3、4、5……,代表这个像素点所属的类别。

语义分割的工作就是对原始的图片的每一个像素点进行分类,所以通过预测结果中每个像素点属于每个类别的概率与标签对比,可以对网络进行训练。

2、LOSS解析

本文所使用的LOSS由两部分组成:
1、Cross Entropy Loss。
2、Dice Loss。

Cross Entropy Loss就是普通的交叉熵损失,当语义分割平台利用Softmax对像素点进行分类的时候,进行使用。

Dice loss将语义分割的评价指标作为Loss,Dice系数是一种集合相似度度量函数,通常用于计算两个样本的相似度,取值范围在[0,1]。

计算公式如下:
在这里插入图片描述
就是预测结果和真实结果的交乘上2,除上预测结果加上真实结果。其值在0-1之间。越大表示预测结果和真实结果重合度越大。所以Dice系数是越大越好。

如果作为LOSS的话是越小越好,所以使得Dice loss = 1 - Dice,就可以将Loss作为语义分割的损失了。
实现代码如下:

def dice_loss_with_CE(beta=1, smooth = 1e-5):
    def _dice_loss_with_CE(y_true, y_pred):
        y_pred = K.clip(y_pred, K.epsilon(), 1.0 - K.epsilon())

        CE_loss = - y_true[...,:-1] * K.log(y_pred)
        CE_loss = K.mean(K.sum(CE_loss, axis = -1))

        tp = K.sum(y_true[...,:-1] * y_pred, axis=[0,1,2])
        fp = K.sum(y_pred         , axis=[0,1,2]) - tp
        fn = K.sum(y_true[...,:-1], axis=[0,1,2]) - tp

        score = ((1 + beta ** 2) * tp + smooth) / ((1 + beta ** 2) * tp + beta ** 2 * fn + fp + smooth)
        score = tf.reduce_mean(score)
        dice_loss = 1 - score
        # dice_loss = tf.Print(dice_loss, [dice_loss, CE_loss])
        return CE_loss + dice_loss
    return _dice_loss_with_CE

训练自己的HRNetV2模型

首先前往Github下载对应的仓库,下载完后利用解压软件解压,之后用编程软件打开文件夹。
注意打开的根目录必须正确,否则相对目录不正确的情况下,代码将无法运行。

一定要注意打开后的根目录是文件存放的目录。
在这里插入图片描述

一、数据集的准备

本文使用VOC格式进行训练,训练前需要自己制作好数据集,如果没有自己的数据集,可以通过Github连接下载VOC12+07的数据集尝试下。
训练前将图片文件放在VOCdevkit文件夹下的VOC2007文件夹下的JPEGImages中。
训练前将标签文件放在VOCdevkit文件夹下的VOC2007文件夹下的SegmentationClass中。

在这里插入图片描述

二、数据集的处理

在完成数据集的摆放之后,我们需要对数据集进行下一步的处理,目的是获得训练用的train.txt以及val.txt,需要用到根目录下的voc_annotation.py。

如果下载的是我上传的voc数据集,那么就不需要运行根目录下的voc_annotation.py。
如果是自己制作的数据集,那么需要运行根目录下的voc_annotation.py,从而生成train.txt和val.txt。
在这里插入图片描述

三、开始网络训练

通过voc_annotation.py我们已经生成了train.txt以及val.txt,此时我们可以开始训练了。训练的参数较多,大家可以在下载库后仔细看注释,其中最重要的部分依然是train.py里的num_classes。

num_classes用于指向检测类别的个数+1!训练自己的数据集必须要修改!

除此之外在train.py文件夹下面,选择自己要使用的主干模型backbone、预训练权重model_path和下采样因子downsample_factor。预训练模型需要和主干模型相对应。下采样因子可以在8和16中选择。
在这里插入图片描述
之后就可以开始训练了。

四、训练结果预测

训练结果预测需要用到两个文件,分别是deeplab.py和predict.py。
我们首先需要去deeplab.py里面修改model_path以及num_classes,这两个参数必须要修改。

model_path指向训练好的权值文件,在logs文件夹里。
num_classes指向检测类别的个数+1。

在这里插入图片描述
完成修改后就可以运行predict.py进行检测了。运行后输入图片路径即可检测。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/6554.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python123

文章目录温度转换异常处理百分制成绩转换五分制F正整数AB奇偶求和判断数据类型温度转换异常处理 描述‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪…

数组切分 蓝桥杯 DFS DP

⭐ 数组切分 输入 4 1 3 2 4输出 5⭐ 区间最大值 - 区间最小值 区间长度:说明该区间为连续的自然数 🤠 暴馊dfs (过 50 % 的案例) import java.util.*;public class Main {static int mod 1000000007, n;static int res 0…

OBCP第七章 OB迁移-备份恢复技术架构及操作方法

为什么需要备份恢复 为满足监管要求 防止管理员误操作后,错误数据同步到所有副本,导致数据无法恢复 防止数据库因各种故障而造成数据丢失,降低灾难性数据丢失的风险,从而达到灾难恢复的目的 硬盘驱动器损坏 黑客攻击、病毒 …

九龙证券|券商积极布局A股 新进171家公司前十大流通股东

随着上市公司2022年年报连续出炉,券商自营盘的操作途径同步浮现。Choice数据显示,到4月4日,券商新进171家上市公司前十大流通股东之列,有48家上市公司获加仓。从装备方向来看,制造业、非银金融、电子、电力设备等板块依…

【电源专题】充电过程中指示灯怎么就红绿闪烁或是同时亮

本案例是在工作中发现的,同事测试的时候发现产品充电时指示灯红绿闪烁。正常的表现应该是充电为红灯,充满为绿灯。怎么会出现红绿闪烁的情况呢? 首先前提条件是产品已经量产,并且出货数量巨大,因此是否为个例性问题?通过排查后发现,最终现象是跟着充电器走,使用正常产品…

chatGPT的未来应用有哪些-ChatGPT对未来工作的影响

ChatGPT对未来的影响 ChatGPT 是一种先进的自然语言处理技术,能够处理和理解大量的自然语言数据和信息,具有广泛的应用价值。以下是 ChatGPT 可能对未来的影响: 改变人与计算机的交互方式。ChatGPT 的普及应用,将使得人们可以通过…

聊聊移动端动态化的由来和各流派的优缺点

移动端动态化的由来 “动态化”并不是最近几年才产生的名词,而是从从互联网诞生的初期,这个词就已经出现了。大家所认知的早期互联网,其实就是各种各类的“动态网站”,内容数据和页面外观都不是固定的,都是随着服务器…

命名空间和程序集

目录 一、什么是命名空间 1. 命名空间的作用 2. 命名空间跨文件伸展 3.嵌套命名空间 二、using指令 1. using命名空间指令 2. using别名指令 三、程序集的结构 1. 程序集标识符 2.强命名程序集 一、什么是命名空间 1. 命名空间的作用 命名空间是共享命名空间名的一组…

kotlin协程原理分析

使用kotlin的协程一段时间后,我们或多或少会产生一些疑问:协程和线程有什么关系?协程之间到底怎么来回传递的?协程真的比线程(池)好吗? 初窥 首先我们从最简单协程开始: fun main…

XXX客户挖矿应急响应

此次为真实客户案例! 0x01 前言: 同事反馈,有客户设备告警连接挖矿域名。 0x02 事件分析: 登录设备TOP: CPU巨高!!查。 定位进程: 通过netstat -anop 查看可疑进程: 通过netstat -anop | grep 28564 查看对应进程:

初始Vue【Vue】

Vue 简介 1. Vue是什么? 一套用于构建用户界面的渐进式JavaScript框架 渐进式:Vue可以自底向上逐层的应用 简单应用:只需一个轻量小巧的核心库 复杂应用:可以引入各式各样的Vue插件 2. Vue 的特点 采用组件化的模式&#xff0c…

JVM七大垃圾回收器上篇Serial、ParNeW、Parallel Scavenge、 Serial Old、 Parallel Old、 CMS、 G1

GC逻辑分类 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商、不同版本的JVM来实现。 由于JDK的版本处于高速迭代过程中,因此Java发展至今已经衍生了众多的GC版本。 从不同角度分析垃圾收集器,可以将GC分为不同的类型。 按线程数…

Linux学习记录——십칠 基础IO(2)

文章目录理解文件系统1、了解磁盘物理结构2、逻辑抽象3、文件系统如何理解文件的增删查改4、软硬连接1、软链接2、硬链接3、继续深入理解文件系统 之前已经知道,文件在没被打开的时候,会存放在磁盘等外设中,这篇的重点就是这些没被打开的文件…

JSON 数据解析的方法

JSON 数据解析 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于ECMAScript的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C、C、C#、Java、JavaScript、Perl、Python等)。这…

百度文心一言与Notion的比较(机器人通信的例子)

文心一言出来有一段时间了,也经常会去问问,感觉对于简单的语义理解还是可以,其答案对于一些常见的常识等还是可以给出不错的答案,但是在数学与代码等方面基本上很差,基本的贷款利率、微积分、没有理解语义的代码等都是…

CSDN——Markdown编辑器——快捷键

CSDN——Markdown编辑器——快捷键CSDN——Markdown编辑器——快捷键如下4个,同 word下面的需要加 Shift标题 Ctrl /⌘Shift H // headlineF 和 G在键盘相邻快捷键 的 图标![在这里插入图片描述](https://img-blog.csdnimg.cn/dc02416cebd34c428de54aba5e66c40a.png…

蓝桥杯 --- 数学与简单DP

蓝桥杯 --- 数学与简单DP数学1205. 买不到的数目1211. 蚂蚁感冒1216. 饮料换购简单DP2. 01背包问题1015. 摘花生895. 最长上升子序列数学 1205. 买不到的数目 小明开了一家糖果店。 他别出心裁:把水果糖包成4颗一包和7颗一包的两种。 糖果不能拆包卖。 小朋友来…

springcloud:xxl-job的任务触发机制及调度过期策略

0.引言 我们都会用xxl-job,但很少有人能够说清楚xxl-job的任务触发机制,面临任务阻塞、服务重启如何处理任务,本期我们就来一起看看xxl-job的任务触发机制 1. 调度过期策略 我们在配置策略时可以看到有一个调度过期策略配置,也…

二叉树的四种遍历方式以及必备的面试题(附图解)

二叉树的四种遍历方式以及必备的面试题 文章目录二叉树的四种遍历方式以及必备的面试题前言一、构建一个二叉树二、四种遍历方式1.前序遍历2.中序遍历3.后序遍历附加: 前三种遍历对比图4.层序遍历三、四种遍历相关的面试题1.第一题:144. 二叉树的前序遍历(1)题目&am…

LeetCode.每日一题 831. 隐藏个人信息

Halo,这里是Ppeua。平时主要更新C语言,C,数据结构算法......感兴趣就关注我吧!你定不会失望。 🌈个人主页:主页链接 🌈算法专栏:专栏链接 我会一直往里填充内容哒! &…
最新文章