【图像处理】经营您的第一个U-Net以进行图像分割

news2024/11/24 3:48:28

一、说明

        AI厨师们,今天您将学习如何准备计算机视觉中最重要的食谱之一:U-Net。本文将叙述:1语义与实例分割,2 图像分割中还使用了其他损失,例如Jaccard损失,焦点损失;3 如果2D图像分割对您来说太容易了,您可以查看3D图像分割,因为模型要大得多,因此要困难得多。

必要的项目代码在下面地址获取,

1)你可以在Github或Google Colab上找到完整的代码。

2)我们将把U-Net应用于Kaggle的MRI分割数据集,可在此处访问:

脑部MRI图像与手动FLAIR异常分割掩模一起

www.kaggle.com

二、食谱的成分 

  1. 数据集的探索
  2. 创建数据集和数据加载器类
  3. 架构的创建
  4. 检查损失(DICE和二进制交叉熵)
  5. 结果
  6. 烹饪后技巧,为事情增添趣味!

三、数据集的探索

        我们得到了一组(255 x 255)MRI扫描的2D图像,以及它们相应的掩模,我们必须将每个像素分类为0(理智)或1(肿瘤)。

以下是一些示例:

第一行:肿瘤,第二行:理智的受试者

四、数据集和数据加载器类

        这是您在涉及神经网络的每个项目中找到的步骤。我们

4.1 数据集类


import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader

class BrainMriDataset(Dataset):
    def __init__(self, df, transforms):
        # df contains the paths to all files
        self.df = df
        # transforms is the set of data augmentation operations we use
        self.transforms = transforms
        
    def __len__(self):
        return len(self.df)
    
    def __getitem__(self, idx):
        image = cv2.imread(self.df.iloc[idx, 1])
        mask = cv2.imread(self.df.iloc[idx, 2], 0)
        
        augmented = self.transforms(image=image, 
                                    mask=mask)
 
        image = augmented['image'] # Dimension (3, 255, 255)
        mask = augmented['mask']   # Dimension (255, 255)

        # We notice that the image has one more dimension (3 color channels), so we have to one one "artificial" dimension to the mask to match it
        mask = np.expand_dims(mask, axis=0) # Dimension (1, 255, 255)
        
        return image, mask

4.2 数据加载器

        现在我们已经创建了 Dataset 类来重塑张量,我们需要首先定义训练集(用于训练模型)、验证集(用于监视训练和避免过度拟合)和一个测试集,以最终评估模型在看不见的数据上的性能。

# Split df into train_df and val_df
train_df, val_df = train_test_split(df, stratify=df.diagnosis, test_size=0.1)
train_df = train_df.reset_index(drop=True)
val_df = val_df.reset_index(drop=True)

# Split train_df into train_df and test_df
train_df, test_df = train_test_split(train_df, stratify=train_df.diagnosis, test_size=0.15)
train_df = train_df.reset_index(drop=True)

train_dataset = BrainMriDataset(train_df, transforms=transforms)
train_dataloader = DataLoader(train_dataset, batch_size=32, shuffle=True)

val_dataset = BrainMriDataset(val_df, transforms=transforms)
val_dataloader = DataLoader(val_dataset, batch_size=32, shuffle=False)

test_dataset = BrainMriDataset(test_df, transforms=transforms)
test_dataloader = DataLoader(test_dataset, batch_size=32, shuffle=False)

4.3 U-Net 架构

U-Net架构是图像分割任务的强大模型,是一种卷积神经网络(CNN),其名称来自其U形结构。U-Net最初由Olaf Ronneberger等人在2015年题为“U-Net:用于生物医学图像分割的卷积网络”的论文中开发。

其结构涉及编码(下采样)路径和解码(上采样)路径。

如今,U-Net仍然是一个非常成功的模式,它的成功来自两个主要因素:

  1. 对称结构(U形)
  2. 前向连接(图片上的灰色箭头)

