利用VGG16搭建全卷积神经网络(FCN)实现语义分割

2024-03-29 13:48

本文主要是介绍利用VGG16搭建全卷积神经网络(FCN)实现语义分割,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

利用VGG16搭建全卷积神经网络(FCN)实现语义分割

文章目录

  • 利用VGG16搭建全卷积神经网络(FCN)实现语义分割
    • 1.简介
      • 1.1 FCN的介绍
      • 1.2语义分割任务是什么
    • 2.数据准备以及预处理
    • 3.模型的搭建
    • 结语

1.简介

1.1 FCN的介绍

​ FCN,全卷积神经网络,用于实现语义分割,是深度学习从此可以很好解决语义分割的开山之作,作者认为我们在使用卷积层提取特征后,连接到全连接层后,由于全连接层的层数是我们人为设定的,在这之中其实我们抛弃了图片的空间信息,这对语义分割会造成影响,所以作者提出抛弃全连接层,在网络中只使用卷积层,所以网络称为全卷积神经网络。该网络的结构如下:

在这里插入图片描述

可以看到我们该网络有利用前面的卷积提取的部分,将最后卷积提取的部分,上采样放大后,然后与之前卷积提取的部分相加(因为作者认为我们到最后得到的是全局的特征,而我们在前面获得的是局部特征,所以我们的融合可以将全局特征与局部特征融合在一起,加大了我们对于结果预测的准确率)。

重复几次最终恢复到原图大小,并且通道数为21(这里的21指的是物体的类别,对于每一个像素我们都要推测他是什么种类,所以通道数为种类数),这就是FCN的网络架构。

1.2语义分割任务是什么

​ 语义分割是相较于图像识别更为复杂的任务,他的分类是在像素级别上的,对于每个像素我们都要区别他们的种类,如本例中,我们要区别的是一张动物图片的背景,边缘,身体,三类(每行依次为真实图片,预测结果,真值)
在这里插入图片描述

2.数据准备以及预处理

​ 在本例中我们采用的是牛津大学提供的开源数据集Oxford-IIIT-PetDataset,它提供了许多不同种类动物的图片结构如下:

在这里插入图片描述

并且对所有图片都提供了他的掩码,在文件夹trimap下的png文件就是他提供的掩码
在这里插入图片描述

我们可以读取一张图片并展示一下

import random
random.shuffle(all_img_label)
def load_label(path):label=tf.io.read_file(path)label=tf.image.decode_png(label)label=tf.squeeze(label)return label
img_label=load_label(all_img_label[0])
plt.imshow(img_label.numpy())#这样我们就可以看到标签的实际样子
img_label.shape

在这里插入图片描述

那么在这里,我们就知道了对于给定的图片,的标签如下,那么我们再查看一下他有多少种类

np.unique(img_label.numpy())#这样我们就可以看到这张图上有多少种
#可以看到这张图像上有三类分别是背景身体边缘
array([1, 2, 3], dtype=uint8)

那么,这里我们就确定了我们的问题,我们的输入是一张三通道彩色图片,标签是一张单通道的图片,那么我们这边就直接省略读取数据的部分直接

