TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)

基本配置

  • DeepLab的基本环境和基本demo要运行成功。

  • 如何使用DeepLabV3+训练CityScapes。


数据集处理

我的数据集是3分类问题,但因为数据集的保密协议,在后面的demo中我没有放出原图片,我会尽量将训练细节写出来。为了方便记录,我又使用了CamVid数据集(从这里下载)测试了一下。

数据集处理分成三大步:

  • 标注数据
  • 制作指示文件
  • 将数据打包成TFRecord

相关资源:

对于CamVid数据集,我将制作好的TFRecord上传到了CSDN上,如果只是单单测试程序的话,可以直接下载使用。
同时,对应的用于生成CamVid不同数据集的index文件我也上传到github了。

标注数据

这里需要提前说明一个概念,我自己在这里耽误了很长时间:

ignore_label

如果你的数据集存在ignore_label,注意不要把ignore_labelbackground混淆。 ignore_label是没有做标注的,不在预测范围内的,ignore_label是不参与计算loss的。我们在mask中将ignore_label的灰度值标记为255

mask的灰度值设置

CamVid有11个分类,我自己的数据集有3个分类。生成的mask必须为单通道(灰度图),建议为png格式。

注意,在制作mask时,对所有object的灰度像素值有要求

对于所有object包括background在内,在mask中要将灰度值标注为0~n,虽然产生的图片很难看出区别,但是这对训练是有效的。注意,不要把object的灰度值标注成10,20,100…(这样代码会直接将灰度值和class匹配,这在调整权重等操作会有麻烦~)

再强调一下:我在制作个人的数据集上将background标注为0,注意这是background而不是ignore_label.

最终,我在个人的数据集上关于图像的mask,设置如下:

  • background的灰度值设置为0
  • object1-2的灰度值设置为1,2
  • 因为我的数据集中没有ignore_label,所以没有点设置为255(我的数据集和CamVid都是没有的,如果存在ingore_label,在视觉上是白色的)

对于CamVid,原标注的数据集格式满足要求。示例如下,可以看到这里面是不存在ignore_label(没有白色),取一个图片示例如下:

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第1张图片

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第2张图片

制作指引文件

再制作TFRecord之前,需要有文件指引将数据集分类成训练/测试/验证集,故我们需要创建指引文件。

将上述生成的所有mask和原输入图片分在两个文件夹下,我的设置如下:

  • /root/dataset/CamVid/image:存放所有的输入图片,共有701张,这其中包括训练/测试/验证集的图片
  • /root/dataset/CamVid/mask:存放所有的labeled图片,共有701张,和输入图片是一一对应的

对于CamVid数据集,创建了一个目录/root/dataset/CamVid/index,该目录下包含三个.txt文件:

  • train.txt:所有训练集的文件名称
  • trainval.txt:所有验证集的文件名称
  • val.txt:所有测试集的文件名称

对应的CamVid数据集目录如下:

#from /root/dataset/CamVid/
    + image
    + mask
    + index # 自己创建
        - train.txt
        - trainval.txt
        - val.txt
    + tfrecord #自己创建

上述的三个.txt文件,也是从CamVid上下载的,参照制作脚本的要求。

详细的来讲,只存放图片的名称,对应train/val.txt文件部分截取(可以使用Sublime或其他文本编辑工具统一find然后删除):

# train.txt
0001TP_006690
0001TP_006720
...
0016E5_08640
#共367行

# val.txt
0016E5_07959
...
0016E5_08157
0016E5_08159
# 共101行

对应的用于生成CamVid不同数据集的index文件我也上传到github了。

将数据打包成TFRecord

将上述制作的数据集打包成TFRecord,使用的是build_voc2012_data.py,参考download_and_convert_voc2012.sh中的指令格式。

对于CamVid数据集,我将制作好的TFRecord上传到了CSDN上,如果只是单单测试程序的话,可以直接下载使用。

标准的指令格式如下:

