深度学习(8)之 UNet详解(附图文和代码实现)

这篇具有很好参考价值的文章主要介绍了深度学习(8)之 UNet详解(附图文和代码实现)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

卷积神经网络被大规模的应用在分类任务中,输出的结果是整个图像的类标签。但是UNet是像素级分类,输出的则是每个像素点的类别,且不同类别的像素会显示不同颜色,UNet常常用在生物医学图像上,而该任务中图片数据往往较少。所以,Ciresan等人训练了一个卷积神经网络,用滑动窗口提供像素的周围区域(patch)作为输入来预测每个像素的类标签。这个网络有两个优点:(1)输出结果可以定位出目标类别的位置;(2)由于输入的训练数据是patches,这样就相当于进行了数据增强,从而解决了生物医学图像数量少的问题。

但是,采用该方法的神经网络也有两个很明显的缺点:(1)它很慢,因为这个网络必须训练每个patch,并且因为patch之间的重叠有很多冗余,这样会导致同样特征被多次训练,造成资源的浪费,导致训练时间的加长且效率也会有所降低,也有人会问神经网络经过多次训练这个特征后,会对这个特征的印象加深,从而准确率也会上升,但是举个例子一个图片复制50张,用这50张图片去训练网络,虽说数据集增大了,可是导致的后果是神经网络会出现过拟合,也就是说神经网络对训练图片很熟悉,可是换了一张图片,神经网络就有可能分辨不出来了。(2)定位准确性和获取上下文信息不可兼得,大的patches需要更多的max-pooling,这样会减少定位准确性,因为最大池化会丢失目标像素和周围像素之间的空间关系,而小patches只能看到很小的局部信息,包含的背景信息不够。
UNet主要贡献是在U型结构上,该结构可以使它使用更少的训练图片的同时,且分割的准确度也不会差,UNet的网络结构如下图:

unet模型流程图,# 深度学习,深度学习,人工智能,神经网络
(1)UNet采用全卷积神经网络。
(2)左边网络为特征提取网络:使用conv和pooling
(3)右边网络为特征融合网络:使用上采样产生的特征图与左侧特征图进行concatenate操作。(pooling层会丢失图像信息和降低图像分辨率且是永久性的,对于图像分割任务有一些影响,对图像分类任务的影响不大,为什么要做上采样呢?上采样可以让包含高级抽象特征低分辨率图片在保留高级抽象特征的同时变为高分辨率,然后再与左边低级表层特征高分辨率图片进行concatenate操作)
(4)最后再经过两次卷积操作,生成特征图,再用两个卷积核大小为1*1的卷积做分类得到最后的两张heatmap,例如第一张表示第一类的得分,第二张表示第二类的得分heatmap,然后作为softmax函数的输入,算出概率比较大的softmax,然后再进行loss,反向传播计算。文章来源地址https://www.toymoban.com/news/detail-596229.html

1.Unet模型的代码实现(基于keras)

def get_unet():
    inputs = Input((img_rows, img_cols, 1))
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(inputs)
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
    # pool1 = Dropout(0.25)(pool1)
    # pool1 = BatchNormalization()(pool1)

    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1)
    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
    # pool2 = Dropout(0.5)(pool2)
    # pool2 = BatchNormalization()(pool2)

    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2)
    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3)
    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)
    # pool3 = Dropout(0.5)(pool3)
    # pool3 = BatchNormalization()(pool3)

    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(pool3)
    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv4)
    pool4 = MaxPooling2D(pool_size=(2, 2))(conv4)
    # pool4 = Dropout(0.5)(pool4)
    # pool4 = BatchNormalization()(pool4)

    conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(pool4)
    conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(conv5)

    up6 = concatenate([Conv2DTranspose(256, (2, 2), strides=(
        2, 2), padding='same')(conv5), conv4], axis=3)
    # up6 = Dropout(0.5)(up6)
    # up6 = BatchNormalization()(up6)
    conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(up6)
    conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv6)

    up7 = concatenate([Conv2DTranspose(128, (2, 2), strides=(
        2, 2), padding='same')(conv6), conv3], axis=3)
    # up7 = Dropout(0.5)(up7)
    # up7 = BatchNormalization()(up7)
    conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(up7)
    conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv7)

    up8 = concatenate([Conv2DTranspose(64, (2, 2), strides=(
        2, 2), padding='same')(conv7), conv2], axis=3)
    # up8 = Dropout(0.5)(up8)
    # up8 = BatchNormalization()(up8)
    conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(up8)
    conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv8)

    up9 = concatenate([Conv2DTranspose(32, (2, 2), strides=(
        2, 2), padding='same')(conv8), conv1], axis=3)
    # up9 = Dropout(0.5)(up9)
    # up9 = BatchNormalization()(up9)
    conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(up9)
    conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv9)

    # conv9 = Dropout(0.5)(conv9)

    conv10 = Conv2D(1, (1, 1), activation='sigmoid')(conv9)

    model = Model(inputs=[inputs], outputs=[conv10])

    model.compile(optimizer=Adam(lr=1e-5),
                  loss=dice_coef_loss, metrics=[dice_coef])

    return model

