首页IT科技语义分割视频教程(憨批的语义分割重制版11——Keras 搭建自己的HRNetV2语义分割平台)

语义分割视频教程(憨批的语义分割重制版11——Keras 搭建自己的HRNetV2语义分割平台)

时间2025-06-19 21:52:16分类IT科技浏览3983
导读:学习前言 最近学了一下HRnet,代码真的好难看懂,还只有Pytorch版本的,Keras复现很有难度,作者写的逻辑很厉害,只能慢慢攻破啦!...

学习前言

最近学了一下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进行检测了                  。运行后输入图片路径即可检测         。
声明:本站所有文章                  ,如无特殊说明或标注               ,均为本站原创发布      。任何个人或组织,在未征得本站同意时               ,禁止复制               、盗用                  、采集   、发布本站内容到任何网站            、书籍等各类媒体平台                  。如若本站内容侵犯了原著者的合法权益                  ,可联系我们进行处理            。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
服务器的作用是什么(服务器的作用是啥) 影响搜索引擎排名的因素有哪些(搜索引擎排名的因素有哪些方面)