diff --git a/README.md b/README.md index 93452b43e75fe20182b6d81e47829f1be3ed18ec..dcf69d92352704ecf4728e1154e90386b9465ee8 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ PaddleSeg支持多进程IO、多卡并行、跨卡Batch Norm同步等训练加 ### 高级功能 * [PaddleSeg的数据增强](./docs/data_aug.md) +* [PaddleSeg的loss选择](./docs/loss_select.md) * [特色垂类模型使用](./contrib) * [多进程训练和混合精度训练](./docs/multiple_gpus_train_and_mixed_precision_train.md) diff --git a/configs/deepglobe_road_extraction.yaml b/configs/deepglobe_road_extraction.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b0446fc56ea1e56115618281a3c82a837cccfe73 --- /dev/null +++ b/configs/deepglobe_road_extraction.yaml @@ -0,0 +1,44 @@ +EVAL_CROP_SIZE: (1025, 1025) # (width, height), for unpadding rangescaling and stepscaling +TRAIN_CROP_SIZE: (769, 769) # (width, height), for unpadding rangescaling and stepscaling +AUG: + AUG_METHOD: u"stepscaling" # choice unpadding rangescaling and stepscaling + FIX_RESIZE_SIZE: (640, 640) # (width, height), for unpadding + INF_RESIZE_VALUE: 500 # for rangescaling + MAX_RESIZE_VALUE: 600 # for rangescaling + MIN_RESIZE_VALUE: 400 # for rangescaling + MAX_SCALE_FACTOR: 2.0 # for stepscaling + MIN_SCALE_FACTOR: 0.5 # for stepscaling + SCALE_STEP_SIZE: 0.25 # for stepscaling +BATCH_SIZE: 8 +DATASET: + DATA_DIR: "./dataset/MiniDeepGlobeRoadExtraction/" + IMAGE_TYPE: "rgb" # choice rgb or rgba + NUM_CLASSES: 2 + TEST_FILE_LIST: "dataset/MiniDeepGlobeRoadExtraction/val.txt" + TRAIN_FILE_LIST: "dataset/MiniDeepGlobeRoadExtraction/train.txt" + VAL_FILE_LIST: "dataset/MiniDeepGlobeRoadExtraction/val.txt" + IGNORE_INDEX: 255 + SEPARATOR: '|' +FREEZE: + MODEL_FILENAME: "model" + PARAMS_FILENAME: "params" + SAVE_DIR: "freeze_model" +MODEL: + DEFAULT_NORM_TYPE: "bn" + MODEL_NAME: "deeplabv3p" + DEEPLAB: + BACKBONE: "mobilenet" + DEPTH_MULTIPLIER: 1.0 + ENCODER_WITH_ASPP: False + ENABLE_DECODER: False +TEST: + TEST_MODEL: "./saved_model/deeplabv3p_mobilenetv2-1-0_bn_deepglobe_road_extraction/" +TRAIN: + MODEL_SAVE_DIR: "./saved_model/deeplabv3p_mobilenetv2-1-0_bn_deepglobe_road_extraction/" + PRETRAINED_MODEL_DIR: "./pretrained_model/deeplabv3p_mobilenetv2-1-0_bn_coco/" + SNAPSHOT_EPOCH: 10 +SOLVER: + LR: 0.001 + LR_POLICY: "poly" + OPTIMIZER: "adam" + NUM_EPOCHS: 300 diff --git a/dataset/download_mini_deepglobe_road_extraction.py b/dataset/download_mini_deepglobe_road_extraction.py new file mode 100644 index 0000000000000000000000000000000000000000..13312407434b7186a782a99d38c9281c5c006c32 --- /dev/null +++ b/dataset/download_mini_deepglobe_road_extraction.py @@ -0,0 +1,33 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License" +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import os + +LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) +TEST_PATH = os.path.join(LOCAL_PATH, "..", "test") +sys.path.append(TEST_PATH) + +from test_utils import download_file_and_uncompress + + +def download_deepglobe_road_dataset(savepath, extrapath): + url = "https://paddleseg.bj.bcebos.com/dataset/MiniDeepGlobeRoadExtraction.zip" + download_file_and_uncompress( + url=url, savepath=savepath, extrapath=extrapath) + + +if __name__ == "__main__": + download_deepglobe_road_dataset(LOCAL_PATH, LOCAL_PATH) + print("Dataset download finish!") diff --git a/docs/configs/solver_group.md b/docs/configs/solver_group.md index f38d9574d92404aa809d7eb51a9e6f18ff81ed7f..e068b9d36eedf2cb74e912f1705b66eac421cfdc 100644 --- a/docs/configs/solver_group.md +++ b/docs/configs/solver_group.md @@ -132,27 +132,6 @@ L2正则化系数 `['softmax_loss']`或`['dice_loss','bce_loss']` -* softmax_loss - -![equation](http://latex.codecogs.com/gif.latex?softmax\\_loss=\sum_{i=1}^Ny_i{log(p_i)}) - -
- -* dice_loss - -![equation](http://latex.codecogs.com/gif.latex?dice\\_loss=1-\frac{2|Y\bigcap{P}|}{|Y|\bigcup|P|}) - -[dice系数](https://zh.wikipedia.org/wiki/Dice%E7%B3%BB%E6%95%B0) - -
- -* bce_loss - -![equation](http://latex.codecogs.com/gif.latex?bce\\_loss=y_i{log(p_i)}+(1-y_i)log(1-p_i)) - -其中![equation](http://latex.codecogs.com/gif.latex?y_i)和*Y*为标签, - ![equation](http://latex.codecogs.com/gif.latex?p_i)和*P*为预测结果 - ### 默认值 ['softmax_loss'] diff --git a/docs/imgs/deepglobe.png b/docs/imgs/deepglobe.png new file mode 100644 index 0000000000000000000000000000000000000000..cfd77f6b654dc16ad9687d16f8e836b278d26594 Binary files /dev/null and b/docs/imgs/deepglobe.png differ diff --git a/docs/imgs/dice1.png b/docs/imgs/dice1.png new file mode 100644 index 0000000000000000000000000000000000000000..f8520802296cc264849fae4a8442792cf56cb20a Binary files /dev/null and b/docs/imgs/dice1.png differ diff --git a/docs/imgs/loss_comparison.png b/docs/imgs/loss_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..9a475570930f9c97e433ae9eb1f34ccae1b2444c Binary files /dev/null and b/docs/imgs/loss_comparison.png differ diff --git a/docs/loss_select.md b/docs/loss_select.md new file mode 100644 index 0000000000000000000000000000000000000000..d90525f4d3ebfec2cc4a336dba39386b97b6fd89 --- /dev/null +++ b/docs/loss_select.md @@ -0,0 +1,77 @@ +# dice loss解决二分类中样本不均衡问题 + +对于二类图像分割任务中,往往存在类别分布不均的情况,如:瑕疵检测,道路提取及病变区域提取等等。 +在DeepGlobe比赛的Road Extraction中,训练数据道路占比为:%4.5。如下为其图片样例: +

+
+

+可以看出道路在整张图片中的比例很小。 + +## 数据集下载 +我们从DeepGlobe比赛的Road Extraction的训练集中随机抽取了800张图片作为训练集,200张图片作为验证集, +制作了一个小型的道路提取数据集[MiniDeepGlobeRoadExtraction](https://paddleseg.bj.bcebos.com/dataset/MiniDeepGlobeRoadExtraction.zip) + +## softmax loss与dice loss + +在图像分割中,softmax loss(sotfmax with cross entroy loss)同等的对待每一像素,因此当背景占据绝大部分的情况下, +网络将偏向于背景的学习,使网络对目标的提取能力变差。`dice loss(dice coefficient loss)`通过计算预测与标注之间的重叠部分计算损失函数,避免了类别不均衡带来的影响,能够取得更好的效果。 +在实际应用中`dice loss`往往与`bce loss(binary cross entroy loss)`结合使用,提高模型训练的稳定性。 + +dice loss的定义如下: + +![equation](http://latex.codecogs.com/gif.latex?dice\\_loss=1-\frac{2|Y\bigcap{P}|}{|Y|+|P|}) + +其中 ![equation](http://latex.codecogs.com/gif.latex?|Y\bigcap{P}|) 表示*Y*和*P*的共有元素数, +实际计算通过求两者的乘积之和进行计算。如下所示: + +

+
+

+ +[dice系数详解](https://zh.wikipedia.org/wiki/Dice%E7%B3%BB%E6%95%B0) + +## PaddleSeg指定训练loss + +PaddleSeg通过`cfg.SOLVER.LOSS`参数可以选择训练时的损失函数, +如`cfg.SOLVER.LOSS=['dice_loss','bce_loss']`将指定训练loss为`dice loss`与`bce loss`的组合 + +## 实验比较 + +在MiniDeepGlobeRoadExtraction数据集进行了实验比较。 + +* 数据集下载 +```shell +python dataset/download_mini_deepglobe_road_extraction.py +``` + +* 预训练模型下载 +```shell +python pretrained_model/download_model.py deeplabv3p_mobilenetv2-1-0_bn_coco +``` +* 配置/数据校验 +```shell +python pdseg/check.py --cfg ./configs/deepglobe_road_extraction.yaml +``` + +* 训练 +```shell +python pdseg/train.py --cfg ./configs/deepglobe_road_extraction.yaml --use_gpu SOLVER.LOSS ['dice_loss','bce_loss'] + +``` + +* 评估 +``` +python pdseg/eval.py --cfg ./configs/deepglobe_road_extraction.yaml --use_gpu SOLVER.LOSS ['dice_loss','bce_loss'] + +``` + +* 结果比较 + +softmax loss和dice loss + bce loss实验结果如下图所示。 +图中橙色曲线为dice loss + bce loss,最高mIoU为76.02%,蓝色曲线为softmax loss, 最高mIoU为73.62%。 +

+
+

+ + + diff --git a/pdseg/models/model_builder.py b/pdseg/models/model_builder.py index 894aedaf0aac33cb211be09071bf7a37d477c795..8d075a2117434600e19e0a8d661a59e71ee10de3 100644 --- a/pdseg/models/model_builder.py +++ b/pdseg/models/model_builder.py @@ -156,8 +156,12 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN): model_name = map_model_name(cfg.MODEL.MODEL_NAME) model_func = get_func("modeling." + model_name) - + loss_type = cfg.SOLVER.LOSS + + if class_num > 2 and (("dice_loss" in loss_type) or ("bce_loss" in loss_type)): + raise Exception("dice loss and bce loss is only applicable to binary classfication") + if ("dice_loss" in loss_type) or ("bce_loss" in loss_type): class_num = 1 if "softmax_loss" in loss_type: @@ -168,20 +172,29 @@ def build_model(main_prog, start_prog, phase=ModelPhase.TRAIN): if ModelPhase.is_train(phase) or ModelPhase.is_eval(phase): loss_valid = False avg_loss_list = [] + valid_loss = [] if "softmax_loss" in loss_type: avg_loss_list.append(multi_softmax_with_loss(logits, label, mask,class_num)) loss_valid = True + valid_loss.append("softmax_loss") if "dice_loss" in loss_type: avg_loss_list.append(multi_dice_loss(logits, label, mask)) loss_valid = True + valid_loss.append("dice_loss") if "bce_loss" in loss_type: avg_loss_list.append(multi_bce_loss(logits, label, mask)) loss_valid = True + valid_loss.append("bce_loss") if not loss_valid: raise Exception("SOLVER.LOSS: {} is set wrong. it should " "include one of (softmax_loss, bce_loss, dice_loss) at least" " example: ['softmax_loss'], ['dice_loss'], ['bce_loss', 'dice_loss']".format(cfg.SOLVER.LOSS)) + + invalid_loss = [x for x in loss_type if x not in valid_loss] + if len(invalid_loss) > 0: + print("Warning: the loss {} you set is invalid. it will not be included in loss computed.".format(invalid_loss)) + avg_loss = 0 for i in range(0, len(avg_loss_list)): avg_loss += avg_loss_list[i]