task 7_修改 FCN(图像读取使用开源代码)

1. FCN 论文学习

1.1 写作背景

卷积网络是视觉处理中可以有效生成多层特征的架构,是最前沿的技术。因此作者想构造一个“全卷积网络”,来处理任意尺寸的输入图片,并生成相应尺寸的输出。

通过改造当下热门的分类网络(VGG,AlexNet,GoogleLeNet等),我们可以让它们的架构应用于图像分类任务。

该网络得到很好的语义分割的效果,另外处理每张图片的速度也很快只需要五分之一秒。
通过skip-architecture,我们可以把深层的输出特征(更全面但更粗糙)与浅层的输出特征(更细节但更精确)相结合。这种操作有利于生成更加准确、细节饱满的分割结果。

1.2 FCN架构

卷积网络里每一层的数据都是三维数组。如果这三维为h × w × d,则h和w是图片的高和宽,d为图片的特征或者是通道数。

第一层输入图片,图片维度为[高, 宽, 色彩通道数]。网络深层的每个数据都与网络浅层的一片数据有关,这就叫做感知野

  • FCN的输入更灵活。普通的LeNet,AlexNet等卷积网络只能接受固定维度的输入,而FCN可以接受任意维度的输入
  • FCN的运算更高效。
    • 进行预测时,在普通的GPU上对某个227227的图片进行预测,AlexNet耗费1.2ms;而FCN从500500的图片中生成10*10的输出只需要22ms,其中的效率相差了5倍。
    • 进行后向传播时,AlexNet需要2.4ms,而FCN只需要37ms。

1.3 把粗糙输出转换为原尺寸图片

对于如何把coarse output转换得到dense prediction,作者研究过3种方案:

  1. shift-and-stitch
  2. filter rarefaction
  3. deconvolution
    具体见分析三种粗糙图片转换为原尺寸图案的方案

3.1 shift-and-stich

另外,此文还详细分析了shift-and-stich方案:
shift-and-stich解释

1.4 Patchwise training is loss sampling

参考[深度学习论文阅读]Fully Convolutional Networks for Semantic Segmentation(FCN网络)
通常做语义分割的方法都是使用Patchwise训练,就是指将一张图片中的重要部分裁剪下来进行训练以避免整张照片直接进行训练所产生的信息冗余,这种方法有助于快速收敛

但是本文章提出直接使用整张图片也许可能使效果更好而Patchwise可能使信息受损(所以此节名为Patchwise training is loss sampling)。

这里一个直觉得想法是一整张图像可能是有空间相关性的,那么Patchwise就减少了这种相关性

1.5 Segmentation Architecture(skip Architecture)


FCN的skip Achitecture有三种架构:FCN-32s、FCN-16s和FCN-8s。skip Achitecture通过把深层数据的结果与浅层的准确结果相结合,再恢复到原图的输出,可以生成更准确的结果。

  1. FCN-32s是指用逆卷积把conv7放大到32倍。
  2. FCN-16s是指先用逆卷积把conv7放大到2倍,将放大结果与pool4的输出相加,再把相加结果放大16倍。
  3. 同理,FCN-8s是指用逆卷积把conv7放大到2倍,将放大结果与pool4的输出相加,再把相加结果用逆卷积放大两倍,与pool3相加。最后把第二次的相加结果放大8倍到原来的图像尺寸。

根据试验,FCN-8s的效果最好,而如果再叠加层数,反而效果变差了,所以论文做到此处就停止了。
这也是FCN提升预测结果最关键的部分。

2. FCN重写

2.1 概括

在参考了github上别人的FCN框架后,我认真研究了它的代码,并结合自己的想法,重新写了一遍。
我的代码主要分为以下几个模块:

  1. FCN.py, FCN_down_sizing.py. FCN_down_sizing.py定义了FCN网络中downsizing的部分,而FCN.py结合downsizing的部分来组装FCN-8s, FCN-16s和FCN-32s
  2. read_MITSceneParsingData.py.用于为每个image找到对应的annotation,并把这些关系组保存为一个.pickle文件,供处理图片时读取。
  3. BatchDatsetReader.py.给定image与annotation相互对应的关系组,找到并读取这些图片的数据。
  4. FCN_train.py, FCN_test.py, FCN_infer.py. 显然是用于train, test和infer的。