前向连接的主要思想是,随着我们在图层中越来越深入,我们会丢失一些关于原始图像的信息。然而,我们的任务是分割图像,我们需要精确的图像来分类每个像素。这就是为什么我们在对称解码器层的编码层的每一层重新注入图像的原因。

以下是在 Pytorch 中对其进行编码的方法:

train_dataset = BrainMriDataset(train_df, transforms=transforms)
train_dataloader = DataLoader(train_dataset, batch_size=32, shuffle=True)

val_dataset = BrainMriDataset(val_df, transforms=transforms)
val_dataloader = DataLoader(val_dataset, batch_size=32, shuffle=False)

test_dataset = BrainMriDataset(test_df, transforms=transforms)
test_dataloader = DataLoader(test_dataset, batch_size=32, shuffle=False)


class UNet(nn.Module):

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

        # Define convolutional layers
        # These are used in the "down" path of the U-Net,
        # where the image is successively downsampled
        self.conv_down1 = double_conv(3, 64)
        self.conv_down2 = double_conv(64, 128)
        self.conv_down3 = double_conv(128, 256)
        self.conv_down4 = double_conv(256, 512)

        # Define max pooling layer for downsampling
        self.maxpool = nn.MaxPool2d(2)

        # Define upsampling layer
        self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)

        # Define convolutional layers
        # These are used in the "up" path of the U-Net,
        # where the image is successively upsampled
        self.conv_up3 = double_conv(256 + 512, 256)
        self.conv_up2 = double_conv(128 + 256, 128)
        self.conv_up1 = double_conv(128 + 64, 64)

        # Define final convolution to output correct number of classes
        # 1 because there are only two classes (tumor or not tumor)
        self.last_conv = nn.Conv2d(64, 1, kernel_size=1)

    def forward(self, x):
        # Forward pass through the network

        # Down path
        conv1 = self.conv_down1(x)
        x = self.maxpool(conv1)
        conv2 = self.conv_down2(x)
        x = self.maxpool(conv2)
        conv3 = self.conv_down3(x)
        x = self.maxpool(conv3)
        x = self.conv_down4(x)

        # Up path
        x = self.upsample(x)
        x = torch.cat([x, conv3], dim=1)
        x = self.conv_up3(x)
        x = self.upsample(x)
        x = torch.cat([x, conv2], dim=1)
        x = self.conv_up2(x)
        x = self.upsample(x)
        x = torch.cat([x, conv1], dim=1)
        x = self.conv_up1(x)

        # Final output
        out = self.last_conv(x)
        out = torch.sigmoid(out)

        return out

五、损失和评估标准

        像每个神经网络一样,有一个目标函数,一个损失,我们用梯度下降来最小化。我们还引入了评估标准,这有助于我们训练模型(如果它没有改善,比如说连续 3 个 epoch,那么我们在模型过度拟合的情况下进行训练)。

        这里要保留本段的两点主要内容:

  1. 损失函数是两个损失函数的组合(DICE损失,二进制交叉熵)
  2. 评估功能是DICE分数,不要与DICE损失混淆

        如果你做到了这一步,恭喜你!你已经做了最努力的事情。现在,让我们训练模型并观察结果。

5.1 骰子损失 

骰子损失

备注:我们添加了一个平滑参数(epsilon)以避免被零除。

5.2 二进制交叉熵损失:

        公元前,最后,我们的总损失是:

        让我们一起实现它:

def dice_coef_loss(inputs, target):
    smooth = 1.0
    intersection = 2.0 * ((target * inputs).sum()) + smooth
    union = target.sum() + inputs.sum() + smooth

    return 1 - (intersection / union)


def bce_dice_loss(inputs, target):
    inputs = inputs.float()
    target = target.float()
    
    dicescore = dice_coef_loss(inputs, target)
    bcescore = nn.BCELoss()
    bceloss = bcescore(inputs, target)

    return bceloss + dicescore

