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

2023-11-11

注意事项

这是重新构建了的Unet语义分割网络,主要是文件框架上的构建,还有代码的实现,和之前的语义分割网络相比,更加完整也更清晰一些。建议还是学习这个版本的Unet。

学习前言

重置一下我最喜欢的Unet。
在这里插入图片描述

什么是Unet模型

Unet是一个优秀的语义分割模型,其主要执行过程与其它语义分割模型类似。

Unet可以分为三个部分,如下图所示:

第一部分是主干特征提取部分,我们可以利用主干部分获得一个又一个的特征层,Unet的主干特征提取部分与VGG相似,为卷积和最大池化的堆叠。利用主干特征提取部分我们可以获得五个初步有效特征层,在第二步中,我们会利用这五个有效特征层可以进行特征融合。

第二部分是加强特征提取部分,我们可以利用主干部分获取到的五个初步有效特征层进行上采样,并且进行特征融合,获得一个最终的,融合了所有特征的有效特征层

第三部分是预测部分,我们会利用最终获得的最后一个有效特征层对每一个特征点进行分类,相当于对每一个像素点进行分类。
在这里插入图片描述

代码下载

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

Unet实现思路

一、预测部分

1、主干网络介绍

在这里插入图片描述
Unet的主干特征提取部分由卷积+最大池化组成,整体结构与VGG类似。

本文所采用的主干特征提取网络为VGG16,这样也方便使用imagnet上的预训练权重。

VGG是由Simonyan 和Zisserman在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型,其名称来源于作者所在的牛津大学视觉几何组(Visual Geometry Group)的缩写。

该模型参加2014年的 ImageNet图像分类与定位挑战赛,取得了优异成绩:在分类任务上排名第二,在定位任务上排名第一。
它的结构如下图所示:
在这里插入图片描述
这是一个VGG16被用到烂的图,但确实很好的反应了VGG16的结构。

当我们使用VGG16作为主干特征提取网络的时候,我们只会用到两种类型的层,分别是卷积层最大池化层

当输入的图像大小为512x512x3的时候,具体执行方式如下:
1、conv1:进行两次[3,3]的64通道的卷积,获得一个[512,512,64]的初步有效特征层,再进行2X2最大池化,获得一个[256,256,64]的特征层。
2、conv2:进行两次[3,3]的128通道的卷积,获得一个[256,256,128]的初步有效特征层,再进行2X2最大池化,获得一个[128,128,128]的特征层。
3、conv3:进行三次[3,3]的256通道的卷积,获得一个[128,128,256]的初步有效特征层,再进行2X2最大池化,获得一个[64,64,256]的特征层。
4、conv4:进行三次[3,3]的512通道的卷积,获得一个[64,64,512]的初步有效特征层,再进行2X2最大池化,获得一个[32,32,512]的特征层。
5、conv5:进行三次[3,3]的512通道的卷积,获得一个[32,32,512]的初步有效特征层。

在这里插入图片描述

from keras import layers

