经典语义分割(二)医学图像分割模型UNet

经典语义分割(二)医学图像分割模型UNet

  • 我们之前介绍了全卷积神经网络( FCN) ,FCN是基于深度学习的语义分割算法的开山之作。

  • 今天我们介绍另一个语义分割的经典模型—UNet,它兼具轻量化与高性能,通常作为语义分割任务的基线测试模型,至今仍是如此。

  • UNet从本质上来说也属于一种全卷积神经网络模型,它的取名来源于其架构形状:模型整体呈现U形

    • 它原本是为了解决医疗影像语义分割问题的。在2015年的ISBI细胞跟踪挑战赛中,Ronnebreger等人利用UNet网络以较大优势赢得比赛。
      • 由于隐私问题、注释过程的复杂性、专家技能要求以及使用生物医学成像系统拍摄图像的高价格,在生物医学任务中,收集大量的数据很困难。
      • 而UNet能够在小样本数据集上训练并取得优秀成绩,因此各种基于其改进的网络模型广泛运用于医学图像分割任务中。特别是在肺结节、视网膜血管、皮肤病以及颅内肿瘤四大医学分割领域,出现了大量基于U-Net 改进的模型。
    • 下面几点或许能够解释为何UNet在医疗影像上表现突出:
      • UNet的U形网络结构密集融合了浅层特征与深层特征;
      • 医疗影像数据量与UNet模型体量上相匹配,有效避免了过拟合;
      • 医疗影像结构简单且固定,具有较低语义信息。
    • 不过,之后几年的发展,也证实了它是语义分割任务中的全能选手。
  • 论文地址:U-Net: Convolutional Networks for Biomedical Image Segmentation

1 UNet网络

1.1 UNet网络简述

UNet网络结构如下图所示,最主要的两个特点是:U型网络结构和Skip Connection跳层连接。

  • Unet通过跳接的U形网络结构结合了浅层特征与深层特征,用于最后的语义分割图生成。

    • 与FCN不同的是,UNet以拼接方式来结合浅层特征与深层特征;
    • 而FCN则是以相加方式来结合浅层特征与深层特征;
  • U形网络架构能够更充分地融合浅层特征和深层特征,这也是UNet性能优于FCN的主要原因。

    • 浅层特征图更倾向于表达例如点、线、边缘轮廓等基本特征单元;蕴含的空间信息更多。

    • 深层特征图更倾向于表达图像的语义信息;蕴含的空间信息更少,语义特征更多。

在这里插入图片描述

1.2 网络架构详解

UNet的主干分为对称的左右两部分:

  • 左边为特征提取网络(编码器,encoder),原始输入图像通过卷积-最大池化进行四次下采样,获得四层级的特征图;

  • 右边为特征融合网络(解码器,decoder),各层级特征图与经过反卷积获得的特征图通过跳接方式进行特征融合;

  • 最后一层通过与标签计算损失进行语义图预测。

1.2.1 DoubleConv模块

  • 从UNet网络中可以看出,不管是下采样过程还是上采样过程,每一层都会连续进行两次卷积操作,这种操作在UNet网络中重复很多次,可以单独写一个DoubleConv模块

    • 如下图所示,in_channels设为1,out_channels为64。
    • 输入图片大小为572×572,经过步长为1,padding为0的3×3卷积(注意原文没有进行填充),因此得到feature map为570×570,而非572×572,再经过一次卷积得到568×568的feature map。
    import torch.nn as nnclass DoubleConv(nn.Module):"""(convolution => [BN] => ReLU) * 2"""def __init__(self, in_channels, out_channels):super().__init__()self.double_conv = nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=0),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True),nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=0),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True))def forward(self, x):return self.double_conv(x)
    

在这里插入图片描述

1.2.2 Down模块

UNet网络一共有4次下采样过程,模块化代码如下:

  • Down模块先进行一个最大化池化,将高宽减半
  • 然后接一个DoubleConv模块,让通道加倍
  • 至此,UNet网络的左半部分的下采样过程的代码都写好了,接下来是右半部分的上采样过程
class Down(nn.Module):"""Downscaling with maxpool then double conv"""def __init__(self, in_channels, out_channels):super().__init__()self.maxpool_conv = nn.Sequential(nn.MaxPool2d(2),DoubleConv(in_channels, out_channels))def forward(self, x):return self.maxpool_conv(x)

在这里插入图片描述

1.2.3 Up模块

  • Up模块除了常规的上采样操作,还有进行特征的融合。
  • UP模块定义了两种方法:Upsample和ConvTranspose2d,即双线性插值反卷积
  • 在forward前向传播函数中,x1接收的是上采样的数据,x2接收的是特征融合的数据。特征融合方法就是先对小的feature map进行padding,再进行concat。
  • 注意:在下面代码中,上采样后会进行padding,和左边encoder相应的feature map的高宽一致,这点和图中不一样。