2.Unet的代码实现(pytorch版)

"""
这是根据UNet模型搭建出的一个基本网络结构
输入和输出大小是一样的,可以根据需求进行修改
"""
import torch
import torch.nn as nn
from torch.nn import functional as F


# 基本卷积块
class Conv(nn.Module):
    def __init__(self, C_in, C_out):
        super(Conv, self).__init__()
        self.layer = nn.Sequential(

            nn.Conv2d(C_in, C_out, 3, 1, 1),
            nn.BatchNorm2d(C_out),
            # 防止过拟合
            nn.Dropout(0.3),
            nn.LeakyReLU(),

            nn.Conv2d(C_out, C_out, 3, 1, 1),
            nn.BatchNorm2d(C_out),
            # 防止过拟合
            nn.Dropout(0.4),
            nn.LeakyReLU(),
        )

    def forward(self, x):
        return self.layer(x)


# 下采样模块
class DownSampling(nn.Module):
    def __init__(self, C):
        super(DownSampling, self).__init__()
        self.Down = nn.Sequential(
            # 使用卷积进行2倍的下采样,通道数不变
            nn.Conv2d(C, C, 3, 2, 1),
            nn.LeakyReLU()
        )

    def forward(self, x):
        return self.Down(x)


# 上采样模块
class UpSampling(nn.Module):

    def __init__(self, C):
        super(UpSampling, self).__init__()
        # 特征图大小扩大2倍,通道数减半
        self.Up = nn.Conv2d(C, C // 2, 1, 1)

    def forward(self, x, r):
        # 使用邻近插值进行下采样
        up = F.interpolate(x, scale_factor=2, mode="nearest")
        x = self.Up(up)
        # 拼接,当前上采样的,和之前下采样过程中的
        return torch.cat((x, r), 1)


# 主干网络
class UNet(nn.Module):

    def __init__(self):
        super(UNet, self).__init__()

        # 4次下采样
        self.C1 = Conv(3, 64)
        self.D1 = DownSampling(64)
        self.C2 = Conv(64, 128)
        self.D2 = DownSampling(128)
        self.C3 = Conv(128, 256)
        self.D3 = DownSampling(256)
        self.C4 = Conv(256, 512)
        self.D4 = DownSampling(512)
        self.C5 = Conv(512, 1024)

        # 4次上采样
        self.U1 = UpSampling(1024)
        self.C6 = Conv(1024, 512)
        self.U2 = UpSampling(512)
        self.C7 = Conv(512, 256)
        self.U3 = UpSampling(256)
        self.C8 = Conv(256, 128)
        self.U4 = UpSampling(128)
        self.C9 = Conv(128, 64)

        self.Th = torch.nn.Sigmoid()
        self.pred = torch.nn.Conv2d(64, 3, 3, 1, 1)

    def forward(self, x):
        # 下采样部分
        R1 = self.C1(x)
        R2 = self.C2(self.D1(R1))
        R3 = self.C3(self.D2(R2))
        R4 = self.C4(self.D3(R3))
        Y1 = self.C5(self.D4(R4))

        # 上采样部分
        # 上采样的时候需要拼接起来
        O1 = self.C6(self.U1(Y1, R4))
        O2 = self.C7(self.U2(O1, R3))
        O3 = self.C8(self.U3(O2, R2))
        O4 = self.C9(self.U4(O3, R1))

        # 输出预测,这里大小跟输入是一致的
        # 可以把下采样时的中间抠出来再进行拼接,这样修改后输出就会更小
        return self.Th(self.pred(O4))


if __name__ == '__main__':
    a = torch.randn(2, 3, 256, 256)
    net = UNet()
    print(net(a).shape)

3. Unet的代码实现(TensorFlow版)

# -*-coding: utf-8 -*-
import tensorflow as tf
import tensorflow.contrib.slim as slim
 
 
def lrelu(x):
    return tf.maximum(x * 0.2, x)
 
activation_fn=lrelu
 
def UNet(inputs, reg):  # Unet
    conv1 = slim.conv2d(inputs, 32, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv1_1', weights_regularizer=reg)
    conv1 = slim.conv2d(conv1, 32, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv1_2',weights_regularizer=reg)
    pool1 = slim.max_pool2d(conv1, [2, 2], padding='SAME')
 
    conv2 = slim.conv2d(pool1, 64, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv2_1',weights_regularizer=reg)
    conv2 = slim.conv2d(conv2, 64, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv2_2',weights_regularizer=reg)
    pool2 = slim.max_pool2d(conv2, [2, 2], padding='SAME')
 
    conv3 = slim.conv2d(pool2, 128, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv3_1',weights_regularizer=reg)
    conv3 = slim.conv2d(conv3, 128, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv3_2',weights_regularizer=reg)
    pool3 = slim.max_pool2d(conv3, [2, 2], padding='SAME')
 
    conv4 = slim.conv2d(pool3, 256, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv4_1',weights_regularizer=reg)
    conv4 = slim.conv2d(conv4, 256, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv4_2',weights_regularizer=reg)
    pool4 = slim.max_pool2d(conv4, [2, 2], padding='SAME')
 
    conv5 = slim.conv2d(pool4, 512, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv5_1',weights_regularizer=reg)
    conv5 = slim.conv2d(conv5, 512, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv5_2',weights_regularizer=reg)
 
    up6 = upsample_and_concat(conv5, conv4, 256, 512)
    conv6 = slim.conv2d(up6, 256, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv6_1',weights_regularizer=reg)
    conv6 = slim.conv2d(conv6, 256, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv6_2',weights_regularizer=reg)
 
    up7 = upsample_and_concat(conv6, conv3, 128, 256)
    conv7 = slim.conv2d(up7, 128, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv7_1',weights_regularizer=reg)
    conv7 = slim.conv2d(conv7, 128, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv7_2',weights_regularizer=reg)
 
    up8 = upsample_and_concat(conv7, conv2, 64, 128)
    conv8 = slim.conv2d(up8, 64, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv8_1',weights_regularizer=reg)
    conv8 = slim.conv2d(conv8, 64, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv8_2',weights_regularizer=reg)
 
    up9 = upsample_and_concat(conv8, conv1, 32, 64)
    conv9 = slim.conv2d(up9, 32, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv9_1', weights_regularizer=reg)
    conv9 = slim.conv2d(conv9, 32, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv9_2',weights_regularizer=reg)
    print("conv9.shape:{}".format(conv9.get_shape()))
 
    type='UNet_1X'
    with tf.variable_scope(name_or_scope="output"):
        if type=='UNet_3X':#UNet放大三倍
            conv10 = slim.conv2d(conv9, 27, [1, 1], rate=1, activation_fn=None, scope='g_conv10',weights_regularizer=reg)
            out = tf.depth_to_space(conv10, 3)
        if type=='UNet_1X':#输入输出维度相同
            out = slim.conv2d(conv9, 6, [1, 1], rate=1, activation_fn=None, scope='g_conv10',weights_regularizer=reg)
    return out
 
def upsample_and_concat(x1, x2, output_channels, in_channels):
    pool_size = 2
    deconv_filter = tf.Variable(tf.truncated_normal([pool_size, pool_size, output_channels, in_channels], stddev=0.02))
    deconv = tf.nn.conv2d_transpose(x1, deconv_filter, tf.shape(x2), strides=[1, pool_size, pool_size, 1])
 
    deconv_output = tf.concat([deconv, x2], 3)
    deconv_output.set_shape([None, None, None, output_channels * 2])
    return deconv_output
 
if __name__=="__main__":
    weight_decay=0.001
    reg = slim.l2_regularizer(scale=weight_decay)
    inputs = tf.ones(shape=[4, 256, 256, 3])
    out=UNet(inputs,reg)
    print("net1.shape:{}".format(inputs.get_shape()))
    print("out.shape:{}".format(out.get_shape()))
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())

卷积神经网络被大规模的应用在分类任务中,输出的结果是整个图像的类标签。但是UNet是像素级分类,输出的则是每个像素点的类别,且不同类别的像素会显示不同颜色,UNet常常用在生物医学图像上,而该任务中图片数据往往较少。所以,Ciresan等人训练了一个卷积神经网络,用滑动窗口提供像素的周围区域(patch)作为输入来预测每个像素的类标签。这个网络有两个优点:(1)输出结果可以定位出目标类别的位置;(2)由于输入的训练数据是patches,这样就相当于进行了数据增强,从而解决了生物医学图像数量少的问题。

但是,采用该方法的神经网络也有两个很明显的缺点:(1)它很慢,因为这个网络必须训练每个patch,并且因为patch之间的重叠有很多冗余,这样会导致同样特征被多次训练,造成资源的浪费,导致训练时间的加长且效率也会有所降低,也有人会问神经网络经过多次训练这个特征后,会对这个特征的印象加深,从而准确率也会上升,但是举个例子一个图片复制50张,用这50张图片去训练网络,虽说数据集增大了,可是导致的后果是神经网络会出现过拟合,也就是说神经网络对训练图片很熟悉,可是换了一张图片,神经网络就有可能分辨不出来了。(2)定位准确性和获取上下文信息不可兼得,大的patches需要更多的max-pooling,这样会减少定位准确性,因为最大池化会丢失目标像素和周围像素之间的空间关系,而小patches只能看到很小的局部信息,包含的背景信息不够。
UNet主要贡献是在U型结构上,该结构可以使它使用更少的训练图片的同时,且分割的准确度也不会差,UNet的网络结构如下图:

unet模型流程图,# 深度学习,深度学习,人工智能,神经网络
(1)UNet采用全卷积神经网络。
(2)左边网络为特征提取网络:使用conv和pooling
(3)右边网络为特征融合网络:使用上采样产生的特征图与左侧特征图进行concatenate操作。(pooling层会丢失图像信息和降低图像分辨率且是永久性的,对于图像分割任务有一些影响,对图像分类任务的影响不大,为什么要做上采样呢?上采样可以让包含高级抽象特征低分辨率图片在保留高级抽象特征的同时变为高分辨率,然后再与左边低级表层特征高分辨率图片进行concatenate操作)
(4)最后再经过两次卷积操作,生成特征图,再用两个卷积核大小为1*1的卷积做分类得到最后的两张heatmap,例如第一张表示第一类的得分,第二张表示第二类的得分heatmap,然后作为softmax函数的输入,算出概率比较大的softmax,然后再进行loss,反向传播计算。

到了这里,关于深度学习(8)之 UNet详解(附图文和代码实现)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Swin-Unet模型代码详解及改进思路

    Swim-unet是针对水下图像分割任务提出的一种模型结构,其基于U-Net模型并加入了Swin Transformer模块,可以有效地解决水下图像分割中的光照不均匀、噪声干扰等问题。 Swim-unet模型代码详解 首先,在导入必要的库后,我们需要定义Swin Transformer模块中的一些函数和类: import torc

    2024年02月09日
    浏览(39)
  • 【PyTorch 实战2:UNet 分割模型】10min揭秘 UNet 分割网络如何工作以及pytorch代码实现(详细代码实现)

      U-Net,自2015年诞生以来,便以其卓越的性能在生物医学图像分割领域崭露头角。作为FCN的一种变体,U-Net凭借其Encoder-Decoder的精巧结构,不仅在医学图像分析中大放异彩,更在卫星图像分割、工业瑕疵检测等多个领域展现出强大的应用能力。UNet是一种常用于图像分割的卷

    2024年04月28日
    浏览(41)
  • 深度学习:使用UNet做图像语义分割,训练自己制作的数据集,详细教程

    语义分割(Semantic Segmentation)是图像处理和机器视觉一个重要分支。与分类任务不同,语义分割需要判断图像每个像素点的类别,进行精确分割。语义分割目前在自动驾驶、自动抠图、医疗影像等领域有着比较广泛的应用。我总结了使用UNet网络做图像语义分割的方法,教程很详

    2024年02月03日
    浏览(46)
  • 【图像分割】Unet系列深度讲解(FCN、UNET、UNET++)

    1.1 背景介绍: 自2015年以来,在生物医学图像分割领域,U-Net得到了广泛的应用,目前已达到四千多次引用。至今,U-Net已经有了很多变体。目前已有许多新的卷积神经网络设计方式,但很多仍延续了U-Net的核心思想,加入了新的模块或者融入其他设计理念。 编码和解码,早在

    2024年02月03日
    浏览(42)
  • 《图像分割Unet网络分析及其Pytorch版本代码实现》

      最近两个月在做学习图像分割方面的学习,踩了无数的坑,也学到了很多的东西,想了想还是趁着国庆节有时间来做个总结,以后有这方面需要可以来看看。   神经网络被大规模的应用到计算机视觉中的分类任务中,说到神经网络的分类任务这里不得不提到CNN(卷积神经网

    2024年02月05日
    浏览(45)
  • 深度学习:使用UNet做图像语义分割,训练自己制作的数据集并推理测试(详细图文教程)

    语义分割(Semantic Segmentation)是图像处理和机器视觉一个重要分支。与分类任务不同,语义分割需要判断图像每个像素点的类别,进行精确分割。语义分割目前在自动驾驶、自动抠图、医疗影像等领域有着比较广泛的应用。我总结了使用UNet网络做图像语义分割的方法,教程很详

    2024年01月18日
    浏览(50)
  • unet网络详解

    参考文献:U-Net: Convolutional Networks for Biomedical Image Segmentation 作者:Olaf Ronneberger, Philipp Fischer, and Thomas Brox Unet是一个优秀的语义分割模型,其主要执行过程与其它语义分割模型类似。与CNN不同的之处在于CNN是图像级的分类,而unet是像素级的分类,其输出的是每个像素点的类别

    2024年02月02日
    浏览(33)
  • 《VM-UNet: Vision Mamba UNet for Medical Image Segmentation》论文阅读及代码复现

    论文地址:[2402.02491] VM-UNet: Vision Mamba UNet for Medical Image Segmentation (arxiv.org) 代码地址:JCruan519/VM-UNet: (ARXIV24) This is the official code repository for \\\"VM-UNet: Vision Mamba UNet for Medical Image Segmentation\\\". (github.com) 摘要 : 在医学图像分割领域,基于细胞神经网络和基于变换器的模型都得到了广

    2024年03月24日
    浏览(64)
  • 提升图像分割精度:学习UNet++算法

    由于工作需要对 UNet++ 算法进行调参,对规则做较大的修改,初次涉及,有误的地方,请各位大佬指教哈。 1.1 什么是 UNet++ 算法 UNet++ 算法是基于 UNet 算法的改进版本,旨在提高图像分割的性能和效果。它由 Zhou et al. 在论文 “ UNet++: A Nested U-Net Architecture for Medical Image Segment

    2024年02月03日
    浏览(45)
  • 【语义分割】ST_Unet论文 逐步代码解读

    主要工程文件为这5个 分别作用为: 构造相应的deform 卷积 DCNN的残差网络 编写相应的配置文件,可以改变相应参数 模型的主函数和主框架 模型的连接部分 代码框架由3部分组成,encode,decode和decode中将图像还原成语义分割预测图 Transformer(config, img_size) 组成编码部分,包含主

    2024年02月07日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包