2.2 FCN.py, FCN_down_sizing.py

我把FCN网络看做两个部分:

  1. downsizing,通过卷积使矩阵的尺寸缩小
  2. upscaling,通过逆卷积使图片恢复原本的尺寸。

不管是FCN-8s, FCN-16s还是FCN-32s,他们都需要用到把图片downsize的过程,所以FCN_down_sizing.py定义了FCN中进行downsize的这个部分。
而FCN.py则利用FCN_down_sizing.py的部分组装成FCN-8s, FCN-16s和FCN-32s(由于时间缘故,只完成了FCN-8s)。

  • downsize的部分是用vgg19搭建的(论文里是用vgg16,但效果差不多)。
  • FCN_down_sizing.py中的get_FCN_8s_net则实现了论文中的sky-architecture

2.2 read_MITSceneParsingData.py

用于从.pickle文件中读取image-annotation的路径数组,供BatchDatsetReader.py读取图片数据。
如果没有.pickle文件,则要先生成一个.pickle文件。具体做法是:遍历images目录,对每个jpg图片在annotations目录中找到对应的png图像分割文件。由此生成image-annotation的文件名集合。
这样,我们为所有的image都找到了对应的annotation的路径,就可以把它们存储为.pickle文件,供日后训练用。

2.3 BatchDatsetReader.py

在开始训练之前要读取所有的图片和图片分割。读取.pickle文件,利用其中的信息可以找到所有的image和annotation并读取为矩阵的形式。
这样,image_list的维度为(num_images, height, width, 3), annotation_list的维度为(num_annotations, height, width, 1),且类型都是numpy.ndarray。

  • 由于训练的数据并非全部是Rgb的三通道图,有些是灰度图(只有单通道)。为了统一处理,要把这些灰度图转换为三通道的形式。

2.3 ImageReader.py

training中,通常的程序逻辑是这样的:

  1. 生成image-annotation的文件名集合。遍历images目录,对每个jpg图片在annotations目录中找到对应的png图像分割文件。这样,我们为所有的image都找到了对应的annotation的路径,就可以把它们存储为.pickle文件,供日后训练用。
  2. 在开始训练之前要读取所有的图片和图片分割。读取.pickle文件,利用其中的信息可以找到所有的image和annotation并读取为矩阵的形式。这样,image_list的维度为(num_images, height, width, 3), annotation_list的维度为(num_annotations, height, width, 1),且类型都是numpy.ndarray。
    • 由于训练的数据并非全部是Rgb的三通道图,有些是灰度图(只有单通道)。为了统一处理,要把这些灰度图转换为三通道的形式。重写时在此栽过跟头。

3. 遇到的问题

3.1 问题1 image维度不统一

image数据大部分是三维的(h, w, 3),但有少部分是灰度图,也就是二维的(h, w)
annotation数据则都是二维的(h, w)

因此处理image数据时,如果遇到二维的图片,要先转为三维且有3个通道的图片。

3.2 问题2 scipy.misc.imresize is deprecated

问题描述
原作者的代码中,图片的变形使用的是scipy.misc.imresize函数。
但我发现这个函数除了对图片变形,还会自行做一些多余的动作。它会把数组里的值标准归一化到[0, 255]的区间内,破坏图片原本的信息。

arr = np.array([[[100, 2, 220], [3, 4, 5]], [[1, 2, 3], [3, 4, 5]]])

print(type(arr))
print(arr.shape)

resize_size = 4
arr = misc.imresize(arr, [resize_size, resize_size], interp='nearest')
print(type(arr))
print(arr.shape)
print(arr)

输出

<class 'numpy.ndarray'>
(2, 2, 3)
<class 'numpy.ndarray'>
(4, 4, 3)
[[[115   1 255]
  [115   1 255]
  [  2   3   5]
  [  2   3   5]]

 [[115   1 255]
  [115   1 255]
  [  2   3   5]
  [  2   3   5]]

 [[  0   1   2]
  [  0   1   2]
  [  2   3   5]
  [  2   3   5]]

 [[  0   1   2]
  [  0   1   2]
  [  2   3   5]
  [  2   3   5]]]

解决方法
最后查阅官方文档才知道这个函数已经被废止。
于是我将对图片的操作都改用skimage库实现了。而对图片的变形则使用skimage.transform.resize函数。

