diff --git a/official/cv/fastscnn/README_CN.md b/official/cv/fastscnn/README_CN.md index 2dcf9dccc21c58a98aec7aeb39c308ee2d2734f0..105e39cdd36482881b9e836f22e86d06e3e90a43 100644 --- a/official/cv/fastscnn/README_CN.md +++ b/official/cv/fastscnn/README_CN.md @@ -80,7 +80,7 @@ python train.py \ bash ./scripts/run_train.sh [train_code_path] [dataset] [epochs] [batch_size] [lr] [output_path] #Ascend多卡训练。 -bash ./scripts/run_distribute_train.sh [train_code_path] [dataset] [epochs] [batch_size] [lr] [output_path] +bash ./scripts/run_distribute_train.sh [train_code_path] [dataset] [epochs] [batch_size] [lr] [output_path] [rank_table_file_path] # 通过 python 命令行运行推理脚本。 # resume_path 指 ckpt 所在目录,为了兼容 modelarts,将其拆分为了 “路径” 与 “文件名” @@ -116,16 +116,15 @@ Ascend训练:生成[RANK_TABLE_FILE](https://gitee.com/mindspore/models/tree/m │ ├──fusion_switch.cfg // 配置文件 ├──cal_mIoU.py // 310 推理时计算mIoU的脚本 ├──preprocess.py // 310 推理时预处理验证集的脚本 - ├──score.py // 310 推理时计算mIoU的脚本 ├── README_CN.md // fastscnn 的说明文件 ├── scripts │ ├──run_distribute_train.sh // Ascend 8卡训练脚本 │ ├──run_eval.sh // 推理启动脚本 │ ├──run_train.sh // 训练启动脚本 │ ├──run_infer_310.sh // 启动310推理的脚本 + │ ├──docker_start.sh // 使用 MindX 推理时的 docker 启动脚本 ├── src │ ├──dataloader.py // 数据集处理 - │ ├──distributed_sampler.py // 8卡并行时的数据集切分操作 │ ├──fast_scnn.py // 模型结构 │ ├──logger.py // 日志打印文件 │ ├──loss.py // 损失函数 @@ -237,7 +236,7 @@ cal_mIoU.py 中的主要参数如下: #上述命令均会使脚本在后台运行,日志将输出到 log.txt,可通过查看该文件了解训练详情 #Ascend多卡训练(2、4、8卡配置请自行修改run_distribute_train.sh,默认8卡) - bash ./scripts/run_distribute_train.sh [train_code_path] [dataset] [epochs] [batch_size] [lr] [output_path] + bash ./scripts/run_distribute_train.sh [train_code_path] [dataset] [epochs] [batch_size] [lr] [output_path] [rank_table_file_path] ``` 训练完成后,您可以在 --output_path 参数指定的目录下找到保存的权重文件,训练过程中的部分 loss 收敛情况如下(4卡并行): @@ -349,7 +348,7 @@ FastSCNN on “Cityscapes ” | Accuracy | 55.48% | | Total time | 8p:8h20m | | Checkpoint for Fine tuning | 8p: 14.51MB(.ckpt file) | -| Scripts | https://gitee.com/mindspore/models/tree/master/official/cv/fastscnn | +| Scripts | [FastSCNN脚本](https://gitee.com/mindspore/models/tree/master/official/cv/fastscnn) | ## 随机情况说明 diff --git a/official/cv/fastscnn/cal_mIoU.py b/official/cv/fastscnn/cal_mIoU.py index 6d300e98ee7fc5017784e8376043358781fefcbf..ffb08edfd495c0c9fa31bd513b19a96a99959fa4 100644 --- a/official/cv/fastscnn/cal_mIoU.py +++ b/official/cv/fastscnn/cal_mIoU.py @@ -17,18 +17,20 @@ import os import time import glob import numpy as np +import PIL.Image as Image from tabulate import tabulate -from score import SegmentationMetric ## Params parser = argparse.ArgumentParser() parser.add_argument('--label_path', type=str , help='directory of dataset label') -parser.add_argument('--output_path', default='', type=str, - help='path of the predict files that generated by the model') -parser.add_argument('--image_width', default=768, type=int, help='image_width') +parser.add_argument('--output_path', default=None, type=str + , help='path of the predict files that generated by the model') parser.add_argument('--image_height', default=768, type=int, help='image_height') +parser.add_argument('--image_width', default=768, type=int, help='image_width') parser.add_argument('--save_mask', default=0, type=int, help='0 for False, 1 for True') +parser.add_argument('--mask_result_path', default='./mask_result', type=str + , help='the folder to save the semantic mask images') args = parser.parse_args() @@ -56,37 +58,114 @@ cityspallete = [ classes = ('road', 'sidewalk', 'building', 'wall', 'fence', 'pole', 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', 'bicycle') -def cal_mIoU(label_path, output_path, image_width, image_height, save_mask): - file_list = glob.glob(label_path+'*') # label_path must end by '/' +class SegmentationMetric(): + """Computes pixAcc and mIoU metric scores + """ + def __init__(self, nclass): + super(SegmentationMetric, self).__init__() + self.nclass = nclass + self.reset() + + def update(self, preds, labels): + """Updates the internal evaluation result. + Parameters + ---------- + labels : 'NumpyArray' or list of `NumpyArray` + The labels of the data. + preds : 'NumpyArray' or list of `NumpyArray` + Predicted values. + """ + def evaluate_worker(self, pred, label): + correct, labeled = batch_pix_accuracy(pred, label) + inter, union = batch_intersection_union(pred, label, self.nclass) + self.total_correct += correct + self.total_label += labeled + self.total_inter += inter + self.total_union += union + evaluate_worker(self, preds, labels) + + def get(self, return_category_iou=False): + """Gets the current evaluation result. + Returns + ------- + metrics : tuple of float + pixAcc and mIoU + """ + # remove np.spacing(1) + pixAcc = 1.0 * self.total_correct / (2.220446049250313e-16 + self.total_label) + IoU = 1.0 * self.total_inter / (2.220446049250313e-16 + self.total_union) + mIoU = IoU.mean().item() + if return_category_iou: + return pixAcc, mIoU, IoU + return pixAcc, mIoU + + def reset(self): + """Resets the internal evaluation result to initial state.""" + self.total_inter = np.zeros(self.nclass) + self.total_union = np.zeros(self.nclass) + self.total_correct = 0 + self.total_label = 0 + +def batch_pix_accuracy(output, target): + """PixAcc""" + # inputs are numpy array, output 4D NCHW where 'C' means label classes, target 3D NHW + predict = np.argmax(output.astype(np.int64), 1) + 1 + target = target.astype(np.int64) + 1 + pixel_labeled = (target > 0).sum() + pixel_correct = ((predict == target) * (target > 0)).sum() + assert pixel_correct <= pixel_labeled, "Correct area should be smaller than Labeled" + return pixel_correct, pixel_labeled + +def batch_intersection_union(output, target, nclass): + """mIoU""" + # inputs are numpy array, output 4D, target 3D + mini = 1 + maxi = nclass + nbins = nclass + predict = np.argmax(output.astype(np.float32), 1) + 1 + target = target.astype(np.float32) + 1 + + predict = predict.astype(np.float32) * (target > 0).astype(np.float32) + intersection = predict * (predict == target).astype(np.float32) + # areas of intersection and union + # element 0 in intersection occur the main difference from np.bincount. set boundary to -1 is necessary. + area_inter, _ = np.histogram(intersection, bins=nbins, range=(mini, maxi)) + area_pred, _ = np.histogram(predict, bins=nbins, range=(mini, maxi)) + area_lab, _ = np.histogram(target, bins=nbins, range=(mini, maxi)) + area_union = area_pred + area_lab - area_inter + assert (area_inter > area_union).sum() == 0, "Intersection area should be smaller than Union area" + return area_inter.astype(np.float32), area_union.astype(np.float32) + +def cal_mIoU(): + file_list = glob.glob(os.path.join(args.label_path, '*')) start_time = time.time() metric = SegmentationMetric(19) metric.reset() - index = 0 - for file in file_list: + if args.save_mask and not os.path.exists(args.mask_result_path): + os.makedirs(args.mask_result_path) + for index, file in enumerate(sorted(file_list)): label = np.fromfile(file, dtype=np.int32) - label = label.reshape(image_height, image_width) + label = label.reshape(args.image_height, args.image_width) - filename = file.split(os.sep)[-1][:-10] # get the name of image file - predict_path = os.path.join(output_path, filename+"_img_0.bin") + filename = file.split(os.sep)[-1][:-10] # get the name of image file + predict_path = os.path.join(args.output_path, filename + "_img_0.bin") predict = np.fromfile(predict_path, dtype=np.float32) - predict = predict.reshape(1, 19, image_height, image_width) + predict = predict.reshape(1, 19, args.image_height, args.image_width) metric.update(predict, label) pixAcc, mIoU = metric.get() print("[EVAL] Sample: {:d}, pixAcc: {:.3f}, mIoU: {:.3f}".format(index + 1, pixAcc * 100, mIoU * 100)) - index += 1 - if save_mask == 1: + if args.save_mask: output = np.argmax(predict[0], axis=0) out_img = Image.fromarray(output.astype('uint8')) out_img.putpalette(cityspallete) - outname = str(index) + '.png' - out_img.save(os.path.join(output_path, outname)) - + outname = str(filename) + '.png' + out_img.save(os.path.join(args.mask_result_path, outname)) pixAcc, mIoU, category_iou = metric.get(return_category_iou=True) print('End validation pixAcc: {:.3f}, mIoU: {:.3f}'.format(pixAcc * 100, mIoU * 100)) - txtName = os.path.join(output_path, "eval_results.txt") + txtName = os.path.join(args.mask_result_path, "eval_results.txt") with open(txtName, "w") as f: string = 'validation pixAcc:' + str(pixAcc * 100) + ', mIoU:' + str(mIoU * 100) f.write(string) @@ -103,4 +182,4 @@ def cal_mIoU(label_path, output_path, image_width, image_height, save_mask): print("Time cost:"+str(time_used)+" seconds!") if __name__ == '__main__': - cal_mIoU(args.label_path, args.output_path, args.image_width, args.image_height, args.save_mask) + cal_mIoU() diff --git a/official/cv/fastscnn/export.py b/official/cv/fastscnn/export.py index 719430f7f85fac1d0816796b8cfbafb2677cfecc..16da751390025b475c05feac83b807c080b20eee 100644 --- a/official/cv/fastscnn/export.py +++ b/official/cv/fastscnn/export.py @@ -1,17 +1,17 @@ -# Copyright 2021 Huawei Technologies Co., Ltd +# Copyright 2021 Huawei Technologies Co., Ltd # -# 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 +# 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 +# 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. -# ============================================================================ +# 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. +# ============================================================================ """ ##############export checkpoint file into air, onnx, mindir models################# python export.py diff --git a/official/cv/fastscnn/infer/Dockerfile b/official/cv/fastscnn/infer/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..053bf80cd2309a41b6033b3e8d5ab4f87d41bd5e --- /dev/null +++ b/official/cv/fastscnn/infer/Dockerfile @@ -0,0 +1,5 @@ +ARG FROM_IMAGE_NAME +FROM ${FROM_IMAGE_NAME} + +COPY requirements.txt . +RUN pip3.7 install -r requirements.txt \ No newline at end of file diff --git a/official/cv/fastscnn/infer/README_CN.md b/official/cv/fastscnn/infer/README_CN.md new file mode 100644 index 0000000000000000000000000000000000000000..e294cba022fa25b1619835a6f73d1feebe2e87b8 --- /dev/null +++ b/official/cv/fastscnn/infer/README_CN.md @@ -0,0 +1,314 @@ +# FastSCNN MindX推理及mx Base推理 + +<!-- TOC --> + +- [脚本说明](#脚本说明) + - [脚本及样例代码](#脚本及样例代码) + - [模型转换](#模型转换) + - [MindX SDK 启动流程](#{mindx\quadsdk\quad启动流程}) + - [mx Base 推理流程](#{mx\quadbase\quad推理流程}) + +<!-- /TOC --> + +## 脚本说明 + +### 脚本及样例代码 + +```tex +├── fastscnn + ├── README_CN.md // fastscnn 的说明文件 + ├── infer + ├── README_CN.md // fastscnn 的 MindX SDK 推理及 mx Base 推理的说明文件 + ├── convert + │ ├──ATC_AIR_2_OM.sh // 将 air 模型转换为 om 模型的脚本 + ├── data + │ ├──config + │ │ ├──fastscnn.pipeline // MindX SDK运行所需的 pipline 配置文件 + ├── mxbase // mx Base 推理目录(C++) + │ ├──src + │ │ ├──FastSCNN.h // 头文件 + │ │ ├──FastSCNN.cpp // 详细实现 + │ │ ├──main.cpp // mx Base 主函数 + │ ├──build.sh // 代码编译脚本 + │ ├──CMakeLists.txt // 代码编译设置 + ├── sdk + │ ├──main.py // MindX SDK 运行脚本 + │ ├──run.sh // 启动 main.py 的 sh 文件 + ├── ...... // 其他代码文件 +``` + +### 模型转换 + +1、首先执行 fastscnn 目录下的 export.py 将准备好的权重文件转换为 air 模型文件。 + +```python +# 此处简要举例 +python export.py \ +--batch_size=1 \ +--image_height=768 \ +--image_width=768 \ +--ckpt_file=xxx/fastscnn.ckpt \ +--file_name=fastscnn \ +--file_format=AIR \ +--device_target=Ascend \ +--device_id=0 \ + +#转换完成后请将生成的fastscnn.air文件移动至infer目录下。 +``` + +2、然后执行 convert 目录下的 ATC_AIR_2_OM.sh 将刚刚转换好的 air 模型文件转换为 om 模型文件以备后续使用。 + +```bash +# bash ./ATC_AIR_2_OM.sh -h 或者 bash ./ATC_AIR_2_OM.sh --help 可以查看帮助信息 +bash ATC_AIR_2_OM.sh [--model --output] + +e.g. +bash ATC_AIR_2_OM.sh --model=../fastscnn.air --output=../data/model/fastscnn +#转换后的模型结果为fastscnn.om,将放在{fastscnn}/infer/data/model/目录下 +``` + +### 数据集准备 + +此处以原始数据集 Cityscapes 为例。 + +1、在 infer 目录下或其他文件夹下创建数据集存放文件夹(记为“xxx/dataset/”),并将 Cityscapes 中的 gtFine、leftImg8bit 文件夹上传至此,gtFine 和 leftImg8bit 中的 train、test文件夹可不上传。“xxx/dataset/” 下的数据集组织结构如下: + +```tex +├── xxx/dataset/ // 数据集根目录 + │ ├──gtFine // 标注内容 + │ │ ├──val // 验证集 + │ │ │ ├── .... + │ │ │ ├── .... + │ ├──leftImg8bit // 原始图片 + │ │ ├──val // 验证集 + │ │ │ ├── .... + │ │ │ ├── .... +``` + +2、MindX SDK 直接使用原始数据集,上述数据集准备好后即可执行 “MindX SDK 启动流程” 步骤。 + +3、mx Base 推理并不直接处理图片和计算语义分割后的 mIoU 值。因此我们需要首先执行 fastscnn 目录下的 preprocess.py 对测试图片进行归一化、对 label 进行 class 映射等操作并以 "bin" 文件形式进行存储。 + +```python +# 此处简要举例,“xx/preprocess_data” 为处理后的数据存储路径,不存在时会自动创建 +python preprocess.py \ +--image_path=xx/dataset \ +--out_dir=xx/preprocess_data \ +--image_height=768 \ +--image_width=768 +``` + +数据预处理完毕后即可执行 “mx Base 推理流程” 步骤。 + +### MindX SDK 启动流程 + +```shell +# 通过 bash 脚本启动 MindX SDK 推理 +# bash ./run.sh -h 或者 bash ./run.sh --help 可以查看帮助信息 +bash ./run.sh [--pipeline --image_path --image_width --image_height --save_mask --mask_result_path] +# 注意: data/config/fastscnn.pipeline 中默认 MindX SDK 推理所需的模型文件为 "fastscnn.om",且放在 data/model/ 目录下,具体可以修改该文件中的 "modelPath" 属性进行配置。 + +# 通过 python 命令启动 MindX SDK 推理 +python main.py \ +--pipeline=../data/config/fastscnn.pipeline \ +--image_path=xxx/dataset/ \ +--image_width=768 \ +--image_height=768 \ +--save_mask=1 \ +--mask_result_path=./mask_result +# 注意: data/config/fastscnn.pipeline 中默认 MindX SDK 推理所需的模型文件为 "fastscnn.om",且放在 data/model/ 目录下,具体可以修改该文件中的 "modelPath" 属性进行配置。 +``` + +推理结果示例: + +```tex +Begin to initialize Log. +The output directory of logs file exist. +Save logs information to specified directory. +Found 500 images in the folder /home/data/FastSCNN/dataset/leftImg8bit/val +Processing ---> frankfurt_000001_029086_leftImg8bit +[EVAL] Sample: 1, pixAcc: 93.023, mIoU: 34.965 +Processing ---> frankfurt_000001_064651_leftImg8bit +[EVAL] Sample: 2, pixAcc: 94.525, mIoU: 39.346 +Processing ---> frankfurt_000001_023235_leftImg8bit +[EVAL] Sample: 3, pixAcc: 93.609, mIoU: 43.201 +........ +Processing ---> lindau_000030_000019_leftImg8bit +[EVAL] Sample: 497, pixAcc: 93.535, mIoU: 55.196 +Processing ---> lindau_000001_000019_leftImg8bit +[EVAL] Sample: 498, pixAcc: 93.543, mIoU: 55.501 +Processing ---> lindau_000025_000019_leftImg8bit +[EVAL] Sample: 499, pixAcc: 93.539, mIoU: 55.485 +Processing ---> lindau_000040_000019_leftImg8bit +[EVAL] Sample: 500, pixAcc: 93.546, mIoU: 55.487 +End validation pixAcc: 93.546, mIoU: 55.487 +Category iou: + +------------+---------------+----------+ +| class id | class name | iou | ++============+===============+==========+ +| 0 | road | 0.976416 | ++------------+---------------+----------+ +| 1 | sidewalk | 0.662959 | ++------------+---------------+----------+ +| 2 | building | 0.866088 | ++------------+---------------+----------+ +| 3 | wall | 0.320282 | ++------------+---------------+----------+ +| 4 | fence | 0.248646 | ++------------+---------------+----------+ +| 5 | pole | 0.31713 | ++------------+---------------+----------+ +| 6 | traffic light | 0.360871 | ++------------+---------------+----------+ +| 7 | traffic sign | 0.485951 | ++------------+---------------+----------+ +| 8 | vegetation | 0.896945 | ++------------+---------------+----------+ +| 9 | terrain | 0.503119 | ++------------+---------------+----------+ +| 10 | sky | 0.928662 | ++------------+---------------+----------+ +| 11 | person | 0.632751 | ++------------+---------------+----------+ +| 12 | rider | 0.370751 | ++------------+---------------+----------+ +| 13 | car | 0.851971 | ++------------+---------------+----------+ +| 14 | truck | 0.496429 | ++------------+---------------+----------+ +| 15 | bus | 0.565111 | ++------------+---------------+----------+ +| 16 | train | 0.289486 | ++------------+---------------+----------+ +| 17 | motorcycle | 0.259988 | ++------------+---------------+----------+ +| 18 | bicycle | 0.508992 | ++------------+---------------+----------+ +Testing finished.... +======================================= +The total time of inference is 412.33897733688354 s +======================================= +``` + +### mx Base 推理流程 + +1、编译 mx Base + +```shell +bash ./build.sh +# 编译后的可执行文件 "fastscnn" 将保存在当前目录下 +``` + +2、执行 mx Base 推理 + +```tex +./fastscnn [model_path input_data_path output_data_path] +# 按顺序传入模型路径、图像路径(“数据集准备” 中的图片保存路径 “xx/preprocess_data” 下的 images 目录,以"/"结尾)、输出路径(需要提前创建) +例如: + ./fastscnn ../data/model/fastscnn.om xx/preprocess_data/images ./result/ +``` + +mx Base 推理结果示例: + +```tex +I1108 03:06:01.198787 86423 main.cpp:53] ======================================= !!!Parameters setting!!! ======================================== +I1108 03:06:01.198843 86423 main.cpp:55] ========== loading model weights from: ../data/model/fastscnn.om +I1108 03:06:01.198853 86423 main.cpp:58] ========== input data path = ../preprocess_data/images/ +I1108 03:06:01.198858 86423 main.cpp:61] ========== output data path = ./result/ WARNING: please make sure that this folder is created in advance!!! +I1108 03:06:01.198861 86423 main.cpp:63] ======================================== !!!Parameters setting!!! ======================================== +I1108 03:06:01.552381 86423 ModelInferenceProcessor.cpp:22] Begin to ModelInferenceProcessor init +I1108 03:06:01.661561 86423 ModelInferenceProcessor.cpp:69] End to ModelInferenceProcessor init +I1108 03:06:01.661762 86423 main.cpp:82] Processing: 1/500 ---> frankfurt_000001_023769_leftImg8bit_img.bin +I1108 03:06:02.280328 86423 main.cpp:82] Processing: 2/500 ---> frankfurt_000001_067295_leftImg8bit_img.bin +I1108 03:06:02.903029 86423 main.cpp:82] Processing: 3/500 ---> frankfurt_000000_011074_leftImg8bit_img.bin +I1108 03:06:03.528358 86423 main.cpp:82] Processing: 4/500 ---> frankfurt_000000_002196_leftImg8bit_img.bin +I1108 03:06:04.150723 86423 main.cpp:82] Processing: 5/500 ---> frankfurt_000001_073243_leftImg8bit_img.bin +I1108 03:06:04.769243 86423 main.cpp:82] Processing: 6/500 ---> frankfurt_000001_082087_leftImg8bit_img.bin +I1108 03:06:05.391845 86423 main.cpp:82] Processing: 7/500 ---> frankfurt_000001_055172_leftImg8bit_img.bin +........ +I1108 03:07:17.471393 86423 main.cpp:91] infer succeed and write the result data with binary file ! +I1108 03:07:17.758675 86423 DeviceManager.cpp:83] DestroyDevices begin +I1108 03:07:17.758706 86423 DeviceManager.cpp:85] destroy device:0 +I1108 03:07:17.950556 86423 DeviceManager.cpp:91] aclrtDestroyContext successfully! +I1108 03:07:18.839597 86423 DeviceManager.cpp:99] DestroyDevices successfully +I1108 03:07:18.839629 86423 main.cpp:98] Infer images sum 500, cost total time: 256694.6 ms. +I1108 03:07:18.839658 86423 main.cpp:99] The throughput: 1.94784 bin/sec. +I1108 03:07:18.839663 86423 main.cpp:100] ========== The infer result has been saved in ---> ./result/ + +``` + +mx Base 的推理结果为 "图片的语义分割表示",将以 "bin" 文件的形式存储在指定路径下。 + +3、计算语义分割的 mIoU 值和将语义分割结果进行可视化展示 + +如果需要计算语义分割的 mIoU 值和将语义分割结果进行可视化展示,请执行 fastscnn 目录下的 cal_mIoU.py 脚本 + +```python +# 此处简要举例 +python cal_mIoU.py \ +--label_path=xx/preprocess_data/labels \ +--output_path=xxx/infer/mxbase/result \ +--image_height=768 \ +--image_width=768 \ +--save_mask=1 +--mask_result_path=./mask_result +label_path 指第1步处理后的label保存路径,output_path 指 mx Base 推理后的结果保存路径 +``` + +mIoU 计算结果示例: + +```tex +[EVAL] Sample: 1, pixAcc: 96.204, mIoU: 32.133 +[EVAL] Sample: 2, pixAcc: 96.826, mIoU: 37.931 +[EVAL] Sample: 3, pixAcc: 96.307, mIoU: 36.358 +[EVAL] Sample: 4, pixAcc: 95.621, mIoU: 39.828 +....... +[EVAL] Sample: 497, pixAcc: 93.530, mIoU: 55.492 +[EVAL] Sample: 498, pixAcc: 93.536, mIoU: 55.489 +[EVAL] Sample: 499, pixAcc: 93.543, mIoU: 55.495 +[EVAL] Sample: 500, pixAcc: 93.546, mIoU: 55.487 +End validation pixAcc: 93.546, mIoU: 55.487 +Category iou: + +------------+---------------+----------+ +| class id | class name | iou | ++============+===============+==========+ +| 0 | road | 0.976416 | ++------------+---------------+----------+ +| 1 | sidewalk | 0.662959 | ++------------+---------------+----------+ +| 2 | building | 0.866088 | ++------------+---------------+----------+ +| 3 | wall | 0.320282 | ++------------+---------------+----------+ +| 4 | fence | 0.248646 | ++------------+---------------+----------+ +| 5 | pole | 0.31713 | ++------------+---------------+----------+ +| 6 | traffic light | 0.360871 | ++------------+---------------+----------+ +| 7 | traffic sign | 0.485951 | ++------------+---------------+----------+ +| 8 | vegetation | 0.896945 | ++------------+---------------+----------+ +| 9 | terrain | 0.503119 | ++------------+---------------+----------+ +| 10 | sky | 0.928662 | ++------------+---------------+----------+ +| 11 | person | 0.632751 | ++------------+---------------+----------+ +| 12 | rider | 0.370751 | ++------------+---------------+----------+ +| 13 | car | 0.851971 | ++------------+---------------+----------+ +| 14 | truck | 0.496429 | ++------------+---------------+----------+ +| 15 | bus | 0.565111 | ++------------+---------------+----------+ +| 16 | train | 0.289486 | ++------------+---------------+----------+ +| 17 | motorcycle | 0.259988 | ++------------+---------------+----------+ +| 18 | bicycle | 0.508992 | ++------------+---------------+----------+ +Time cost:188.06731748580933 seconds! +``` diff --git a/official/cv/fastscnn/infer/convert/ATC_AIR_2_OM.sh b/official/cv/fastscnn/infer/convert/ATC_AIR_2_OM.sh new file mode 100644 index 0000000000000000000000000000000000000000..049f88542e3a73a25a18a021b66726368b88b0dd --- /dev/null +++ b/official/cv/fastscnn/infer/convert/ATC_AIR_2_OM.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Copyright 2021 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ + +soc_version=Ascend310 +input_shape="x:1,3,768,768" +# help message +if [[ $1 == --help || $1 == -h ]];then + echo "usage:bash ./ATC_AIR_2_OM.sh <args>" + echo "parameter explain: + --model set model place, e.g. --model=../fastscnn.air + --output set the name and place of OM model, e.g. --output=../data/model/fastscnn + --soc_version set the soc_version, default: --soc_version=Ascend310 + --input_shape set the input node and shape, default: --input_shape='x:1,3,768,768' + -h/--help show help message + " + exit 1 +fi + +for para in "$@" +do + if [[ $para == --model* ]];then + model=`echo ${para#*=}` + elif [[ $para == --output* ]];then + output=`echo ${para#*=}` + elif [[ $para == --soc_version* ]];then + soc_version=`echo ${para#*=}` + elif [[ $para == --input_shape* ]];then + input_shape=`echo ${para#*=}` + fi +done + +if [[ $model == "" ]];then + echo "[Error] para \"model \" must be config" + exit 1 +fi + +if [[ $output == "" ]];then + echo "[Error] para \"output \" must be config" + exit 1 +fi + +atc \ + --model=${model} \ + --output=${output} \ + --soc_version=${soc_version} \ + --input_shape=${input_shape} \ + --framework=1 \ + --input_format=NCHW \ No newline at end of file diff --git a/official/cv/fastscnn/infer/data/config/fastscnn.pipeline b/official/cv/fastscnn/infer/data/config/fastscnn.pipeline new file mode 100644 index 0000000000000000000000000000000000000000..0be93150dfb56f5a81cd9ddc5d918a90a73b290c --- /dev/null +++ b/official/cv/fastscnn/infer/data/config/fastscnn.pipeline @@ -0,0 +1,26 @@ +{ +"fastscnn": { + "appsrc0": { + "factory": "appsrc", + "next": "modelInfer" + }, + "modelInfer": { + "props": { + "modelPath": "../data/model/fastscnn.om", + "dataSource": "appsrc0" + }, + "factory": "mxpi_tensorinfer", + "next": "dataserialize" + }, + "dataserialize": { + "props": { + "outputDataKeys": "modelInfer" + }, + "factory": "mxpi_dataserialize", + "next": "appsink0" + }, + "appsink0": { + "factory": "appsink" + } + } +} diff --git a/official/cv/fastscnn/infer/docker_start_infer.sh b/official/cv/fastscnn/infer/docker_start_infer.sh new file mode 100644 index 0000000000000000000000000000000000000000..64cf90a2311bdfb21d68a4e90e08602670fdf632 --- /dev/null +++ b/official/cv/fastscnn/infer/docker_start_infer.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright 2021 Huawei Technologies Co., Ltd +# +# 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. + +docker_image=$1 +data_dir=$2 + +function show_help() { + echo "Usage: docker_start.sh docker_image data_dir" +} + +function param_check() { + if [ -z "${docker_image}" ]; then + echo "please input docker_image" + show_help + exit 1 + fi + + if [ -z "${data_dir}" ]; then + echo "please input data_dir" + show_help + exit 1 + fi +} + +param_check + +docker run -it \ + --device=/dev/davinci0 \ + --device=/dev/davinci_manager \ + --device=/dev/devmm_svm \ + --device=/dev/hisi_hdc \ + -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ + -v ${data_dir}:${data_dir} \ + ${docker_image} \ + /bin/bash diff --git a/official/cv/fastscnn/infer/mxbase/CMakeLists.txt b/official/cv/fastscnn/infer/mxbase/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..37ef3af8de31b77bee12149bce40b3ccd2eea3e3 --- /dev/null +++ b/official/cv/fastscnn/infer/mxbase/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.14.0) +project(fastscnn) +set(TARGET fastscnn) +add_definitions(-DENABLE_DVPP_INTERFACE) +add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) +add_definitions(-Dgoogle=mindxsdk_private) +add_compile_options(-std=c++11 -fPIE -fstack-protector-all -fPIC -Wall) +add_link_options(-Wl,-z,relro,-z,now,-z,noexecstack -s -pie) +# Check environment variable +if(NOT DEFINED ENV{ASCEND_HOME}) + message(FATAL_ERROR "please define environment variable:ASCEND_HOME") +endif() +if(NOT DEFINED ENV{ASCEND_VERSION}) + message(WARNING "please define environment variable:ASCEND_VERSION") +endif() +if(NOT DEFINED ENV{ARCH_PATTERN}) + message(WARNING "please define environment variable:ARCH_PATTERN") +endif() +set(ACL_INC_DIR $ENV{ASCEND_HOME}/$ENV{ASCEND_VERSION}/$ENV{ARCH_PATTERN}/acllib/include) +set(ACL_LIB_DIR $ENV{ASCEND_HOME}/$ENV{ASCEND_VERSION}/$ENV{ARCH_PATTERN}/acllib/lib64) +set(MXBASE_ROOT_DIR $ENV{MX_SDK_HOME}) +set(MXBASE_INC ${MXBASE_ROOT_DIR}/include) +set(MXBASE_LIB_DIR ${MXBASE_ROOT_DIR}/lib) +set(MXBASE_POST_LIB_DIR ${MXBASE_ROOT_DIR}/lib/modelpostprocessors) +set(MXBASE_POST_PROCESS_DIR ${MXBASE_ROOT_DIR}/include/MxBase/postprocess/include) + +if(NOT DEFINED ENV{MXSDK_OPENSOURCE_DIR}) + message(WARNING "please define environment variable:MXSDK_OPENSOURCE_DIR") +endif() + +set(OPENSOURCE_DIR $ENV{MXSDK_OPENSOURCE_DIR}) + +include_directories(src) +include_directories(${ACL_INC_DIR}) +include_directories(${OPENSOURCE_DIR}/include) +include_directories(${OPENSOURCE_DIR}/include/opencv4) + + +include_directories(${MXBASE_INC}) +include_directories(${MXBASE_POST_PROCESS_DIR}) + +link_directories(${ACL_LIB_DIR}) +link_directories(${OPENSOURCE_DIR}/lib) +link_directories(${MXBASE_LIB_DIR}) +link_directories(${MXBASE_POST_LIB_DIR}) + +add_executable(${TARGET} src/main.cpp src/FastSCNN.cpp) +target_link_libraries(${TARGET} glog cpprest mxbase opencv_world stdc++fs) +install(TARGETS ${TARGET} RUNTIME DESTINATION ${PROJECT_SOURCE_DIR}/) diff --git a/official/cv/fastscnn/infer/mxbase/build.sh b/official/cv/fastscnn/infer/mxbase/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..2a239241a5d82ada71d8e60852033b7c84d40566 --- /dev/null +++ b/official/cv/fastscnn/infer/mxbase/build.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +# Copyright 2021 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ +export ASCEND_HOME=/usr/local/Ascend +export ASCEND_VERSION=nnrt/latest +export ARCH_PATTERN=. +export MXSDK_OPENSOURCE_DIR=/usr/local/sdk_home/mxManufacture/opensource +export LD_LIBRARY_PATH="${MX_SDK_HOME}/lib/plugins:${MX_SDK_HOME}/opensource/lib64:${MX_SDK_HOME}/lib:${MX_SDK_HOME}/lib/modelpostprocessors:${MX_SDK_HOME}/opensource/lib:/usr/local/Ascend/nnae/latest/fwkacllib/lib64:${LD_LIBRARY_PATH}" +export ASCEND_OPP_PATH="/usr/local/Ascend/nnae/latest/opp" +export ASCEND_AICPU_PATH="/usr/local/Ascend/nnae/latest" + +function check_env() +{ + # set ASCEND_VERSION to ascend-toolkit/latest when it was not specified by user + if [ ! "${ASCEND_VERSION}" ]; then + export ASCEND_VERSION=ascend-toolkit/latest + echo "Set ASCEND_VERSION to the default value: ${ASCEND_VERSION}" + else + echo "ASCEND_VERSION is set to ${ASCEND_VERSION} by user" + fi + + if [ ! "${ARCH_PATTERN}" ]; then + # set ARCH_PATTERN to ./ when it was not specified by user + export ARCH_PATTERN=./ + echo "ARCH_PATTERN is set to the default value: ${ARCH_PATTERN}" + else + echo "ARCH_PATTERN is set to ${ARCH_PATTERN} by user" + fi +} + +function build_fastscnn() +{ + cd . + rm -rf build + mkdir -p build + cd build + cmake .. + make + ret=$? + if [ ${ret} -ne 0 ]; then + echo "Failed to build brdnet." + exit ${ret} + fi + make install +} + +rm -rf ./result +mkdir -p ./result + +check_env +build_fastscnn diff --git a/official/cv/fastscnn/infer/mxbase/src/FastSCNN.cpp b/official/cv/fastscnn/infer/mxbase/src/FastSCNN.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ecdab3995b8a532fc09f0a7a2407a1a72d353176 --- /dev/null +++ b/official/cv/fastscnn/infer/mxbase/src/FastSCNN.cpp @@ -0,0 +1,177 @@ +/** + * Copyright 2021 Huawei Technologies Co., Ltd + * + * 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. + */ +#include "FastSCNN.h" +#include <unistd.h> +#include <sys/stat.h> +#include <map> +#include <fstream> +#include "MxBase/DeviceManager/DeviceManager.h" +#include "MxBase/Log/Log.h" + +APP_ERROR FastSCNN::Init(const InitParam &initParam) { + this->deviceId_ = initParam.deviceId; + this->outputDataPath_ = initParam.outputDataPath; + APP_ERROR ret = MxBase::DeviceManager::GetInstance()->InitDevices(); + if (ret != APP_ERR_OK) { + LogError << "Init devices failed, ret=" << ret << "."; + return ret; + } + + ret = MxBase::TensorContext::GetInstance()->SetContext(initParam.deviceId); + if (ret != APP_ERR_OK) { + LogError << "Set context failed, ret=" << ret << "."; + return ret; + } + + this->model_ = std::make_shared<MxBase::ModelInferenceProcessor>(); + ret = this->model_->Init(initParam.modelPath, this->modelDesc_); + if (ret != APP_ERR_OK) { + LogError << "ModelInferenceProcessor init failed, ret=" << ret << "."; + return ret; + } + uint32_t input_data_size = 1; + for (size_t j = 0; j < this->modelDesc_.inputTensors[0].tensorDims.size(); ++j) { + this->inputDataShape_[j] = (uint32_t)this->modelDesc_.inputTensors[0].tensorDims[j]; + input_data_size *= this->inputDataShape_[j]; + } + this->inputDataSize_ = input_data_size; + + return APP_ERR_OK; +} + +APP_ERROR FastSCNN::DeInit() { + this->model_->DeInit(); + MxBase::DeviceManager::GetInstance()->DestroyDevices(); + return APP_ERR_OK; +} + +APP_ERROR FastSCNN::ReadTensorFromFile(const std::string &file, float *data) { + if (data == NULL) { + LogError << "input data is invalid."; + return APP_ERR_COMM_INVALID_POINTER; + } + + std::ifstream infile; + // open data file + infile.open(file, std::ios_base::in | std::ios_base::binary); + // check data file validity + if (infile.fail()) { + LogError << "Failed to open data file: " << file << "."; + return APP_ERR_COMM_OPEN_FAIL; + } + infile.read(reinterpret_cast<char*>(data), sizeof(float) * this->inputDataSize_); + infile.close(); + return APP_ERR_OK; +} + +APP_ERROR FastSCNN::ReadInputTensor(const std::string &fileName, std::vector<MxBase::TensorBase> *inputs) { + float data[this->inputDataSize_] = {0}; + APP_ERROR ret = ReadTensorFromFile(fileName, data); + if (ret != APP_ERR_OK) { + LogError << "ReadTensorFromFile failed."; + return ret; + } + const uint32_t dataSize = this->modelDesc_.inputTensors[0].tensorSize; + MxBase::MemoryData memoryDataDst(dataSize, MxBase::MemoryData::MEMORY_DEVICE, this->deviceId_); + MxBase::MemoryData memoryDataSrc(reinterpret_cast<void*>(data), dataSize, MxBase::MemoryData::MEMORY_HOST_MALLOC); + + ret = MxBase::MemoryHelper::MxbsMallocAndCopy(memoryDataDst, memoryDataSrc); + if (ret != APP_ERR_OK) { + LogError << GetError(ret) << "Memory malloc and copy failed."; + return ret; + } + + inputs->push_back(MxBase::TensorBase(memoryDataDst, false, this->inputDataShape_, MxBase::TENSOR_DTYPE_FLOAT32)); + return APP_ERR_OK; +} + + +APP_ERROR FastSCNN::Inference(const std::vector<MxBase::TensorBase> &inputs, + std::vector<MxBase::TensorBase> *outputs) { + auto dtypes = this->model_->GetOutputDataType(); + for (size_t i = 0; i < this->modelDesc_.outputTensors.size(); ++i) { + std::vector<uint32_t> shape = {}; + for (size_t j = 0; j < modelDesc_.outputTensors[i].tensorDims.size(); ++j) { + shape.push_back((uint32_t)this->modelDesc_.outputTensors[i].tensorDims[j]); + } + MxBase::TensorBase tensor(shape, dtypes[i], MxBase::MemoryData::MemoryType::MEMORY_DEVICE, this->deviceId_); + APP_ERROR ret = MxBase::TensorBase::TensorBaseMalloc(tensor); + if (ret != APP_ERR_OK) { + LogError << "TensorBaseMalloc failed, ret=" << ret << "."; + return ret; + } + outputs->push_back(tensor); + } + + MxBase::DynamicInfo dynamicInfo = {}; + dynamicInfo.dynamicType = MxBase::DynamicType::STATIC_BATCH; + auto startTime = std::chrono::high_resolution_clock::now(); + APP_ERROR ret = this->model_->ModelInference(inputs, *outputs, dynamicInfo); + auto endTime = std::chrono::high_resolution_clock::now(); + double costMs = std::chrono::duration<double, std::milli>(endTime - startTime).count(); + g_inferCost.push_back(costMs); + + if (ret != APP_ERR_OK) { + LogError << "ModelInference failed, ret=" << ret << "."; + return ret; + } + return APP_ERR_OK; +} + + +APP_ERROR FastSCNN::WriteResult(const std::string &imageFile, std::vector<MxBase::TensorBase> outputs) { + for (size_t i = 0; i < 1; ++i) { // when model's aux==True, we don't use the other two outputs. + APP_ERROR ret = outputs[i].ToHost(); + if (ret != APP_ERR_OK) { + LogError << GetError(ret) << "tohost fail."; + return ret; + } + void *netOutput = outputs[i].GetBuffer(); + std::vector<uint32_t> out_shape = outputs[i].GetShape(); + int pos = imageFile.rfind('/'); + std::string fileName(imageFile, pos + 1); + fileName.replace(fileName.find('.'), fileName.size() - fileName.find('.'), "_0.bin"); + std::string outFileName = this->outputDataPath_ + "/" + fileName; + FILE *outputFile_ = fopen(outFileName.c_str(), "wb"); + fwrite(netOutput, out_shape[0]*out_shape[1]*out_shape[2]*out_shape[3], sizeof(float), outputFile_); + fclose(outputFile_); + } + return APP_ERR_OK; +} + + +APP_ERROR FastSCNN::Process(const std::string &inferPath, const std::string &fileName) { + std::vector<MxBase::TensorBase> inputs = {}; + std::string inputIdsFile = inferPath + fileName; + APP_ERROR ret = ReadInputTensor(inputIdsFile, &inputs); + if (ret != APP_ERR_OK) { + LogError << "Read input ids failed, ret=" << ret << "."; + return ret; + } + std::vector<MxBase::TensorBase> outputs = {}; + ret = Inference(inputs, &outputs); + if (ret != APP_ERR_OK) { + LogError << "Inference failed, ret=" << ret << "."; + return ret; + } + + ret = WriteResult(fileName, outputs); + if (ret != APP_ERR_OK) { + LogError << "Write result failed, ret=" << ret << "."; + return ret; + } + return APP_ERR_OK; +} diff --git a/official/cv/fastscnn/infer/mxbase/src/FastSCNN.h b/official/cv/fastscnn/infer/mxbase/src/FastSCNN.h new file mode 100644 index 0000000000000000000000000000000000000000..38b9d380550a8fb99c6a1871fb4567f9e554209b --- /dev/null +++ b/official/cv/fastscnn/infer/mxbase/src/FastSCNN.h @@ -0,0 +1,57 @@ +/* + * Copyright 2021 Huawei Technologies Co., Ltd + * + * 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. + */ + +#ifndef MXBASE_FastSCNN_H +#define MXBASE_FastSCNN_H + +#include <memory> +#include <utility> +#include <vector> +#include <string> +#include <map> +#include "MxBase/ModelInfer/ModelInferenceProcessor.h" +#include "MxBase/Tensor/TensorContext/TensorContext.h" + +extern std::vector<double> g_inferCost; + +struct InitParam { + uint32_t deviceId; + std::string modelPath; + std::string outputDataPath; +}; + +class FastSCNN { + public: + APP_ERROR Init(const InitParam &initParam); + APP_ERROR DeInit(); + APP_ERROR Inference(const std::vector<MxBase::TensorBase> &inputs, std::vector<MxBase::TensorBase> *outputs); + APP_ERROR Process(const std::string &inferPath, const std::string &fileName); + + protected: + APP_ERROR ReadTensorFromFile(const std::string &file, float *data); + APP_ERROR ReadInputTensor(const std::string &fileName, std::vector<MxBase::TensorBase> *inputs); + APP_ERROR WriteResult(const std::string &imageFile, std::vector<MxBase::TensorBase> outputs); + + private: + std::shared_ptr<MxBase::ModelInferenceProcessor> model_; + MxBase::ModelDesc modelDesc_ = {}; + uint32_t deviceId_ = 0; + std::string outputDataPath_ = "./result"; + std::vector<uint32_t> inputDataShape_ = {1, 3, 768, 768}; + uint32_t inputDataSize_ = 1769472; +}; + +#endif diff --git a/official/cv/fastscnn/infer/mxbase/src/main.cpp b/official/cv/fastscnn/infer/mxbase/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..125d7f92491963faf573f294b35abca4b128f111 --- /dev/null +++ b/official/cv/fastscnn/infer/mxbase/src/main.cpp @@ -0,0 +1,105 @@ +/** + * Copyright 2021 Huawei Technologies Co., Ltd + * + * 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. + */ + +#include <unistd.h> +#include <dirent.h> +#include <iostream> +#include <fstream> +#include <vector> +#include "FastSCNN.h" +#include "MxBase/Log/Log.h" + +std::vector<double> g_inferCost; + +void InitProtonetParam(InitParam* initParam, const std::string &model_path, const std::string &output_data_path) { + initParam->deviceId = 0; + initParam->modelPath = model_path; + initParam->outputDataPath = output_data_path; +} + +APP_ERROR ReadFilesFromPath(const std::string &path, std::vector<std::string> *files) { + DIR *dir = NULL; + struct dirent *ptr = NULL; + + if ((dir=opendir(path.c_str())) == NULL) { + LogError << "Open dir error: " << path; + return APP_ERR_COMM_OPEN_FAIL; + } + + while ((ptr=readdir(dir)) != NULL) { + if (ptr->d_type == 8) { + files->push_back(ptr->d_name); + } + } + closedir(dir); + return APP_ERR_OK; +} + + +int main(int argc, char* argv[]) { + LogInfo << "======================================= !!!Parameters setting!!! " << \ + "========================================"; + std::string model_path = argv[1]; + LogInfo << "========== loading model weights from: " << model_path; + + std::string input_data_path = argv[2]; + LogInfo << "========== input data path = " << input_data_path; + + std::string output_data_path = argv[3]; + LogInfo << "========== output data path = " << output_data_path << \ + " WARNING: please make sure that this folder is created in advance!!!"; + + LogInfo << "======================================== !!!Parameters setting!!! " << \ + "========================================"; + + InitParam initParam; + InitProtonetParam(&initParam, model_path, output_data_path); + auto fastscnn = std::make_shared<FastSCNN>(); + APP_ERROR ret = fastscnn->Init(initParam); + if (ret != APP_ERR_OK) { + LogError << "FastSCNN init failed, ret=" << ret << "."; + return ret; + } + std::vector<std::string> files; + ret = ReadFilesFromPath(input_data_path, &files); + if (ret != APP_ERR_OK) { + LogError << "Read files from path failed, ret=" << ret << "."; + return ret; + } + + // do infer + for (uint32_t i = 0; i < files.size(); i++) { + LogInfo << "Processing: " + std::to_string(i+1) + "/" + std::to_string(files.size()) + " ---> " + files[i]; + ret = fastscnn->Process(input_data_path, files[i]); + if (ret != APP_ERR_OK) { + LogError << "FastSCNN process failed, ret=" << ret << "."; + fastscnn->DeInit(); + return ret; + } + } + + LogInfo << "infer succeed and write the result data with binary file !"; + + fastscnn->DeInit(); + double costSum = 0; + for (uint32_t i = 0; i < g_inferCost.size(); i++) { + costSum += g_inferCost[i]; + } + LogInfo << "Infer images sum " << g_inferCost.size() << ", cost total time: " << costSum << " ms."; + LogInfo << "The throughput: " << g_inferCost.size() * 1000 / costSum << " bin/sec."; + LogInfo << "========== The infer result has been saved in ---> " << output_data_path; + return APP_ERR_OK; +} diff --git a/official/cv/fastscnn/infer/requirements.txt b/official/cv/fastscnn/infer/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..87f6e8b602e7bc48f954aeb512eb2e37ad18ef52 --- /dev/null +++ b/official/cv/fastscnn/infer/requirements.txt @@ -0,0 +1,3 @@ +pillow +numpy +tabulate \ No newline at end of file diff --git a/official/cv/fastscnn/infer/sdk/main.py b/official/cv/fastscnn/infer/sdk/main.py new file mode 100644 index 0000000000000000000000000000000000000000..055dbd21f9914f317100d134cb4bb20c0c841ad9 --- /dev/null +++ b/official/cv/fastscnn/infer/sdk/main.py @@ -0,0 +1,374 @@ +''' +The scripts to execute sdk infer +''' +# Copyright 2021 Huawei Technologies Co., Ltd +# +# 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 argparse +import os +import time +import numpy as np +import PIL.Image as Image +from tabulate import tabulate + +import MxpiDataType_pb2 as MxpiDataType +from StreamManagerApi import StreamManagerApi, InProtobufVector, \ + MxProtobufIn, StringVector + +def parse_args(): + """set and check parameters.""" + parser = argparse.ArgumentParser(description="FastSCNN process") + parser.add_argument("--pipeline", type=str, default=None, help="SDK infer pipeline") + parser.add_argument("--image_path", type=str, default=None, help="root path of image") + parser.add_argument('--image_width', default=768, type=int, help='image width') + parser.add_argument('--image_height', default=768, type=int, help='image height') + parser.add_argument('--save_mask', default=1, type=int, help='0 for False, 1 for True') + parser.add_argument('--mask_result_path', default='./mask_result', type=str, + help='the folder to save the semantic mask images') + args_opt = parser.parse_args() + return args_opt + +def send_source_data(appsrc_id, tensor, stream_name, stream_manager): + """ + Construct the input of the stream, + send inputs data to a specified stream based on streamName. + + Returns: + bool: send data success or not + """ + tensor_package_list = MxpiDataType.MxpiTensorPackageList() + tensor_package = tensor_package_list.tensorPackageVec.add() + array_bytes = tensor.tobytes() + tensor_vec = tensor_package.tensorVec.add() + tensor_vec.deviceId = 0 + tensor_vec.memType = 0 + for i in tensor.shape: + tensor_vec.tensorShape.append(i) + tensor_vec.dataStr = array_bytes + tensor_vec.tensorDataSize = len(array_bytes) + key = "appsrc{}".format(appsrc_id).encode('utf-8') + protobuf_vec = InProtobufVector() + protobuf = MxProtobufIn() + protobuf.key = key + protobuf.type = b'MxTools.MxpiTensorPackageList' + protobuf.protobuf = tensor_package_list.SerializeToString() + protobuf_vec.push_back(protobuf) + + ret = stream_manager.SendProtobuf(stream_name, appsrc_id, protobuf_vec) + if ret < 0: + print("Failed to send data to stream.") + return False + return True + +cityspallete = [ + 128, 64, 128, + 244, 35, 232, + 70, 70, 70, + 102, 102, 156, + 190, 153, 153, + 153, 153, 153, + 250, 170, 30, + 220, 220, 0, + 107, 142, 35, + 152, 251, 152, + 0, 130, 180, + 220, 20, 60, + 255, 0, 0, + 0, 0, 142, + 0, 0, 70, + 0, 60, 100, + 0, 80, 100, + 0, 0, 230, + 119, 11, 32, +] +classes = ('road', 'sidewalk', 'building', 'wall', 'fence', 'pole', 'traffic light', + 'traffic sign', 'vegetation', 'terrain', 'sky', 'person', 'rider', 'car', + 'truck', 'bus', 'train', 'motorcycle', 'bicycle') + +valid_classes = [7, 8, 11, 12, 13, 17, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 31, 32, 33] + +_key = np.array([-1, -1, -1, -1, -1, -1, + -1, -1, 0, 1, -1, -1, + 2, 3, 4, -1, -1, -1, + 5, -1, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, + -1, -1, 16, 17, 18]) +_mapping = np.array(range(-1, len(_key) - 1)).astype('int32') + +def _get_city_pairs(folder, split='train'): + '''_get_city_pairs''' + def get_path_pairs(img_folder, mask_folder): + img_paths = [] + mask_paths = [] + for root, _, files in os.walk(img_folder): + for filename in files: + if filename.startswith('._'): + continue + if filename.endswith('.png'): + imgpath = os.path.join(root, filename) + foldername = os.path.basename(os.path.dirname(imgpath)) + maskname = filename.replace('leftImg8bit', 'gtFine_labelIds') + maskpath = os.path.join(mask_folder, foldername, maskname) + if os.path.isfile(imgpath) and os.path.isfile(maskpath): + img_paths.append(imgpath) + mask_paths.append(maskpath) + else: + print('cannot find the mask or image:', imgpath, maskpath) + print('Found {} images in the folder {}'.format(len(img_paths), img_folder)) + return img_paths, mask_paths + + if split in ('train', 'val'): + img_folder = os.path.join(folder, 'leftImg8bit' + os.sep + split) + mask_folder = os.path.join(folder, 'gtFine' + os.sep + split) + img_paths, mask_paths = get_path_pairs(img_folder, mask_folder) + return img_paths, mask_paths + assert split == 'trainval' + print('trainval set') + train_img_folder = os.path.join(folder, 'leftImg8bit' + os.sep + 'train') + train_mask_folder = os.path.join(folder, 'gtFine' + os.sep + 'train') + val_img_folder = os.path.join(folder, 'leftImg8bit' + os.sep + 'val') + val_mask_folder = os.path.join(folder, 'gtFine' + os.sep + 'val') + train_img_paths, train_mask_paths = get_path_pairs(train_img_folder, train_mask_folder) + val_img_paths, val_mask_paths = get_path_pairs(val_img_folder, val_mask_folder) + img_paths = train_img_paths + val_img_paths + mask_paths = train_mask_paths + val_mask_paths + return img_paths, mask_paths + +def _val_sync_transform(outsize, img, mask): + '''_val_sync_transform''' + short_size = min(outsize) + w, h = img.size + if w > h: + oh = short_size + ow = int(1.0 * w * oh / h) + else: + ow = short_size + oh = int(1.0 * h * ow / w) + img = img.resize((ow, oh), Image.BILINEAR) + mask = mask.resize((ow, oh), Image.NEAREST) + # center crop + w, h = img.size + x1 = int(round((w - outsize[1]) / 2.)) + y1 = int(round((h - outsize[0]) / 2.)) + img = img.crop((x1, y1, x1 + outsize[1], y1 + outsize[0])) + mask = mask.crop((x1, y1, x1 + outsize[1], y1 + outsize[0])) + + # final transform + img, mask = np.array(img), _mask_transform(mask) + return img, mask + +def _class_to_index(mask): + # assert the value + values = np.unique(mask) + for value in values: + assert value in _mapping + index = np.digitize(mask.ravel(), _mapping, right=True) + return _key[index].reshape(mask.shape) + +def _mask_transform(mask): + target = _class_to_index(np.array(mask).astype('int32')) + return np.array(target).astype('int32') +class SegmentationMetric(): + """Computes pixAcc and mIoU metric scores + """ + + def __init__(self, nclass): + super(SegmentationMetric, self).__init__() + self.nclass = nclass + self.reset() + + def update(self, preds, labels): + """Updates the internal evaluation result. + + Parameters + ---------- + labels : 'NumpyArray' or list of `NumpyArray` + The labels of the data. + preds : 'NumpyArray' or list of `NumpyArray` + Predicted values. + """ + def evaluate_worker(self, pred, label): + correct, labeled = batch_pix_accuracy(pred, label) + inter, union = batch_intersection_union(pred, label, self.nclass) + self.total_correct += correct + self.total_label += labeled + self.total_inter += inter + self.total_union += union + evaluate_worker(self, preds, labels) + + def get(self, return_category_iou=False): + """Gets the current evaluation result. + + Returns + ------- + metrics : tuple of float + pixAcc and mIoU + """ + # remove np.spacing(1) + pixAcc = 1.0 * self.total_correct / (2.220446049250313e-16 + self.total_label) + IoU = 1.0 * self.total_inter / (2.220446049250313e-16 + self.total_union) + mIoU = IoU.mean().item() + if return_category_iou: + return pixAcc, mIoU, IoU + return pixAcc, mIoU + + def reset(self): + """Resets the internal evaluation result to initial state.""" + self.total_inter = np.zeros(self.nclass) + self.total_union = np.zeros(self.nclass) + self.total_correct = 0 + self.total_label = 0 + +def batch_pix_accuracy(output, target): + """PixAcc""" + # inputs are numpy array, output 4D NCHW where 'C' means label classes, target 3D NHW + + predict = np.argmax(output.astype(np.int64), 1) + 1 + target = target.astype(np.int64) + 1 + pixel_labeled = (target > 0).sum() + pixel_correct = ((predict == target) * (target > 0)).sum() + assert pixel_correct <= pixel_labeled, "Correct area should be smaller than Labeled" + return pixel_correct, pixel_labeled + +def batch_intersection_union(output, target, nclass): + """mIoU""" + # inputs are numpy array, output 4D, target 3D + mini = 1 + maxi = nclass + nbins = nclass + predict = np.argmax(output.astype(np.float32), 1) + 1 + target = target.astype(np.float32) + 1 + + predict = predict.astype(np.float32) * (target > 0).astype(np.float32) + intersection = predict * (predict == target).astype(np.float32) + # areas of intersection and union + # element 0 in intersection occur the main difference from np.bincount. set boundary to -1 is necessary. + area_inter, _ = np.histogram(intersection, bins=nbins, range=(mini, maxi)) + area_pred, _ = np.histogram(predict, bins=nbins, range=(mini, maxi)) + area_lab, _ = np.histogram(target, bins=nbins, range=(mini, maxi)) + area_union = area_pred + area_lab - area_inter + assert (area_inter > area_union).sum() == 0, "Intersection area should be smaller than Union area" + return area_inter.astype(np.float32), area_union.astype(np.float32) + +def main(): + """ + read pipeline and do infer + """ + + args = parse_args() + + # init stream manager + stream_manager_api = StreamManagerApi() + ret = stream_manager_api.InitManager() + if ret != 0: + print("Failed to init Stream manager, ret=%s" % str(ret)) + return + + # create streams by pipeline config file + with open(os.path.realpath(args.pipeline), 'rb') as f: + pipeline_str = f.read() + ret = stream_manager_api.CreateMultipleStreams(pipeline_str) + if ret != 0: + print("Failed to create Stream, ret=%s" % str(ret)) + return + + stream_name = b'fastscnn' + infer_total_time = 0 + assert os.path.exists(args.image_path), "Please put dataset in " + str(args.image_path) + images, mask_paths = _get_city_pairs(args.image_path, 'val') + assert len(images) == len(mask_paths) + if not images: + raise RuntimeError("Found 0 images in subfolders of:" + args.image_path + "\n") + + if args.save_mask and not os.path.exists(args.mask_result_path): + os.makedirs(args.mask_result_path) + metric = SegmentationMetric(19) + metric.reset() + for index in range(len(images)): + image_name = images[index].split(os.sep)[-1].split(".")[0] # get the name of image file + print("Processing ---> ", image_name) + img = Image.open(images[index]).convert('RGB') + mask = Image.open(mask_paths[index]) + img, mask = _val_sync_transform((args.image_height, args.image_width), img, mask) + + img = img.astype(np.float32) + mask = mask.astype(np.int32) + mean = [0.485, 0.456, 0.406] + std = [0.229, 0.224, 0.225] + img = img.transpose((2, 0, 1))#HWC->CHW + for channel, _ in enumerate(img): + # Normalization + img[channel] /= 255 + img[channel] -= mean[channel] + img[channel] /= std[channel] + + img = np.expand_dims(img, 0)#NCHW + mask = np.expand_dims(mask, 0)#NHW + + if not send_source_data(0, img, stream_name, stream_manager_api): + return + # Obtain the inference result by specifying streamName and uniqueId. + key_vec = StringVector() + key_vec.push_back(b'modelInfer') + start_time = time.time() + infer_result = stream_manager_api.GetProtobuf(stream_name, 0, key_vec) + infer_total_time += time.time() - start_time + if infer_result.size() == 0: + print("inferResult is null") + return + if infer_result[0].errorCode != 0: + print("GetProtobuf error. errorCode=%d" % (infer_result[0].errorCode)) + return + result = MxpiDataType.MxpiTensorPackageList() + result.ParseFromString(infer_result[0].messageBuf) + res = np.frombuffer(result.tensorPackageVec[0].tensorVec[0].dataStr, dtype='<f4') + mask_image = res.reshape(1, 19, args.image_height, args.image_width) + + metric.update(mask_image, mask) + pixAcc, mIoU = metric.get() + print("[EVAL] Sample: {:d}, pixAcc: {:.3f}, mIoU: {:.3f}".format(index + 1, pixAcc * 100, mIoU * 100)) + if args.save_mask: + output = np.argmax(mask_image[0], axis=0) + out_img = Image.fromarray(output.astype('uint8')) + out_img.putpalette(cityspallete) + outname = str(image_name) + '.png' + out_img.save(os.path.join(args.mask_result_path, outname)) + + pixAcc, mIoU, category_iou = metric.get(return_category_iou=True) + print('End validation pixAcc: {:.3f}, mIoU: {:.3f}'.format(pixAcc * 100, mIoU * 100)) + txtName = os.path.join(args.mask_result_path, "eval_results.txt") + with open(txtName, "w") as f: + string = 'validation pixAcc:' + str(pixAcc * 100) + ', mIoU:' + str(mIoU * 100) + f.write(string) + f.write('\n') + headers = ['class id', 'class name', 'iou'] + table = [] + for i, cls_name in enumerate(classes): + table.append([cls_name, category_iou[i]]) + string = 'class name: ' + cls_name + ' iou: ' + str(category_iou[i]) + '\n' + f.write(string) + print('Category iou: \n {}'.format(tabulate(table, headers, \ + tablefmt='grid', showindex="always", numalign='center', stralign='center'))) + print("Testing finished....") + print("=======================================") + print("The total time of inference is {} s".format(infer_total_time)) + print("=======================================") + + # destroy streams + stream_manager_api.DestroyAllStreams() + +if __name__ == '__main__': + main() diff --git a/official/cv/fastscnn/infer/sdk/run.sh b/official/cv/fastscnn/infer/sdk/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..2c73eefeb82c7b14036813bb2115a64435d288ff --- /dev/null +++ b/official/cv/fastscnn/infer/sdk/run.sh @@ -0,0 +1,69 @@ +#!/bin/bash +# Copyright 2021 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ + +image_width=768 +image_height=768 +save_mask=1 +mask_result_path=./mask_result +# help message +if [[ $1 == --help || $1 == -h ]];then + echo "usage:bash ./run.sh <args>" + echo "parameter explain: + --pipeline set SDK infer pipeline, e.g. --pipeline=../data/config/fastscnn.pipeline + --image_path root path of processed images, e.g. --image_path=../data/ + --image_width set the image width, default: --image_width=768 + --image_height set the image height, default: --image_height=768 + --save_mask whether to save the semantic mask images, 0 for False, 1 for True, default: --save_mask=1 + --mask_result_path the folder to save the semantic mask images, default: --mask_result_path=./mask_result + -h/--help show help message + " + exit 1 +fi + +for para in "$@" +do + if [[ $para == --pipeline* ]];then + pipeline=`echo ${para#*=}` + elif [[ $para == --image_path* ]];then + image_path=`echo ${para#*=}` + elif [[ $para == --image_width* ]];then + image_width=`echo ${para#*=}` + elif [[ $para == --image_height* ]];then + image_height=`echo ${para#*=}` + elif [[ $para == --save_mask* ]];then + save_mask=`echo ${para#*=}` + elif [[ $para == --mask_result_path* ]];then + mask_result_path=`echo ${para#*=}` + fi +done + +if [[ $pipeline == "" ]];then + echo "[Error] para \"pipeline \" must be config" + exit 1 +fi +if [[ $image_path == "" ]];then + echo "[Error] para \"image_path \" must be config" + exit 1 +fi + +python3 main.py --pipeline=$pipeline \ + --image_path=$image_path \ + --image_width=$image_width \ + --image_height=$image_height \ + --save_mask=$save_mask \ + --mask_result_path=$mask_result_path + +exit 0 diff --git a/official/cv/fastscnn/modelarts/start_train.py b/official/cv/fastscnn/modelarts/start_train.py new file mode 100644 index 0000000000000000000000000000000000000000..57efcf596cc4619de6b10df8b603938c99ee3089 --- /dev/null +++ b/official/cv/fastscnn/modelarts/start_train.py @@ -0,0 +1,239 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ +'''train scripts for modelarts''' +import os +import argparse +import datetime +import moxing as mox +import numpy as np + +import mindspore +import mindspore.nn as nn +from mindspore import export +from mindspore import context +from mindspore.train import Model +from mindspore.dataset import config +from mindspore.common import set_seed +from mindspore.common.tensor import Tensor +from mindspore.context import ParallelMode +from mindspore import FixedLossScaleManager +from mindspore import load_checkpoint, load_param_into_net +from mindspore.dataset.transforms.py_transforms import Compose +from mindspore.dataset.vision.py_transforms import ToTensor, Normalize +from mindspore.communication.management import init, get_rank, get_group_size +from mindspore.train.callback import TimeMonitor, LossMonitor, CheckpointConfig, ModelCheckpoint + +from src.logger import get_logger +from src.lr_scheduler import LRScheduler +from src.dataloader import create_CitySegmentation +from src.fast_scnn import FastSCNN, FastSCNNWithLossCell +from src.util import SegmentationMetric, EvalCallBack, TempLoss + +def parse_args(): + """Training Options for Segmentation Experiments""" + parser = argparse.ArgumentParser(description='Fast-SCNN on mindspore') + parser.add_argument('--dataset', type=str, default='/data/dataset/citys/', + help='dataset name (default: /data/dataset/citys/)') + parser.add_argument('--base_size', type=int, default=1024, help='base image size') + parser.add_argument('--crop_size', type=int, default=(768, 768), help='crop image size') + parser.add_argument('--train_split', type=str, default='train', + help='dataset train split (default: train)') + parser.add_argument('--aux', action='store_true', default=True, help='Auxiliary loss') + parser.add_argument('--aux_weight', type=float, default=0.4, + help='auxiliary loss weight') + parser.add_argument('--epochs', type=int, default=1000, metavar='N', + help='number of epochs to train (default: 1000)') + parser.add_argument('--save_every', type=int, default=1, metavar='N', + help='save ckpt every N epoch') + parser.add_argument('--resume_path', type=str, default=None, + help='put the path to resuming file if needed') + parser.add_argument('--resume_name', type=str, default=None, + help='resuming file name') + parser.add_argument('--batch_size', type=int, default=2, metavar='N', + help='input batch size for training (default: 2)') + parser.add_argument('--lr', type=float, default=0.001, metavar='LR', + help='base learning rate (default: 0.045)') + parser.add_argument('--momentum', type=float, default=0.9, metavar='M', + help='momentum (default: 0.9)') + parser.add_argument('--weight_decay', type=float, default=4e-5, metavar='M', + help='w-decay (default: 4e-5)') + + parser.add_argument('--eval_while_train', type=int, default=1, help='eval while training') + parser.add_argument('--eval_steps', type=int, default=10, help='each N epochs we eval') + parser.add_argument('--eval_start_epoch', type=int, default=850, help='eval_start_epoch') + parser.add_argument('--train_url', type=str, default='train_url/', + help='needed by modelarts, but we donot use it because the name is ambiguous') + parser.add_argument('--data_url', type=str, default='data_url/', + help='needed by modelarts, but we donot use it because the name is ambiguous') + parser.add_argument('--output_path', type=str, default='cache/output/', + help='output_path, default is cache/output/') + parser.add_argument('--outer_path', type=str, default='s3://output/', + help='obs path,to store e.g ckpt files ') + parser.add_argument("--file_format", type=str, choices=["AIR", "ONNX", "MINDIR"], \ + default="AIR", help="file format") + + parser.add_argument('--device_target', type=str, default='Ascend', + help='device where the code will be implemented. (Default: Ascend)') + parser.add_argument('--is_distributed', type=int, default=0, help='if multi device') + parser.add_argument('--rank', type=int, default=0, help='local rank of distributed') + parser.add_argument('--group_size', type=int, default=1, help='world size of distributed') + parser.add_argument('--is_save_on_master', type=int, default=1, + help='save ckpt on master or all rank') + parser.add_argument('--ckpt_save_max', type=int, default=800, + help='Maximum number of checkpoint files can be saved. Default: 800') + # the parser + args_ = parser.parse_args() + return args_ + +args = parse_args() +set_seed(1) +device_id = int(os.getenv('DEVICE_ID', '0')) +context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target, save_graphs=False) +save_dir = os.path.join(args.output_path, datetime.datetime.now().strftime('%Y-%m-%d_time_%H_%M_%S')) + + +def copy_data_from_obs(): + args.logger.info("copying dataset from obs to cache....") + mox.file.copy_parallel(args.dataset, 'cache/dataset') + args.logger.info("copying dataset finished....") + args.dataset = 'cache/dataset/' + + # resume checkpoint if needed + if args.resume_path: + args.logger.info("copying resume checkpoint from obs to cache....") + mox.file.copy_parallel(args.resume_path, 'cache/resume_path') + args.logger.info("copying resume checkpoint finished....") + args.resume_path = 'cache/resume_path/' + +def copy_data_to_obs(): + args.logger.info("copying files from cache to obs....") + mox.file.copy_parallel(save_dir, args.outer_path) + args.logger.info("copying finished....") + +def export_models(): + args.logger.info("exporting model....") + net = FastSCNN(num_classes=19, aux=args.aux) + param_dict = load_checkpoint(os.path.join(save_dir, str(args.rank) + "_best_map.ckpt")) + load_param_into_net(net, param_dict) + input_arr = Tensor(np.zeros([1, 3, \ + args.crop_size[0], args.crop_size[1]]), mindspore.float32) + export(net, input_arr, file_name=os.path.join(save_dir, str(args.rank) + "_best_map"), \ + file_format=args.file_format) + args.logger.info("export model finished....") + +def train(): + '''train''' + # image transform + input_transform = Compose([ + ToTensor(), + Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), + ]) + + train_dataset, args.steps_per_epoch = create_CitySegmentation(args, data_path=args.dataset, \ + split=args.train_split, mode='train', transform=input_transform, \ + base_size=args.base_size, crop_size=args.crop_size, batch_size=args.batch_size, \ + device_num=args.group_size, rank=args.rank, shuffle=True) + + # create network + f_model = FastSCNN(num_classes=19, aux=args.aux) + + # resume checkpoint if needed + if args.resume_path: + args.resume_path = os.path.join(args.resume_path, args.resume_name) + args.logger.info('loading resume checkpoint {} into network'.format(args.resume_path)) + load_param_into_net(f_model, load_checkpoint(args.resume_path)) + args.logger.info('loaded resume checkpoint {} into network'.format(args.resume_path)) + + model = FastSCNNWithLossCell(f_model, args) + model.set_train() + + # lr scheduling + lr_list = LRScheduler(mode='cosine', base_lr=args.lr, nepochs=args.epochs, \ + iters_per_epoch=args.steps_per_epoch, power=0.9)(args.epochs*args.steps_per_epoch) + + # optimizer + optimizer = nn.SGD(params=model.trainable_params(), momentum=args.momentum, \ + learning_rate=Tensor(lr_list, mindspore.float32), \ + weight_decay=args.weight_decay, loss_scale=1024) + loss_scale = FixedLossScaleManager(1024, drop_overflow_update=False) + model = Model(model, optimizer=optimizer, loss_scale_manager=loss_scale, amp_level="O0") + + # define callbacks + if args.rank == 0: + time_cb = TimeMonitor(data_size=args.steps_per_epoch) + loss_cb = LossMonitor() + callbacks = [time_cb, loss_cb] + else: + callbacks = [] + + if args.rank_save_ckpt_flag: + ckpt_config = CheckpointConfig(save_checkpoint_steps=args.steps_per_epoch*args.save_every, + keep_checkpoint_max=args.ckpt_save_max) + save_ckpt_path = os.path.join(save_dir, 'ckpt_' + str(args.rank) + '/') + ckpt_cb = ModelCheckpoint(config=ckpt_config, + directory=save_ckpt_path, + prefix='rank_'+str(args.rank)) + callbacks.append(ckpt_cb) + + if args.eval_while_train: + + val_dataset, _ = create_CitySegmentation(args, data_path=args.dataset, \ + split='val', mode='val', transform=input_transform, \ + base_size=args.base_size, crop_size=args.crop_size, \ + batch_size=1, device_num=1, \ + rank=args.rank, shuffle=False) + loss_f = TempLoss() + network_eval = Model(f_model, loss_fn=loss_f, metrics={"SegmentationMetric": SegmentationMetric(19)}) + + eval_cb = EvalCallBack(network_eval, val_dataset, interval=args.eval_steps, + eval_start_epoch=args.eval_start_epoch, save_best_ckpt=True, + ckpt_directory=save_dir, besk_ckpt_name=str(args.rank) + "_best_map.ckpt", + metrics_name=("pixAcc", "mIou")) + callbacks.append(eval_cb) + + model.train(args.epochs, train_dataset, callbacks=callbacks, dataset_sink_mode=True) + args.logger.info("training finished....") + +if __name__ == '__main__': + if args.is_distributed: + assert args.device_target == "Ascend" + context.set_context(device_id=device_id) + init() + args.rank = get_rank() + args.group_size = get_group_size() + device_num = args.group_size + context.reset_auto_parallel_context() + context.set_auto_parallel_context(device_num=device_num, parallel_mode=ParallelMode.DATA_PARALLEL) + else: + if args.device_target in ["Ascend", "GPU"]: + context.set_context(device_id=device_id) + + # select for master rank save ckpt or all rank save, compatible for model parallel + args.rank_save_ckpt_flag = 0 + if args.is_save_on_master: + if args.rank == 0: + args.rank_save_ckpt_flag = 1 + else: + args.rank_save_ckpt_flag = 1 + + config.set_enable_shared_mem(False) + args.logger = get_logger(save_dir, "Fast_SCNN", args.rank) + args.logger.save_args(args) + + print('Starting training, Total Epochs: %d' % (args.epochs)) + copy_data_from_obs() + train() + export_models() + copy_data_to_obs() diff --git a/official/cv/fastscnn/preprocess.py b/official/cv/fastscnn/preprocess.py index 5a5ff78f0425871cf83cfddf5f1b0eda7ca9ae23..574b439c55fcf237e1b6011c4bb8424df2e45cc3 100644 --- a/official/cv/fastscnn/preprocess.py +++ b/official/cv/fastscnn/preprocess.py @@ -119,7 +119,7 @@ def crop_imageAndLabel(out_dir, image_path, image_height, image_width): if not os.path.exists(os.path.join(out_dir, "labels")): os.makedirs(os.path.join(out_dir, "labels")) - assert os.path.exists(image_path), "Please put dataset in {SEG_ROOT}/datasets/cityscapes" + assert os.path.exists(image_path), "Please put dataset in " + str(image_path) images, mask_paths = _get_city_pairs(image_path, 'val') assert len(images) == len(mask_paths) if not images: diff --git a/official/cv/fastscnn/score.py b/official/cv/fastscnn/score.py deleted file mode 100644 index ff820b377f2cea0fde40f8de9f8cf8ed007e1506..0000000000000000000000000000000000000000 --- a/official/cv/fastscnn/score.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright 2021 Huawei Technologies Co., Ltd -# -# 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. -# ============================================================================ -"""Evaluation Metrics for Semantic Segmentation""" -import numpy as np - -__all__ = ['SegmentationMetric', 'batch_pix_accuracy', 'batch_intersection_union', - 'pixelAccuracy', 'intersectionAndUnion', 'hist_info', 'compute_score'] - -class SegmentationMetric(): - """Computes pixAcc and mIoU metric scores - """ - - def __init__(self, nclass): - super(SegmentationMetric, self).__init__() - self.nclass = nclass - self.reset() - - def update(self, preds, labels): - """Updates the internal evaluation result. - - Parameters - ---------- - labels : 'NumpyArray' or list of `NumpyArray` - The labels of the data. - preds : 'NumpyArray' or list of `NumpyArray` - Predicted values. - """ - def evaluate_worker(self, pred, label): - correct, labeled = batch_pix_accuracy(pred, label) - inter, union = batch_intersection_union(pred, label, self.nclass) - self.total_correct += correct - self.total_label += labeled - self.total_inter += inter - self.total_union += union - evaluate_worker(self, preds, labels) - - def get(self, return_category_iou=False): - """Gets the current evaluation result. - - Returns - ------- - metrics : tuple of float - pixAcc and mIoU - """ - # remove np.spacing(1) - pixAcc = 1.0 * self.total_correct / (2.220446049250313e-16 + self.total_label) - IoU = 1.0 * self.total_inter / (2.220446049250313e-16 + self.total_union) - mIoU = IoU.mean().item() - if return_category_iou: - return pixAcc, mIoU, IoU - return pixAcc, mIoU - - def reset(self): - """Resets the internal evaluation result to initial state.""" - self.total_inter = np.zeros(self.nclass) - self.total_union = np.zeros(self.nclass) - self.total_correct = 0 - self.total_label = 0 - -def batch_pix_accuracy(output, target): - """PixAcc""" - # inputs are numpy array, output 4D NCHW where 'C' means label classes, target 3D NHW - - predict = np.argmax(output.astype(np.int64), 1) + 1 - target = target.astype(np.int64) + 1 - pixel_labeled = (target > 0).sum() - pixel_correct = ((predict == target) * (target > 0)).sum() - assert pixel_correct <= pixel_labeled, "Correct area should be smaller than Labeled" - return pixel_correct, pixel_labeled - -def batch_intersection_union(output, target, nclass): - """mIoU""" - # inputs are numpy array, output 4D, target 3D - mini = 1 - maxi = nclass - nbins = nclass - predict = np.argmax(output.astype(np.float32), 1) + 1 - target = target.astype(np.float32) + 1 - - predict = predict.astype(np.float32) * (target > 0).astype(np.float32) - intersection = predict * (predict == target).astype(np.float32) - # areas of intersection and union - # element 0 in intersection occur the main difference from np.bincount. set boundary to -1 is necessary. - area_inter, _ = np.histogram(intersection, bins=nbins, range=(mini, maxi)) - area_pred, _ = np.histogram(predict, bins=nbins, range=(mini, maxi)) - area_lab, _ = np.histogram(target, bins=nbins, range=(mini, maxi)) - area_union = area_pred + area_lab - area_inter - assert (area_inter > area_union).sum() == 0, "Intersection area should be smaller than Union area" - return area_inter.astype(np.float32), area_union.astype(np.float32) - - -def pixelAccuracy(imPred, imLab): - """ - This function takes the prediction and label of a single image, returns pixel-wise accuracy - To compute over many images do: - for i = range(Nimages): - (pixel_accuracy[i], pixel_correct[i], pixel_labeled[i]) = \ - pixelAccuracy(imPred[i], imLab[i]) - mean_pixel_accuracy = 1.0 * np.sum(pixel_correct) / (np.spacing(1) + np.sum(pixel_labeled)) - """ - # Remove classes from unlabeled pixels in gt image. - # We should not penalize detections in unlabeled portions of the image. - pixel_labeled = np.sum(imLab >= 0) - pixel_correct = np.sum((imPred == imLab) * (imLab >= 0)) - pixel_accuracy = 1.0 * pixel_correct / pixel_labeled - return (pixel_accuracy, pixel_correct, pixel_labeled) - - -def intersectionAndUnion(imPred, imLab, numClass): - """ - This function takes the prediction and label of a single image, - returns intersection and union areas for each class - To compute over many images do: - for i in range(Nimages): - (area_intersection[:,i], area_union[:,i]) = intersectionAndUnion(imPred[i], imLab[i]) - IoU = 1.0 * np.sum(area_intersection, axis=1) / np.sum(np.spacing(1)+area_union, axis=1) - """ - # Remove classes from unlabeled pixels in gt image. - # We should not penalize detections in unlabeled portions of the image. - imPred = imPred * (imLab >= 0) - - # Compute area intersection: - intersection = imPred * (imPred == imLab) - (area_intersection, _) = np.histogram(intersection, bins=numClass, range=(1, numClass)) - - # Compute area union: - (area_pred, _) = np.histogram(imPred, bins=numClass, range=(1, numClass)) - (area_lab, _) = np.histogram(imLab, bins=numClass, range=(1, numClass)) - area_union = area_pred + area_lab - area_intersection - return (area_intersection, area_union) - - -def hist_info(pred, label, num_cls): - assert pred.shape == label.shape - k = (label >= 0) & (label < num_cls) - labeled = np.sum(k) - correct = np.sum((pred[k] == label[k])) - - return np.bincount(num_cls * label[k].astype(int) + pred[k], \ - minlength=num_cls ** 2).reshape(num_cls, num_cls), labeled, correct - -def compute_score(hist, correct, labeled): - iu = np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist)) - mean_IU = np.nanmean(iu) - mean_IU_no_back = np.nanmean(iu[1:]) - mean_pixel_acc = correct / labeled - - return iu, mean_IU, mean_IU_no_back, mean_pixel_acc diff --git a/official/cv/fastscnn/scripts/docker_start.sh b/official/cv/fastscnn/scripts/docker_start.sh new file mode 100644 index 0000000000000000000000000000000000000000..e39553192b435f70dae6aa6b6adbfd6ffe901b40 --- /dev/null +++ b/official/cv/fastscnn/scripts/docker_start.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright 2021 Huawei Technologies Co., Ltd +# +# 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.mitations under the License. + +docker_image=$1 +data_dir=$2 +model_dir=$3 + +docker run -it --ipc=host \ + --device=/dev/davinci0 \ + --device=/dev/davinci1 \ + --device=/dev/davinci2 \ + --device=/dev/davinci3 \ + --device=/dev/davinci4 \ + --device=/dev/davinci5 \ + --device=/dev/davinci6 \ + --device=/dev/davinci7 \ + --device=/dev/davinci_manager \ + --device=/dev/devmm_svm --device=/dev/hisi_hdc \ + -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ + -v /usr/local/Ascend/add-ons/:/usr/local/Ascend/add-ons/ \ + -v ${model_dir}:${model_dir} \ + -v ${data_dir}:${data_dir} \ + -v /root/ascend/log:/root/ascend/log ${docker_image} /bin/bash \ No newline at end of file diff --git a/official/cv/fastscnn/scripts/run_distribute_train.sh b/official/cv/fastscnn/scripts/run_distribute_train.sh index 499b4a621dff15539165f57e6ab1787201ecd3c5..dab1c97c3709cbb35af47c4175e3a037271267aa 100644 --- a/official/cv/fastscnn/scripts/run_distribute_train.sh +++ b/official/cv/fastscnn/scripts/run_distribute_train.sh @@ -14,9 +14,9 @@ # limitations under the License. # ============================================================================ -if [ $# != 6 ]; then +if [ $# != 7 ]; then echo "Usage: sh run_distribute_train.sh [train_code_path] [dataset]" \ - "[epochs] [batch_size] [lr] [output_path]" + "[epochs] [batch_size] [lr] [output_path] [rank_table_file_path]" exit 1 fi @@ -50,11 +50,10 @@ fi ulimit -c unlimited export SLOG_PRINT_TO_STDOUT=0 -export RANK_TABLE_FILE=${train_code_path}scripts/hccl_8p_01234567_10.155.170.118.json +export RANK_TABLE_FILE=${7} export RANK_SIZE=8 export RANK_START_ID=0 - for((i=0;i<=$RANK_SIZE-1;i++)); do export RANK_ID=${i} @@ -65,7 +64,7 @@ do fi mkdir ${train_code_path}/device${DEVICE_ID} cd ${train_code_path}/device${DEVICE_ID} || exit - nohup python ${train_code_path}train.py --is_distributed=1 \ + nohup python ${train_code_path}train.py --is_distributed=1 --device_target=Ascend \ --dataset=${dataset} \ --epochs=$3 \ --batch_size=$4 \ diff --git a/official/cv/fastscnn/src/dataloader.py b/official/cv/fastscnn/src/dataloader.py index 88d941af3692a5c10248d3e279561389a38afd07..6868b2baa8f091b4ba9b2669da770c1058a367d7 100644 --- a/official/cv/fastscnn/src/dataloader.py +++ b/official/cv/fastscnn/src/dataloader.py @@ -21,8 +21,6 @@ import mindspore.dataset as ds import mindspore.dataset.vision.c_transforms as CV from src.seg_data_base import SegmentationDataset -from src.distributed_sampler import DistributedSampler - __all__ = ['CitySegmentation'] @@ -162,20 +160,17 @@ def create_CitySegmentation(args, data_path='../dataset/', split='train', mode=N '''create_CitySegmentation''' dataset = CitySegmentation(args, root=data_path, split=split, mode=mode, \ base_size=base_size, crop_size=crop_size) - dataset_len = len(dataset) - distributed_sampler = DistributedSampler(dataset_len, device_num, rank, shuffle=shuffle) - data_set = ds.GeneratorDataset(dataset, column_names=["image", "label"], num_parallel_workers=8, \ - shuffle=shuffle, sampler=distributed_sampler) + shuffle=shuffle, num_shards=device_num, shard_id=rank) # general resize, normalize and toTensor if transform is not None: data_set = data_set.map(input_columns=["image"], operations=transform, num_parallel_workers=8) else: hwc_to_chw = CV.HWC2CHW() data_set = data_set.map(input_columns=["image"], operations=hwc_to_chw, num_parallel_workers=8) - data_set = data_set.batch(batch_size, drop_remainder=True) - return data_set, dataset_len + + return data_set, data_set.get_dataset_size() if __name__ == '__main__': diff --git a/official/cv/fastscnn/src/distributed_sampler.py b/official/cv/fastscnn/src/distributed_sampler.py deleted file mode 100644 index 11c4eff96fb1e8b3f3b6e702fd520874adf3183d..0000000000000000000000000000000000000000 --- a/official/cv/fastscnn/src/distributed_sampler.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2021 Huawei Technologies Co., Ltd -# -# 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. -# ============================================================================ -"""distributed sampler.""" -from __future__ import division -import math -import numpy as np - - -class DistributedSampler: - """Distributed sampler.""" - def __init__(self, dataset_size, num_replicas=None, rank=None, shuffle=True): - if num_replicas is None: - print("***********Setting world_size to 1 since it is not passed in ******************") - num_replicas = 1 - if rank is None: - print("***********Setting rank to 0 since it is not passed in ******************") - rank = 0 - self.dataset_size = dataset_size - self.num_replicas = num_replicas - self.rank = rank - self.epoch = 0 - self.num_samples = int(math.ceil(dataset_size * 1.0 / self.num_replicas)) - self.total_size = self.num_samples * self.num_replicas - self.shuffle = shuffle - - def __iter__(self): - # deterministically shuffle based on epoch - if self.shuffle: - indices = np.random.RandomState(seed=self.epoch).permutation(self.dataset_size) - # np.array type. number from 0 to len(dataset_size)-1, used as index of dataset - indices = indices.tolist() - self.epoch += 1 - # change to list type - else: - indices = list(range(self.dataset_size)) - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) - - def __len__(self): - return self.num_samples diff --git a/official/cv/fastscnn/src/fast_scnn.py b/official/cv/fastscnn/src/fast_scnn.py index 3f7accabd3376e35a4dc507169593f917a95cd57..d4cb4441d2c06dd6e78a4a4e1c8b69188050605a 100644 --- a/official/cv/fastscnn/src/fast_scnn.py +++ b/official/cv/fastscnn/src/fast_scnn.py @@ -256,7 +256,8 @@ class FastSCNNWithLossCell(nn.Cell): super(FastSCNNWithLossCell, self).__init__() self.network = network self.aux = args.aux - self.loss = MixSoftmaxCrossEntropyLoss(args, aux=args.aux, aux_weight=args.aux_weight) + self.loss = MixSoftmaxCrossEntropyLoss(args, aux=args.aux, aux_weight=args.aux_weight, + one_d_length=args.batch_size*args.crop_size[0]*args.crop_size[1]) def construct(self, images, targets): outputs = self.network(images) if self.aux: diff --git a/official/cv/fastscnn/src/score.py b/official/cv/fastscnn/src/score.py index 275fcccff895045073d49b675d37097b09c4042e..465c41d6a0c506bbde91f3a5ac778352c410a2c9 100644 --- a/official/cv/fastscnn/src/score.py +++ b/official/cv/fastscnn/src/score.py @@ -16,8 +16,7 @@ import numpy as np from mindspore.common.tensor import Tensor -__all__ = ['SegmentationMetric', 'batch_pix_accuracy', 'batch_intersection_union', - 'pixelAccuracy', 'intersectionAndUnion', 'hist_info', 'compute_score'] +__all__ = ['SegmentationMetric', 'batch_pix_accuracy', 'batch_intersection_union'] class SegmentationMetric(): """Computes pixAcc and mIoU metric scores @@ -106,62 +105,3 @@ def batch_intersection_union(output, target, nclass): area_union = area_pred + area_lab - area_inter assert (area_inter > area_union).sum() == 0, "Intersection area should be smaller than Union area" return area_inter.astype(np.float32), area_union.astype(np.float32) - - -def pixelAccuracy(imPred, imLab): - """ - This function takes the prediction and label of a single image, returns pixel-wise accuracy - To compute over many images do: - for i = range(Nimages): - (pixel_accuracy[i], pixel_correct[i], pixel_labeled[i]) = \ - pixelAccuracy(imPred[i], imLab[i]) - mean_pixel_accuracy = 1.0 * np.sum(pixel_correct) / (np.spacing(1) + np.sum(pixel_labeled)) - """ - # Remove classes from unlabeled pixels in gt image. - # We should not penalize detections in unlabeled portions of the image. - pixel_labeled = np.sum(imLab >= 0) - pixel_correct = np.sum((imPred == imLab) * (imLab >= 0)) - pixel_accuracy = 1.0 * pixel_correct / pixel_labeled - return (pixel_accuracy, pixel_correct, pixel_labeled) - - -def intersectionAndUnion(imPred, imLab, numClass): - """ - This function takes the prediction and label of a single image, - returns intersection and union areas for each class - To compute over many images do: - for i in range(Nimages): - (area_intersection[:,i], area_union[:,i]) = intersectionAndUnion(imPred[i], imLab[i]) - IoU = 1.0 * np.sum(area_intersection, axis=1) / np.sum(np.spacing(1)+area_union, axis=1) - """ - # Remove classes from unlabeled pixels in gt image. - # We should not penalize detections in unlabeled portions of the image. - imPred = imPred * (imLab >= 0) - - # Compute area intersection: - intersection = imPred * (imPred == imLab) - (area_intersection, _) = np.histogram(intersection, bins=numClass, range=(1, numClass)) - - # Compute area union: - (area_pred, _) = np.histogram(imPred, bins=numClass, range=(1, numClass)) - (area_lab, _) = np.histogram(imLab, bins=numClass, range=(1, numClass)) - area_union = area_pred + area_lab - area_intersection - return (area_intersection, area_union) - - -def hist_info(pred, label, num_cls): - assert pred.shape == label.shape - k = (label >= 0) & (label < num_cls) - labeled = np.sum(k) - correct = np.sum((pred[k] == label[k])) - - return np.bincount(num_cls * label[k].astype(int) + pred[k], \ - minlength=num_cls ** 2).reshape(num_cls, num_cls), labeled, correct - -def compute_score(hist, correct, labeled): - iu = np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist)) - mean_IU = np.nanmean(iu) - mean_IU_no_back = np.nanmean(iu[1:]) - mean_pixel_acc = correct / labeled - - return iu, mean_IU, mean_IU_no_back, mean_pixel_acc diff --git a/official/cv/fastscnn/src/util.py b/official/cv/fastscnn/src/util.py index 36162888d5800c52742c6fe8bd8efee060a6357d..eb1ab7cdc01c54c949eb5c6cb1ee47d4748d8849 100644 --- a/official/cv/fastscnn/src/util.py +++ b/official/cv/fastscnn/src/util.py @@ -24,13 +24,7 @@ from mindspore import save_checkpoint from mindspore import log as logger from mindspore.train.callback import Callback from mindspore.common.tensor import Tensor - -def apply_eval(eval_param_dict): - """run Evaluation""" - model = eval_param_dict["model"] - dataset = eval_param_dict["dataset"] - eval_score = model.eval(dataset, dataset_sink_mode=False)["SegmentationMetric"] - return eval_score +from src.score import batch_pix_accuracy, batch_intersection_union class TempLoss(nn.Cell): """A temp loss cell.""" @@ -63,8 +57,6 @@ class SegmentationMetric(nn.Metric): """ preds, labels = inputs[0], inputs[-1] preds = preds[0] - #print("preds:",preds) - #print("labels:",labels) def evaluate_worker(self, pred, label): correct, labeled = batch_pix_accuracy(pred.asnumpy(), label.asnumpy()) inter, union = batch_intersection_union(pred.asnumpy(), label.asnumpy(), self.nclass) @@ -100,26 +92,26 @@ class EvalCallBack(Callback): Evaluation callback when training. Args: - eval_function (function): evaluation function. - eval_param_dict (dict): evaluation parameters' configure dict. + network (function): evaluation network. + dataloader (dict): evaluation dataloader. interval (int): run evaluation interval, default is 1. eval_start_epoch (int): evaluation start epoch, default is 1. save_best_ckpt (bool): Whether to save best checkpoint, default is True. besk_ckpt_name (str): bast checkpoint name, default is `best.ckpt`. - metrics_name (str): evaluation metrics name, default is `acc`. + metrics_name (str): evaluation metrics name, default is ("pixAcc", "mIou"). Returns: None Examples: - >>> EvalCallBack(eval_function, eval_param_dict) + >>> EvalCallBack(network, dataloader) """ - def __init__(self, eval_function, eval_param_dict, interval=1, eval_start_epoch=1, \ - save_best_ckpt=True, ckpt_directory="./", besk_ckpt_name="best.ckpt", metrics_name="acc"): + def __init__(self, network, dataloader, interval=1, eval_start_epoch=1, \ + save_best_ckpt=True, ckpt_directory="./", besk_ckpt_name="best.ckpt", metrics_name=("pixAcc", "mIou")): super(EvalCallBack, self).__init__() - self.eval_param_dict = eval_param_dict - self.eval_function = eval_function + self.network = network + self.dataloader = dataloader self.eval_start_epoch = eval_start_epoch if interval < 1: raise ValueError("interval should >= 1.") @@ -147,7 +139,7 @@ class EvalCallBack(Callback): cb_params = run_context.original_args() cur_epoch = cb_params.cur_epoch_num if cur_epoch >= self.eval_start_epoch and (cur_epoch - self.eval_start_epoch) % self.interval == 0: - res = self.eval_function(self.eval_param_dict) + res = self.network.eval(self.dataloader, dataset_sink_mode=True)['SegmentationMetric'] print(datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3],\ ":INFO: epoch: {}, {}: {}, {}: {}".format(cur_epoch, self.metrics_name[0], \ res[0]*100, self.metrics_name[1], res[1]*100), flush=True) @@ -167,94 +159,3 @@ class EvalCallBack(Callback): print(datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3],\ ":INFO: End training, the best {0} is: {1}, it's epoch is {2}".format(self.metrics_name[1],\ self.best_res*100, self.best_epoch), flush=True) - -def batch_pix_accuracy(output, target): - """PixAcc""" - # inputs are numpy array, output 4D NCHW where 'C' means label classes, target 3D NHW - - predict = np.argmax(output.astype(np.int64), 1) + 1 - target = target.astype(np.int64) + 1 - pixel_labeled = (target > 0).sum() - pixel_correct = ((predict == target) * (target > 0)).sum() - assert pixel_correct <= pixel_labeled, "Correct area should be smaller than Labeled" - return pixel_correct, pixel_labeled - -def batch_intersection_union(output, target, nclass): - """mIoU""" - # inputs are numpy array, output 4D, target 3D - mini = 1 - maxi = nclass - nbins = nclass - predict = np.argmax(output.astype(np.float32), 1) + 1 - target = target.astype(np.float32) + 1 - - predict = predict.astype(np.float32) * (target > 0).astype(np.float32) - intersection = predict * (predict == target).astype(np.float32) - # areas of intersection and union - # element 0 in intersection occur the main difference from np.bincount. set boundary to -1 is necessary. - area_inter, _ = np.histogram(intersection, bins=nbins, range=(mini, maxi)) - area_pred, _ = np.histogram(predict, bins=nbins, range=(mini, maxi)) - area_lab, _ = np.histogram(target, bins=nbins, range=(mini, maxi)) - area_union = area_pred + area_lab - area_inter - assert (area_inter > area_union).sum() == 0, "Intersection area should be smaller than Union area" - return area_inter.astype(np.float32), area_union.astype(np.float32) - - -def pixelAccuracy(imPred, imLab): - """ - This function takes the prediction and label of a single image, returns pixel-wise accuracy - To compute over many images do: - for i = range(Nimages): - (pixel_accuracy[i], pixel_correct[i], pixel_labeled[i]) = \ - pixelAccuracy(imPred[i], imLab[i]) - mean_pixel_accuracy = 1.0 * np.sum(pixel_correct) / (np.spacing(1) + np.sum(pixel_labeled)) - """ - # Remove classes from unlabeled pixels in gt image. - # We should not penalize detections in unlabeled portions of the image. - pixel_labeled = np.sum(imLab >= 0) - pixel_correct = np.sum((imPred == imLab) * (imLab >= 0)) - pixel_accuracy = 1.0 * pixel_correct / pixel_labeled - return (pixel_accuracy, pixel_correct, pixel_labeled) - -def intersectionAndUnion(imPred, imLab, numClass): - """ - This function takes the prediction and label of a single image, - returns intersection and union areas for each class - To compute over many images do: - for i in range(Nimages): - (area_intersection[:,i], area_union[:,i]) = intersectionAndUnion(imPred[i], imLab[i]) - IoU = 1.0 * np.sum(area_intersection, axis=1) / np.sum(np.spacing(1)+area_union, axis=1) - """ - # Remove classes from unlabeled pixels in gt image. - # We should not penalize detections in unlabeled portions of the image. - imPred = imPred * (imLab >= 0) - - # Compute area intersection: - intersection = imPred * (imPred == imLab) - (area_intersection, _) = np.histogram(intersection, bins=numClass, range=(1, numClass)) - - # Compute area union: - (area_pred, _) = np.histogram(imPred, bins=numClass, range=(1, numClass)) - (area_lab, _) = np.histogram(imLab, bins=numClass, range=(1, numClass)) - area_union = area_pred + area_lab - area_intersection - return (area_intersection, area_union) - - -def hist_info(pred, label, num_cls): - assert pred.shape == label.shape - k = (label >= 0) & (label < num_cls) - labeled = np.sum(k) - correct = np.sum((pred[k] == label[k])) - - return np.bincount(num_cls * label[k].astype(int) + pred[k], minlength=num_cls ** 2).\ - reshape(num_cls, num_cls), labeled, correct - -def compute_score(hist, correct, labeled): - iu = np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist)) - mean_IU = np.nanmean(iu) - mean_IU_no_back = np.nanmean(iu[1:]) - #freq = hist.sum(1) / hist.sum() - # freq_IU = (iu[freq > 0] * freq[freq > 0]).sum() - mean_pixel_acc = correct / labeled - - return iu, mean_IU, mean_IU_no_back, mean_pixel_acc diff --git a/official/cv/fastscnn/train.py b/official/cv/fastscnn/train.py index a2bca20074422110a9f9f33079c5aab0931be730..125396df6cf5468e11c586b5ad3526778ba0fe41 100644 --- a/official/cv/fastscnn/train.py +++ b/official/cv/fastscnn/train.py @@ -14,7 +14,6 @@ # ============================================================================ '''train.py''' import os -import math import argparse import datetime @@ -22,6 +21,7 @@ import mindspore import mindspore.nn as nn from mindspore import context from mindspore.train import Model +from mindspore.dataset import config from mindspore.common import set_seed from mindspore.common.tensor import Tensor from mindspore.context import ParallelMode @@ -36,7 +36,7 @@ from src.logger import get_logger from src.lr_scheduler import LRScheduler from src.dataloader import create_CitySegmentation from src.fast_scnn import FastSCNN, FastSCNNWithLossCell -from src.util import SegmentationMetric, EvalCallBack, apply_eval, TempLoss +from src.util import SegmentationMetric, EvalCallBack, TempLoss def parse_args(): @@ -106,7 +106,7 @@ def train(): if args.is_distributed: assert args.device_target == "Ascend" context.set_context(device_id=device_id) - init() + init("hccl") args.rank = get_rank() args.group_size = get_group_size() device_num = args.group_size @@ -116,6 +116,7 @@ def train(): if args.device_target in ["Ascend", "GPU"]: context.set_context(device_id=device_id) + config.set_enable_shared_mem(False) #we may get OOM when it set to 'True' # select for master rank save ckpt or all rank save, compatible for model parallel args.rank_save_ckpt_flag = 0 if args.is_save_on_master: @@ -140,17 +141,14 @@ def train(): args.logger.info("copying dataset finished....") args.dataset = 'cache/dataset/' - train_dataset, train_dataset_len = create_CitySegmentation(args, data_path=args.dataset, \ + train_dataset, args.steps_per_epoch = create_CitySegmentation(args, data_path=args.dataset, \ split=args.train_split, mode='train', transform=input_transform, \ base_size=args.base_size, crop_size=args.crop_size, batch_size=args.batch_size, \ device_num=args.group_size, rank=args.rank, shuffle=True) - args.steps_per_epoch = math.ceil(train_dataset_len / args.batch_size / args.group_size) - # create network f_model = FastSCNN(num_classes=19, aux=args.aux) - # resume checkpoint if needed # resume checkpoint if needed if args.resume_path: if args.use_modelarts: @@ -181,7 +179,7 @@ def train(): # define callbacks if args.rank == 0: - time_cb = TimeMonitor(data_size=train_dataset_len) + time_cb = TimeMonitor(data_size=args.steps_per_epoch) loss_cb = LossMonitor() callbacks = [time_cb, loss_cb] else: @@ -196,7 +194,7 @@ def train(): prefix='rank_'+str(args.rank)) callbacks.append(ckpt_cb) - if args.eval_while_train == 1 and args.rank == 0: + if args.eval_while_train: val_dataset, _ = create_CitySegmentation(args, data_path=args.dataset, \ split='val', mode='val', transform=input_transform, \ @@ -206,8 +204,7 @@ def train(): loss_f = TempLoss() network_eval = Model(f_model, loss_fn=loss_f, metrics={"SegmentationMetric": SegmentationMetric(19)}) - eval_param_dict = {"model": network_eval, "dataset": val_dataset} - eval_cb = EvalCallBack(apply_eval, eval_param_dict, interval=args.eval_steps, + eval_cb = EvalCallBack(network_eval, val_dataset, interval=args.eval_steps, eval_start_epoch=args.eval_start_epoch, save_best_ckpt=True, ckpt_directory=save_dir, besk_ckpt_name="best_map.ckpt", metrics_name=("pixAcc", "mIou"))