python ./build_voc2012_data.py \
  --image_folder="${IMAGE_FOLDER}" \
  --semantic_segmentation_folder="${SEMANTIC_SEG_FOLDER}" \
  --list_folder="${LIST_FOLDER}" \
  --image_format="jpg" \
  --output_dir="${OUTPUT_DIR}"
  • ${IMAGE_FOLDER} :数据集中原输入数据的文件目录地址
  • ${SEMANTIC_SEG_FOLDER}:数据集中标签的文件目录地址
  • ${LIST_FOLDER}: 将数据集分类成训练集、验证集等的指示目录文件目录
  • image_format:输入图片数据的格式,CamVid的是png格式
  • output_dir:制作的TFRecord存放的目录地址(自己创建)

依据上面对于数据集的处理,对于CamVid数据集,制作TFRecord,使用的指令如下:

# from /research/deeplab/dataset
python ./build_voc2012_data.py \
  --image_folder="/root/dataset/CamVid/image" \
  --semantic_segmentation_folder="/root/dataset/CamVid/mask" \
  --list_folder="/root/dataset/CamVid/index" \
  --image_format="png" \
  --output_dir="/root/dataset/CamVid/tfrecord"

指定运行结果部分输出如下:

#trainval.txt
>> Converting image 59/233 shard 0
>> Converting image 118/233 shard 1
>> Converting image 177/233 shard 2
>> Converting image 233/233 shard 3
#train.txt
>> Converting image 92/367 shard 0
>> Converting image 184/367 shard 1
>> Converting image 276/367 shard 2
>> Converting image 367/367 shard 3
#val.txt
>> Converting image 26/101 shard 0
>> Converting image 52/101 shard 1
>> Converting image 78/101 shard 2
>> Converting image 101/101 shard 3

可以在对应的/root/dataset/CamVid/tfrecord目录下查看已经制作好的TFRecord文件:

# from  /root/dataset/CamVid/tfrecord
$: ls
train-00000-of-00004.tfrecord     trainval-00002-of-00004.tfrecord
train-00001-of-00004.tfrecord     trainval-00003-of-00004.tfrecord
train-00002-of-00004.tfrecord     val-00000-of-00004.tfrecord
train-00003-of-00004.tfrecord     val-00001-of-00004.tfrecord
trainval-00000-of-00004.tfrecord  val-00002-of-00004.tfrecord
trainval-00001-of-00004.tfrecord  val-00003-of-00004.tfrecord

这样关于数据集的处理都完事了。


修改训练脚本

在DeepLabv3+模型的基础上,主要需要修改以下几个文件

  • segmentation_dataset.py文件
  • train_utils.py

segmentation_dataset.py

修改segmentation_dataset.py的110行,添加对应的关于_CAMVID数据集的描述设置:

CamVid描述配置

# segmentation_dataset.py line 110
_CAMVID_INFORMATION = DatasetDescriptor(
    splits_to_sizes={
        'train': 367,  # num of samples in images/training
        'val': 101,  # num of samples in images/validation
    },
    num_classes=12,
    ignore_label=255,
)

因为CamVid共有11个classes,所以加上ignore_label共12个。

个人数据集描述配置

对于我个人的数据集,一共有object1,object2,background三个类别,加上ignore_label,所以num_classes=4.

_MYDATA_INFORMATION = DatasetDescriptor(
    splits_to_sizes={
        'train': 444,  # num of samples in images/training
        'val': 46,  # num of samples in images/validation
    },
    num_classes=4,
    ignore_label=255,
)

注册数据集

同时在segmentation_dataset.py文件的112行,上添加对应数据集的名称:

_DATASETS_INFORMATION = {
    'cityscapes': _CITYSCAPES_INFORMATION,
    'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION,
    'ade20k': _ADE20K_INFORMATION,
    'mydata':_MYDATA_INFORMATION, #我自己的数据集
    'camvid':_CAMVID_INFORMATION, #camvid示例
}

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)

数据不平衡

参考DeepLabv3+一作aquariusjay的解疑,如果你的问题是二分类或者和我的数据集类似,存在object之间imablance,这需要在train_utils.py的70行修改权重.

