PyTorch 深度学习实战
方远
LINE China 数据科学家
10381 人已学习
新⼈⾸单¥59
登录后,你可以任选3讲全文学习
课程目录
已完结/共 32 讲
开篇词 (1讲)
PyTorch 深度学习实战
15
15
1.0x
00:00/00:00
登录|注册

20 | 图像分割(下):如何构建一个图像分割模型?

你好,我是方远。
在上一节课中,我们掌握了图像分割的理论知识,你是不是已经迫不及待要上手体验一下,找找手感了呢?
今天我们就从头开始,来完成一个图像分割项目。项目的内容是,对图片中的小猫进行语义分割。为了实现这个项目,我会引入一个简单但实用的网络结构:UNet。通过这节课的学习,你不但能再次体验一下完整机器学习的模型实现过程,还能实际训练一个语义分割模型。
课程代码你可以从这里下载。

数据部分

我们还是从机器学习开发三件套:数据、训练、评估说起。首先是数据准备部分,我们先对训练数据进行标记,然后完成数据读取工作。

分割图像的标记

之前也提到过,图像分割的准备相比图像分类的准备更加复杂。那我们如何标记语义分割所需要的图片呢?在图像分割中,我们使用的每张图片都要有一张与之对应的 Mask,如下所示:
上节课我们说过,Mask 就是含有像素类别的特征图。结合这里的示例图片,我们可以看到,Mask 就是原图所对应的一张图片,它的每个位置都记录着原图每个位置对应的像素类别。对于 Mask 的标记,我们需要使用到 Labelme 工具。
标记的方法一共包括七步,我们挨个看一下。
第一步,下载安装Labelme。我们按照 Github 中的安装方式进行安装即可。如果安装比较慢的话,你可以使用国内的镜像(例如清华的)进行安装。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了如何使用UNet实现图像分割项目。作者首先介绍了数据准备部分,包括对训练数据进行标记和数据读取工作。标记工作需要使用Labelme工具,作者详细介绍了标记的七个步骤,并提供了相应的代码示例。数据读取部分则使用PyTorch将数据读入,并展示了相关的代码。接着,文章介绍了UNet的网络结构,包括Encoder-Decoder类型的分割网络、重复的卷积块、上采样过程以及对二分类问题的处理。作者还介绍了Dice Loss作为常用的损失函数,并提供了相应的代码实现。最后,文章展示了模型训练的流程,包括模型实例化、损失函数定义、优化方法选择以及训练过程的代码示例。整篇文章结合理论知识和实际操作,为读者提供了一个完整的图像分割项目实现过程。文章内容涵盖了数据准备、网络结构、损失函数和训练流程,适合对图像分割感兴趣的读者快速了解UNet的实现方法。文章还介绍了模型预测和评估的过程,以及对学习任务的小结和每课一练的建议,为读者提供了进一步学习和实践的方向。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《PyTorch 深度学习实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(22)

  • 最新
  • 精选
  • 克bug体质
    老师你好,Loss也要放到放到gpu后也会报和之前一样的错误:【 Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu! (when checking argument for argument weight in method wrapper__cudnn_batch_norm) File "D:\U-Net\unet.py", line 25, in forward x = nn.BatchNorm2d(num_features=self.features)(x) File "D:\U-Net\unet.py", line 67, in forward conv_encoder_1_1 = self.conv_encoder_1(x) File "D:\U-Net\train.py", line 50, in main y_pred = unet(x) File "D:\U-Net\train.py", line 136, in <module> main(args) 】 我尝试在unet.py文件中的创建class UNet中self.conv_encoder_1 = Block(in_channels, features)改为:self.conv_encoder_1 = Block(in_channels, features).to(device)后也没解决这个问题,会报一样的错。

    作者回复: hi,原因是如果在forward中使用了nn调用网络中的层的话,它默认会放在cpu上计算。 解决的办法就是把bn和relu放到__init__中,如下代码所示。 class Block(nn.Module): def __init__(self, in_channels, features): super().__init__() self.features = features self.conv1 = nn.Conv2d( in_channels=in_channels, out_channels=features, kernel_size=3, padding='same', ) self.bn1 = nn.BatchNorm2d(num_features=self.features) self.relu1 = nn.ReLU() self.conv2 = nn.Conv2d( in_channels=features, out_channels=features, kernel_size=3, padding='same', ) self.bn2 = nn.BatchNorm2d(num_features=self.features) self.relu2 = nn.ReLU() def forward(self, input): x = self.conv1(input) x = self.bn1(x) x = self.relu1(x) x = self.conv2(x) x = self.bn2(x) x = self.relu2(x) return x 同理U-Net中的pooling也要放到__init__()中。

    2022-02-14
    4
    5
  • ..................
    真的很棒,我要亲手操作一遍

    作者回复: ^^ 感谢支持,加油

    2022-05-06
    2
  • 蓝色天空 好萌啊
    老师你好,请问有这节课的完整代码地址吗?

    作者回复: hello,你好,我传到这里了。 https://github.com/syuu1987/geekTime-semantic-segmentation/tree/main

    2022-01-21
    1
  • Geek_a95f0e
    方老师,可不可以解释一下自定义类中 super().__init__() 和super(class_name,self).__init__() 有什么区别?

    作者回复: hi 你好。感谢你的留言。 这块可能我误导你了,都是一样的。 Python3中super().__init__()代替了Python2中的super(class_name,self).__init__()

    2021-12-13
    2
    1
  • vcjmhg
    老师您好,我自己有尝试复现deeplab v3+这个比较主流的语义分割网络,然后发现针对一些尺寸较大的目标其分割效果还是比较不错的,但是对小的目标分割效果很差,甚至好多时候都分割不到,请问针对这种小目标,除了在数据集上做处理外,还有哪些好的处理或者优化方法呢?

    作者回复: hello,小物体分割不好的一个原因是在backbone的特征提取过程中,小物体的信息已经被忽略了。 举个例子,假设输入是256x256的数据,经过多层的特征提取后特征图依次为128x128,64x64,32x32,16x16。有可能小物体的信息在32x32那里就消失了。 从这个角度出发,可以看看能不能调整网络最小的特征图的大小,来解决小物体分割不好的问题(比如Unet中,删除最后一次下采样)。另外,你也要看看小物体是不是resize到训练数据尺寸的时候已经没有了。

    2021-11-27
    2
    1
  • Geek_niu
    老师你好,请问数据准备中,标记好的图片有什么作用呢,谢谢

    作者回复: 你好,标记好数据就是label,也就是告诉模型需要学什么。

    2024-03-05归属地:北京
  • 学渣汪在央企打怪升级
    老师你好,非常感谢老师的耐心,我成功的跑通了自己的一个实例。但目前遇到如下问题,麻烦老师有空的时候能提点一下。 使用unet训练,还是老师的代码,差不多是40 epoch之后,预测出来的图像分割,预测对的点的概率非常集中,大概都在[1,...,0.99999535 0.9999949 0.9999943 0.9999933 0.999992 0.9999907 0.9999889 0.99998796 0.99998724 0.9999856 0.99998343 0.99998116 0.99997723 0.99997675 0.9999764 0.9999703 0.9999577 0.9999535 0.9999486 0.9999453 0.999944 0.99992955 0.99992025 0.9999181 0.9999089 0.9999058 0.9998952 0.99984705 0.9998202 0.9996326 0.9996008 0.9995078 0.99922884 0.9967386 0.99539775 0.995391 0.991197 0.9906724 0.9892915 0.9892553 0.9782649 0.96968466 0.95937544 0.9416338 0.93462884 0.91682875 0.8949944 0.87671226 0.75603426 0.7475551 0.7284518 0.6420492 0.62891126 0.5325708 0.52544403, ... ] 请教一下,这样是不是过拟合了。有没有什么好的方法可以使得预测概率有阈值可选?丰富样本吗?麻烦老师了。

    作者回复: 感觉不像过拟合。数据标的没有问题吗?可以看看前几个epoch结果什么样子。

    2022-11-29归属地:辽宁
    2
  • 学渣汪在央企打怪升级
    方老师,请教一下: 如果我不是对图片进行语义分割,而是对类似视频,比如(400,384,288)的数据立方体进行语义分割,还可以使用unet吗?如果使用的话,这个示例里的通道数是从3->512->32->1,那么这种400通道的该怎么处理? 谢谢。

    作者回复: 理论上,代码是肯定能执行通过的。不过我对视频没有研究,不知道分割效果如何。

    2022-10-30归属地:北京
  • gavin
    方老师,请问一下如果是多分类,特征图输出要怎么弄?

    作者回复: 您好,多分类时让网络最终输出的个数等于类别数,然后加softmax即可。

    2022-09-16归属地:辽宁
  • John(易筋)
    predict_single.py 代码改了一下文件的相对路径可以跑通,发现output.jpg 大小为256x256. 原图微1024x640. 请教方老师,为啥语义分割出来的图片不是原始图片的等比缩小呢? 谢谢 ``` import torch import numpy as np from PIL import Image img_size = (256, 256) unet = torch.load('./ckpts/unet_epoch_51.pth') unet.eval() im = np.asarray(Image.open('./data/JPEGImages/6.jpg').resize(img_size)) im = im / 255. im = im.transpose(2, 0, 1) im = im[np.newaxis, :, :] im = im.astype('float32') output = unet(torch.from_numpy(im)).detach().numpy() output = np.squeeze(output) output = np.where(output>0.5, 150, 0).astype(np.uint8) print(output.shape, type(output)) im = Image.fromarray(output) im.save('./output.jpg') ```

    作者回复: hi,你好。感谢留言。 这个尺寸是取决于网络的设计。 256x256的分割结果是可以resize回1024x640的。 当然,如果你设计的网络的输入输出支持1024x640的等比例缩放,也是可以的。

    2022-08-23归属地:北京
收起评论
显示
设置
留言
22
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部