本人学生小白一枚 ~ 这段时间搞比赛,入坑深度学习,经过一个月的奋斗,总算是完成了作品,撒花 ~ 前期准备时参考了众多大神们的精品博客,手痒了,第一次尝试写博客,不足之处恳请指教。
FCN的介绍我就不细说了,全卷积嘛,理论上接受任意大小尺寸的图片输入(但是分辨率越大的图片越吃电脑性能,仅仅是测试一张图,4248*6016大小的图直接报错溢出)我的MX150小显存根本跑不动大的图片,只好放弃GPU,用CPU跑程序。
配置环境
啊,这个很重要,我最终是在Win10+tensorflow1.6(CPU)+python3.6 中实现的。如果是使用GPU的朋友可以参考如下配置:Win10+python3.6+tensorflow-gpu 1.6+CUDA9.1+CUDNN 7。当然啦,CUDA版本得看NVIDIA驱动的版本,总之CUDA9.x的tensorflow-gpu版本别下载太高的,会出问题的。另外FCN对电脑配置要求挺高的,我的是2个G显存,跑YOLO调低batchsize还能跑通,可是FCN一开始训练直接OOM(用了释放显存,调batchsize为1,切小输入图片等无数种办法,最后果断放弃了 。。捂脸)。我估计4个G(甚至还要更高)的显存才能跑得动吧。
运行速度
那既然选择了CPU,你就别指望它的训练速度能有多快咯,我大概测试了下,batchsize=32时,迭代10次需要15分钟;batchsize=16的时候,迭代10次大约六分半,我最终是选择16大小的batchsize进行训练,刚好内存够,不至于运行得太卡。
OK,不多废话,开始FCN的训练之旅。
1.FCN源码下载
2.网络权重文件下载(提取码:4i8j)
其实这一步也不是必须这么设置,只是源码中写好了数据集呀,权重呀存放的位置的路径,如果你自定义文件夹名就会找不到。如果想自定义文件夹名字就去修改FCN.py中的路径。如果懒得改源码就按照作者的数据集格式来创建文件夹。具体如下:在FCN-TensorFlow-master文件夹中创建Model_zoo和Data_zoo两个文件夹,其中Model_zoo中存放网络权重文件(mat文件),然后在Data_zoo文件夹中创建MIT_SceneParsing文件夹和 test文件夹(这个是后面测试会用到的),再继续,MIT_SceneParsing文件夹中创建ADEChallengeData2016文件夹,多提一句,后面开始训练之后,这个目录下会生成一个MITSceneParsing.pickle文件,这个文件是程序读取你自己的数据集时的记录文件,也就是说,如果你要中途换数据集了,请记得把这个pickle文件删除哦。
好的,我们继续。在这个ADEChallengeData2016文件夹中,我们需要分别创建annotations和images文件夹,annotations中放的是你的数据集标签(掩模图),images中放的是你的数据集原图。还没结束,在annotations文件夹中创建training和validation两个文件夹,根据英文不难猜出,分别是放训练集和验证集的标签;images文件夹中也一样创建training和validation两个文件夹,里面放原图。标签与原图要一 一对应,这就不需要我唠叨了吧。
OK,至此,你的整个框架都弄好啦,下面就需要将数据集放入训练就可以了。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
----------------------------
#实现批量转8位深,灰度值归一化
----------------------------
import glob
import cv2
import os
path='C:/Users/AS/Desktop/1/' #标签路径(二值图像哦)
def picture_shape(picture):
img=cv2.imread(picture,2)
cv2.cvtcolor
ret2,th2=cv2.threshold(img,0,1,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imwrite(path + '%03d.png'%i,th2) #图片保存格式,可自定义
#print(th2.item(100,100))
def calculation(path):
global i
i=0
for picture in glob.glob(path + '*.png'):
i+=1
print('%03d'%i)
picture_shape(picture)
calculation(path)
4.将处理好的标签和数据集分为训练集,验证集和测试集,训练集的标签和原图分别放入annotations和images文件夹中的training中,再次提醒,检查原图与标签一 一对应(格式不要求一样哦,比如原图jpg,标签png),验证集则分别放入annotations和images文件夹中的validation中。
5.OK,数据集制作完成,当然咯,想训练出一个比较好的模型,你可以进行打乱数据集,数据平衡与清洗,数据扩充等操作。电脑性能不好的童鞋建议将输入图片切小再输入,一可以缓解内存或显卡的压力,二可以到达扩充数据集的目的。虽然FCN是可以接受任意尺寸的图片输入,但我也在刚开始的时候说了这只是理论上,事实上,我们穷苦人家的电脑配置大都支撑不了大分辨率的图片输入。当然咯,测试的时候我们直接输入原图尺寸就好了,测试图也会以原尺寸输出,FCN的优势就展现出来啦!
#参数设置
FLAGS = tf.flags.FLAGS
# batchsize设置
tf.flags.DEFINE_integer("batch_size", "16", "batch size for training")
# log路径,一般存放权重文件,预测结果图等
tf.flags.DEFINE_string("logs_dir", "logs/", "path to logs directory")
# data路径,就是咱们之前创建的那么多的文件夹,如果想自定义的话把这里改成自己的文件名就可以了
tf.flags.DEFINE_string("data_dir", "Data_zoo/MIT_SceneParsing/", "path to dataset")
# 学习率,很重要的一个参数,前期不宜调太大
tf.flags.DEFINE_float("learning_rate", "1e-6", "Learning rate for Adam Optimizer")
# model路径,放之前下载的网络权重mat文件
tf.flags.DEFINE_string("model_dir", "Model_zoo/", "Path to vgg model mat")
# debuge这个可以不用管他
tf.flags.DEFINE_bool('debug', "False", "Debug mode: True/ False")
# 模式切换,训练时就默认的train,后面的验证及单张图检测,就需要用visualize和test
tf.flags.DEFINE_string('mode', "train", "Mode train/ test/ visualize")
# 源码中提供了mat文件在线下载,但个人建议先下载好
MODEL_URL = 'http://www.vlfeat.org/matconvnet/models/beta16/imagenet-vgg-verydeep-19.mat'
#最大迭代次数
MAX_ITERATION = 20001
# 类别数(分割目标+背景),我做的是二分类
NUM_OF_CLASSESS = 2
# 这是个困扰我挺久的地方,FCN将输入图像都统一resize了,resize之后的尺寸就是由这个参数控制的。默认就好。
IMAGE_SIZE = 224
# 改成True,比如说你中断后想继续训练,它可以帮助你restore模型
fine_tuning = True
调好参数之后,直接运行FCN.py就开始训练了。训练过程就没啥好说的了,如果刚开始运行有报错,一般是缺少包吧,pip安装一下就好了,显存不够OOM报错的问题我是没办法解决了,2G很难跑起来。可以和我一样换CPU去跑。如果开始正常训练了,那就基本代码没问题了。
奥对了,源码中设置的是每10次迭代打印一次train_loss,每500次验证一次,打印validation_loss,每500次生成一次权重,我数据集不大,就把它们都改小了一些,修改的地方大概在250行左右吧,贴出我修改后的样子:
if itr % 1 == 0:
# 迭代每次打印显示
train_loss, summary_str = sess.run([loss, summary_op], feed_dict=feed_dict)
print("Step: %d, Train_loss:%g" % (itr, train_loss))
summary_writer.add_summary(summary_str, itr)
if itr % 10 == 0:
# 迭代10 次验证
valid_images, valid_annotations = validation_dataset_reader.next_batch(FLAGS.batch_size)
valid_loss = sess.run(loss, feed_dict={image: valid_images, annotation: valid_annotations,
keep_probability: 1.0})
print("%s ---> Validation_loss: %g" % (datetime.datetime.now(), valid_loss))
# 每迭代250次保存模型
if itr % 250 == 0:
saver.save(sess, FLAGS.logs_dir + "model.ckpt", itr)
源代码中虽然有test模式,但是却没有写test的代码段,只有train和visualize两种模式的代码。visualize模式是从验证集中随机抽取一个batchsize 的图片进行预测。那么如果我们想要测试特定的单张图像该怎么办呢?我查了很多资料,都没有讲这一块的,唯一的方法就是在FCN.py源码中补充,我补充的如下(大概是在280行左右):
'''visualize模式'''
elif FLAGS.mode == "visualize":
# get_random_batch()随机抽取
valid_images, valid_annotations = validation_dataset_reader.get_random_batch(FLAGS.batch_size)
# pred_annotation预测结果图
pred = sess.run(pred_annotation, feed_dict={image: valid_images, annotation: valid_annotations,
keep_probability: 1.0})
valid_annotations = np.squeeze(valid_annotations, axis=3)
pred = np.squeeze(pred, axis=3)
for itr in range(FLAGS.batch_size):
#输出inp_原图
utils.save_image(valid_images[itr].astype(np.uint8), FLAGS.logs_dir, name="inp_" + str(5+itr))
#输出gt_标签
utils.save_image(valid_annotations[itr].astype(np.uint8), FLAGS.logs_dir, name="gt_" + str(5+itr))
#输出pred_验证结果
utils.save_image(pred[itr].astype(np.uint8), FLAGS.logs_dir, name="pred_" + str(5+itr))
print("Saved image: %d" % itr)
'''重点来了!!!需要自己补充的test模式'''
else:
#还记得Data_zoo下我们还创建的一个test文件夹嘛,在这里就用到了,当然咯,你可以自定义路径。
test_image = scipy.misc.imread('./Data_zoo/test/038.jpg')
#resize_image = scipy.misc.imresize(test_image, [224, 224], interp='nearest')
a = np.expand_dims(test_image, axis=0)#resize_image
a = np.array(a)
pred = sess.run(pred_annotation, feed_dict={image: a, keep_probability: 1.0})
pred = np.squeeze(pred, axis=3)
#输出的预测图在log文件夹中,图名可以自己修改
utils.save_image(pred[0].astype(np.uint8), FLAGS.logs_dir, name="pred_" + str(5))
print("Saved image: succeed")
到这里还没结束!!!请接着看下面
我们兴冲冲的开始测试了,结果测出来一看,嗯???图像怎么是全黑的?为什么输出图像变成了224*224的尺寸?我们期望测试的时候任意尺寸输入,原尺寸输出该怎么做?你还有几个需要修改的地方:
这边再提醒一下,这只是测试的时候用,训练时开着resize,对模型训练的流畅度和缓解内存压力都有不小的作用
我们需要修改三处,大约是第24行,第188行,第224行,贴代码如下:
#mode模式改成test
tf.flags.DEFINE_string('mode', "test", "Mode train/ test/ visualize")
#将原本的注释掉,多加一行,其实就是把IMAGE_SIZE改成None
#image = tf.placeholder(tf.float32, shape=[None, IMAGE_SIZE, IMAGE_SIZE, 3], name="input_image")
image = tf.placeholder(tf.float32, shape=[None, None,None, 3], name="input_image")
#将resize属性改为False,如果不关掉,当你重启模型时,还是将你的测试图先resize成224*224了
image_options = {'resize':False,'resize_size': IMAGE_SIZE}
OK啦,现在再测试,输出的就是原图像尺寸啦,测试一张图时间蛮久的,我测试一张就需要5分钟,可能是原图太大的缘故吧。
那测试出来是全黑的咋办?你别忘了,之前我们将灰度值归一化了,0,1,2,3…这些灰度值都是近似于黑色的,肉眼分辨不出来,所以我们需要把灰度值再还原回来,二分类就用简单的阈值就可以看到结果啦,其实就是将上面提到的归一化代码中稍微改改就行了,贴出代码如下:
import cv2
import numpy
path='C:/Users/AS/Desktop/FCN-TensorFlow-master/logs/'
#换成自己的图名
img=cv2.imread(path+'pred1_35.png',2)
#简单阈值分割
ret2,th2 = cv2.threshold(img,0.5,255,cv2.THRESH_BINARY)
cv2.namedWindow('',cv2.WINDOW_NORMAL)
cv2.imshow('',th2)
cv2.waitKey(0)
#保存的图名、路径可以自定义
cv2.imwrite(path+'p35(1).png',th2)
三分类及以上应该需要先遍历像素值吧,再把每个像素还原放大,我就没有去写了。应该也是很容易实现的。
还有一点,我在log文件夹中生成了好几个权重,我想指定某个权重测试咋办?这个好办,我们先去log目录下,找到一个叫做checkpoint的文件,这里面存放的就是你生成的所有权重路径啦,一般默认的是最新的权重,如果想要调用之前的,只需要将第一行改成那个权重的名字就好啦!
图像分割一般用的是Dice吧,医学领域会用到灵敏度和特异度。感觉Dice用的最为广泛,贴出计算Dice的代码:
import cv2
from matplotlib import pyplot as plt
#解决matplotlib不能正常显示中文
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
path_1 =r'C:\Users\AS\Desktop\035.png' #标准掩模图路径
path_2 = r'C:\Users\AS\Desktop\FCN-TensorFlow-master\logs\processed35(1).png' #预测图路径
# 计算DICE系数,即DSI
def calDSI(img_GT,img_R):
row, col = img_GT.shape # 矩阵的行与列
DSI_s,DSI_t = 0,0,0,0
for i in range(row):
for j in range(col):
if img_GT[i][j] == 255 and img_R[i][j] == 255:
DSI_s += 1
if img_GT[i][j] == 255:
DSI_t += 1
if img_R[i][j] == 255:
DSI_t += 1
DSI = 2*DSI_s/DSI_t
print(' DICE计算结果 : {0:.4} '.format(DSI)) # 保留四位有效数字
if __name__ == '__main__':
# step 1:读入图像,并灰度化
img_GT = cv2.imread(path_1,2)
img_R = cv2.imread(path_2,2)
#二值化
ret3,img_GT = cv2.threshold(img_GT,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
ret3,img_R = cv2.threshold(img_R,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
calDSI(img_GT,img_R)
补充一点,有童鞋问我要ADEChallengeData2016数据集,直接把网盘链接贴在这里了(提取码:rkbb ):link
哎,第一次写博客,权当是这次比赛自己的一点收获和小结吧。有不懂的童鞋可以在评论区戳我,我只要看到了都会认真回复的。有写错的也请大家多多包涵哈!也期望有大神能指点指点,一起交流~