class Up(nn.Module):def __init__(self, in_channels, out_channels, bilinear=True):super(Up, self).__init__()if bilinear:self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)else:self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)self.conv = DoubleConv(in_channels, out_channels)def forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:x1 = self.up(x1)# [N, C, H, W]diff_y = x2.size()[2] - x1.size()[2]diff_x = x2.size()[3] - x1.size()[3]# padding_left, padding_right, padding_top, padding_bottomx1 = F.pad(x1, [diff_x // 2, diff_x - diff_x // 2,diff_y // 2, diff_y - diff_y // 2])x = torch.cat([x2, x1], dim=1)x = self.conv(x)return x

1.2.4 OutConv模块

  • 用上述的DoubleConv模块、Down模块、Up模块就可以拼出UNet的主体网络结构了。

  • UNet网络的输出需要根据分割数量,整合输出通道。

  • 下图展示的是分类为2的情况

在这里插入图片描述

class OutConv(nn.Module):def __init__(self, in_channels, out_channels):super(OutConv, self).__init__()self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)def forward(self, x):return self.conv(x)

1.2.5 UNet网络构建

import torch
import torch.nn as nn
import torch.nn.functional as F
# pip install torchinfo
from torchinfo import summaryclass UNet(nn.Module):def __init__(self, n_channels, n_classes, bilinear=False):super(UNet, self).__init__()self.n_channels = n_channelsself.n_classes = n_classesself.bilinear = bilinearself.inc = DoubleConv(n_channels, 64)self.down1 = Down(64, 128)self.down2 = Down(128, 256)self.down3 = Down(256, 512)self.down4 = Down(512, 1024)self.up1 = Up(1024, 512, bilinear)self.up2 = Up(512, 256, bilinear)self.up3 = Up(256, 128, bilinear)self.up4 = Up(128, 64, bilinear)self.outc = OutConv(64, n_classes)def forward(self, x):x1 = self.inc(x)x2 = self.down1(x1)x3 = self.down2(x2)x4 = self.down3(x3)x5 = self.down4(x4)x = self.up1(x5, x4)x = self.up2(x, x3)x = self.up3(x, x2)x = self.up4(x, x1)logits = self.outc(x)return logitsif __name__ == '__main__':net = UNet(n_channels=1, n_classes=1)summary(model=net, input_size=(1, 1, 572, 572))
===============================================================================================
Layer (type:depth-idx)                        Output Shape              Param #
===============================================================================================
UNet                                          [1, 1, 564, 564]          --
├─DoubleConv: 1-1                             [1, 64, 568, 568]         --
│    └─Sequential: 2-1                        [1, 64, 568, 568]         --
│    │    └─Conv2d: 3-1                       [1, 64, 570, 570]         640
│    │    └─BatchNorm2d: 3-2                  [1, 64, 570, 570]         128
│    │    └─ReLU: 3-3                         [1, 64, 570, 570]         --
│    │    └─Conv2d: 3-4                       [1, 64, 568, 568]         36,928
│    │    └─BatchNorm2d: 3-5                  [1, 64, 568, 568]         128
│    │    └─ReLU: 3-6                         [1, 64, 568, 568]         --
├─Down: 1-2                                   [1, 128, 280, 280]        --
│    └─Sequential: 2-2                        [1, 128, 280, 280]        --
│    │    └─MaxPool2d: 3-7                    [1, 64, 284, 284]         --
│    │    └─DoubleConv: 3-8                   [1, 128, 280, 280]        221,952
├─Down: 1-3                                   [1, 256, 136, 136]        --
│    └─Sequential: 2-3                        [1, 256, 136, 136]        --
│    │    └─MaxPool2d: 3-9                    [1, 128, 140, 140]        --
│    │    └─DoubleConv: 3-10                  [1, 256, 136, 136]        886,272
├─Down: 1-4                                   [1, 512, 64, 64]          --
│    └─Sequential: 2-4                        [1, 512, 64, 64]          --
│    │    └─MaxPool2d: 3-11                   [1, 256, 68, 68]          --
│    │    └─DoubleConv: 3-12                  [1, 512, 64, 64]          3,542,016
├─Down: 1-5                                   [1, 1024, 28, 28]         --
│    └─Sequential: 2-5                        [1, 1024, 28, 28]         --
│    │    └─MaxPool2d: 3-13                   [1, 512, 32, 32]          --
│    │    └─DoubleConv: 3-14                  [1, 1024, 28, 28]         14,161,920
├─Up: 1-6                                     [1, 512, 60, 60]          --
│    └─ConvTranspose2d: 2-6                   [1, 512, 56, 56]          2,097,664
│    └─DoubleConv: 2-7                        [1, 512, 60, 60]          --
│    │    └─Sequential: 3-15                  [1, 512, 60, 60]          7,080,960
├─Up: 1-7                                     [1, 256, 132, 132]        --
│    └─ConvTranspose2d: 2-8                   [1, 256, 120, 120]        524,544
│    └─DoubleConv: 2-9                        [1, 256, 132, 132]        --
│    │    └─Sequential: 3-16                  [1, 256, 132, 132]        1,771,008
├─Up: 1-8                                     [1, 128, 276, 276]        --
│    └─ConvTranspose2d: 2-10                  [1, 128, 264, 264]        131,200
│    └─DoubleConv: 2-11                       [1, 128, 276, 276]        --
│    │    └─Sequential: 3-17                  [1, 128, 276, 276]        443,136
├─Up: 1-9                                     [1, 64, 564, 564]         --
│    └─ConvTranspose2d: 2-12                  [1, 64, 552, 552]         32,832
│    └─DoubleConv: 2-13                       [1, 64, 564, 564]         --
│    │    └─Sequential: 3-18                  [1, 64, 564, 564]         110,976
├─OutConv: 1-10                               [1, 1, 564, 564]          --
│    └─Conv2d: 2-14                           [1, 1, 564, 564]          65
===============================================================================================
Total params: 31,042,369
Trainable params: 31,042,369
Non-trainable params: 0
Total mult-adds (G): 233.39
===============================================================================================
Input size (MB): 1.31
Forward/backward pass size (MB): 2683.30
Params size (MB): 124.17
Estimated Total Size (MB): 2808.78
===============================================================================================