5.3 评估标准(骰子系数):

        我们使用的评估函数是DICE分数。它介于 0 和 1 之间,1 是最好的。

        骰子分数的插图,其数学实现如下:

def dice_coef_metric(inputs, target):
    intersection = 2.0 * (target * inputs).sum()
    union = target.sum() + inputs.sum()
    if target.sum() == 0 and inputs.sum() == 0:
        return 1.0

    return intersection / union

六、训练循环

def train_model(model_name, model, train_loader, val_loader, train_loss, optimizer, lr_scheduler, num_epochs):  
    
    print(model_name)
    loss_history = []
    train_history = []
    val_history = []

    for epoch in range(num_epochs):
        model.train()  # Enter train mode
        
        # We store the training loss and dice scores
        losses = []
        train_iou = []
                
        if lr_scheduler:
            warmup_factor = 1.0 / 100
            warmup_iters = min(100, len(train_loader) - 1)
            lr_scheduler = warmup_lr_scheduler(optimizer, warmup_iters, warmup_factor)
        
        # Add tqdm to the loop (to visualize progress)
        for i_step, (data, target) in enumerate(tqdm(train_loader, desc=f"Training epoch {epoch+1}/{num_epochs}")):
            data = data.to(device)
            target = target.to(device)
                      
            outputs = model(data)
            
            out_cut = np.copy(outputs.data.cpu().numpy())

            # If the score is less than a threshold (0.5), the prediction is 0, otherwise its 1
            out_cut[np.nonzero(out_cut < 0.5)] = 0.0
            out_cut[np.nonzero(out_cut >= 0.5)] = 1.0
            
            train_dice = dice_coef_metric(out_cut, target.data.cpu().numpy())
            
            loss = train_loss(outputs, target)
            
            losses.append(loss.item())
            train_iou.append(train_dice)

            # Reset the gradients
            optimizer.zero_grad()
            # Perform backpropagation to compute gradients
            loss.backward()
            # Update the parameters with the computed gradients
            optimizer.step()
    
            if lr_scheduler:
                lr_scheduler.step()
        
        val_mean_iou = compute_iou(model, val_loader)
        
        loss_history.append(np.array(losses).mean())
        train_history.append(np.array(train_iou).mean())
        val_history.append(val_mean_iou)
        
        print("Epoch [%d]" % (epoch))
        print("Mean loss on train:", np.array(losses).mean(), 
              "\nMean DICE on train:", np.array(train_iou).mean(), 
              "\nMean DICE on validation:", val_mean_iou)
        
    return loss_history, train_history, val_history

七、结果

        让我们评估一下我们在肿瘤受试者上的模型:

        结果看起来还不错!我们可以看到,该模型肯定已经学习了一些关于图像结构的有用信息。但是,它可以更好地优化细分,这可以通过我们稍后将审查的更高级技术来实现。

八、后记!

        如果你到目前为止已经成功了,恭喜你!如果你想为最后一餐增添趣味,这里有一些有趣的资源可以看看:

  • 语义与实例分割
  • 图像分割中还使用了其他损失,例如Jaccard损失,焦点损失
  • 如果2D图像分割对您来说太容易了,您可以查看3D图像分割,因为模型要大得多,因此要困难得多。
  • nnUNet,在许多不同的领域都是最先进的。自U-Net以来,该神经网络没有引入突破性的新功能,但是它的设计非常好,并测试了UNet的不同配置,并将它们集成以构建最强大的基线。

参考来源

  1. Ronneberger O.,Fischer P.,Brox T.(2015)U-Net:用于生物医学图像分割的卷积网络。在:Navab N.,Hornegger J.,Wells W.,Frangi A.(编辑)医学图像计算和计算机辅助干预 - MICCAI 2015。MICCAI 2015.计算机科学讲义,第 9351 卷。斯普林格,湛。U-Net: Convolutional Networks for Biomedical Image Segmentation | SpringerLink
  2. Brain MRI segmentation | Kaggle
  3. Brain MRI | Data Visualization | UNet | FPN | Kaggle
  4. Isensee,Fabian,Paul F. Jaeger,Simon AA Kohl,Jens Petersen和Klaus H. Maier-Hein。“nnU-Net:一种基于深度学习的生物医学图像分割的自我配置方法。”自然方法 18,第 2 期(2021):203–211。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