all_img_path=glob.glob('Image Location all_img_label=glob.glob('Image Location Dataset\\annotations\\trimaps/*.png')Dataset\\images\\*.jpg')
all_img_path.sort()
all_img_label.sort()
dataset=tf.data.Dataset.from_tensor_slices((all_img_path,all_img_label))
def read_jpg(path):img=tf.io.read_file(path)img=tf.image.decode_jpeg(img,channels=3)return img
def read_png(path):label=tf.io.read_file(path)label=tf.image.decode_png(label,channels=1)return label
@tf.function #在读取数据的最后一步加上这个会转换为图运算,加大效率
#这里我们规范图片值到[-1,1],然后标签在[0,2]之间,并且尺寸都改为[224,224]
def normal_data(img,label):img=tf.cast(img,tf.float32)img=img/255.0img=img*2-1label-=1return img,label
def path_to_data(img_path,label_path):img=read_jpg(img_path)label=read_png(label_path)img=tf.image.resize(img,(224,224))label=tf.image.resize(label,(224,224))return normal_data(img,label)
dataset=dataset.map(path_to_data)
dataset
<MapDataset shapes: ((224, 224, 3), (224, 224, 1)), types: (tf.float32, tf.float32)>

这样我们就完成了对于数据的读取,然后划分验证集与测试集

dataset=dataset.shuffle(len(dataset))#提前打乱数据集
test_count=int(len(dataset)*0.2)
train_count=len(dataset)-test_count
train_data=dataset.skip(test_count)
test_data=dataset.take(test_count)

接下来开始我们模型的搭建。

3.模型的搭建

模型的结构,我们这里用一张图来说明(该图是由keras提供的plot_model绘画的很简单使用,搜索一下就好了):

在这里插入图片描述

接下来我们开始对上面这张图的解说,首先我们使用的是VGG16预训练神经网络,架构如下:

在这里插入图片描述

这里我们可以看到这是一个非常经典的卷积神经网络,步骤无非卷积-池化-卷积-池化,那么我们之前有提到我们要将局部信息与最后的输出结合,也就是我们的结构是图片(7,7,512)反卷积(这个是上采样的一种方法,是最广泛采用的一种方法,因为他是通过学习权重来恢复原图)放大到(14,14,512)然后与,block5_conv3的输出相加,再上采样放大到(28,28,512)然后与block4_conv3相加,再上采样放大到(56,56,256),再与block3_conv3相加最后不断上采样恢复到(224,224,3)之所以是三是因为我们要对每个类别预测一个三维向量(以上上采样皆运用反卷积,最终我们获得的图片为原来的大小),说明了原理,接下来我们贴代码:

#获取预训练神经网络 
conv_base=keras.applications.VGG16(weights='imagenet',input_shape=(224,224,3),include_top=False)#获得我们接下来要对层操作的输出的layers_names=['block5_conv3','block4_conv3','block3_conv3','block5_pool']layers_output=[conv_base.get_layer(layer_name).output for layer_name in layers_names]#然后利用函数式API搭建我们的多输出模型  
multi_out_model=keras.models.Model(inputs=conv_base.input,outputs=layers_output)multi_out_model.trainable=Falseinputs=layers.Input(shape=(224,224,3))
#获取我们的输出    
out_block5_conv3,out_block4_conv3,out_block3_conv3,out_block5_pool=multi_out_model(inputs)
#接下来开始按照我们的之前的设计进行创建网络x1=layers.Conv2DTranspose(512,3,strides=(2,2),padding='same',activation='relu')(out_block5_pool)x1=layers.Conv2D(512,3,padding='same',activation='relu')(x1)x2=tf.add(x1,out_block5_conv3)x2=layers.Conv2DTranspose(512,3,strides=(2,2),padding='same',activation='relu')(x2)x2=layers.Conv2D(512,3,padding='same',activation='relu')(x2)x3=tf.add(x2,out_block4_conv3)x3=layers.Conv2DTranspose(256,3,strides=(2,2),padding='same',activation='relu')(x3)x3=layers.Conv2D(256,3,padding='same',activation='relu')(x3)x4=tf.add(x3,out_block3_conv3)x4=layers.Conv2DTranspose(128,3,strides=(2,2),padding='same',activation='relu')(x4)#对每个像素都输出一个三维的向量x4=layers.Conv2D(128,3,padding='same',activation='relu')(x4)prediction=layers.Conv2DTranspose(3,3,strides=(2,2),padding='same',activation='relu')(x4)prediction=layers.Conv2D(3,3,padding='same',activation='softmax')(prediction)#仍然利用我们的函数式API创建我们的模型  model=keras.models.Model(inputs=inputs,outputs=prediction)model.compile(optimizer=keras.optimizers.Adam(0.001),loss='sparse_categorical_crossentropy',metrics=['acc'])

然后我们就可以开始训练了,这里之所以设置为1是因为设备扛不住了。。。导致我是多次断点训练,每间隔两次保存权重再重新训练

model.fit(train_data,epochs=1,steps_per_epoch=train_count//BATCH_SIZE)

4.预测结果的分析与可视化

Epoch 1/2
369/369 [==============================] - ETA: 0s - loss: 0.1918 - acc: 0.9195

可以看到仅仅用了几次就达到了接近92的准确率(并不是一次,我这里其实经过了多次重新训练,大概四次,

model.evaluate(test_data,steps=test_count//BATCH_SIZE)
92/92 [==============================] - 55s 70ms/step - loss: 0.1796 - acc: 0.9236

而且在测试集上就达到了92.36%,其实还有上升的趋势,但是设备问题我没有在训练了)那么说明我们的准确性还是很高的,接下来我们对结果进行可视化

num=3
for image,mask in test_data.take(1):pred=model.predict(image)pred=tf.argmax(pred,axis=-1)pred=pred[...,tf.newaxis]plt.figure(figsize=(10,10))for i in range(3):plt.subplot(num,3,i*num+1)plt.imshow(keras.preprocessing.image.array_to_img(image[i]))plt.subplot(num,3,i*num+2)plt.imshow(keras.preprocessing.image.array_to_img(pred[i]))plt.subplot(num,3,i*num+3)plt.imshow(keras.preprocessing.image.array_to_img(mask[i]))

在这里插入图片描述

在这里插入图片描述

可以看到效果虽然有瑕疵其实是还可以的,也有设备问题(每训练一次内存就满了)。。。

结语

在本例中我们实现了全卷积神经网络FCN,并且完成了对该模型的训练,评估,结果的可视化与真值的对比,有什么更好的建议,或者问题可以在评论区交流,谢谢。

这篇关于利用VGG16搭建全卷积神经网络(FCN)实现语义分割的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/858761

相关文章

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

MySQL8.0设置redo缓存大小的实现

《MySQL8.0设置redo缓存大小的实现》本文主要在MySQL8.0.30及之后版本中使用innodb_redo_log_capacity参数在线更改redo缓存文件大小,下面就来介绍一下,具有一... mysql 8.0.30及之后版本可以使用innodb_redo_log_capacity参数来更改

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形