3.3 问题3 ValueError: could not broadcast input array from shape (224,224,3) into shape (224,224)

问题描述

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    reader = ImageReader("train")
  File "/root/Desktop/FCN/ImageReader.py", line 58, in __init__
    self.image_list = np.array([self.readImage(record["image"]) for record in self.records])
ValueError: could not broadcast input array from shape (224,224,3) into shape (224,224)

在改用skimage库操作图片后,出现了无法把元素合并到一个数组的问题。对image里的图片的操作失败了。

查阅stackoverflow的问题发现原来是元素的维度并不统一。

我原以为所有image里的图片都是三通道的,也就是(h, w, 3)的。这样如果我要得到固定尺寸的图片(比如224 * 224),只需调用skimage.transform.resize,就能把图片转为(224, 224, 3)。理应所有图片都会被转换成(224, 224, 3)的维度。可是既然图片们无法共容在一个数组里,说明有的图片没有转换成这种维度

问题原因
原来,image里并不是所有图片都是(h, w, 3)形式的,有的图片是灰度图(在20210张图片中有4张是灰度图),也就是(h, w)形式。而我的代码没有考虑到这一点,导致这几张灰度图被转换后的维度错误。

解决方法
对于这几张灰度图,需要将其转换为三通道的形式。只需要把单通道上的值重复三次作为三个通道的值即可。

3.4 问题4 图片转换后内容被破坏

问题描述


在给skimage.transform.resize添加reserve_range = True设置后,发现转换后的图片内容完全被破坏。似乎维持值的范围会破坏图片的可见性。

问题原因

查阅了stackoverflow
原来pyplot.imshow只能显示[0.0, 1.0]范围的图片,而reserve_range = True会使图片仍然在[0, 255]范围内,且数据类型为float64,被以[0.0, 1.0]的范围来看待,这就无法正确显示了。

另外,查阅官方文档reserve_range参数

preserve_range : bool, optional
Whether to keep the original range of values. Otherwise, the input image is converted according to the conventions of img_as_float.
确实如果不设置reserve_range = True,函数会把值的范围标准归一化到[0.0, 1.0]内,也就是img_as_float.

问题解决

显示图片时先使用image = np.copy(old_image).astype('uint8'),把类型从float64转换为uint8即可。

3.5 问题5 查看源代码的卷积核维度

通过在源代码中添加如下代码可输出各层卷积核的维度

输出:

仅截取部分输出

根据输出,我发现源代码使用的是VGG-19,而论文中使用的是VGG-16。两者的效果应该差不多,为了保持一致,我依旧按照VGG-19来叠加。

3.6 问题6 tf.layers.conv2d_transpose的放大倍数

tf.layers.conv2d_transpose只能指定strides来调整输出图片的尺寸。
strides = [2, 2]时放大两倍,strides = [8, 8]时放大8倍

3.7 问题7 numpy array的特殊索引方式

代码中此段是用来打乱images和annotations的,开始看的时候不懂,感觉这不符合Python的语法,后来查了官方文档发现,原来这是numpy.ndarray重载过的行为,是numpy.ndarray的特殊的索引方式。

基本用法如下:


所以,通过传递一

3.8 mean_iou过低

问题描述
发现不管如何调优,mean_iou的数值很低。
问题原因
原来是因为在缩放annotation的过程中,使用的方法会拉扯那些值,使得annotation出现了本来不存在的分类。比如原本annotation里的值只有3,4,5,经过缩放,值被拉扯,变成了二点几、三点几、和六点几,等等。而图原本是没有6这个分类的,这就导致mean_iou的计算出现巨大的偏差。
解决方法
图片缩放函用回scipy.sisc.imresize,因为这个函数有按nearest模式缩放的功能,在缩放图片的同时不改变图片内的值的种类。

感想

  1. 在测试的时候图方便,总是读取整个数据集,其中等待浪费了很长时间,经常读取完以后才发现bug。以后应该先只读取一部分,保证代码正确运行,再读取整个数据集
  2. 代码对内存的优化不好。由于每次测试都读取整个数据集,有时候出现内存用爆导致Memory Error的情况,以后编程要注意节省内存空间
  3. 第一次从头到尾地进行编程,有些手忙脚乱。以后应当先分析数据集的成分,构建读取器,再构建神经网络,读取部分数据进行测试。保证无bug后才对。

参考

相关阅读

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容