前言
deeplabv3是当前较为常用的语义分割的神经网络,且整个训练工程已经全部开源,使用公布的模型进行测试或基于自己的训练都可以得到一个较好的结果。
deeplabv3开源工程详解(1)—— 开源模型测试自己的图片
deeplabv3开源工程详解(2)—— 使用自己的数据集进行训练、迁移学习
deeplabv3开源工程(3)—— 报错:2 root error(s) found. (0) Invalid argument: padded_shape[0]=168 is not…
1 工程准备
环境
【TITAN XP】+【Ubuntu】+【tensorflow-gpu-1.14】+【cuda10.2】
(使用1.8.0及以上版本,低版本缺少函数,会报错)
下载包
- 下载工程并解压
https://github.com/tensorflow/models
解压后,deeplabv3的工程在路径【./research/deeplab】下。- 下载deeplabv3的开源的权重
https://github.com/tensorflow/models/blob/master/research/deeplab/g3doc/model_zoo.md
在这个界面可以看到,基于PASCAL VOC数据集训练的模型、基于Cityscapes数据集训练的模型。本篇使用的是前者模型
![]()
- 进入 ./research/deeplab 路径下,创建model文件夹,将下载的权重文件解压在该文件夹下。
![]()
可以看见每个文件夹下有相同命名的三个文件。
其中,pb文件用于实际测试;ckpt文件用于预训练模型。
2 deeplabv3 训练自己的数据
在下载并解压完工程以及模型后。
需要注意的问题:
- 确认tensorflow1.8 以上的版本
- deeplab工程实现的代码在【./model-master/research/deeplab】路径下;但deeplab代码实现时,默认的根目录为【models/research/】。所以脚本的运行,需要在根目录下运行。(也可以在代码中进行修改,本篇博客记录的方式不修改)
2.1 确认工程配置
- 到【research】路径下,打开终端,激活虚拟环境。
- 将【./slim】路径导入到PYTHONPATH 环境变量中
export PYTHONPATH=$PYTHONPATH:./slim
。- 在【research】路径下运行
./deeplab/model_test.py
![]()
运行结束后显示下图,则表示基本配置成功,可以进行训练的配置。
2.2 数据集标注
标签的标注,在另外一篇博客 labelme标注软件的使用 || 语义分割数据标注 中记录。
标注的结果是得到了数据集的原始标签,然后需要根据实际情况,编写个小脚本来完成 原始图片和标签的命名的统一。
2.3 数据的整理
2.3.1 数据的目录结构
在【./deeplab/datasets/】路径下创建文件夹【dataset】,进行数据目录的整理(这里仿用PASCAL数据集的目录形式,当然也可以自己命名路径)。
数据的目录组织的形式如下:
其中黑颜色文件夹是要自己创建的路径;红颜色的文件或文件夹,是接下来将要生成的。+ datasets
+ dataset
|+ VOC2012
| + JPEGImages (存放原始数据)
| + SegmentationClass (存放标注后的png图片数据)
| + SegmentationClassDeal
| + ImageSets
| + Segmentation
| +train.txt
| +val.txt
| +trainval.txt
|+ tfrecord (后面接介绍制作相应文件方法)
- JPEGImages:存放RGB图像。对应的图像和标签的文件名相同,扩展名为 .jpg 或 .png
- SegmentationClass:存放着原始标签,用于转化为神经网络训练的class标签
- SegmentationClassDeal:存放转化后的神经网络训练的class标签(该路径不用创建,运行代码会自动生成)
- xxx.txt文件:对应图片的不带扩展名的文件名(对于自己的数据集,需要自己生成该文件)
2007_000032
2007_000039
2007_000063
2007_000068
…- tfrecord:存放着将训练所需的数据标签转化成 tfrecord格式的文件(需要自己创建,当前为空文件夹)。
2.3.2 生成灰度图(制作SegmentationClassDeal下数据)
SegmentationClass文件夹下存放着原始标签:
- 图片中每一种颜色对应了一个类别。
- VOC数据集中,该文件夹下图片格式为单通道的png图像。图片是8位彩色图片,使用PIL库保存图片选择模式“P”的效果,它的每个像素用8个bit表示,其对应的彩色值是按照调色板查询出来的。
- 自己制作的数据集的标签,有可能是3通道的RGB图片(一些标注软件得到的也是8位的彩色图)此处需要注意。
deeplab的class标签:
- deeplab神经网络的label是用单通道的标注图,也就是灰度图。类别像素标记应为 0,1,2,3…n(共n+1 类,n个目标类+1个背景类)。此外,标注图上忽略类的像素值标记为255。
所以需要将原始标签转化为神经网络训练的class标签:
在【./deeplab/datasets/】路径下自带的remove_gt_colormap.py
脚本适用于将单通道的彩色图转化为灰度图。使用方法如下:
# 在 research/deeplab/datasets/ 路径下 python remove_gt_colormap.py \ --original_gt_folder="./dataset/VOC2012/SegmentationClass" \ --output_dir="./dataset/VOC2012/SegmentationClassDeal"
运行完在【dataset/VOC2012】路径下生成【文件夹 SegmentationClassDeal】,存放这神经网络训练时使用的class标签图片。
下图是PASCAL VOC2012的图片的三种形式,该数据集在part边界处存在“白边”,用作计算位于这些标签的 trimap 里的像素平均IOU。通常自己标注的数据集不会存在这个“白边”,class label 看起来是全黑的。
2.3.3 生成记录数据名字的txt文件
在制作 tfrecord之前,需要现将数据集分配成 训练数据、验证数据、测试数据。
前两者将相应的数据的名字保存在train.txt、trainval.txt、val.txt。放置文件保存的是对应图片的不带扩展名的文件名。
自己写个小脚本就可完成该txt文件的制作。
2.3.4 生成 tfrecord 文件
对于不大的数据集来说,tensorflow提供了一种高效的数据读取模式,将数据转换成TFRecord 格式。
在路径【./research/deeplab/datasets/】路径下,存在脚本build_voc2012_data.py
,然后在该路径下运行命令:
python ./build_voc2012_data.py \ --image_folder="./dataset/VOC2012/JPEGImages" \ --semantic_segmentation_folder="./dataset/VOC2012/SegmentationClassDeal" \ --list_folder="./dataset/VOC2012/ImageSets/Segmentation" \ --image_format="jpg" \ --output_dir="./dataset/tfrecord"
可在代码中调节参数
_NUM_SJARDS
(默认为4),改变数据分块的数目。运行结束后在【文件夹 tfrecord】下,保存了文件:
2.4 deeplab 的代码修改
2.4.1 新的数据集的注册
分别在【./research/deeplab/deprecated/
segmentation_dataset.py
】中 、以及在【./research/deeplab/datasets/data_generator.py
】中, 完成自己的数据集的注册。操作如下:
Ctrl+F 查找到_DATASETS_INFORMATION
,在这里原代码为:
_DATASETS_INFORMATION = { 'cityscapes': _CITYSCAPES_INFORMATION, 'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION, 'ade20k': _ADE20K_INFORMATION, }
这里面注册着三个数据集,我们需要将自己的数据集添加进去:
_MYDATA_INFORMATION = DatasetDescriptor( splits_to_sizes={ 'train': 1464, # 训练集数量 'val': 1449, # 测试集数量 }, num_classes=21, # 种类+背景类 ignore_label=255, # ignore_label 用来 crop size 做填充的,默认为255 ) _DATASETS_INFORMATION = { 'cityscapes': _CITYSCAPES_INFORMATION, 'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION, 'ade20k': _ADE20K_INFORMATION, 'mydata':_MYDATA_INFORMATION, # 添加自己的数据集 }
2.4.2 权重文件加载的设置
在【./research/deeplab/utils/】路径下的
train_utils.py
脚本中,Ctrl+F 搜索exclude_list
,能看到原代码
exclude_list = ['global_step'] if not initialize_last_layer: exclude_list.extend(last_layers)
由于我们需要使用预训练权重训练自己的数据集,标签的类别不同,所以不应该加载 logit层。修改代码如下:
exclude_list = ['global_step', 'logits'] if not initialize_last_layer: exclude_list.extend(last_layers)
2.4.3 样本不平衡的处理
如果我们的数据集出现了样本不平衡,也就是图片中每个类别的占比不同。比如:当普遍存在background占比较大,训练的结果预测可能出现经常全黑。
此时就需要修改相应的权重,使得能够正确训练。本篇博客记录与2020年11月,根据当前开源的工程修改方式如下:
在【./research/deeplab/common.py
】脚本中,配置参数label_weights
。源码中可见
flags.DEFINE_multi_float( 'label_weights', None, 'A list of label weights, each element represents the weight for the label ' 'of its index, for example, label_weights = [0.1, 0.5] means the weight ' 'for label 0 is 0.1 and the weight for label 1 is 0.5. If set as None, all ' 'the labels have the same weight 1.0.')
在这里可以给不同的类别配置权重
2.5 工程训练、结果可视化、测试
2.5.1 训练
在运行
train.py
之前,我们需要明确一些设置。
如果要在Deeplab的预训练模型基础上fine-tune自己的数据集,根据不同的情况配置参数:
- 使用预训练所有的权重,设置
initialize_last_layer=True
- 不使用预训练的权重,设置
initialize_last_layer=False、last_layer_contain_logits_only=False
- 使用 除去logits层的所有权重,设置
initialize_last_layer=False、last_layer_contain_logits_only=True
为了方便修改,个人喜欢把修改的部分单独写出来,在脚本
train.py
的最后添加:
if __name__ == '__main__': flags.initialize_last_layer = False # 添加 flags.last_layers_contain_logits_only = True # 添加 flags.mark_flag_as_required('train_logdir') flags.mark_flag_as_required('dataset_dir') tf.app.run()
然后就可以使用命令进行训练了。在【./research/】路径下运行
export PYTHONPATH=$PYTHONPATH:./slim #如果重新打开了终端,确保该条命令执行过 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="513,513" \ --train_batch_size=4 \ --fine_tune_batch_norm=true \ --dataset="mydata" \ --tf_initial_checkpoint="./deeplab/model/deeplabv3_pascal_train_aug/model.ckpt" \ --train_logdir="./deeplab/train/" \ --dataset_dir="./deeplab/datasets/dataset/tfrecord/"
其中,需要注意的是:
- train_crop_size:裁剪后的图片大小。
- 不得小于 [321,321]
- (crop_size-1)/4 = 整数
- 将crop_size 设置成 [256,256]时,结果不好。因为ASPP模块的存在,当图片过小时,到 feature map 时没有扩张卷积的范围大。
- fine_tune_batch_norm:是否要训练BN层的参数。
- 如果要训练BN层的参数,fine_tune_batch_norm=True、batch_size最好大于12。如果显存不够,可调整crop_size大小(但要不小于[321,321])
2.5.2 可视化
使用VOC数据集简单按照上述方法简单训练了下,然后进行可视化。在【./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="513,513" \ --dataset="mydata" \ --colormap_type="pascal" \ --checkpoint_dir="./deeplab/train/" \ --vis_logdir="./deeplab/train/vis/" \ --dataset_dir="./deeplab/datasets/dataset/tfrecord/"
其中
- colormap_type:可以方便起见,直接设置为“pascal”。如果自己想修改为自定义的颜色显示列表,又或者类别数大于21类,就需要在脚本【./research/deeplab/utils/
get_dataset_colormap.py
】进行修改。这里选择“pascal”
在【./research/deeplab/train/vis/】路径下显示着结果:
2.5.3 验证
当我们训练结束后,想要知道对于每类对象的预测效果如何,在【./research】路径下执行如下命令,就可获取到训练后验证集的 每类对象的IOU。
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="513,513" \ --dataset="mydata" \ --checkpoint_dir="./deeplab/train/" \ --eval_logdir="./deeplab/train/eval/" \ --dataset_dir="./deeplab/datasets/dataset/tfrecord/"
终端输出部分内容:
2.6 需要注意的问题
1 数据集中分类不均衡
- 如果出现训练后,进行图片可视化,是全黑的。多半是由于 训练数据中种类不均衡,背景太多,又或者背景权重过大(另外,当训练轮次较少,也会出现预测结果全黑的现象)
2 输入数据大小各异时
- 需要自己做一个预处理,将所有图片的尺寸进行统一
3 分割类别不宜过多。会导致训练效果不理想。