刘铁猛C#教程笔记——详解类型,变量和对象第二节

C#类型派生谱系 C#语言五大基本类型&#xff1a; 1.类类型&#xff1a;由class关键字声明的类型&#xff0c;常见的类类型有&#xff1a;string&#xff0c;object 2.结构体类型&#xff1a;由struct关键字声明的类型&#xff0c;常见的结构体类型有&#xff1a;int&#xff0c…

Vue3 基本语法

尤雨溪 文章目录 前言MVVM框架认识Vue文本渲染指令 v-text属性绑定指令title 属性动态绑定class属性动态绑定style 属性动态绑定 事件绑定 事件名条件渲染指令v-ifv-if 和 v-showv-else 和 v-else-if 循环遍历指令 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&am…

Documentation 代码注释文档规范

1. 代码注释文档规范的添加 /// 代码规范文档 struct DocumentationBootcamp: View {// MARK: PROPERTIESState var data:[String] ["Apples", "Oranges", "Bananas"]State var showAlert: Bool false// MARK: BODY// Code Folding : 代码折叠…

Spring Boot原理分析(二):IoC

文章目录 〇、IoC思想和IoC容器IoC体现了什么思想什么是IoC容器 一、Spring IoC容器的继承层次1.BeanFactory2.ListableBeanFactory3.HierarchicalBeanFactory4.ApplicationContext5.常用的ApplicationContext的实现类ClassPathXmlApplicationContext&#xff08;基于XML配置&a…

FreeRTOS详细目录结构和作用

FreeRTOS各个目录的功能简介 1. Demo目录&#xff1a;这个目录下主要包含了大量官方支持的微控制器和不同平台上的项目&#xff0c;包括常用的stm32和keil平台下进行开发的项目。我主要使用的是CORTEX_STM32F103_Keil和common两个目录&#xff0c;对系统进行精简的话可以只保留…

光伏行业中的设备自动化系统EAP:优化生产流程的重要工具

随着清洁能源的需求不断增长&#xff0c;光伏行业作为可再生能源的重要组成部分&#xff0c;正迅速发展。为了满足日益增长的市场需求&#xff0c;光伏企业需要提高生产效率、降低成本&#xff0c;并保证产品的质量和可靠性。 图.光伏面板&#xff08;pexels&#xff09; 在这…

校园跑腿小程序怎么做

校园跑腿小程序是为了方便学生在校园内解决各类需求而设计的一款应用程序。该小程序具有多种功能&#xff0c;包括校园跑腿、二手市场、骑手接单、校园表白墙、下单支付、学校代理以及佣金抽成等。 1. 校园跑腿&#xff1a; 该小程序提供校园内的跑腿服务&#xff0c;学生可以…

【开源项目】中后台开发框架vue-admin-work-pro

vue-admin-work-pro 基本介绍 基于Vue3、Vite、TypeScript、NaiveUI、Vuex等最新技术栈开发的开箱即用的中后台前端框架。 内置常用模板&#xff0c;无需考虑交互排版&#xff0c;助你高效开发样式美观大方&#xff0c;无缝对接 Naive Ui&#xff0c;随意组合页面&#xff0c…

ME GO小车

ME GO小车 ⚫ 体积小巧 ⚫ 集成多种传感器和执行器 ⚫ Mixly图形化编程 避障检测、自动巡线、灯光显示、 声音报警、自动测距、物联遥控等 ME GO小车——俯视图 ME GO小车——车底 ME GO CE 以上选自芯”向未来 元控智联挑战赛&#xff08;小学组&#xff09;赛事介绍资料二…

操作系统Linux—day03