因为CamVid数据集这个问题可以忽略,这里重点说我个人数据集,因为是三分类问题,其中background占了非常大的比例,并且object2比object1要稍微少一点,最终的设置的权重比例为1:10:15

irgore_weight = 0
label0_weight =1  # 对应灰度值0,即background
label1_weight = 10  # 对应object1, mask 中灰度值1
label2_weight = 15  # 对应object2,.mask 中灰度值2

not_ignore_mask = 
tf.to_float(tf.equal(scaled_labels, 0)) * label0_weight +
tf.to_float(tf.equal(scaled_labels, 1)) * label1_weight +
tf.to_float(tf.equal(scaled_labels, 2)) * label2_weight +
tf.to_float(tf.equal(scaled_labels, ignore_label)) * irgore_weight 

tf.losses.softmax_cross_entropy(
    one_hot_labels,
    tf.reshape(logits, shape=[-1, num_classes]),
    weights=not_ignore_mask,
    scope=loss_scope)

在这个步骤,我曾把ignore_labelbackground混淆了,最后我在mask 中将background的标注值为0,对应的label0_weight=1,object1的标注值为1,对应的为label1_weight = 10,… 最后才训练成功。


训练和可视化

参考aquariusjay在github上的解疑.

如果想在DeepLab的基础上fine-tune其他数据集, 可在train.py中修改输入参数。有一些选项:

  • 使用预训练的所有权重,设置initialize_last_layer=True
  • 只使用网络的backbone,设置initialize_last_layer=Falselast_layers_contain_logits_only=False
  • 使用所有的预训练权重,除了logits,因为如果是自己的数据集,对应的classes不同(这个我们前面已经设置不加载logits),可设置initialize_last_layer=Falselast_layers_contain_logits_only=True

最终,我的设置是:

  • initialize_last_layer=False
  • last_layers_contain_logits_only=True

初试训练

注意:我在训练CamVid时,是没有考虑类别之间的的平衡问题,所以没有做imblance的修正,如果你是二分类或者存在严重的imblance情况,可以参考部分troubleshot中我遇到的问题。

依据repo上的训练示例,注意如下几个参数:

  • tf_initial_checkpoint:预训练的权重,因为CamVid和我的数据集都和CityScapes类似,所以我使用的是CityScapes的预训练权重

  • train_logdir:训练产生的文件存放位置

  • dataset_dir:数据集的TFRecord文件
  • dataset:设置为我们在segmentation_dataset.py文件设置的数据集名称

我在CamVid上的训练指令如下:

python deeplab/train.py \
    --logtostderr \
    --training_number_of_steps=300 \
    --train_split="train" \
    --model_variant="xception_65" \
    --atrous_rates=6 \
    --atrous_rates=12 \
    --atrous_rates=18 \
    --output_stride=16 \
    --decoder_output_stride=4 \
    --train_crop_size=513 \
    --train_crop_size=513 \
    --train_batch_size=2 \
    --dataset="camvid" \
    --tf_initial_checkpoint='/root/newP/official_tf/models-master/research/deeplab/backbone/deeplabv3_cityscapes_train/model.ckpt' \
    --train_logdir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train' \
    --dataset_dir='/root/dataset/CamVid/tfrecord'

这里只设置了300个step,对于crop_size设置为513,只是为了测试训练是否能够正确执行。 考虑到我的计算资源显示,我设置batchsize=2。

部分的训练输出如下:

INFO:tensorflow:Starting Session.
INFO:tensorflow:Saving checkpoint to path /root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train/model.ckpt
INFO:tensorflow:Starting Queues.
INFO:tensorflow:global_step/sec: 0
INFO:tensorflow:Recording summary at step 0.
INFO:tensorflow:global step 10: loss = 2.7773 (0.550 sec/step)
INFO:tensorflow:global step 20: loss = 2.6438 (0.531 sec/step)
INFO:tensorflow:global step 30: loss = 2.4824 (0.555 sec/step)
INFO:tensorflow:global step 40: loss = 2.4652 (0.564 sec/step)
#...
INFO:tensorflow:global step 300: loss = 1.9276 (0.534 sec/step)
INFO:tensorflow:Stopping Training.
INFO:tensorflow:Finished training! Saving model to disk.