def VGG16(img_input):
    # Block 1
    x = layers.Conv2D(64, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block1_conv1')(img_input)
    x = layers.Conv2D(64, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block1_conv2')(x)
    feat1 = x
    x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)

    # Block 2
    x = layers.Conv2D(128, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block2_conv1')(x)
    x = layers.Conv2D(128, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block2_conv2')(x)
    feat2 = x
    x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)


    # Block 3
    x = layers.Conv2D(256, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block3_conv1')(x)
    x = layers.Conv2D(256, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block3_conv2')(x)
    x = layers.Conv2D(256, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block3_conv3')(x)
    feat3 = x
    x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x)

    # Block 4
    x = layers.Conv2D(512, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block4_conv1')(x)
    x = layers.Conv2D(512, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block4_conv2')(x)
    x = layers.Conv2D(512, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block4_conv3')(x)
    feat4 = x

    x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x)

    # Block 5
    x = layers.Conv2D(512, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block5_conv1')(x)
    x = layers.Conv2D(512, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block5_conv2')(x)
    x = layers.Conv2D(512, (3, 3),
                      activation='relu',
                      padding='same',
                      name='block5_conv3')(x)
    feat5 = x
    return feat1, feat2, feat3, feat4, feat5

2、加强特征提取结构

在这里插入图片描述
Unet所使用的加强特征提取网络是一个U的形状。

利用第一步我们可以获得五个初步的有效特征层,在加强特征提取网络这里,我们会利用这五个初步的有效特征层进行特征融合特征融合的方式就是对特征层进行上采样并且进行堆叠

为了方便网络的构建与更好的通用性,我们的Unet和上图的Unet结构有些许不同,在上采样时直接进行两倍上采样再进行特征融合,最终获得的特征层和输入图片的高宽相同。

具体示意图如下:
在这里插入图片描述

import numpy as np
from keras.models import *
from keras.layers import *
from nets.vgg16 import VGG16

def Unet(input_shape=(256,256,3), num_classes=21):
    inputs = Input(input_shape)
    feat1, feat2, feat3, feat4, feat5 = VGG16(inputs) 
      
    channels = [64, 128, 256, 512]

    P5_up = UpSampling2D(size=(2, 2))(feat5)
    P4 = Concatenate(axis=3)([feat4, P5_up])
    P4 = Conv2D(channels[3], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P4)
    P4 = Conv2D(channels[3], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P4)

    P4_up = UpSampling2D(size=(2, 2))(P4)
    P3 = Concatenate(axis=3)([feat3, P4_up])
    P3 = Conv2D(channels[2], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P3)
    P3 = Conv2D(channels[2], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P3)

    P3_up = UpSampling2D(size=(2, 2))(P3)
    P2 = Concatenate(axis=3)([feat2, P3_up])
    P2 = Conv2D(channels[1], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P2)
    P2 = Conv2D(channels[1], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P2)

    P2_up = UpSampling2D(size=(2, 2))(P2)
    P1 = Concatenate(axis=3)([feat1, P2_up])
    P1 = Conv2D(channels[0], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P1)
    P1 = Conv2D(channels[0], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P1)

    P1 = Conv2D(num_classes, 1, activation="softmax")(P1)

    model = Model(inputs=inputs, outputs=P1)
    return model

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

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

利用特征获得预测结果的过程为:
利用一个1x1卷积进行通道调整,将最终特征层的通道数调整成num_classes。
在这里插入图片描述

import numpy as np
from keras.models import *
from keras.layers import *
from nets.vgg16 import VGG16

def Unet(input_shape=(256,256,3), num_classes=21):
    inputs = Input(input_shape)
    feat1, feat2, feat3, feat4, feat5 = VGG16(inputs) 
      
    channels = [64, 128, 256, 512]

    P5_up = UpSampling2D(size=(2, 2))(feat5)
    P4 = Concatenate(axis=3)([feat4, P5_up])
    P4 = Conv2D(channels[3], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P4)
    P4 = Conv2D(channels[3], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P4)

    P4_up = UpSampling2D(size=(2, 2))(P4)
    P3 = Concatenate(axis=3)([feat3, P4_up])
    P3 = Conv2D(channels[2], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P3)
    P3 = Conv2D(channels[2], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P3)

    P3_up = UpSampling2D(size=(2, 2))(P3)
    P2 = Concatenate(axis=3)([feat2, P3_up])
    P2 = Conv2D(channels[1], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P2)
    P2 = Conv2D(channels[1], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P2)

    P2_up = UpSampling2D(size=(2, 2))(P2)
    P1 = Concatenate(axis=3)([feat1, P2_up])
    P1 = Conv2D(channels[0], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P1)
    P1 = Conv2D(channels[0], 3, activation='relu', padding='same', kernel_initializer='he_normal')(P1)

    P1 = Conv2D(num_classes, 1, activation="softmax")(P1)

    model = Model(inputs=inputs, outputs=P1)
    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, alpha = 0.25, gamma=2.0, threhold=0.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, focal_loss])
        return CE_loss + dice_loss
    return _dice_loss_with_CE

训练自己的Unet模型

首先前往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!训练自己的数据集必须要修改!

在这里插入图片描述
之后就可以开始训练了。

四、训练结果预测

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

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

憨批的语义分割重制版5——Keras 搭建自己的Unet语义分割平台 的相关文章

  • 用c语言浮点输出时,如何让小数点后没用的0不显示

    动态控制浮点数小数位数 2020年7月29日 存在问题 C语言把浮点数直接通过sprintf函数保存在字符数组中 末尾的0显得很多余 想办法把末尾的0去掉 解决问题 在打印时 通过格式控制输出 一般情况使用 f即可输出浮点数 我们通过 f
  • linux查看3306是哪个进程占用,linux查看端口占用

    发表于 2019 08 18 21 00 36 by 月小升 一 例子 lsof i 7000 COMMAND PID USER FD TYPE DEVICE SIZE OFF NODE NAME frps 1249 root 3u IPv
  • 二叉树代码

    1 普通二叉树的构建 构建结点以及搜索与删除的方法 public class Node private int no private String name private Node left private Node right priv
  • C++ srand()只能调用一次,否则rand()每次返回相同值

    C srand 只能调用一次 否则rand 每次返回相同值 面试的时候写一个洗牌算法 结果遇到这个问题坑死我了 幸运的是面试官也不太看得出来问题出在哪 他主攻Java 所以给了我足够时间去调试 问题描述 自己创作的洗牌算法 include
  • npm的一些常用命令和被墙问题的解决

    npm node package manager 1 npm网站 npmjs com 2 npm命令行工具 安装 node同时也就已经安装了npm 1 查询npm版本号 在cmd中输入npm v或者npm version即可 2 升级npm
  • java select 函数_五种I/O模型和select函数简介

    一 五种I O模型 1 阻塞I O 我们在前面所说的I O模型都是阻塞I O 即调用recv系统调用 如果没有数据则阻塞等待 当数据到来则将数据从内核空间 套接口缓冲区 拷贝到用户空间 recv函数提供的buf 然后recv返回 进行数据处

随机推荐

  • 策略模式(Strategy)

    设计模式之策略模式 策略模式的主要作用是 整体地替换算法 无论什么程序 其目的都是解决问题 而为了解决问题 我们需要编写特定的算法 使用Strategy模式可以整体地替换算法的实现部分 能够整体地替换算法 能让我们轻松地以不同的算法解决同一
  • win10添加环境变量后没用_教你Win10系统配置的环境变量未生效的解决方法

    教你Win10系统配置的环境变量未生效的解决方法 一些Win10小伙伴经常遇到配置了环境变量无法生效的情况 那么Win10系统安装了jdk 修改环境变量配置不生效要如何解决呢 有遇到此类问题的小伙伴就跟着Win10专业版官网小编一起来看看W
  • 协同过滤推荐算法

    Collaborative Filtering Recommendation 转自 http www cnblogs com zhangchaoyang articles 2664366 html 另 一定要看文献 协同过滤推荐算法综述 马
  • gazebo崩溃gazebo-2 process has died exit code 139

    错误信息 gazebo 2 process has died pid 7605 exit code 139 cmd opt ros kinetic lib gazebo ros gzserver e ode Ubuntu 16 04 on
  • JUC常用到的类

    JUC java util concurrent 并发包中包含了许多并发编程中需要用到的类 锁 如ReentratLock ReadWriteLock ReentrantLock重入锁 可以替代synchronized使用 并且有更多强大的
  • 小白,想入门程序员,应该从什么开始学,顺序是什么?

    首先 你需要选择一门自己感兴趣的编程语言 现在常见的编程语言有 1 C C 属于大多数情况下能接触到的相对基础的编程语言了 优势是基本掌握以后面对其他语言均可以 无压力 坏处是学习起来非常的困难 需要系统性的了解非常多知识点 且耗时一般较长
  • 锁(Lock)、内存屏障(Memory barrier)与 缓存一致性( Cache coherence)

    文章目录 前言 Optimization Barrier Memory barrier Cache coherence 总结 参考资料 前言 在应用层 关于锁的使用大家应该都很熟悉了 作用就是为了保护共享变量不被同时操作而导致无法预测的情况
  • 01-----在Ubuntu16.04上搭建git服务器

    一 在Ubuntu16 04上搭建git服务器的步骤 1 下载git apt get install y git 注 上面可能下载失败 更新一下包即可 然后重新下载 apt get update apt get install y git
  • fullCalendar日程表在Vue项目中的应用

    fullCalendar日程表在Vue项目中的应用 fullCalendar相关API 使用fullCalendar插件在项目中实现日程排版 支持天 月日程转换 自定义事件 页面布局 通过按钮切换天与月视图的转换 天视图回显具体的时分排版项
  • 物联网LoRa系列-21:LoRa终端--射频芯片SX1261 SX1262的FSK调制解调器与配置

    目录 1 SX1261 2功能模块 2 调制技术的基本原理 3 FSK调制技术的基本原理 4 SX1261 2的FSK调制器主要的性能参数 5 SX1261 2的FSK调制器主要寄存器设置 1 SX1261 2功能模块 MCU与SX1261
  • Linux I/O(输入和输出)

    1 文件I O 1 文件描述符 对于内核而言 所有打开的文件都通过文件描述符引用 文件描述符通常是一个小的非负整数 内核用它标识一个特定进程正在访问的文件 当内核打开一个已有文件或创建一个新文件时 它返回一个文件描述符 2 按照惯例 UNI
  • 68.文件操作(打开、读写和关闭)&69.文件操作(文件读写位置)& 70 ftell 、feof、‘EOF‘

    函数库的使用 不要类比字符串函数 仅掌握正确使用 不考虑如何实现 文件有编号 暂时认为是文件指针 FILE指针 文件指针 句柄 实际上是结构体的重定义 struct file int fd typedef struct file FILE
  • C语言从键盘上输入一个大写字母,C语言编程输入一个字符,如果它是一个大写字 – 手机爱问...

    2008 03 17 设计字符界面application程序 输入一字符 如果为小写字母 则转换为大写字符 如果为大写字母 则转换为小写字母 其它字符不变 我写了下 总是错误 帮忙看下 import java awt import java
  • anaconda3在创建虚拟环境中途出错,想要删掉,如何删彻底

    这个文件夹里面是专门放不同环境中的包的 只是没有区分环境 都混在一起了 一般在想要删除一个虚拟环境 除了在命令行中输入conda remove n your env name 虚拟环境名称 all 然后在envs中删除虚拟环境的文件夹 还可
  • Kanzi学习教程培训教程-Kanzi的简介和安装

    如果你认为本系列文章对你有所帮助 请大家有钱的捧个钱场 点击此处赞助 赞助额1元起步 多少随意 锋影 e mail 174176320 qq com Kanzi UI Solution是一个完整的UI解决方案 为嵌入式的UI的设计 开发和部
  • QLineEdit 设置输入掩码

    背景 QLineEdit 是单行文本编辑器 常用于界面中的文本输入 QLineEdit 提供了 inputMask 使用一些特定到字符来设置输入的格式和内容 inputMask 概述 输入掩码包括两部分组成 之前是输入格式及占位符设置 之后
  • pytorch的学习

    torch save net1 net pkl 保存entire net整个网络 torch save net1 state dict net params pkl 保存参数
  • MySQL5.7忘记root密码-手动修改密码教程

    MySQL 5 7相对于MySQL 5 6在应用上发生了一些新的变化 这里就MySQL5 7忘记root密码情况下 手动去修改root密码做一些介绍 操作系统 Windows10 数据库版本 MySQL 5 7 20 1 Windows10
  • TCL中变量嵌套使用

    TCL中变量嵌套使用 在使用多重嵌套变量时候 因为 对于tcl来说属于非运算符号 因此在使用变量嵌套 直接调用会出现问题 即变量不能正确调用 set mm list 0 1 set nn list 2 3 set index mm puts
  • 憨批的语义分割重制版5——Keras 搭建自己的Unet语义分割平台

    憨批的语义分割重制版5 Keras 搭建自己的Unet语义分割平台 注意事项 学习前言 什么是Unet模型 代码下载 Unet实现思路 一 预测部分 1 主干网络介绍 2 加强特征提取结构 3 利用特征获得预测结果 二 训练部分 1 训练文