操作命令 搜索文件 find命令 1按照文件名称搜索 语法&#xff1a;find [路径] -name “文件名 ” -print 打印出所有查找文件名的路径 2按照更新时间搜索 语法&#xff1a; find [路径] -mtime 3 -print 查找更改时间为3天前的文件&#xff0c;3表示3天前&#xff0c;-3…

python 第三方包安装

1 MAC 打开终端 ,证明安装上python了 2 直接输入 pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark 其中 python3. 多的版本 需要 pip3 也可以直接用 pip 试一试 -i https://pypi.tuna.tsinghua.edu.cn/simple : 为 国内镜像地址 pyspark 第三方包

苹果pencil和普通的有什么区别?ipad第三方电容笔推荐

苹果原装电容笔和那种只具备倾斜压感的平替电容笔不一样&#xff0c;平替电容笔并没有具备重力压感。但是&#xff0c;如果你并不经常需要绘画的话&#xff0c;那么你也不必花费太多的金钱来购买一支价格如此贵的苹果电容笔&#xff0c;选择一款平替电容笔即可。在这里&#xf…

c++11 标准模板(STL)(std::basic_istream)(六)

定义于头文件 <istream> template< class CharT, class Traits std::char_traits<CharT> > class basic_istream : virtual public std::basic_ios<CharT, Traits> 类模板 basic_istream 提供字符流上的高层输入支持。受支持操作包含带格式…

阿里云容蓓:DCDN 助力云原生时代的应用构建及最佳实践

在数字化转型速度不断提升的今天&#xff0c;大带宽、低时延、高并发的场景不断涌现&#xff0c;内容分发网络(Content Delivery Network&#xff0c;CDN)应用需求还在不断攀升&#xff0c;打造更高质量的CDN服务将成为新时代产业竞争的关键所在。亚太CDN峰会自2012年第一届以来…

python_day7_画图

json数据与python字典的相互转换 import json列表&#xff0c;其中每个元素均为一个字典 data [{"name": "张三", "age": 10},{"name": "李四", "age": 13},{"name": "jay", "age&qu…

【论文阅读】Scaling Laws for Neural Language Models

前言 本文简要介绍 Scaling law 的主要结论原文地址&#xff1a;Scaling Laws for Neural Language Models个人认为不需要特别关注公式内各种符号的具体数值&#xff0c;而更应该关注不同因素之间的关系&#xff0c;比例等 Summary Performance depends strongly on scale, w…

windows安装netcat

netcat 就是 nc netcat官网&#xff1a;https://eternallybored.org/misc/netcat/ 将下载的netcat解压到C:\Program Files重命名为netcat &#xff08;安装位置自定义&#xff09; 配置一下环境变量&#xff0c;让nc命令可以在任何地方使用 在系统变量中加入netcat的启动路径 …

【面试题37】优化MYSQL数据库的方法有哪些

文章目录 一、前言二、优化的方向2.1 数据库设计优化2.2 查询优化2.3 硬件优化2.4 配置优化2.5. 定期维护和监控 三、MySQL查询优化的一些方向3.1 使用索引3.2 优化查询语句3.3 避免使用SELECT *3.4 分析查询计划3.5 设置合适的缓存大小3.6 分区表3.7 使用连接池3.8 定期优化数…

记录一组excel函数-将同一个id的多组数据整合到一格并用逗号隔开

初始数据如上图&#xff0c;用函数整理成下图 主要涉及两点&#xff1a;去重和集合 1.去重&#xff1a; 方法1: UNIQUE函数 使用方法&#xff1a;输入函数UNIQUE&#xff08;范围1:范围2&#xff09; 方法2:数据->删除重复项 使用方法&#xff1a;选中需要删除重复项的列&a…

Android :Activity生命周期

MainActivity .java import android.app.Activity; import android.os.Bundle; import android.util.Log; import java.util.function.LongToDoubleFunction; public class MainActivity extends Activity { //日志标记 private String TAG this.getClass().getSimpleNa…