可视化和评估

deepLab提供了可视化和评估工具,这里对于刚才在CamVid上训练的权重测试。

可视化的结果

因为CamVid的图片大小和CityScapes是不同的,这里相关的参数设置为:

  • vis_split:设置为测试集
  • vis_crop_size:设置360,480为图片的大小
  • dataset:设置为我们在segmentation_dataset.py文件设置的数据集名称
  • dataset_dir:设置为创建的TFRecord
  • colormap_type:可视化标注的颜色

最终,可视化的指令如下:

# From tensorflow/models/research/
python deeplab/vis.py \
    --logtostderr \
    --vis_split="val" \
    --model_variant="xception_65" \
    --atrous_rates=6 \
    --atrous_rates=12 \
    --atrous_rates=18 \
    --output_stride=16 \
    --decoder_output_stride=4 \
    --vis_crop_size=360 \
    --vis_crop_size=480 \
    --dataset="camvid" \
    --colormap_type="pascal" \
    --checkpoint_dir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train' \
    --vis_logdir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/vis' \
    --dataset_dir='/root/dataset/CamVid/tfrecord'

可视化指令的输出部分如下:

INFO:tensorflow:Restoring parameters from /root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train/model.ckpt-300
INFO:tensorflow:Visualizing batch 1 / 101
INFO:tensorflow:Visualizing batch 2 / 101
INFO:tensorflow:Visualizing batch 3 / 101
...
INFO:tensorflow:Visualizing batch 100 / 101
INFO:tensorflow:Visualizing batch 101 / 101

我截取了可视化输出图:

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第3张图片

可以看到模型是可以正确分类的,虽然结果不怎么好。

评估结果

同样的,eval的参数也需要做一些改变:

  • eval_split:设置为测试集
  • crop_size:同样设置为360和480
  • dataset:设置为camvid
  • dataset_dir:设置为我们创建的数据集

我们评估的指令如下:

python deeplab/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=360 \
    --eval_crop_size=480 \
    --dataset="camvid" \
    --checkpoint_dir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train' \
    --eval_logdir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/eval' \
    --dataset_dir='/root/dataset/CamVid/tfrecord'

评估指令的输出部分如下:

INFO:tensorflow:Restoring parameters from /root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train/model.ckpt-300
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Starting evaluation at 2018-07-06-06:34:28
INFO:tensorflow:Evaluation [10/101]
INFO:tensorflow:Evaluation [20/101]
#...
INFO:tensorflow:Evaluation [101/101]
INFO:tensorflow:Finished evaluation at 2018-07-06-06:34:34
miou_1.0[0.149601415]

结果一般般,但是证明了我们训练过程是没有大问题~

进阶训练

在前面初始训练后,针对CamVid数据集,我做了进一步参考修改,简单测试了一下新的结果。

我将前面存储在train_logdir里面的权重删除了,一些参数的修改:

  • training_number_of_steps设置为3000
  • crop_size缩小为321
  • batch_size可以提高到4

进阶的训练指令如下:

python deeplab/train.py \
    --logtostderr \
    --training_number_of_steps=3000 \
    --train_split="train" \
    --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=4 \
    --dataset="camvid" \
    --tf_initial_checkpoint='/root/newP/official_tf/models-master/research/deeplab/backbone/deeplabv3_cityscapes_train/model.ckpt' \
    --train_logdir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train' \
    --dataset_dir='/root/dataset/CamVid/tfrecord'

进阶训练的输出部分如下:

INFO:tensorflow:global step 2960: loss = 0.6281 (0.407 sec/step)
INFO:tensorflow:Saving checkpoint to path /root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train/model.ckpt
INFO:tensorflow:global_step/sec: 2.45499
INFO:tensorflow:Recording summary at step 2962.
INFO:tensorflow:global step 2970: loss = 0.8240 (0.439 sec/step)
INFO:tensorflow:global step 2980: loss = 0.9588 (0.385 sec/step)
INFO:tensorflow:global step 2990: loss = 0.8880 (0.412 sec/step)
INFO:tensorflow:global step 3000: loss = 0.8292 (0.392 sec/step)

