本文章来自: Fully_Convolutional_Neural_Networks_rendered.ipynb
因学习而翻译了该练习的内容,不妥删。转载也请说明情况。
语义分割
在练习前,先了解下关于语义分割的一些知识。刚开始看到语义分割这个词的时候,以为是自然语言处理里面的知识,后来看的多了,就知道还是计算机视觉中的内容。那么什么是语义分割?别看名字这么高级,其实就是像素级别的分类问题。即对一张图片中的每一个像素进行分类。
为什么要进行语义分割?如果当精确的物体边界很重要的时候,比如,在自动驾驶中,需要精确地识别每个像素中的物体,这时候就需要用到语义分割。同时,在医学图像识别的问题上,也会用到语义分割。
语义分割中是怎么进行数据标注的?看到关于语义分割的各种分类的结果通常都会如下所示,一片蓝,一片红的。这时候就会好奇语义分割是怎么进行数据标注的?其实语义分割分类的结果是为了便于展示,所以用不同的颜色块来区分。在语义分割中,数据标注也很简单。因为语义分割做的是对一张图片中的每一个像素进行分类,所以,数据标注的时候,就需要对每一个像素标注出标签。当然,标注的时候,还会对每一个类别和颜色进行一个颜色关联,这样就方便后续的展示。
练习目标
- 加载一个在 ImageNet 上预训练过的模型 - ResNet50
- 将 ResNet50 模型转换成全卷积网络
- 应用该网络在图片上实行弱分割 - 利用热力图
1 | %matplotlib inline |
1 | # 包装函数来禁用一些警告 |
1 | # 加载 ResNet50 |
1 | (None, None, None, 2048) |
1 | res5c = base_model.layers[-1] |
1 | keras.layers.core.Activation |
1 | res5c.output_shape |
1 | (None, None, None, 2048) |
全卷积 ResNet
除了残差块,resnet 输出形状为 WxHx2048
ImageNet 的默认输入形状为 224x224,其对应的输出为 7x7x2048
- 正规的 ResNet 的网络层:
1 | x = base_model.output |
- 之后修正的版本:
- 我们想要检索存储在 Dense 图层中的标签信息。之后我们将加载这些权重。
- 将 Dense 层更改为 Convolution2D 层以保留空间信息,输出为 WxHx1000
- 对于新的 Convolution2D 层,可以使用(1,1)的 kernel 来保持前一层的空间组织不变(它称为逐点卷积)
- 只想在最后一个维度上应用 softmax,以便保留空间信息。
1 | # 自定义的 softmax |
1 | # 将 resnet 改成全卷积网络 |
可以使用以下随机数据来检查是否可以在随机 RGB 图像上运行正向传递:
1 | prediction_maps = fully_conv_ResNet.predict(np.random.randn(1, 200, 300, 3)) |
1 | (1, 7, 10, 1000) |
如何解释最终的输出形状? 一个类的概率应该在输出映射的每个区域中总和为 1:
1 | prediction_maps.sum(axis=-1) |
1 | array([[[1. , 1. , 1. , 1. , 0.99999994, |
加载权重
- 在文件
weights_dense.h5
中提供 ResNet50 的最后一个 Dense 层的权重和偏差 - 现在,最后一层现在是 1x1 卷积层而不是全连接层
1 | import h5py |
1 | last_layer = fully_conv_ResNet.layers[-2] |
1 | (1, 1, 2048, 1000) |
1 | last_layer = fully_conv_ResNet.layers[-2] |
1 | 加载的权重形状: (2048, 1000) |
1 | # 重组权重的形状 |
前向传播
- 定义下面的函数来测试我们的网络
- 它将输入调整为给定大小,然后使用
model.predict
计算输出
1 | from keras.applications.imagenet_utils import preprocess_input |
1 | output = forward_pass_resize("dog.jpg", (800, 600)) |
1 | Image shape before resizing: (1600, 2560, 3) |
找到与狗关联的类别
ImageNet 使用概念本体,从中派生类。 synset 对应于本体中的节点。
例如,所有种类的狗都是同义词 n02084071
(狗,家犬,犬科动物)的孩子:
1 | # Helper file importing synsets from imagenet |
1 | All dog classes ids (118): |
1 | for dog_id in ids[:10]: |
1 | dalmatian, coach dog, carriage dog |
狗类热力图
下面的函数是从前向传播中建立热力图的辅助函数。它对与 synset 相对应的所有 id 的表示求和。
1 | def build_heatmap(z, synset): |
1 | def display_img_and_heatmap(img_path, heatmap): |
下面用 3 种不同的大小:
- (400, 640)
- (800, 1280)
- (1600, 2560)
1 | # dog synset |
1 | Image shape before resizing: (1600, 2560, 3) |
1 | # size 2 - (800, 1280) |
1 | Image shape before resizing: (1600, 2560, 3) |
1 | # size 3 - (1600, 2560) |
1 | Image shape before resizing: (1600, 2560, 3) |
可以观察到 heatmap_1 和 heatmap_2 提供了比 heatmap_3 更粗略的分个。但是,heatmap_3 在狗区域外面有一些小的杂碎,heatmap_3 会编码更多关于狗的局部纹理级别的信息,而较低的分辨率则会编码更多关于整个对象的语义信息。结合它们可能是一个好主意!
1 | heatmap_1_r = imresize(heatmap_1, (50,80)).astype("float32") |