2 针对UNet模型结构的改进

2.1 和transformers结合

在这里插入图片描述

2.2 概率设计

在这里插入图片描述

2.1.3 丰富表示增强

在这里插入图片描述

2.4 主干设计增强

在这里插入图片描述

2.5 跳过连接增强

在这里插入图片描述

2.6 bottleneck增强

在这里插入图片描述

以上改进总结来自这篇综述,感兴趣的可以参考:Medical Image Segmentation Review: The success of U-Net

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Springboot 的几种配置文件形式

方式一:多个yml文件 步骤1:创建多个配置文件 application.yml #主配置文件 application-dev.yml #开发环境的配置 application-prod.yml #生产环境的配置 application-test.yml #测试环境的配置步骤2:applicaiton.yml中指定配置 在a…

时光机关:探秘Java中的Timer和TimerTask

欢迎来到我的博客,代码的世界里,每一行都是一个故事 时光机关:探秘Java中的Timer和TimerTask 前言Timer和TimerTask的基本概念Timer:TimerTask:为何它们是 Java 中任务调度的得力工具: Timer的使用方法创建…

2024【问题解决】Github 2024无法克隆git clone自从签了2F2安全协议之后

项目场景:ping通Github但没法clone–502 问题描述 提示:ping通Github但没法clone--502: 例如:git clone https://gitclone.com/l.git/*** $ git clone https://github.com/darrenpig/Yocto Cloning into Yocto_tutorial... fatal: unable to access https://gitclone.co…

Yii2中如何使用scenario场景,使rules按不同运用进行字段验证

Yii2中如何使用scenario场景,使rules按不同运用进行字段验证 当创建news新闻form表单时: 添加新闻的时候执行create动作。 必填字段:title-标题,picture-图片,description-描述。 这时候在model里News.php下rules规则…

web3时事粥报

比特币正成为更具有吸引力的通胀对冲工具 在通胀的宏观经济浪潮中,比特币正逐渐崭露头角,成为那些渴望多元化投资组合的投资者眼中的璀璨明星。Kooner 预测,2024年,各种宏观经济挑战可能进一步提升比特币、黄金和白银等资产的避险…

shell脚本一键部署docker

Docker介绍 Docker 是一个开源的平台,用于开发、交付和运行应用程序。它利用容器化技术,可以帮助开发人员更轻松地打包应用程序及其依赖项,并将其部署到任何环境中,无论是开发工作站、数据中心还是云中。以下是 Docker 的一些关键…

python中的文件操作2

文件遍历 在Python中,遍历文件通常指的是逐行读取文件中的内容。这种方式对于处理大型文件特别有用,因为它不需要一次性将整个文件加载到内存中。下面是几种常见的遍历文件内容的方法: 1. 使用with语句和for循环 这是最推荐的方式&#xf…

两天学会微服务网关Gateway-Gateway工作原理

