环境:ubuntu 16.04 + TensorFlow 1.9.1 + cuda 9.0 + cudnn 7.0 +python3.6
tensorflow 项目链接 https://github.com/tensorflow/models.git (deeplabv3+)
首先添加slim路径,每次打开terminal都要加载路径
# From tensorflow/models/research/
export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
注意,运行后要将所以终端关闭,重启~
快速测试,调用model_test.py:
# From tensorflow/models/research/
python deeplab/model_test.py
若出现ok,就成功啦
简单来说,需要把数据预处理成规定的格式和尺寸范围,生成包含文件名的文本文件,最后以规定的目录形式组织起来。
数据的目录组织形式应当这样:
+ Database # 自己的数据集名称
+ JPEGImages
+ SegmentationClass
+ ImageSets
+ Segmentation
- train.txt
- val.txt
- trainval.txt
- test.txt
+ tfrecord
其中:
JPGImages
文件夹存放RGB图像;
SegmentationClass
存放转换为class的标签,格式为单通道的png图像,将数据值转化为对应目标类型(0,1,2,3……)。对应的图像和标签的文件名相同!!扩展名分别为.png
和.png
ImageSets/Segmentation
存放有图像文件名的 .txt
文件,这里我按文件名将数据分为 train
, val
, trainval
;
tfrecord
存放转换的 tfrecord
格式的数据。
.txt
文件中的内容应当为对应图像的文件名,不带扩展名,如下所示:
filename1
filename2
filename3
.....
1、对于不大的数据集来说,tensorflow提供了一种高效率的数据读取模式,将数据转换为 TFRecord
格式。这里不多作解释,想要更深入的了解请寻它处。tensorflow读取数据-tfrecord格式
运行datasets中的build_voc2012_data.py,将数据集转换为tfrecord格式,修改参数
这里可以直接使用 datasets/build_voc2012_data.py
的代码。
python build_voc2012_data.py --image_folder=./Database/JPEGImages \
--semantic_segmentation_folder=./Database/SegmentationClass \
--list_folder=./Database/ImageSets/Segmentation \
--output_dir=./Database/tfrecord
image_folder 图像位置
semantic_segmentation_folder 标注位置(生成的mask)
list_folder 列表位置(segmentation里的txt文件)
output_dir 输出位置
可在代码中调节参数 _NUM_SHARDS
(默认为4),改变数据分块的数目。(一些文件系统有最大单个文件大小的限制,如果数据集非常大,增加 _NUM_SHARDS
可减小单个文件的大小)。
在DeepLabv3+模型的基础上,主要需要修改以下几个文件
segmentation_dataset.py
文件train_utils.py
距离开始训练我们的数据集仅一步之遥了,接下来注册我们的数据集,在 segmentation_dataset.py
文件中找到这段:
_DATASETS_INFORMATION = {
'cityscapes': _CITYSCAPES_INFORMATION,
'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION,
'ade20k': _ADE20K_INFORMATION,
'dataset_name': _DATASET_NAME, # 自己的数据集名字及对应配置放在这里
}
然后添加数据集的相关配置,名字要和上面注册的数据集相同。
_DATASET_NAME = DatasetDescriptor(
splits_to_sizes={
'train': 10000,
'val': 2000, # 这里根据Segmentation中的.txt文件名对应,
'trainval': 12000 # 数字代表对应数据集包含图像的数量
},
num_classes=5, # 类别数目,包括背景
ignore_label=255, # 有些数据集标注有白色描边(VOC 2012),不代表任何实际类别
)
对应的train_utils.py
中,先将109行关于exclude_list
的设置修改,作用是在使用预训练权重时候,不加载该logit
层:
# Variables that will not be restored.
exclude_list = ['global_step','logits']
if not initialize_last_layer:
exclude_list.extend(last_layers)
commom.py中
将mobilenet_v2改为xception_65
参考aquariusjay在github上的解疑.
如果想在DeepLab的基础上fine-tune其他数据集, 可在train.py中修改输入参数。有一些选项:
initialize_last_layer=True
initialize_last_layer=False
和last_layers_contain_logits_only=False
logits
,因为如果是自己的数据集,对应的classes不同(这个我们前面已经设置不加载logits),可设置initialize_last_layer=False
和last_layers_contain_logits_only=True
最终,我的设置是:
initialize_last_layer=True
last_layers_contain_logits_only=False
training_number_of_steps
设置为50000crop_size
缩小为321(由于内存不够,将其改小,但是crop_size至少要大于300,遵循的公式是(crop_size-1)/4为整数)batch_size
改到8python train.py \
--logtostderr \
--train_split="trainval" \ 可以选择train/val/trainval 不同的数据集
--model_variant="xception_65" \
--atrous_rates=6 \
--atrous_rates=12 \
--atrous_rates=18 \
--output_stride=16 \
--decoder_output_stride=4 \
--train_crop_size=321 \
--train_crop_size=321\
--train_batch_size=8\
--training_number_of_steps=50000 \
--fine_tune_batch_norm=False \(由于batchsize小于12,将其改为false)
--tf_initial_checkpoint="加载与训练模型/model.ckpt" \
--train_logdir="保存训练的中间结果" \
--dataset_dir="生成的tfrecord的路径"
# When fine_tune_batch_norm=True, use at least batch size larger than 12
# (batch size more than 16 is better). Otherwise, one could use smaller batch
# size and set fine_tune_batch_norm=False.
# For `xception_65`, use atrous_rates = [12, 24, 36] if output_stride = 8, or
# rates = [6, 12, 18] if output_stride = 16. Note one could use different
eval.py中的相关参数设置:
python eval.py \
--logtostderr \
--eval_split="val" \
--model_variant="xception_65" \
--atrous_rates=6 \
--atrous_rates=12 \
--atrous_rates=18 \
--output_stride=16 \
--decoder_output_stride=4 \
--eval_crop_size=513 \
--eval_crop_size=513 \
--checkpoint_dir="${TRAIN_LOGDIR}" \
--eval_logdir="${EVAL_LOGDIR}" \
--dataset_dir="${DATASET}"
输出miou,来评估模型的好坏。
可一边训练一边可视化训练的 log,访问 http://localhost:6006/
即可看到 loss 等的变化。
# From deeplab/datasets/rs/exp
tensorboard --logdir train/
可视化验证的 log,可看到 miou_1.0
的变化,这里指定了另一个端口。
或同时可视化训练与验证的log:
# From deeplab/datasets/pascal_voc_seg/exp/train_on_train_set
tensorboard --logdir .
最后为了最后的分割结果可视化,也就是将输出的 class map 着色为 colored map 。需要在 get_dataset_colormap.py
文件中修改
# Dataset names.
_ADE20K = 'ade20k'
_CITYSCAPES = 'cityscapes'
_MAPILLARY_VISTAS = 'mapillary_vistas'
_PASCAL = 'pascal'
_DATASET_NAME='dataset_name' # 添加在这里,和注册的名字相同
# Max number of entries in the colormap for each dataset.
_DATASET_MAX_ENTRIES = {
_ADE20K: 151,
_CITYSCAPES: 19,
_MAPILLARY_VISTAS: 66,
_PASCAL: 256,
_DATASET_NAME: 151, # 在这里添加 colormap 的颜色数
}
接下来写一个函数,用途是返回一个 np.ndarray
对象,尺寸为 [classes, 3]
,即colormap共有 classes 种RGB颜色,分别代表不同的类别。
这个函数具体怎么写,还是由数据集的实际情况来定,我的数据集提供了 colormap ,就直接返回就可以了,就像下面这样。
def create_dataset_name_label_colormap():
return np.asarray([
[165, 42, 42],
[0, 192, 0],
[196, 196, 196],
[190, 153, 153],
[180, 165, 180],
[102, 102, 156],
[102, 102, 156],
[128, 64, 255],
...
])
最后修改 create_label_colormap
函数,在这个调用接口中加上我们自己的数据集:
def create_label_colormap(dataset=_PASCAL):
"""Creates a label colormap for the specified dataset.
Args:
dataset: The colormap used in the dataset.
Returns:
A numpy array of the dataset colormap.
Raises:
ValueError: If the dataset is not supported.
"""
if dataset == _ADE20K:
return create_ade20k_label_colormap()
elif dataset == _CITYSCAPES:
return create_cityscapes_label_colormap()
elif dataset == _MAPILLARY_VISTAS:
return create_mapillary_vistas_label_colormap()
elif dataset == _PASCAL:
return create_pascal_label_colormap()
elif dataset == _DATASET_NAME: # 添加在这里
return create_dataset_name_label_colormap()
else:
raise ValueError('Unsupported dataset.')
vis.py中的相关参数设置为:
vis_split
:设置为valvis_crop_size
:设置513,513为真实图片的大小dataset
:设置为我们在segmentation_dataset.py
文件设置的数据集名称dataset_dir
:设置为创建的TFRecordcolormap_type
:可视化标注的颜色 (vis.py中的此处要改下)vis.py有点问题
ignore_label 如果你的数据集存在ignore_label,注意不要把ignore_label和background混淆。 ignore_label是没有做标注的,不在预测范围内的,ignore_label是不参与计算loss的。我们在mask中将ignore_label的灰度值标记为255。
参考的文献:
https://blog.csdn.net/weixin_41713230/article/details/81937763#commentsedit
https://blog.csdn.net/u011974639/article/details/80948990
https://zhuanlan.zhihu.com/p/42756363
https://blog.csdn.net/qq_32799915/article/details/80070711#commentsedit
https://blog.csdn.net/Kelvin_XX/article/details/81946091
https://blog.csdn.net/ncloveqy/article/details/82285106
https://blog.csdn.net/qq_32799915/article/details/80070711
https://blog.csdn.net/u011974639/article/details/80948990
https://blog.csdn.net/weixin_41713230/article/details/81081120
https://blog.csdn.net/weixin_41713230/article/details/81076292(单通道)
https://blog.csdn.net/qq_17130909/article/details/82980542(单通道)