进阶训练结果评估

重新使用eval.py做了验证:

INFO:tensorflow:Evaluation [101/101]
INFO:tensorflow:Finished evaluation at 2018-07-06-07:03:30
miou_1.0[0.4015598]

可以看到,新的训练结果有了显著的提示。

进阶训练结果可视化

重新使用vis.py,部分可视化结果:

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第4张图片

可以看到明显比前面好多了。


部分troubleshot

一开始我是一直在DeepLabv3+上测试自己的数据集,故问题都是集中于自己的数据集上。

我主要犯了三个错误:

  • 1:数据集的mask的生成
  • 2:混淆了ignore_label和background
  • 3:错误的设置各个object和background之间的权重

对于mask的生成,我在前面详细的讲了如何生成,这是非常重要的一步。,我犯的错误是将不同的object和background的像素值设置为0,100,150.这导致后续的预测会有问题,无法解决imblance。

混淆了ignore_label和background

对于ignore_label我曾经设置为0,对应的segmentation_dataset.py文件:

# segmentation_dataset.py

_CAMVID_INFORMATION = DatasetDescriptor(
    splits_to_sizes={
        'train': 367,  # num of samples in images/training
        'val': 101,  # num of samples in images/validation
    },
    num_classes=3,
    ignore_label=0,
)

这里我错误的把ignore_label设置为0,并且错误的将num_classes设置为3.

train_utils.py文件中计算如下:

ignore_weight = 0
label0_weight =20
label1_weight = 20

not_ignore_mask = 
tf.to_float(tf.equal(scaled_labels, 1)) * label0_weight 
+ tf.to_float(tf.equal(scaled_labels, 2)) * label1_weight 
+ tf.to_float(tf.equal(scaled_labels, ignore_label)) * ignore_weight

因为ignore_label设置为0了,这会导致结果背景不做任何loss,因为我每次都是训练了200个step,可以看到如下两个图,是有学习到信息,但是在预测上是有存在问题的。

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第5张图片

设置权重问题

预测是一种颜色

对应各个类别之间权重设置有问题,预测权重设置出现了问题:

## 输出全蓝色
irgore_weight = 0
label0_weight =10
label1_weight = 20


## 输出全黑色
irgore_weight = 10
label0_weight =10
label1_weight = 10

## 输出全绿色
irgore_weight = 0
label0_weight =20
label1_weight = 10

输出结果为全蓝色/黑色/绿色,对应的object2/background/object1设置过大,且没有考虑background,这导致模型不计算object2/background/object1的loss,一股脑的预测蓝色/黑色/绿色就能得到不错的loss。

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第6张图片

成功的训练

我的数据集中大部分都是background,object1一般会比object2大一些,故最终测试的权重比例为1:10:15,在实际过程中可通过像素统计给出一个更为精确的值:

irgore_weight = 0
label0_weight =1  # background
label1_weight = 10 # object1
label2_weight = 15 #object2

not_ignore_mask = 
tf.to_float(tf.equal(scaled_labels, 0)) * label0_weight +
tf.to_float(tf.equal(scaled_labels, 1)) * label1_weight +
tf.to_float(tf.equal(scaled_labels, 2)) * label2_weight +
tf.to_float(tf.equal(scaled_labels, ignore_label)) * irgore_weight 

这是我在训练200个step和4000个step之间模型的测试结果如下:

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第7张图片


参考资料

参考aquariusjay

参考aquariusjay关于训练参数的设置:

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第8张图片

aquariusjay关于imblance的设置:

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第9张图片

参考zhaolewen

参考github-@zhaolewen的关于数据标签的回答:

TensorFlow实战:Chapter-9下(DeepLabv3+在自己的数据集训练)_第10张图片

你可能感兴趣的:(TensorFlow,深度学习,TensorFlow实战)