锋哥原创的微服务网关Gateway视频教程: Gateway微服务网关视频教程(无废话版)_哔哩哔哩_bilibiliGateway微服务网关视频教程(无废话版)共计17条视频,包括:1_Gateway简介、2_Gateway工作原理、3…

ROS2学习(七) Foxy版本ros2替换中间件。

在ros2使用的过程中,一开始选用的foxy版本,后来发现,foxy版本的ros2有很多问题。一个是foxy版本已经停止维护了。另一个问题是这个版本有很多bug, 后续的版本在功能实现上做了很大的改动,甚至说进行了重写。修复的一些问题&#x…

【应用多元统计分析】--多元数据的描述和展示(R语言)

一元随机变量 我们用协方差来刻画两个变量的相关关系,这里指的是线性相关关系。 对于一元随机变量的可视化最简单的就是散点图,大致可以看出X和Y之间的相关关系。如果想更好的看X、Y之间的相关关系,可以画二维的散点图。 总结: 均…

Postman如何做接口测试?你居然还不知道

Postman如何做接口测试1:如何导入 swagger 接口文档 在使用 postman 做接口测试过程中,测试工程师会往界面中填入非常多的参数,包括 url 地址,请求方法,消息头和消息体等一系列数据,在请求参数比较多的情况…

[项目设计] 从零实现的高并发内存池(五)

🌈 博客个人主页:Chris在Coding 🎥 本文所属专栏:[高并发内存池] ❤️ 前置学习专栏:[Linux学习] ⏰ 我们仍在旅途 ​ 目录 8 使用定长内存池脱离new 9. 释放对象时不传大小 10.性能优化 10.1…

API协议设计的十种技术

文章目录 前言1.REST2. GraphQL3. gRPC (google Remote Procedure Calls)4.Webhook5. 服务端的事件发送——SSE(Servver - Sent Events )6. EDI(Electronic Data Interchange)7. 面向API 的事件驱动设计8. WebSocket9.简单对象访问协议(SOAP)10. Message Queuing Telemetry …

vue系列——vscode,node.js vue开发环境搭建

第一步安装node.js 推荐使用nvm进行node.js 的安装 nvm(Node.js version manager) 是一个命令行应用,可以协助您快速地 更新、安装、使用、卸载 本机的全局 node.js 版本。 可以去网上查找相关版本 我这里使用 nvm-setu… 链接:https://pan.baidu.com/s/1UEUtmzw5x…

饮料换购 刷题笔记

直接开个计数器mask 每当饮料现存数-1&#xff1b; cnt;且mask; 一旦mask达到3 饮料现存数 计数器清零3 代码 #include <iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int main(){ int n; …

【STM32】HAL库 CubeMX教程---基本定时器 定时

目录 一、基本定时器的作用 二、常用型号的TIM时钟频率 三、CubeMX配置 四、编写执行代码 实验目标&#xff1a; 通过CUbeMXHAL&#xff0c;配置TIM6&#xff0c;1s中断一次&#xff0c;闪烁LED。 一、基本定时器的作用 基本定时器&#xff0c;主要用于实现定时和计数功能…

【重要!!退税!退税!】一年一度个人所得税综合年度汇算开始了!

目录标题 如何退税&#xff1f;2023年度个人所得税综合所得汇算清缴操作指南汇算准备标准申报 退税骗局&#xff1f;1.“您有一笔退税待领取”骗局2.“专业人员帮你多退税”骗局3.“诱导填报虚假个税信息”骗局4.“税务稽查人员联系你”骗局 如何退税&#xff1f; 2023年度个人…

Java引用传递及基本应用

在 Java 中&#xff0c;传递参数的方式主要有两种&#xff1a;值传递&#xff08;传递的是对象的引用值&#xff09;和引用传递。本教程将重点介绍 Java 中的引用传递以及其基本应用。 1. 引用传递概念 在 Java 中&#xff0c;所有的方法参数都是通过值传递的。对于对象类型的…

腾讯云服务器和阿里云服务器价格测评_2024年费用大PK

2024年阿里云服务器和腾讯云服务器价格战已经打响&#xff0c;阿里云服务器优惠61元一年起&#xff0c;腾讯云服务器61元一年&#xff0c;2核2G3M、2核4G、4核8G、4核16G、8核16G、16核32G、16核64G等配置价格对比&#xff0c;阿腾云atengyun.com整理阿里云和腾讯云服务器详细配…

[数据结构]OJ用队列实现栈

225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; 官方题解&#xff1a;https://leetcode.cn/problems/implement-stack-using-queues/solutions/432204/yong-dui-lie-shi-xian-zhan-by-leetcode-solution/ 首先我们要知道 栈是一种后进先出的数据结构&#xff0c…