deeplabv3+训练自己的数据集
环境:ubuntu 16.04 + TensorFlow 1.9.1 + cuda 9.0 + cudnn 7.0 +python3.6
tensorflow 项目链接 https://github.com/tensorflow/models.git (deeplabv3+)
1、添加依赖库到PYTHONPATH
首先添加slim路径,每次打开terminal都要加载路径
-
# From tensorflow/models/research/
-
export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
注意,运行后要将所以终端关闭,重启~
2、测试一下
快速测试,调用model_test.py:
-
# From tensorflow/models/research/
-
python deeplab/model_test.py
若出现ok,就成功啦
3、数据集的格式
简单来说,需要把数据预处理成规定的格式和尺寸范围,生成包含文件名的文本文件,最后以规定的目录形式组织起来。
数据的目录组织形式应当这样:
-
+ 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
-
.....
4、数据集转换为tfrecord格式
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
可减小单个文件的大小)。
5、训练
在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
对应的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中
将model_variant改为xception_65
训练和可视化
(1)训练(train.py)
参考aquariusjay在github上的解疑.
如果想在DeepLab的基础上fine-tune其他数据集, 可在train.py中修改输入参数。有一些选项:
- 使用预训练的所有权重,设置
initialize_last_layer=True
- 只使用网络的backbone,设置
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=False (
把 True 改成 False ,重新训练最后一层。)
last_layers_contain_logits_only=True
training_number_of_steps
设置为50000
crop_size
缩小为321(由于内存不够,将其改小,但是crop_size至少要大于300,遵循的公式是(crop_size-1)/4为整数)batch_size
改到8-
-
python 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
(2)验证(eval.py)
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,来评估模型的好坏。
loss的可视化
可一边训练一边可视化训练的 log,访问 http://localhost:6006/
即可看到 loss 等的变化。
-
# From deeplab/datasets/rs/exp
-
tensorboard --logdir train/
可视化验证的 log,可看到 miou_1.0
的变化,这里指定了另一个端口。
-
# From deeplab/datasets/rs/exp/
-
tensorboard --logdir eval/ --port 6007
或同时可视化训练与验证的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(单通道)