diff --git a/cv/detection/ssd/MindSpore/Dockerfile b/cv/detection/ssd/MindSpore/Dockerfile new file mode 100755 index 0000000000000000000000000000000000000000..fcb31f207f23664ca2d60bda9a15463af1042dd9 --- /dev/null +++ b/cv/detection/ssd/MindSpore/Dockerfile @@ -0,0 +1,6 @@ +ARG FROM_IMAGE_NAME +FROM ${FROM_IMAGE_NAME} + +RUN apt install libgl1-mesa-glx -y +COPY requirements.txt . +RUN pip3.7 install -r requirements.txt diff --git a/cv/detection/ssd/MindSpore/README.md b/cv/detection/ssd/MindSpore/README.md new file mode 100755 index 0000000000000000000000000000000000000000..92eb19e788f86f05c9995914af1b092c01d335f4 --- /dev/null +++ b/cv/detection/ssd/MindSpore/README.md @@ -0,0 +1,75 @@ +# SSD +## Model description +SSD discretizes the output space of bounding boxes into a set of default boxes over different aspect ratios and scales per feature map location. At prediction time, the network generates scores for the presence of each object category in each default box and produces adjustments to the box to better match the object shape.Additionally, the network combines predictions from multiple feature maps with different resolutions to naturally handle objects of various sizes. + +[Paper](https://arxiv.org/abs/1512.02325): Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott Reed, Cheng-Yang Fu, Alexander C. Berg.European Conference on Computer Vision (ECCV), 2016 (In press). +## Step 1: Installing +``` +pip3 install -r requirements.txt +pip3 install easydict +``` +## Step 2: Prepare Datasets +download dataset in /home/datasets/cv/coco2017 + +Note that you can run the scripts based on the dataset mentioned in original paper or widely used in relevant domain/network architecture. In the following sections, we will introduce how to run the scripts using the related dataset below. + +Dataset used: [COCO2017]() + +- Dataset size:19G + - Train:18G,118000 images + - Val:1G,5000 images + - Annotations:241M,instances,captions,person_keypoints etc +- Data format:image and json files + - Note:Data will be processed in dataset.py + + Change the `coco_root` and other settings you need in `src/config.py`. The directory structure is as follows: + + ```shell + . + └─coco_dataset + ├─annotations + ├─instance_train2017.json + └─instance_val2017.json + ├─val2017 + └─train2017 + ``` + If your own dataset is used. **Select dataset to other when run script.** + Organize the dataset information into a TXT file, each row in the file is as follows: + + ```shell + train2017/0000001.jpg 0,259,401,459,7 35,28,324,201,2 0,30,59,80,2 + ``` + + Each row is an image annotation which split by space, the first column is a relative path of image, the others are box and class infomations of the format [xmin,ymin,xmax,ymax,class]. We read image from an image path joined by the `image_dir`(dataset directory) and the relative path in `anno_path`(the TXT file path), `image_dir` and `anno_path` are setting in `src/config.py`. +# [Pretrained models](#contents) +Please [resnet50](https://pan.baidu.com/s/1rrhsZqDVmNxR-bCnMPvFIw?pwd=8766) download resnet50.ckpt here +``` +mv resnet50.ckpt ./ckpt +``` + +## Step 3: Training +``` +mpirun -allow-run-as-root -n 8 --output-filename log_output --merge-stderr-to-stdout \ +python3 train.py \ +--run_distribute=True \ +--lr=0.05 \ +--dataset=coco \ +--device_num=8 \ +--loss_scale=1 \ +--device_target="GPU" \ +--epoch_size=60 \ +--config_path=./config/ssd_resnet50_fpn_config_gpu.yaml \ +--output_path './output' > log.txt 2>&1 & +``` +### [Evaluation result] +## Results on BI-V100 + +| GPUs | per step time | MAP | +|------|-------------- |-------| +| 1*8 | 0.814s | 0.374 | + +## Results on NV-V100s + +| GPUs | per step time | MAP | +|------|-------------- |-------| +| 1*8 | 0.797s | 0.369 | \ No newline at end of file diff --git a/cv/detection/ssd/MindSpore/ascend310_infer/CMakeLists.txt b/cv/detection/ssd/MindSpore/ascend310_infer/CMakeLists.txt new file mode 100755 index 0000000000000000000000000000000000000000..ee3c85447340e0449ff2b70ed24f60a17e07b2b6 --- /dev/null +++ b/cv/detection/ssd/MindSpore/ascend310_infer/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.14.1) +project(Ascend310Infer) +add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -std=c++17 -Werror -Wall -fPIE -Wl,--allow-shlib-undefined") +set(PROJECT_SRC_ROOT ${CMAKE_CURRENT_LIST_DIR}/) +option(MINDSPORE_PATH "mindspore install path" "") +include_directories(${MINDSPORE_PATH}) +include_directories(${MINDSPORE_PATH}/include) +include_directories(${PROJECT_SRC_ROOT}) +find_library(MS_LIB libmindspore.so ${MINDSPORE_PATH}/lib) +file(GLOB_RECURSE MD_LIB ${MINDSPORE_PATH}/_c_dataengine*) + +add_executable(main src/main.cc src/utils.cc) +target_link_libraries(main ${MS_LIB} ${MD_LIB} gflags) diff --git a/cv/detection/ssd/MindSpore/ascend310_infer/aipp.cfg b/cv/detection/ssd/MindSpore/ascend310_infer/aipp.cfg new file mode 100755 index 0000000000000000000000000000000000000000..363d5d36fd1f24e3a6e880745d7150076f777bd0 --- /dev/null +++ b/cv/detection/ssd/MindSpore/ascend310_infer/aipp.cfg @@ -0,0 +1,26 @@ +aipp_op { + aipp_mode : static + input_format : YUV420SP_U8 + related_input_rank : 0 + csc_switch : true + rbuv_swap_switch : false + matrix_r0c0 : 256 + matrix_r0c1 : 0 + matrix_r0c2 : 359 + matrix_r1c0 : 256 + matrix_r1c1 : -88 + matrix_r1c2 : -183 + matrix_r2c0 : 256 + matrix_r2c1 : 454 + matrix_r2c2 : 0 + input_bias_0 : 0 + input_bias_1 : 128 + input_bias_2 : 128 + + mean_chn_0 : 124 + mean_chn_1 : 117 + mean_chn_2 : 104 + var_reci_chn_0 : 0.0171247538316637 + var_reci_chn_1 : 0.0175070028011204 + var_reci_chn_2 : 0.0174291938997821 +} \ No newline at end of file diff --git a/cv/detection/ssd/MindSpore/ascend310_infer/build.sh b/cv/detection/ssd/MindSpore/ascend310_infer/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..713d7f657ddfa5f75b069351c55f8447f77c72d0 --- /dev/null +++ b/cv/detection/ssd/MindSpore/ascend310_infer/build.sh @@ -0,0 +1,29 @@ +#!/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. +# ============================================================================ +if [ -d out ]; then + rm -rf out +fi + +mkdir out +cd out || exit + +if [ -f "Makefile" ]; then + make clean +fi + +cmake .. \ + -DMINDSPORE_PATH="`pip show mindspore-ascend | grep Location | awk '{print $2"/mindspore"}' | xargs realpath`" +make diff --git a/cv/detection/ssd/MindSpore/ascend310_infer/inc/utils.h b/cv/detection/ssd/MindSpore/ascend310_infer/inc/utils.h new file mode 100755 index 0000000000000000000000000000000000000000..efebe03a8c1179f5a1f9d5f7ee07e0352a9937c6 --- /dev/null +++ b/cv/detection/ssd/MindSpore/ascend310_infer/inc/utils.h @@ -0,0 +1,32 @@ +/** + * 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 MINDSPORE_INFERENCE_UTILS_H_ +#define MINDSPORE_INFERENCE_UTILS_H_ + +#include +#include +#include +#include +#include +#include "include/api/types.h" + +std::vector GetAllFiles(std::string_view dirName); +DIR *OpenDir(std::string_view dirName); +std::string RealPath(std::string_view path); +mindspore::MSTensor ReadFileToTensor(const std::string &file); +int WriteResult(const std::string& imageFile, const std::vector &outputs); +#endif diff --git a/cv/detection/ssd/MindSpore/ascend310_infer/src/main.cc b/cv/detection/ssd/MindSpore/ascend310_infer/src/main.cc new file mode 100755 index 0000000000000000000000000000000000000000..967635de2f57695d29739ae462300daaac5d3621 --- /dev/null +++ b/cv/detection/ssd/MindSpore/ascend310_infer/src/main.cc @@ -0,0 +1,164 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/api/model.h" +#include "include/api/context.h" +#include "include/api/types.h" +#include "include/api/serialization.h" +#include "include/dataset/vision_ascend.h" +#include "include/dataset/execute.h" +#include "include/dataset/vision.h" +#include "inc/utils.h" + +using mindspore::Context; +using mindspore::Serialization; +using mindspore::Model; +using mindspore::Status; +using mindspore::ModelType; +using mindspore::GraphCell; +using mindspore::kSuccess; +using mindspore::MSTensor; +using mindspore::dataset::Execute; +using mindspore::dataset::TensorTransform; +using mindspore::dataset::vision::DvppDecodeResizeJpeg; +using mindspore::dataset::vision::Resize; +using mindspore::dataset::vision::HWC2CHW; +using mindspore::dataset::vision::Normalize; +using mindspore::dataset::vision::Decode; + +DEFINE_string(mindir_path, "", "mindir path"); +DEFINE_string(dataset_path, ".", "dataset path"); +DEFINE_int32(device_id, 0, "device id"); +DEFINE_string(aipp_path, "./aipp.cfg", "aipp path"); +DEFINE_string(cpu_dvpp, "DVPP", "cpu or dvpp process"); +DEFINE_int32(image_height, 640, "image height"); +DEFINE_int32(image_width, 640, "image width"); + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (RealPath(FLAGS_mindir_path).empty()) { + std::cout << "Invalid mindir" << std::endl; + return 1; + } + + auto context = std::make_shared(); + auto ascend310 = std::make_shared(); + ascend310->SetDeviceID(FLAGS_device_id); + context->MutableDeviceInfo().push_back(ascend310); + mindspore::Graph graph; + Serialization::Load(FLAGS_mindir_path, ModelType::kMindIR, &graph); + if (FLAGS_cpu_dvpp == "DVPP") { + if (RealPath(FLAGS_aipp_path).empty()) { + std::cout << "Invalid aipp path" << std::endl; + return 1; + } else { + ascend310->SetInsertOpConfigPath(FLAGS_aipp_path); + } + } + + Model model; + Status ret = model.Build(GraphCell(graph), context); + if (ret != kSuccess) { + std::cout << "ERROR: Build failed." << std::endl; + return 1; + } + + auto all_files = GetAllFiles(FLAGS_dataset_path); + if (all_files.empty()) { + std::cout << "ERROR: no input data." << std::endl; + return 1; + } + + std::map costTime_map; + size_t size = all_files.size(); + + for (size_t i = 0; i < size; ++i) { + struct timeval start = {0}; + struct timeval end = {0}; + double startTimeMs; + double endTimeMs; + std::vector inputs; + std::vector outputs; + std::cout << "Start predict input files:" << all_files[i] << std::endl; + if (FLAGS_cpu_dvpp == "DVPP") { + auto resizeShape = {static_cast (FLAGS_image_height), static_cast (FLAGS_image_width)}; + Execute resize_op(std::shared_ptr(new DvppDecodeResizeJpeg(resizeShape))); + auto imgDvpp = std::make_shared(); + resize_op(ReadFileToTensor(all_files[i]), imgDvpp.get()); + inputs.emplace_back(imgDvpp->Name(), imgDvpp->DataType(), imgDvpp->Shape(), + imgDvpp->Data().get(), imgDvpp->DataSize()); + } else { + std::shared_ptr decode(new Decode()); + std::shared_ptr hwc2chw(new HWC2CHW()); + std::shared_ptr normalize( + new Normalize({123.675, 116.28, 103.53}, {58.395, 57.120, 57.375})); + auto resizeShape = {FLAGS_image_height, FLAGS_image_width}; + std::shared_ptr resize(new Resize(resizeShape)); + Execute composeDecode({decode, resize, normalize, hwc2chw}); + auto img = MSTensor(); + auto image = ReadFileToTensor(all_files[i]); + composeDecode(image, &img); + std::vector model_inputs = model.GetInputs(); + if (model_inputs.empty()) { + std::cout << "Invalid model, inputs is empty." << std::endl; + return 1; + } + inputs.emplace_back(model_inputs[0].Name(), model_inputs[0].DataType(), model_inputs[0].Shape(), + img.Data().get(), img.DataSize()); + } + + gettimeofday(&start, nullptr); + ret = model.Predict(inputs, &outputs); + gettimeofday(&end, nullptr); + if (ret != kSuccess) { + std::cout << "Predict " << all_files[i] << " failed." << std::endl; + return 1; + } + startTimeMs = (1.0 * start.tv_sec * 1000000 + start.tv_usec) / 1000; + endTimeMs = (1.0 * end.tv_sec * 1000000 + end.tv_usec) / 1000; + costTime_map.insert(std::pair(startTimeMs, endTimeMs)); + WriteResult(all_files[i], outputs); + } + double average = 0.0; + int inferCount = 0; + + for (auto iter = costTime_map.begin(); iter != costTime_map.end(); iter++) { + double diff = 0.0; + diff = iter->second - iter->first; + average += diff; + inferCount++; + } + average = average / inferCount; + std::stringstream timeCost; + timeCost << "NN inference cost average time: "<< average << " ms of infer_count " << inferCount << std::endl; + std::cout << "NN inference cost average time: "<< average << "ms of infer_count " << inferCount << std::endl; + std::string fileName = "./time_Result" + std::string("/test_perform_static.txt"); + std::ofstream fileStream(fileName.c_str(), std::ios::trunc); + fileStream << timeCost.str(); + fileStream.close(); + costTime_map.clear(); + return 0; +} diff --git a/cv/detection/ssd/MindSpore/ascend310_infer/src/utils.cc b/cv/detection/ssd/MindSpore/ascend310_infer/src/utils.cc new file mode 100755 index 0000000000000000000000000000000000000000..c947e4d5f451b90bd4728aa3a92c4cfab174f5e6 --- /dev/null +++ b/cv/detection/ssd/MindSpore/ascend310_infer/src/utils.cc @@ -0,0 +1,129 @@ +/** + * 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 +#include +#include +#include "inc/utils.h" + +using mindspore::MSTensor; +using mindspore::DataType; + +std::vector GetAllFiles(std::string_view dirName) { + struct dirent *filename; + DIR *dir = OpenDir(dirName); + if (dir == nullptr) { + return {}; + } + std::vector res; + while ((filename = readdir(dir)) != nullptr) { + std::string dName = std::string(filename->d_name); + if (dName == "." || dName == ".." || filename->d_type != DT_REG) { + continue; + } + res.emplace_back(std::string(dirName) + "/" + filename->d_name); + } + std::sort(res.begin(), res.end()); + for (auto &f : res) { + std::cout << "image file: " << f << std::endl; + } + return res; +} + +int WriteResult(const std::string& imageFile, const std::vector &outputs) { + std::string homePath = "./result_Files"; + for (size_t i = 0; i < outputs.size(); ++i) { + size_t outputSize; + std::shared_ptr netOutput; + netOutput = outputs[i].Data(); + outputSize = outputs[i].DataSize(); + int pos = imageFile.rfind('/'); + std::string fileName(imageFile, pos + 1); + fileName.replace(fileName.find('.'), fileName.size() - fileName.find('.'), '_' + std::to_string(i) + ".bin"); + std::string outFileName = homePath + "/" + fileName; + FILE * outputFile = fopen(outFileName.c_str(), "wb"); + fwrite(netOutput.get(), outputSize, sizeof(char), outputFile); + fclose(outputFile); + outputFile = nullptr; + } + return 0; +} + +mindspore::MSTensor ReadFileToTensor(const std::string &file) { + if (file.empty()) { + std::cout << "Pointer file is nullptr" << std::endl; + return mindspore::MSTensor(); + } + + std::ifstream ifs(file); + if (!ifs.good()) { + std::cout << "File: " << file << " is not exist" << std::endl; + return mindspore::MSTensor(); + } + + if (!ifs.is_open()) { + std::cout << "File: " << file << "open failed" << std::endl; + return mindspore::MSTensor(); + } + + ifs.seekg(0, std::ios::end); + size_t size = ifs.tellg(); + mindspore::MSTensor buffer(file, mindspore::DataType::kNumberTypeUInt8, {static_cast(size)}, nullptr, size); + + ifs.seekg(0, std::ios::beg); + ifs.read(reinterpret_cast(buffer.MutableData()), size); + ifs.close(); + + return buffer; +} + + +DIR *OpenDir(std::string_view dirName) { + if (dirName.empty()) { + std::cout << " dirName is null ! " << std::endl; + return nullptr; + } + std::string realPath = RealPath(dirName); + struct stat s; + lstat(realPath.c_str(), &s); + if (!S_ISDIR(s.st_mode)) { + std::cout << "dirName is not a valid directory !" << std::endl; + return nullptr; + } + DIR *dir; + dir = opendir(realPath.c_str()); + if (dir == nullptr) { + std::cout << "Can not open dir " << dirName << std::endl; + return nullptr; + } + std::cout << "Successfully opened the dir " << dirName << std::endl; + return dir; +} + +std::string RealPath(std::string_view path) { + char realPathMem[PATH_MAX] = {0}; + char *realPathRet = nullptr; + realPathRet = realpath(path.data(), realPathMem); + + if (realPathRet == nullptr) { + std::cout << "File: " << path << " is not exist."; + return ""; + } + + std::string realPath(realPathMem); + std::cout << path << " realpath is: " << realPath << std::endl; + return realPath; +} diff --git a/cv/detection/ssd/MindSpore/config/ssd300_config.yaml b/cv/detection/ssd/MindSpore/config/ssd300_config.yaml new file mode 100755 index 0000000000000000000000000000000000000000..1310d22251b5f0b7f5f5aa78d1222bae130e23da --- /dev/null +++ b/cv/detection/ssd/MindSpore/config/ssd300_config.yaml @@ -0,0 +1,126 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +run_distribute: False +enable_profiling: False +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path/" +device_target: "Ascend" +checkpoint_path: "./checkpoint/" +checkpoint_file_path: "ssd-500_458.ckpt" + +# ============================================================================== +# Training options +model_name: "ssd300" +img_shape: [300, 300] +num_ssd_boxes: 1917 +match_threshold: 0.5 +nms_threshold: 0.6 +min_score: 0.1 +max_boxes: 100 +all_reduce_fusion_config: [29, 58, 89] + +# learing rate settings +lr_init: 0.001 +lr_end_rate: 0.001 +warmup_epochs: 2 +momentum: 0.9 +weight_decay: 0.00015 +ssd_vgg_bn: False +pretrain_vgg_bn: False + + +# network +num_default: [3, 6, 6, 6, 6, 6] +extras_in_channels: [256, 576, 1280, 512, 256, 256] +extras_out_channels: [576, 1280, 512, 256, 256, 128] +extras_strides: [1, 1, 2, 2, 2, 2] +extras_ratio: [0.2, 0.2, 0.2, 0.25, 0.5, 0.25] +feature_size: [19, 10, 5, 3, 2, 1] +min_scale: 0.2 +max_scale: 0.95 +aspect_ratios: [[], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]] +steps: [16, 32, 64, 100, 150, 300] +prior_scaling: [0.1, 0.2] +gamma: 2.0 +alpha: 0.75 + +dataset: "coco" +lr: 0.05 +mode_sink: "sink" +device_id: 0 +device_num: 1 +epoch_size: 500 +batch_size: 32 +loss_scale: 1024 +pre_trained: "" +pre_trained_epoch_size: 0 +save_checkpoint_epochs: 10 +only_create_dataset: False +eval_start_epoch: 40 +eval_interval: 1 +run_eval: False +filter_weight: False +freeze_layer: None +save_best_ckpt: True + +result_path: "" +img_path: "" +drop: False + +# `mindrecord_dir` and `coco_root` are better to use absolute path. +feature_extractor_base_param: "" +checkpoint_filter_list: ['multi_loc_layers', 'multi_cls_layers'] +mindrecord_dir: "MindRecord_COCO" +coco_root: "coco_ori" +train_data_type: "train2017" +val_data_type: "val2017" +instances_set: "annotations/instances_{}.json" +classes: ['background', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', + 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', + 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', + 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', + 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', + 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', + 'kite', 'baseball bat', 'baseball glove', 'skateboard', + 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', + 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', + 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', + 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', + 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', + 'teddy bear', 'hair drier', 'toothbrush'] +num_classes: 81 +# The annotation.json position of voc validation dataset. +voc_json: "annotations/voc_instances_val.json" +# voc original dataset. +voc_root: "/data/voc_dataset" +# if coco or voc used, `image_dir` and `anno_path` are useless. +image_dir: "" +anno_path: "" +file_name: "ssd" +file_format: 'MINDIR' + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Dataset url for obs" +train_url: "Training output url for obs" +checkpoint_url: "The location of checkpoint for obs" +data_path: "Dataset path for local" +output_path: "Training output path for local" +load_path: "The location of checkpoint for obs" +device_target: "Target device type, available: [Ascend, GPU, CPU]" +enable_profiling: "Whether enable profiling while training, default: False" +num_classes: "Class for dataset" +batch_size: "Batch size for training and evaluation" +epoch_size: "Total training epochs." +keep_checkpoint_max: "keep the last keep_checkpoint_max checkpoint" +checkpoint_path: "The location of the checkpoint file." +checkpoint_file_path: "The location of the checkpoint file." diff --git a/cv/detection/ssd/MindSpore/config/ssd300_config_gpu.yaml b/cv/detection/ssd/MindSpore/config/ssd300_config_gpu.yaml new file mode 100755 index 0000000000000000000000000000000000000000..80abf1b12ac9111cc3cf6b0c37a45ccb7bad2de6 --- /dev/null +++ b/cv/detection/ssd/MindSpore/config/ssd300_config_gpu.yaml @@ -0,0 +1,127 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +run_distribute: False +enable_profiling: False +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path/" +device_target: "GPU" +checkpoint_path: "./checkpoint/" +checkpoint_file_path: "ssd-500_458.ckpt" + +# ============================================================================== +# Training options +model_name: "ssd300" +img_shape: [300, 300] +num_ssd_boxes: 1917 +match_threshold: 0.5 +nms_threshold: 0.6 +min_score: 0.1 +max_boxes: 100 +all_reduce_fusion_config: [29, 58, 89] +use_float16: True + +# learing rate settings +lr_init: 0.001 +lr_end_rate: 0.001 +warmup_epochs: 2 +momentum: 0.9 +weight_decay: 0.00015 +ssd_vgg_bn: False +pretrain_vgg_bn: False + + +# network +num_default: [3, 6, 6, 6, 6, 6] +extras_in_channels: [256, 576, 1280, 512, 256, 256] +extras_out_channels: [576, 1280, 512, 256, 256, 128] +extras_strides: [1, 1, 2, 2, 2, 2] +extras_ratio: [0.2, 0.2, 0.2, 0.25, 0.5, 0.25] +feature_size: [19, 10, 5, 3, 2, 1] +min_scale: 0.2 +max_scale: 0.95 +aspect_ratios: [[], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]] +steps: [16, 32, 64, 100, 150, 300] +prior_scaling: [0.1, 0.2] +gamma: 2.0 +alpha: 0.75 + +dataset: "coco" +lr: 0.05 +mode_sink: "sink" +device_id: 0 +device_num: 1 +epoch_size: 500 +batch_size: 32 +loss_scale: 1024 +pre_trained: "" +pre_trained_epoch_size: 0 +save_checkpoint_epochs: 10 +only_create_dataset: False +eval_start_epoch: 40 +eval_interval: 1 +run_eval: False +filter_weight: False +freeze_layer: None +save_best_ckpt: True + +result_path: "" +img_path: "" +drop: False + +# `mindrecord_dir` and `coco_root` are better to use absolute path. +feature_extractor_base_param: "" +checkpoint_filter_list: ['multi_loc_layers', 'multi_cls_layers'] +mindrecord_dir: "MindRecord_COCO" +coco_root: "coco_ori" +train_data_type: "train2017" +val_data_type: "val2017" +instances_set: "annotations/instances_{}.json" +classes: ['background', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', + 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', + 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', + 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', + 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', + 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', + 'kite', 'baseball bat', 'baseball glove', 'skateboard', + 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', + 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', + 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', + 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', + 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', + 'teddy bear', 'hair drier', 'toothbrush'] +num_classes: 81 +# The annotation.json position of voc validation dataset. +voc_json: "annotations/voc_instances_val.json" +# voc original dataset. +voc_root: "/data/voc_dataset" +# if coco or voc used, `image_dir` and `anno_path` are useless. +image_dir: "" +anno_path: "" +file_name: "ssd" +file_format: 'MINDIR' + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Dataset url for obs" +train_url: "Training output url for obs" +checkpoint_url: "The location of checkpoint for obs" +data_path: "Dataset path for local" +output_path: "Training output path for local" +load_path: "The location of checkpoint for obs" +device_target: "Target device type, available: [Ascend, GPU, CPU]" +enable_profiling: "Whether enable profiling while training, default: False" +num_classes: "Class for dataset" +batch_size: "Batch size for training and evaluation" +epoch_size: "Total training epochs." +keep_checkpoint_max: "keep the last keep_checkpoint_max checkpoint" +checkpoint_path: "The location of the checkpoint file." +checkpoint_file_path: "The location of the checkpoint file." diff --git a/cv/detection/ssd/MindSpore/config/ssd_mobilenet_v1_300_config_gpu.yaml b/cv/detection/ssd/MindSpore/config/ssd_mobilenet_v1_300_config_gpu.yaml new file mode 100755 index 0000000000000000000000000000000000000000..4fe82126ebc851c4ed6dffcde4621e9f190ec377 --- /dev/null +++ b/cv/detection/ssd/MindSpore/config/ssd_mobilenet_v1_300_config_gpu.yaml @@ -0,0 +1,129 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +run_distribute: False +enable_profiling: False +data_path: "/DATA_1/cyf" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path/" +device_target: "GPU" +checkpoint_path: "./checkpoint/" +checkpoint_file_path: "ssd-500_458.ckpt" + +# ============================================================================== +# Training options +model_name: "ssd_mobilenet_v1" +img_shape: [300, 300] +num_ssd_boxes: 1917 +match_threshold: 0.5 +nms_threshold: 0.6 +min_score: 0.1 +max_boxes: 100 +all_reduce_fusion_config: [29, 58, 89] +use_float16: True + +# learing rate settings +lr_init: 0.001 +lr_end_rate: 0.001 +warmup_epochs: 2 +momentum: 0.9 +weight_decay: 0.00015 +ssd_vgg_bn: False +pretrain_vgg_bn: False + + +# network +num_addition_layers: 4 +extras_strides: [1, 1, 2, 2, 2, 2] +num_default: [3, 6, 6, 6, 6, 6] +extras_in_channels: [256, 512, 1024, 512, 256, 256] +extras_out_channels: [512, 1024, 512, 256, 256, 128] +extras_strides: [1, 1, 2, 2, 2, 2] +extras_ratio: [0.2, 0.2, 0.2, 0.25, 0.5, 0.25] +feature_size: [19, 10, 5, 3, 2, 1] +min_scale: 0.2 +max_scale: 0.95 +aspect_ratios: [[], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]] +steps: [16, 32, 64, 100, 150, 300] +prior_scaling: [0.1, 0.2] +gamma: 2.0 +alpha: 0.75 + +dataset: "coco" +lr: 0.05 +mode_sink: "sink" +device_id: 0 +device_num: 1 +epoch_size: 500 +batch_size: 32 +loss_scale: 1024 +pre_trained: "" +pre_trained_epoch_size: 0 +save_checkpoint_epochs: 10 +only_create_dataset: False +eval_start_epoch: 40 +eval_interval: 1 +run_eval: False +filter_weight: False +freeze_layer: None +save_best_ckpt: True + +result_path: "" +img_path: "" +drop: False + +# `mindrecord_dir` and `coco_root` are better to use absolute path. +feature_extractor_base_param: "" +checkpoint_filter_list: ['multi_loc_layers', 'multi_cls_layers'] +mindrecord_dir: "/DATA_1/cyf/MindRecord_COCO" +coco_root: "/DATA_1/cyf/" +train_data_type: "train2017" +val_data_type: "val2017" +instances_set: "annotations/instances_{}.json" +classes: ['background', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', + 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', + 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', + 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', + 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', + 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', + 'kite', 'baseball bat', 'baseball glove', 'skateboard', + 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', + 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', + 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', + 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', + 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', + 'teddy bear', 'hair drier', 'toothbrush'] +num_classes: 81 +# The annotation.json position of voc validation dataset. +voc_json: "annotations/voc_instances_val.json" +# voc original dataset. +voc_root: "/data/voc_dataset" +# if coco or voc used, `image_dir` and `anno_path` are useless. +image_dir: "" +anno_path: "" +file_name: "ssd" +file_format: 'MINDIR' + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Dataset url for obs" +train_url: "Training output url for obs" +checkpoint_url: "The location of checkpoint for obs" +data_path: "Dataset path for local" +output_path: "Training output path for local" +load_path: "The location of checkpoint for obs" +device_target: "Target device type, available: [Ascend, GPU, CPU]" +enable_profiling: "Whether enable profiling while training, default: False" +num_classes: "Class for dataset" +batch_size: "Batch size for training and evaluation" +epoch_size: "Total training epochs." +keep_checkpoint_max: "keep the last keep_checkpoint_max checkpoint" +checkpoint_path: "The location of the checkpoint file." +checkpoint_file_path: "The location of the checkpoint file." diff --git a/cv/detection/ssd/MindSpore/config/ssd_mobilenet_v1_fpn_config.yaml b/cv/detection/ssd/MindSpore/config/ssd_mobilenet_v1_fpn_config.yaml new file mode 100755 index 0000000000000000000000000000000000000000..099ea3758203638f96c6cae7886c69047b21b8f5 --- /dev/null +++ b/cv/detection/ssd/MindSpore/config/ssd_mobilenet_v1_fpn_config.yaml @@ -0,0 +1,129 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +run_distribute: False +enable_profiling: False +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path/" +device_target: "Ascend" +checkpoint_path: "./checkpoint/" +checkpoint_file_path: "mobilenet_v1.ckpt" + +# ============================================================================== +# Training options +model_name: "ssd_mobilenet_v1_fpn" +img_shape: [640, 640] +num_ssd_boxes: -1 +match_threshold: 0.5 +nms_threshold: 0.6 +min_score: 0.1 +max_boxes: 100 +all_reduce_fusion_config: [29, 58, 89] + +# learning rate settings +lr_init: 0.01333 +lr_end_rate: 0.0 +warmup_epochs: 2 +weight_decay: 0.00004 +momentum: 0.9 +ssd_vgg_bn: False +pretrain_vgg_bn: False + +# network +num_default: [6, 6, 6, 6, 6] +extras_in_channels: [256, 512, 1024, 256, 256] +extras_out_channels: [256, 256, 256, 256, 256] +extras_strides: [1, 1, 2, 2, 2, 2] +extras_ratio: [0.2, 0.2, 0.2, 0.25, 0.5, 0.25] +feature_size: [80, 40, 20, 10, 5] +min_scale: 0.2 +max_scale: 0.95 +aspect_ratios: [[2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]] +steps: [8, 16, 32, 64, 128] +prior_scaling: [0.1, 0.2] +gamma: 2.0 +alpha: 0.25 +num_addition_layers: 4 +use_anchor_generator: True +use_global_norm: True + +dataset: "coco" +lr: 0.05 +mode_sink: "sink" +device_id: 0 +device_num: 1 +epoch_size: 500 +batch_size: 32 +loss_scale: 1024 +pre_trained: "" +pre_trained_epoch_size: 0 +save_checkpoint_epochs: 10 +only_create_dataset: False +eval_start_epoch: 40 +eval_interval: 1 +run_eval: False +filter_weight: False +freeze_layer: None +save_best_ckpt: True + +result_path: "" +img_path: "" +drop: False + +# `mindrecord_dir` and `coco_root` are better to use absolute path. +feature_extractor_base_param: "/ckpt/mobilenet_v1.ckpt" +checkpoint_filter_list: ['network.multi_box.cls_layers.0.weight', 'network.multi_box.cls_layers.0.bias', + 'network.multi_box.loc_layers.0.weight', 'network.multi_box.loc_layers.0.bias'] +mindrecord_dir: "MindRecord_COCO" +coco_root: "coco_ori" +train_data_type: "train2017" +val_data_type: "val2017" +instances_set: "annotations/instances_{}.json" +classes: ['background', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', + 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', + 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', + 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', + 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', + 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', + 'kite', 'baseball bat', 'baseball glove', 'skateboard', + 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', + 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', + 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', + 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', + 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', + 'teddy bear', 'hair drier', 'toothbrush'] +num_classes: 81 +# The annotation.json position of voc validation dataset. +voc_json: "annotations/voc_instances_val.json" +# voc original dataset. +voc_root: "/data/voc_dataset" +# if coco or voc used, `image_dir` and `anno_path` are useless. +image_dir: "" +anno_path: "" +file_name: "ssd" +file_format: 'MINDIR' + +--- +# Help description for each configuration +enable_modelarts: 'Whether training on modelarts, default: False' +data_url: 'Dataset url for obs' +train_url: 'Training output url for obs' +checkpoint_url: 'The location of checkpoint for obs' +data_path: 'Dataset path for local' +output_path: 'Training output path for local' +load_path: 'The location of checkpoint for obs' +device_target: 'Target device type, available: [Ascend, GPU, CPU]' +enable_profiling: 'Whether enable profiling while training, default: False' +num_classes: 'Class for dataset' +batch_size: "Batch size for training and evaluation" +epoch_size: "Total training epochs." +keep_checkpoint_max: "keep the last keep_checkpoint_max checkpoint" +checkpoint_path: "The location of the checkpoint file." +checkpoint_file_path: "The location of the checkpoint file." diff --git a/cv/detection/ssd/MindSpore/config/ssd_mobilenet_v1_fpn_config_gpu.yaml b/cv/detection/ssd/MindSpore/config/ssd_mobilenet_v1_fpn_config_gpu.yaml new file mode 100755 index 0000000000000000000000000000000000000000..79d10f00d9112a43fccbd0093237dfbaa9a16094 --- /dev/null +++ b/cv/detection/ssd/MindSpore/config/ssd_mobilenet_v1_fpn_config_gpu.yaml @@ -0,0 +1,130 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +run_distribute: False +enable_profiling: False +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path/" +device_target: "GPU" +checkpoint_path: "./checkpoint/" +checkpoint_file_path: "mobilenet_v1.ckpt" + +# ============================================================================== +# Training options +model_name: "ssd_mobilenet_v1_fpn" +img_shape: [640, 640] +num_ssd_boxes: -1 +match_threshold: 0.5 +nms_threshold: 0.6 +min_score: 0.1 +max_boxes: 100 +all_reduce_fusion_config: [29, 58, 89] +use_float16: False + +# learning rate settings +lr_init: 0.01333 +lr_end_rate: 0.0 +warmup_epochs: 2 +weight_decay: 0.00004 +momentum: 0.9 +ssd_vgg_bn: False +pretrain_vgg_bn: False + +# network +num_default: [6, 6, 6, 6, 6] +extras_in_channels: [256, 512, 1024, 256, 256] +extras_out_channels: [256, 256, 256, 256, 256] +extras_strides: [1, 1, 2, 2, 2, 2] +extras_ratio: [0.2, 0.2, 0.2, 0.25, 0.5, 0.25] +feature_size: [80, 40, 20, 10, 5] +min_scale: 0.2 +max_scale: 0.95 +aspect_ratios: [[2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]] +steps: [8, 16, 32, 64, 128] +prior_scaling: [0.1, 0.2] +gamma: 2.0 +alpha: 0.25 +num_addition_layers: 4 +use_anchor_generator: True +use_global_norm: True + +dataset: "coco" +lr: 0.05 +mode_sink: "sink" +device_id: 0 +device_num: 1 +epoch_size: 500 +batch_size: 16 +loss_scale: 1024 +pre_trained: "" +pre_trained_epoch_size: 0 +save_checkpoint_epochs: 10 +only_create_dataset: False +eval_start_epoch: 40 +eval_interval: 1 +run_eval: False +filter_weight: False +freeze_layer: None +save_best_ckpt: True + +result_path: "" +img_path: "" +drop: False + +# `mindrecord_dir` and `coco_root` are better to use absolute path. +feature_extractor_base_param: "/ckpt/mobilenet_v1.ckpt" +checkpoint_filter_list: ['network.multi_box.cls_layers.0.weight', 'network.multi_box.cls_layers.0.bias', + 'network.multi_box.loc_layers.0.weight', 'network.multi_box.loc_layers.0.bias'] +mindrecord_dir: "MindRecord_COCO" +coco_root: "coco_ori" +train_data_type: "train2017" +val_data_type: "val2017" +instances_set: "annotations/instances_{}.json" +classes: ['background', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', + 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', + 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', + 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', + 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', + 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', + 'kite', 'baseball bat', 'baseball glove', 'skateboard', + 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', + 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', + 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', + 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', + 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', + 'teddy bear', 'hair drier', 'toothbrush'] +num_classes: 81 +# The annotation.json position of voc validation dataset. +voc_json: "annotations/voc_instances_val.json" +# voc original dataset. +voc_root: "/data/voc_dataset" +# if coco or voc used, `image_dir` and `anno_path` are useless. +image_dir: "" +anno_path: "" +file_name: "ssd" +file_format: 'MINDIR' + +--- +# Help description for each configuration +enable_modelarts: 'Whether training on modelarts, default: False' +data_url: 'Dataset url for obs' +train_url: 'Training output url for obs' +checkpoint_url: 'The location of checkpoint for obs' +data_path: 'Dataset path for local' +output_path: 'Training output path for local' +load_path: 'The location of checkpoint for obs' +device_target: 'Target device type, available: [Ascend, GPU, CPU]' +enable_profiling: 'Whether enable profiling while training, default: False' +num_classes: 'Class for dataset' +batch_size: "Batch size for training and evaluation" +epoch_size: "Total training epochs." +keep_checkpoint_max: "keep the last keep_checkpoint_max checkpoint" +checkpoint_path: "The location of the checkpoint file." +checkpoint_file_path: "The location of the checkpoint file." diff --git a/cv/detection/ssd/MindSpore/config/ssd_resnet50_fpn_config.yaml b/cv/detection/ssd/MindSpore/config/ssd_resnet50_fpn_config.yaml new file mode 100755 index 0000000000000000000000000000000000000000..8a093419d3f0f8908bbc687d1e1849ede2ce77a3 --- /dev/null +++ b/cv/detection/ssd/MindSpore/config/ssd_resnet50_fpn_config.yaml @@ -0,0 +1,130 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +run_distribute: False +enable_profiling: False +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path/" +device_target: "Ascend" +checkpoint_path: "./checkpoint/" +checkpoint_file_path: "resnet50.ckpt" + +# ============================================================================== +# Training options +model_name: "ssd_resnet50_fpn" +img_shape: [640, 640] +num_ssd_boxes: -1 +match_threshold: 0.5 +nms_threshold: 0.6 +min_score: 0.1 +max_boxes: 100 +all_reduce_fusion_config: [90, 183, 279] + +# learning rate settings +lr_init: 0.01333 +lr_end_rate: 0.0 +warmup_epochs: 2 +weight_decay: 0.0004 +momentum: 0.9 +ssd_vgg_bn: False +pretrain_vgg_bn: False + +# network +num_default: [6, 6, 6, 6, 6] +extras_in_channels: [256, 512, 1024, 256, 256] +extras_out_channels: [256, 256, 256, 256, 256] +extras_strides: [1, 1, 2, 2, 2, 2] +extras_ratio: [0.2, 0.2, 0.2, 0.25, 0.5, 0.25] +feature_size: [80, 40, 20, 10, 5] +min_scale: 0.2 +max_scale: 0.95 +aspect_ratios: [[2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]] +steps: [8, 16, 32, 64, 128] +prior_scaling: [0.1, 0.2] +gamma: 2.0 +alpha: 0.25 +num_addition_layers: 4 +use_anchor_generator: True +use_global_norm: True +use_float16: True + +dataset: "coco" +lr: 0.05 +mode_sink: "sink" +device_id: 0 +device_num: 1 +epoch_size: 500 +batch_size: 32 +loss_scale: 1024 +pre_trained: "" +pre_trained_epoch_size: 0 +save_checkpoint_epochs: 10 +only_create_dataset: False +eval_start_epoch: 40 +eval_interval: 1 +run_eval: False +filter_weight: False +freeze_layer: None +save_best_ckpt: True + +result_path: "" +img_path: "" +drop: False + +# `mindrecord_dir` and `coco_root` are better to use absolute path. +feature_extractor_base_param: "/ckpt/resnet50.ckpt" +checkpoint_filter_list: ['network.multi_box.cls_layers.0.weight', 'network.multi_box.cls_layers.0.bias', + 'network.multi_box.loc_layers.0.weight', 'network.multi_box.loc_layers.0.bias'] +mindrecord_dir: "MindRecord_COCO" +coco_root: "coco_ori" +train_data_type: "train2017" +val_data_type: "val2017" +instances_set: "annotations/instances_{}.json" +classes: ['background', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', + 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', + 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', + 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', + 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', + 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', + 'kite', 'baseball bat', 'baseball glove', 'skateboard', + 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', + 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', + 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', + 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', + 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', + 'teddy bear', 'hair drier', 'toothbrush'] +num_classes: 81 +# The annotation.json position of voc validation dataset. +voc_json: "annotations/voc_instances_val.json" +# voc original dataset. +voc_root: "/data/voc_dataset" +# if coco or voc used, `image_dir` and `anno_path` are useless. +image_dir: "" +anno_path: "" +file_name: "ssd" +file_format: 'MINDIR' + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Dataset url for obs" +train_url: "Training output url for obs" +checkpoint_url: "The location of checkpoint for obs" +data_path: "Dataset path for local" +output_path: "Training output path for local" +load_path: "The location of checkpoint for obs" +device_target: "Target device type, available: [Ascend, GPU, CPU]" +enable_profiling: "Whether enable profiling while training, default: False" +num_classes: "Class for dataset" +batch_size: "Batch size for training and evaluation" +epoch_size: "Total training epochs." +keep_checkpoint_max: "keep the last keep_checkpoint_max checkpoint" +checkpoint_path: "The location of the checkpoint file." +checkpoint_file_path: "The location of the checkpoint file." diff --git a/cv/detection/ssd/MindSpore/config/ssd_resnet50_fpn_config_gpu.yaml b/cv/detection/ssd/MindSpore/config/ssd_resnet50_fpn_config_gpu.yaml new file mode 100755 index 0000000000000000000000000000000000000000..84019b0902a886657cfb850ef4b42ae4eb504c29 --- /dev/null +++ b/cv/detection/ssd/MindSpore/config/ssd_resnet50_fpn_config_gpu.yaml @@ -0,0 +1,132 @@ +# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +run_distribute: False +enable_profiling: False +data_path: "/home/datasets/cv/" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path/" +device_target: "GPU" +checkpoint_path: "./checkpoint/" +checkpoint_file_path: "resnet50.ckpt" + +# ============================================================================== +# Training options +model_name: "ssd_resnet50_fpn" +img_shape: [640, 640] +num_ssd_boxes: -1 +match_threshold: 0.5 +nms_threshold: 0.6 +min_score: 0.1 +max_boxes: 100 +all_reduce_fusion_config: [90, 183, 279] +use_float16: False + +# learning rate settings +lr_init: 0.01333 +lr_end_rate: 0.0 +warmup_epochs: 2 +weight_decay: 0.0004 +momentum: 0.9 +ssd_vgg_bn: False +pretrain_vgg_bn: False + +# network +num_default: [6, 6, 6, 6, 6] +extras_in_channels: [256, 512, 1024, 256, 256] +extras_out_channels: [256, 256, 256, 256, 256] +extras_strides: [1, 1, 2, 2, 2, 2] +extras_ratio: [0.2, 0.2, 0.2, 0.25, 0.5, 0.25] +feature_size: [80, 40, 20, 10, 5] +min_scale: 0.2 +max_scale: 0.95 +aspect_ratios: [[2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]] +steps: [8, 16, 32, 64, 128] +prior_scaling: [0.1, 0.2] +gamma: 2.0 +alpha: 0.25 +num_addition_layers: 4 +use_anchor_generator: True +use_global_norm: True + +dataset: "coco" +lr: 0.05 +mode_sink: "sink" +device_id: 0 +device_num: 1 +epoch_size: 500 +batch_size: 16 +loss_scale: 1024 +pre_trained: "" +pre_trained_epoch_size: 0 +save_checkpoint_epochs: 10 +only_create_dataset: False +eval_start_epoch: 40 +eval_interval: 1 +run_eval: False +filter_weight: False +freeze_layer: None +save_best_ckpt: True + +result_path: "" +img_path: "" +drop: False + +# `mindrecord_dir` and `coco_root` are better to use absolute path. +feature_extractor_base_param: "./ckpt/resnet50.ckpt" +checkpoint_filter_list: ['network.multi_box.cls_layers.0.weight', 'network.multi_box.cls_layers.0.bias', + 'network.multi_box.loc_layers.0.weight', 'network.multi_box.loc_layers.0.bias'] +mindrecord_dir: "ssd_mindrecord" +coco_root: "coco2017" +train_data_type: "train2017" +val_data_type: "val2017" +instances_set: "annotations/instances_{}.json" +classes: ['background', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', + 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', + 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', + 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', + 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', + 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', + 'kite', 'baseball bat', 'baseball glove', 'skateboard', + 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', + 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', + 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', + 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', + 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', + 'teddy bear', 'hair drier', 'toothbrush'] +num_classes: 81 +# The annotation.json position of voc validation dataset. +voc_json: "annotations/voc_instances_val.json" +# voc original dataset. +voc_root: "/data/voc_dataset" +# if coco or voc used, `image_dir` and `anno_path` are useless. +image_dir: "" +anno_path: "" +file_name: "ssd" +file_format: 'MINDIR' + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Dataset url for obs" +train_url: "Training output url for obs" +checkpoint_url: "The location of checkpoint for obs" +data_path: "Dataset path for local" +output_path: "Training output path for local" +load_path: "The location of checkpoint for obs" +device_target: "Target device type, available: [Ascend, GPU, CPU]" +enable_profiling: "Whether enable profiling while training, default: False" +num_classes: "Class for dataset" +batch_size: "Batch size for training and evaluation" +epoch_size: "Total training epochs." +keep_checkpoint_max: "keep the last keep_checkpoint_max checkpoint" +checkpoint_path: "The location of the checkpoint file." +checkpoint_file_path: "The location of the checkpoint file." diff --git a/cv/detection/ssd/MindSpore/config/ssd_vgg16_config.yaml b/cv/detection/ssd/MindSpore/config/ssd_vgg16_config.yaml new file mode 100755 index 0000000000000000000000000000000000000000..7c214b7aa05f15ccbd3402fe19a61b621712e23f --- /dev/null +++ b/cv/detection/ssd/MindSpore/config/ssd_vgg16_config.yaml @@ -0,0 +1,125 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +run_distribute: False +enable_profiling: False +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path/" +device_target: "Ascend" +checkpoint_path: "./checkpoint/" +checkpoint_file_path: "ssd-500_458.ckpt" + +# ============================================================================== +# Training options +model_name: "ssd_vgg16" +img_shape: [300, 300] +num_ssd_boxes: 7308 +match_threshold: 0.5 +nms_threshold: 0.6 +min_score: 0.1 +max_boxes: 100 +all_reduce_fusion_config: [20, 41, 62] + +# learing rate settings +lr_init: 0.001 +lr_end_rate: 0.001 +warmup_epochs: 2 +momentum: 0.9 +weight_decay: 0.00015 +ssd_vgg_bn: False +pretrain_vgg_bn: False + +# network +num_default: [3, 6, 6, 6, 6, 6] +extras_in_channels: [256, 512, 1024, 512, 256, 256] +extras_out_channels: [512, 1024, 512, 256, 256, 256] +extras_strides: [1, 1, 2, 2, 2, 2] +extras_ratio: [0.2, 0.2, 0.2, 0.25, 0.5, 0.25] +feature_size: [38, 19, 10, 5, 3, 1] +min_scale: 0.2 +max_scale: 0.95 +aspect_ratios: [[], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]] +steps: [8, 16, 32, 64, 100, 300] +prior_scaling: [0.1, 0.2] +gamma: 2.0 +alpha: 0.75 + +dataset: "coco" +lr: 0.05 +mode_sink: "sink" +device_id: 0 +device_num: 1 +epoch_size: 500 +batch_size: 32 +loss_scale: 1024 +pre_trained: "" +pre_trained_epoch_size: 0 +save_checkpoint_epochs: 10 +only_create_dataset: False +eval_start_epoch: 40 +eval_interval: 1 +run_eval: False +filter_weight: False +freeze_layer: None +save_best_ckpt: True + +result_path: "" +img_path: "" +drop: False + +# `mindrecord_dir` and `coco_root` are better to use absolute path. +feature_extractor_base_param: "" +checkpoint_filter_list: ['multi_loc_layers', 'multi_cls_layers'] +mindrecord_dir: "MindRecord_COCO" +coco_root: "coco_ori" +train_data_type: "train2017" +val_data_type: "val2017" +instances_set: "annotations/instances_{}.json" +classes: ['background', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', + 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', + 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', + 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', + 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', + 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', + 'kite', 'baseball bat', 'baseball glove', 'skateboard', + 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', + 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', + 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', + 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', + 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', + 'teddy bear', 'hair drier', 'toothbrush'] +num_classes: 81 +# The annotation.json position of voc validation dataset. +voc_json: "annotations/voc_instances_val.json" +# voc original dataset. +voc_root: "/data/voc_dataset" +# if coco or voc used, `image_dir` and `anno_path` are useless. +image_dir: "" +anno_path: "" +file_name: "ssd" +file_format: 'MINDIR' + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Dataset url for obs" +train_url: "Training output url for obs" +checkpoint_url: "The location of checkpoint for obs" +data_path: "Dataset path for local" +output_path: "Training output path for local" +load_path: "The location of checkpoint for obs" +device_target: "Target device type, available: [Ascend, GPU, CPU]" +enable_profiling: "Whether enable profiling while training, default: False" +num_classes: "Class for dataset" +batch_size: "Batch size for training and evaluation" +epoch_size: "Total training epochs." +keep_checkpoint_max: "keep the last keep_checkpoint_max checkpoint" +checkpoint_path: "The location of the checkpoint file." +checkpoint_file_path: "The location of the checkpoint file." diff --git a/cv/detection/ssd/MindSpore/config/ssd_vgg16_config_gpu.yaml b/cv/detection/ssd/MindSpore/config/ssd_vgg16_config_gpu.yaml new file mode 100755 index 0000000000000000000000000000000000000000..c19ae902b044dbf4fa81bfb0eb2d71769a6f77b5 --- /dev/null +++ b/cv/detection/ssd/MindSpore/config/ssd_vgg16_config_gpu.yaml @@ -0,0 +1,126 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +run_distribute: False +enable_profiling: False +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path/" +device_target: "GPU" +checkpoint_path: "./checkpoint/" +checkpoint_file_path: "ssd-500_458.ckpt" + +# ============================================================================== +# Training options +model_name: "ssd_vgg16" +img_shape: [300, 300] +num_ssd_boxes: 7308 +match_threshold: 0.5 +nms_threshold: 0.6 +min_score: 0.1 +max_boxes: 100 +all_reduce_fusion_config: [] +use_float16: False + +# learing rate settings +lr_init: 0.001 +lr_end_rate: 0.001 +warmup_epochs: 2 +momentum: 0.9 +weight_decay: 0.00015 +ssd_vgg_bn: False +pretrain_vgg_bn: False + +# network +num_default: [3, 6, 6, 6, 6, 6] +extras_in_channels: [256, 512, 1024, 512, 256, 256] +extras_out_channels: [512, 1024, 512, 256, 256, 256] +extras_strides: [1, 1, 2, 2, 2, 2] +extras_ratio: [0.2, 0.2, 0.2, 0.25, 0.5, 0.25] +feature_size: [38, 19, 10, 5, 3, 1] +min_scale: 0.1 +max_scale: 0.95 +aspect_ratios: [[], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]] +steps: [8, 16, 32, 64, 100, 300] +prior_scaling: [0.1, 0.2] +gamma: 2.0 +alpha: 0.75 + +dataset: "coco" +lr: 0.05 +mode_sink: "sink" +device_id: 0 +device_num: 1 +epoch_size: 500 +batch_size: 32 +loss_scale: 1024 +pre_trained: "" +pre_trained_epoch_size: 0 +save_checkpoint_epochs: 10 +only_create_dataset: False +eval_start_epoch: 40 +eval_interval: 1 +run_eval: False +filter_weight: False +freeze_layer: None +save_best_ckpt: True + +result_path: "" +img_path: "" +drop: False + +# `mindrecord_dir` and `coco_root` are better to use absolute path. +feature_extractor_base_param: "" +checkpoint_filter_list: ['multi_loc_layers', 'multi_cls_layers'] +mindrecord_dir: "MindRecord_COCO" +coco_root: "coco_ori" +train_data_type: "train2017" +val_data_type: "val2017" +instances_set: "annotations/instances_{}.json" +classes: ['background', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', + 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', + 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', + 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', + 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', + 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', + 'kite', 'baseball bat', 'baseball glove', 'skateboard', + 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', + 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', + 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', + 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', + 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', + 'teddy bear', 'hair drier', 'toothbrush'] +num_classes: 81 +# The annotation.json position of voc validation dataset. +voc_json: "annotations/voc_instances_val.json" +# voc original dataset. +voc_root: "/data/voc_dataset" +# if coco or voc used, `image_dir` and `anno_path` are useless. +image_dir: "" +anno_path: "" +file_name: "ssd" +file_format: 'MINDIR' + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Dataset url for obs" +train_url: "Training output url for obs" +checkpoint_url: "The location of checkpoint for obs" +data_path: "Dataset path for local" +output_path: "Training output path for local" +load_path: "The location of checkpoint for obs" +device_target: "Target device type, available: [Ascend, GPU, CPU]" +enable_profiling: "Whether enable profiling while training, default: False" +num_classes: "Class for dataset" +batch_size: "Batch size for training and evaluation" +epoch_size: "Total training epochs." +keep_checkpoint_max: "keep the last keep_checkpoint_max checkpoint" +checkpoint_path: "The location of the checkpoint file." +checkpoint_file_path: "The location of the checkpoint file." diff --git a/cv/detection/ssd/MindSpore/eval.py b/cv/detection/ssd/MindSpore/eval.py new file mode 100755 index 0000000000000000000000000000000000000000..0fe0e6ce47e67ea23337ad40af4ca4a16fad90a3 --- /dev/null +++ b/cv/detection/ssd/MindSpore/eval.py @@ -0,0 +1,88 @@ +# Copyright 2020-2022 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 for SSD""" + +import os +import mindspore as ms +from mindspore import Tensor +from src.ssd import SSD300, SsdInferWithDecoder, ssd_mobilenet_v2, ssd_mobilenet_v1_fpn, ssd_mobilenet_v1, ssd_resnet50_fpn, ssd_vgg16 +from src.dataset import create_ssd_dataset, create_mindrecord +from src.eval_utils import apply_eval +from src.box_utils import default_boxes +from src.model_utils.config import config +from src.model_utils.moxing_adapter import moxing_wrapper + +def ssd_eval(dataset_path, ckpt_path, anno_json): + """SSD evaluation.""" + batch_size = 1 + ds = create_ssd_dataset(dataset_path, batch_size=batch_size, + is_training=False, use_multiprocessing=False) + if config.model_name == "ssd300": + net = SSD300(ssd_mobilenet_v2(), config, is_training=False) + elif config.model_name == "ssd_vgg16": + net = ssd_vgg16(config=config) + elif config.model_name == "ssd_mobilenet_v1_fpn": + net = ssd_mobilenet_v1_fpn(config=config) + elif config.model_name == "ssd_resnet50_fpn": + net = ssd_resnet50_fpn(config=config) + elif config.model_name == "ssd_mobilenet_v1": + net = ssd_mobilenet_v1(config=config) + else: + raise ValueError(f'config.model: {config.model_name} is not supported') + net = SsdInferWithDecoder(net, Tensor(default_boxes), config) + + print("Load Checkpoint!") + param_dict = ms.load_checkpoint(ckpt_path) + net.init_parameters_data() + ms.load_param_into_net(net, param_dict) + + net.set_train(False) + total = ds.get_dataset_size() * batch_size + print("\n========================================\n") + print("total images num: ", total) + print("Processing, please wait a moment.") + eval_param_dict = {"net": net, "dataset": ds, "anno_json": anno_json} + mAP = apply_eval(eval_param_dict) + print("\n========================================\n") + print(f"mAP: {mAP}") + +@moxing_wrapper() +def eval_net(): + if hasattr(config, 'num_ssd_boxes') and config.num_ssd_boxes == -1: + num = 0 + h, w = config.img_shape + for i in range(len(config.steps)): + num += (h // config.steps[i]) * (w // config.steps[i]) * config.num_default[i] + config.num_ssd_boxes = num + + if config.dataset == "coco": + coco_root = os.path.join(config.data_path, config.coco_root) + json_path = os.path.join(coco_root, config.instances_set.format(config.val_data_type)) + elif config.dataset == "voc": + voc_root = os.path.join(config.data_path, config.voc_root) + json_path = os.path.join(voc_root, config.voc_json) + else: + raise ValueError('SSD eval only support dataset mode is coco and voc!') + + ms.set_context(mode=ms.GRAPH_MODE, device_target=config.device_target, device_id=config.device_id) + + mindrecord_file = create_mindrecord(config.dataset, "ssd_eval.mindrecord", False) + + print("Start Eval!") + ssd_eval(mindrecord_file, config.checkpoint_file_path, json_path) + +if __name__ == '__main__': + eval_net() diff --git a/cv/detection/ssd/MindSpore/eval_onnx.py b/cv/detection/ssd/MindSpore/eval_onnx.py new file mode 100755 index 0000000000000000000000000000000000000000..4ab00f3d71405d7e9a18d56b34f9a8f40bc92c21 --- /dev/null +++ b/cv/detection/ssd/MindSpore/eval_onnx.py @@ -0,0 +1,107 @@ +# Copyright 2022 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. +# ============================================================================ +"""Run evaluation for a model exported to ONNX""" + +import os +import numpy as np +import onnxruntime as ort +from mindspore import context + +from src.dataset import create_ssd_dataset, create_mindrecord +from src.eval_utils import COCOMetrics +from src.model_utils.config import config + + +def create_session(checkpoint_path, target_device): + """Create onnxruntime session""" + if target_device == 'GPU': + providers = ['CUDAExecutionProvider'] + elif target_device == 'CPU': + providers = ['CPUExecutionProvider'] + else: + raise ValueError( + f'Unsupported target device {target_device}, ' + f'Expected one of: "CPU", "GPU"' + ) + session = ort.InferenceSession(checkpoint_path, providers=providers) + input_name = session.get_inputs()[0].name + return session, input_name + + +def ssd_eval(dataset_path, ckpt_path, anno_json): + """SSD evaluation.""" + # Silence false positive + # pylint: disable=unexpected-keyword-arg + ds = create_ssd_dataset(dataset_path, batch_size=config.batch_size, + is_training=False, use_multiprocessing=False) + + session, input_name = create_session(ckpt_path, config.device_target) + total = ds.get_dataset_size() * config.batch_size + print("\n========================================\n") + print("total images num: ", total) + print("Processing, please wait a moment.") + + metrics = COCOMetrics(anno_json=anno_json, + classes=config.classes, + num_classes=config.num_classes, + max_boxes=config.max_boxes, + nms_threshold=config.nms_threshold, + min_score=config.min_score) + + for batch in ds.create_dict_iterator(output_numpy=True, num_epochs=1): + img_id = batch['img_id'] + img_np = batch['image'] + image_shape = batch['image_shape'] + + output = session.run(None, {input_name: batch['image']}) + + for batch_idx in range(img_np.shape[0]): + pred = {"boxes": output[0][batch_idx], + "box_scores": output[1][batch_idx], + "img_id": int(np.squeeze(img_id[batch_idx])), + "image_shape": image_shape[batch_idx] + } + metrics.update(pred) + print(f"mAP: {metrics.get_metrics()}") + + +def eval_net(): + """Eval ssd model""" + if hasattr(config, 'num_ssd_boxes') and config.num_ssd_boxes == -1: + num = 0 + h, w = config.img_shape + for i in range(len(config.steps)): + num += (h // config.steps[i]) * (w // config.steps[i]) * config.num_default[i] + config.num_ssd_boxes = num + + if config.dataset == "coco": + coco_root = os.path.join(config.data_path, config.coco_root) + json_path = os.path.join(coco_root, config.instances_set.format(config.val_data_type)) + elif config.dataset == "voc": + voc_root = os.path.join(config.data_path, config.voc_root) + json_path = os.path.join(voc_root, config.voc_json) + else: + raise ValueError('SSD eval only support dataset mode is coco and voc!') + + context.set_context(mode=context.GRAPH_MODE, device_target=config.device_target, device_id=config.device_id) + + mindrecord_file = create_mindrecord(config.dataset, "ssd_eval.mindrecord", False) + + print("Start Eval!") + ssd_eval(mindrecord_file, config.file_name, json_path) + + +if __name__ == '__main__': + eval_net() diff --git a/cv/detection/ssd/MindSpore/export.py b/cv/detection/ssd/MindSpore/export.py new file mode 100755 index 0000000000000000000000000000000000000000..9917d18d94ac51508e710afe39ce35276f50ee94 --- /dev/null +++ b/cv/detection/ssd/MindSpore/export.py @@ -0,0 +1,69 @@ +# Copyright 2020-2022 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 os +import numpy as np + +import mindspore as ms +from mindspore import Tensor +from src.ssd import SSD300, SsdInferWithDecoder, ssd_mobilenet_v2, ssd_mobilenet_v1_fpn, ssd_mobilenet_v1, ssd_resnet50_fpn, ssd_vgg16 +from src.model_utils.config import config +from src.model_utils.moxing_adapter import moxing_wrapper +from src.box_utils import default_boxes + +ms.set_context(mode=ms.GRAPH_MODE, device_target=config.device_target) +if config.device_target == "Ascend": + ms.set_context(device_id=config.device_id) + +def modelarts_pre_process(): + '''modelarts pre process function.''' + config.file_name = os.path.join(config.output_path, config.file_name) + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_export(): + """run export.""" + if hasattr(config, 'num_ssd_boxes') and config.num_ssd_boxes == -1: + num = 0 + h, w = config.img_shape + for i in range(len(config.steps)): + num += (h // config.steps[i]) * (w // config.steps[i]) * config.num_default[i] + config.num_ssd_boxes = num + + if config.model_name == "ssd300": + net = SSD300(ssd_mobilenet_v2(), config, is_training=False) + elif config.model_name == "ssd_vgg16": + net = ssd_vgg16(config=config) + elif config.model_name == "ssd_mobilenet_v1_fpn": + net = ssd_mobilenet_v1_fpn(config=config) + elif config.model_name == "ssd_resnet50_fpn": + net = ssd_resnet50_fpn(config=config) + elif config.model_name == "ssd_mobilenet_v1": + net = ssd_mobilenet_v1(config=config) + else: + raise ValueError(f'config.model: {config.model_name} is not supported') + + net = SsdInferWithDecoder(net, Tensor(default_boxes), config) + + param_dict = ms.load_checkpoint(config.checkpoint_file_path) + net.init_parameters_data() + ms.load_param_into_net(net, param_dict) + net.set_train(False) + + input_shp = [config.batch_size, 3] + config.img_shape + input_array = Tensor(np.random.uniform(-1.0, 1.0, size=input_shp), ms.float32) + ms.export(net, input_array, file_name=config.file_name, file_format=config.file_format) + +if __name__ == '__main__': + run_export() diff --git a/cv/detection/ssd/MindSpore/mindspore_hub_conf.py b/cv/detection/ssd/MindSpore/mindspore_hub_conf.py new file mode 100755 index 0000000000000000000000000000000000000000..0f2020b74db0ea833a3589ba25e70999680924c8 --- /dev/null +++ b/cv/detection/ssd/MindSpore/mindspore_hub_conf.py @@ -0,0 +1,24 @@ +# Copyright 2020 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. +# ============================================================================ +"""hub config.""" +from src.ssd import SSD300, ssd_mobilenet_v2 +from src.config import config + +def create_network(name, *args, **kwargs): + if name == "ssd300": + backbone = ssd_mobilenet_v2() + ssd = SSD300(backbone=backbone, config=config, *args, **kwargs) + return ssd + raise NotImplementedError(f"{name} is not implemented in the repo") diff --git a/cv/detection/ssd/MindSpore/postprocess.py b/cv/detection/ssd/MindSpore/postprocess.py new file mode 100755 index 0000000000000000000000000000000000000000..6c99a439f5960e1911ae87191e85fdd701bcb8cc --- /dev/null +++ b/cv/detection/ssd/MindSpore/postprocess.py @@ -0,0 +1,89 @@ +# Copyright 2022 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. +# ============================================================================ +"""post process for 310 inference""" +import os +import numpy as np +from PIL import Image + +from src.model_utils.config import config +from src.eval_utils import COCOMetrics + +batch_size = 1 + +def get_imgSize(file_name): + img = Image.open(file_name) + return img.size + +def get_result(result_path, img_id_file_path): + anno_json = os.path.join(config.coco_root, config.instances_set.format(config.val_data_type)) + + if config.drop: + from pycocotools.coco import COCO + train_cls = config.classes + train_cls_dict = {} + for i, cls in enumerate(train_cls): + train_cls_dict[cls] = i + coco = COCO(anno_json) + classs_dict = {} + cat_ids = coco.loadCats(coco.getCatIds()) + for cat in cat_ids: + classs_dict[cat["id"]] = cat["name"] + + files = os.listdir(img_id_file_path) + eval_metrics = COCOMetrics(anno_json=anno_json, + classes=config.classes, + num_classes=config.num_classes, + max_boxes=config.max_boxes, + nms_threshold=config.nms_threshold, + min_score=config.min_score) + + for file in files: + img_ids_name = file.split('.')[0] + img_id = int(np.squeeze(img_ids_name)) + if config.drop: + anno_ids = coco.getAnnIds(imgIds=img_id, iscrowd=None) + anno = coco.loadAnns(anno_ids) + annos = [] + iscrowd = False + for label in anno: + bbox = label["bbox"] + class_name = classs_dict[label["category_id"]] + iscrowd = iscrowd or label["iscrowd"] + if class_name in train_cls: + x_min, x_max = bbox[0], bbox[0] + bbox[2] + y_min, y_max = bbox[1], bbox[1] + bbox[3] + annos.append(list(map(round, [y_min, x_min, y_max, x_max])) + [train_cls_dict[class_name]]) + if iscrowd or (not annos): + continue + + img_size = get_imgSize(os.path.join(img_id_file_path, file)) + image_shape = np.array([img_size[1], img_size[0]]) + result_path_0 = os.path.join(result_path, img_ids_name + "_0.bin") + result_path_1 = os.path.join(result_path, img_ids_name + "_1.bin") + boxes = np.fromfile(result_path_0, dtype=np.float32).reshape(config.num_ssd_boxes, 4) + box_scores = np.fromfile(result_path_1, dtype=np.float32).reshape(config.num_ssd_boxes, config.num_classes) + + eval_metrics.update({ + "boxes": boxes, + "box_scores": box_scores, + "img_id": img_id, + "image_shape": image_shape + }) + + mAP = eval_metrics.get_metrics() + print(f" mAP:{mAP}") + +if __name__ == '__main__': + get_result(config.result_path, config.img_path) diff --git a/cv/detection/ssd/MindSpore/requirements.txt b/cv/detection/ssd/MindSpore/requirements.txt new file mode 100755 index 0000000000000000000000000000000000000000..37482455857ea89188387c1b5b453668dcb3f98d --- /dev/null +++ b/cv/detection/ssd/MindSpore/requirements.txt @@ -0,0 +1,8 @@ +pycocotools >= 2.0.1 +opencv-python +xml-python +Pillow +numpy +pyyaml +onnxruntime-gpu + diff --git a/cv/detection/ssd/MindSpore/scripts/run_distribute_train.sh b/cv/detection/ssd/MindSpore/scripts/run_distribute_train.sh new file mode 100755 index 0000000000000000000000000000000000000000..4d37196688ce069f7c06831268e3bbd76b82f6bb --- /dev/null +++ b/cv/detection/ssd/MindSpore/scripts/run_distribute_train.sh @@ -0,0 +1,96 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash run_distribute_train.sh DEVICE_NUM EPOCH_SIZE LR DATASET RANK_TABLE_FILE CONFIG_PATH PRE_TRAINED PRE_TRAINED_EPOCH_SIZE" +echo "for example: sh run_distribute_train.sh 8 500 0.2 coco /data/hccl.json /config_path /opt/ssd-300.ckpt(optional) 200(optional)" +echo "It is better to use absolute path." +echo "=================================================================================================================" + +if [ $# != 6 ] && [ $# != 8 ] +then + echo "Usage: bash run_distribute_train.sh [DEVICE_NUM] [EPOCH_SIZE] [LR] [DATASET] \ +[RANK_TABLE_FILE] [CONFIG_PATH] [PRE_TRAINED](optional) [PRE_TRAINED_EPOCH_SIZE](optional)" + exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +CONFIG_PATH=$(get_real_path $6) +# Before start distribute train, first create mindrecord files. +BASE_PATH=$(cd "`dirname $0`" || exit; pwd) +cd $BASE_PATH/../ || exit +python train.py --only_create_dataset=True --dataset=$4 --config_path=$CONFIG_PATH + +echo "After running the script, the network runs in the background. The log will be generated in LOGx/log.txt" + +export RANK_SIZE=$1 +EPOCH_SIZE=$2 +LR=$3 +DATASET=$4 +PRE_TRAINED=$7 +PRE_TRAINED_EPOCH_SIZE=$8 +export RANK_TABLE_FILE=$5 + +for((i=0;i env.log + if [ $# == 6 ] + then + python train.py \ + --run_distribute=True \ + --lr=$LR \ + --dataset=$DATASET \ + --device_num=$RANK_SIZE \ + --device_id=$DEVICE_ID \ + --epoch_size=$EPOCH_SIZE \ + --config_path=$CONFIG_PATH \ + --output_path './output' > log.txt 2>&1 & + fi + + if [ $# == 8 ] + then + python train.py \ + --run_distribute=True \ + --lr=$LR \ + --dataset=$DATASET \ + --device_num=$RANK_SIZE \ + --device_id=$DEVICE_ID \ + --pre_trained=$PRE_TRAINED \ + --pre_trained_epoch_size=$PRE_TRAINED_EPOCH_SIZE \ + --epoch_size=$EPOCH_SIZE \ + --config_path=$CONFIG_PATH \ + --output_path './output' > log.txt 2>&1 & + fi + + cd ../ +done diff --git a/cv/detection/ssd/MindSpore/scripts/run_distribute_train_gpu.sh b/cv/detection/ssd/MindSpore/scripts/run_distribute_train_gpu.sh new file mode 100755 index 0000000000000000000000000000000000000000..b62f4e968dfa5f9cd20e524b91316e1f902dd5ab --- /dev/null +++ b/cv/detection/ssd/MindSpore/scripts/run_distribute_train_gpu.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# Copyright 2020-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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash run_distribute_train_gpu.sh DEVICE_NUM EPOCH_SIZE LR DATASET CONFIG_PATH PRE_TRAINED PRE_TRAINED_EPOCH_SIZE" +echo "for example: bash run_distribute_train_gpu.sh 8 500 0.2 coco /config_path /opt/ssd-300.ckpt(optional) 200(optional)" +echo "It is better to use absolute path." +echo "=================================================================================================================" + +if [ $# != 5 ] && [ $# != 7 ] +then + echo "Usage: bash run_distribute_train_gpu.sh [DEVICE_NUM] [EPOCH_SIZE] [LR] [DATASET] \ +[CONFIG_PATH] [PRE_TRAINED](optional) [PRE_TRAINED_EPOCH_SIZE](optional)" + exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +CONFIG_PATH=$(get_real_path $5) +# Before start distribute train, first create mindrecord files. +BASE_PATH=$(cd "`dirname $0`" || exit; pwd) +cd $BASE_PATH/../ || exit +python3 train.py --only_create_dataset=True --device_target="GPU" --dataset=$4 --config_path=$CONFIG_PATH + +echo "After running the script, the network runs in the background. The log will be generated in LOG/log.txt" + +export RANK_SIZE=$1 +EPOCH_SIZE=$2 +LR=$3 +DATASET=$4 +PRE_TRAINED=$6 +PRE_TRAINED_EPOCH_SIZE=$7 + +rm -rf LOG +mkdir ./LOG +cp ./*.py ./LOG +cp ./config/*.yaml ./LOG +cp -r ./src ./LOG +cd ./LOG || exit + +if [ $# == 5 ] +then + mpirun -allow-run-as-root -n $RANK_SIZE --output-filename log_output --merge-stderr-to-stdout \ + python3 train.py \ + --run_distribute=True \ + --lr=$LR \ + --dataset=$DATASET \ + --device_num=$RANK_SIZE \ + --loss_scale=1 \ + --device_target="GPU" \ + --epoch_size=$EPOCH_SIZE \ + --config_path=$CONFIG_PATH \ + --output_path './output' > log.txt 2>&1 & +fi + +if [ $# == 7 ] +then + mpirun -allow-run-as-root -n $RANK_SIZE --output-filename log_output --merge-stderr-to-stdout \ + python3 train.py \ + --run_distribute=True \ + --lr=$LR \ + --dataset=$DATASET \ + --device_num=$RANK_SIZE \ + --pre_trained=$PRE_TRAINED \ + --pre_trained_epoch_size=$PRE_TRAINED_EPOCH_SIZE \ + --loss_scale=1 \ + --device_target="GPU" \ + --epoch_size=$EPOCH_SIZE \ + --config_path=$CONFIG_PATH \ + --output_path './output' > log.txt 2>&1 & +fi diff --git a/cv/detection/ssd/MindSpore/scripts/run_eval.sh b/cv/detection/ssd/MindSpore/scripts/run_eval.sh new file mode 100755 index 0000000000000000000000000000000000000000..27887434bfad450a578c3da6435efa2814e69275 --- /dev/null +++ b/cv/detection/ssd/MindSpore/scripts/run_eval.sh @@ -0,0 +1,69 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +if [ $# != 4 ] +then + echo "Usage: bash run_eval.sh [DATASET] [CHECKPOINT_PATH] [DEVICE_ID] [CONFIG_PATH]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +DATASET=$1 +CHECKPOINT_PATH=$(get_real_path $2) +CONFIG_PATH=$(get_real_path $4) +echo $DATASET +echo $CONFIG_PATH +echo $CHECKPOINT_PATH + +if [ ! -f $CHECKPOINT_PATH ] +then + echo "error: CHECKPOINT_PATH=$PATH2 is not a file" +exit 1 +fi + +export DEVICE_NUM=1 +export DEVICE_ID=$3 +export RANK_SIZE=$DEVICE_NUM +export RANK_ID=0 + +BASE_PATH=$(cd "`dirname $0`" || exit; pwd) +cd $BASE_PATH/../ || exit + +if [ -d "eval$3" ]; +then + rm -rf ./eval$3 +fi + +mkdir ./eval$3 +cp ./*.py ./eval$3 +cp ./config/*.yaml ./eval$3 +cp -r ./src ./eval$3 +cd ./eval$3 || exit +env > env.log +echo "start inferring for device $DEVICE_ID" +python eval.py \ + --dataset=$DATASET \ + --checkpoint_file_path=$CHECKPOINT_PATH \ + --device_id=$3 \ + --config_path=$CONFIG_PATH > log.txt 2>&1 & +cd .. diff --git a/cv/detection/ssd/MindSpore/scripts/run_eval_gpu.sh b/cv/detection/ssd/MindSpore/scripts/run_eval_gpu.sh new file mode 100755 index 0000000000000000000000000000000000000000..777747209a8eccb414b1df15a4f7e788a28e6de1 --- /dev/null +++ b/cv/detection/ssd/MindSpore/scripts/run_eval_gpu.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +if [ $# != 4 ] +then + echo "Usage: bash run_eval_gpu.sh [DATASET] [CHECKPOINT_PATH] [DEVICE_ID] [CONFIG_PATH]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +DATASET=$1 +CHECKPOINT_PATH=$(get_real_path $2) +CONFIG_PATH=$(get_real_path $4) +echo $DATASET +echo $CHECKPOINT_PATH +echo $CONFIG_PATH + +if [ ! -f $CHECKPOINT_PATH ] +then + echo "error: CHECKPOINT_PATH=$PATH2 is not a file" +exit 1 +fi + +export DEVICE_NUM=1 +export DEVICE_ID=$3 +export RANK_SIZE=$DEVICE_NUM +export RANK_ID=0 + +BASE_PATH=$(cd "`dirname $0`" || exit; pwd) +cd $BASE_PATH/../ || exit + +if [ -d "eval$3" ]; +then + rm -rf ./eval$3 +fi + +mkdir ./eval$3 +cp ./*.py ./eval$3 +cp ./config/*.yaml ./eval$3 +cp -r ./src ./eval$3 +cd ./eval$3 || exit +env > env.log +echo "start inferring for device $DEVICE_ID" +python eval.py \ + --dataset=$DATASET \ + --checkpoint_file_path=$CHECKPOINT_PATH \ + --device_target="GPU" \ + --device_id=$3 \ + --config_path=$CONFIG_PATH > log.txt 2>&1 & +cd .. diff --git a/cv/detection/ssd/MindSpore/scripts/run_eval_onnx.sh b/cv/detection/ssd/MindSpore/scripts/run_eval_onnx.sh new file mode 100755 index 0000000000000000000000000000000000000000..4fca9487fa39ad46b9a3c8030005187bc22775a2 --- /dev/null +++ b/cv/detection/ssd/MindSpore/scripts/run_eval_onnx.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ + +if [ $# -lt 3 ] +then + usage="Usage: bash scripts/run_eval_onnx.sh \ + \ +[] [] []" + echo "$usage" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +DATA_PATH=$1 +COCO_ROOT=$2 +ONNX_MODEL_PATH=$3 +INSTANCES_SET=${4:-'annotations/instances_{}.json'} +DEVICE_TARGET=${5:-"GPU"} +CONFIG_PATH=${6:-"config/ssd300_config_gpu.yaml"} + +python eval_onnx.py \ + --dataset coco \ + --data_path $DATA_PATH \ + --coco_root $COCO_ROOT \ + --instances_set $INSTANCES_SET \ + --file_name $ONNX_MODEL_PATH \ + --device_target $DEVICE_TARGET \ + --config_path $CONFIG_PATH \ + --batch_size 1 \ + &> eval.log & diff --git a/cv/detection/ssd/MindSpore/scripts/run_infer_310.sh b/cv/detection/ssd/MindSpore/scripts/run_infer_310.sh new file mode 100755 index 0000000000000000000000000000000000000000..17a1209bfa13ddfcfc04dd7f39f8d164796148b6 --- /dev/null +++ b/cv/detection/ssd/MindSpore/scripts/run_infer_310.sh @@ -0,0 +1,103 @@ +#!/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. +# ============================================================================ + +if [[ $# -lt 4 || $# -gt 5 ]]; then + echo "Usage: bash run_infer_310.sh [MINDIR_PATH] [DATA_PATH] [DVPP] [CONFIG_PATH] [DEVICE_ID] + DVPP is mandatory, and must choose from [DVPP|CPU], it's case-insensitive + DEVICE_ID is optional, it can be set by environment variable device_id, otherwise the value is zero" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} +model=$(get_real_path $1) +data_path=$(get_real_path $2) +cfg_path=$4 + +device_id=0 +if [ $# == 5 ]; then + device_id=$5 +fi + +echo "mindir name: "$model +echo "dataset path: "$data_path +echo "config path: " $cfg_path +echo "device id: "$device_id + +export ASCEND_HOME=/usr/local/Ascend/ +if [ -d ${ASCEND_HOME}/ascend-toolkit ]; then + export PATH=$ASCEND_HOME/fwkacllib/bin:$ASCEND_HOME/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/ascend-toolkit/latest/atc/bin:$PATH + export LD_LIBRARY_PATH=$ASCEND_HOME/fwkacllib/lib64:/usr/local/lib:$ASCEND_HOME/ascend-toolkit/latest/atc/lib64:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export TBE_IMPL_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe + export PYTHONPATH=$ASCEND_HOME/fwkacllib/python/site-packages:${TBE_IMPL_PATH}:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp +else + export ASCEND_HOME=/usr/local/Ascend/latest/ + export PATH=$ASCEND_HOME/fwkacllib/bin:$ASCEND_HOME/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/atc/ccec_compiler/bin:$ASCEND_HOME/atc/bin:$PATH + export LD_LIBRARY_PATH=$ASCEND_HOME/fwkacllib/lib64:/usr/local/lib:$ASCEND_HOME/atc/lib64:$ASCEND_HOME/acllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export PYTHONPATH=$ASCEND_HOME/fwkacllib/python/site-packages:$ASCEND_HOME/atc/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/opp +fi + +function compile_app() +{ + cd ../ascend310_infer || exit + bash build.sh &> build.log +} + +function infer() +{ + cd - || exit + if [ -d result_Files ]; then + rm -rf ./result_Files + fi + if [ -d time_Result ]; then + rm -rf ./time_Result + fi + mkdir result_Files + mkdir time_Result + image_shape=`cat ${cfg_path} | grep img_shape` + height=${image_shape:12:3} + width=${image_shape:17:3} + + ../ascend310_infer/out/main --mindir_path=$model --dataset_path=$data_path --cpu_dvpp=CPU --device_id=$device_id --image_height=$height --image_width=$width &> infer.log +} + +function cal_acc() +{ + python ../postprocess.py --result_path=./result_Files --img_path=$data_path --config_path=${cfg_path} --drop=True &> acc.log & +} + +compile_app +if [ $? -ne 0 ]; then + echo "compile app code failed" + exit 1 +fi +infer +if [ $? -ne 0 ]; then + echo " execute inference failed" + exit 1 +fi +cal_acc +if [ $? -ne 0 ]; then + echo "calculate accuracy failed" + exit 1 +fi \ No newline at end of file diff --git a/cv/detection/ssd/MindSpore/scripts/run_standalone_train.sh b/cv/detection/ssd/MindSpore/scripts/run_standalone_train.sh new file mode 100755 index 0000000000000000000000000000000000000000..33f8a970c0873f130a9c869f0d05a1b7a808834b --- /dev/null +++ b/cv/detection/ssd/MindSpore/scripts/run_standalone_train.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash run_standalone_train.sh DEVICE_ID EPOCH_SIZE LR DATASET CONFIG_PATH PRE_TRAINED PRE_TRAINED_EPOCH_SIZE" +echo "for example: sh run_standalone_train.sh 0 500 0.2 coco /config_path /opt/ssd-300.ckpt(optional) 200(optional)" +echo "It is better to use absolute path." +echo "=================================================================================================================" + +if [ $# != 5 ] && [ $# != 7 ] +then + echo "Usage: bash run_standalone_train.sh [DEVICE_ID] [EPOCH_SIZE] [LR] [DATASET] [CONFIG_PATH] \ + [PRE_TRAINED](optional) [PRE_TRAINED_EPOCH_SIZE](optional)" + exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +CONFIG_PATH=$(get_real_path $5) +# Before start training, first create mindrecord files. +BASE_PATH=$(cd "`dirname $0`" || exit; pwd) +cd $BASE_PATH/../ || exit +python train.py --only_create_dataset=True --dataset=$4 --config_path=$CONFIG_PATH + +echo "After running the script, the network runs in the background. The log will be generated in LOGx/log.txt" +DEVICE_ID=$1 +EPOCH_SIZE=$2 +LR=$3 +DATASET=$4 +PRE_TRAINED=$6 +PRE_TRAINED_EPOCH_SIZE=$7 + +export DEVICE_ID=$DEVICE_ID +rm -rf LOG$DEVICE_ID +mkdir ./LOG$DEVICE_ID +cp ./*.py ./LOG$DEVICE_ID +cp -r ./src ./LOG$DEVICE_ID +cd ./LOG$DEVICE_ID || exit + +echo "start training with device $DEVICE_ID" +env > env.log +if [ $# == 5 ] +then + python train.py \ + --lr=$LR \ + --dataset=$DATASET \ + --device_id=$DEVICE_ID \ + --config_path=$CONFIG_PATH \ + --epoch_size=$EPOCH_SIZE > log.txt 2>&1 & +fi + +if [ $# == 7 ] +then + python train.py \ + --lr=$LR \ + --dataset=$DATASET \ + --device_id=$DEVICE_ID \ + --pre_trained=$PRE_TRAINED \ + --pre_trained_epoch_size=$PRE_TRAINED_EPOCH_SIZE \ + --config_path=$CONFIG_PATH \ + --epoch_size=$EPOCH_SIZE > log.txt 2>&1 & +fi + +cd ../ diff --git a/cv/detection/ssd/MindSpore/scripts/run_standalone_train_gpu.sh b/cv/detection/ssd/MindSpore/scripts/run_standalone_train_gpu.sh new file mode 100755 index 0000000000000000000000000000000000000000..f482aa5ff8fa0577893f774728ecc8eb083a954f --- /dev/null +++ b/cv/detection/ssd/MindSpore/scripts/run_standalone_train_gpu.sh @@ -0,0 +1,86 @@ +#!/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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash run_standalone_train.sh DEVICE_ID EPOCH_SIZE LR DATASET CONFIG_PATH PRE_TRAINED PRE_TRAINED_EPOCH_SIZE" +echo "for example: sh run_standalone_train.sh 0 500 0.2 coco /config_path /opt/ssd-300.ckpt(optional) 200(optional)" +echo "It is better to use absolute path." +echo "=================================================================================================================" + +if [ $# != 5 ] && [ $# != 7 ] +then + echo "Usage: bash run_standalone_train.sh [DEVICE_ID] [EPOCH_SIZE] [LR] [DATASET] [CONFIG_PATH] \ + [PRE_TRAINED](optional) [PRE_TRAINED_EPOCH_SIZE](optional)" + exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +CONFIG_PATH=$(get_real_path $5) +# Before start training, first create mindrecord files. +BASE_PATH=$(cd "`dirname $0`" || exit; pwd) +cd $BASE_PATH/../ || exit +python3 train.py --only_create_dataset=True --dataset=$4 --config_path=$CONFIG_PATH + +echo "After running the script, the network runs in the background. The log will be generated in LOGx/log.txt" +DEVICE_ID=$1 +EPOCH_SIZE=$2 +LR=$3 +DATASET=$4 +PRE_TRAINED=$6 +PRE_TRAINED_EPOCH_SIZE=$7 + +export DEVICE_ID=$DEVICE_ID +rm -rf LOG$DEVICE_ID +mkdir ./LOG$DEVICE_ID +cp ./*.py ./LOG$DEVICE_ID +cp -r ./src ./LOG$DEVICE_ID +cd ./LOG$DEVICE_ID || exit + +echo "start training with device $DEVICE_ID" +env > env.log +if [ $# == 5 ] +then + python3 train.py \ + --lr=$LR \ + --dataset=$DATASET \ + --device_id=$DEVICE_ID \ + --device_target="GPU" \ + --config_path=$CONFIG_PATH \ + --epoch_size=$EPOCH_SIZE > log.txt 2>&1 & +fi + +if [ $# == 7 ] +then + python3 train.py \ + --lr=$LR \ + --dataset=$DATASET \ + --device_id=$DEVICE_ID \ + --device_target="GPU" \ + --pre_trained=$PRE_TRAINED \ + --pre_trained_epoch_size=$PRE_TRAINED_EPOCH_SIZE \ + --config_path=$CONFIG_PATH \ + --epoch_size=$EPOCH_SIZE > log.txt 2>&1 & +fi + +cd ../ diff --git a/cv/detection/ssd/MindSpore/src/__init__.py b/cv/detection/ssd/MindSpore/src/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cv/detection/ssd/MindSpore/src/anchor_generator.py b/cv/detection/ssd/MindSpore/src/anchor_generator.py new file mode 100755 index 0000000000000000000000000000000000000000..9941032f3a2201b5cc5e72ac19477dc885e9e77e --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/anchor_generator.py @@ -0,0 +1,92 @@ +# Copyright 2020 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. +# ============================================================================ + +"""Anchor Generator""" + +import numpy as np + + +class GridAnchorGenerator: + """ + Anchor Generator + """ + def __init__(self, image_shape, scale, scales_per_octave, aspect_ratios): + super(GridAnchorGenerator, self).__init__() + self.scale = scale + self.scales_per_octave = scales_per_octave + self.aspect_ratios = aspect_ratios + self.image_shape = image_shape + + + def generate(self, step): + scales = np.array([2**(float(scale) / self.scales_per_octave) + for scale in range(self.scales_per_octave)]).astype(np.float32) + aspects = np.array(list(self.aspect_ratios)).astype(np.float32) + + scales_grid, aspect_ratios_grid = np.meshgrid(scales, aspects) + scales_grid = scales_grid.reshape([-1]) + aspect_ratios_grid = aspect_ratios_grid.reshape([-1]) + + feature_size = [self.image_shape[0] / step, self.image_shape[1] / step] + grid_height, grid_width = feature_size + + base_size = np.array([self.scale * step, self.scale * step]).astype(np.float32) + anchor_offset = step / 2.0 + + ratio_sqrt = np.sqrt(aspect_ratios_grid) + heights = scales_grid / ratio_sqrt * base_size[0] + widths = scales_grid * ratio_sqrt * base_size[1] + + y_centers = np.arange(grid_height).astype(np.float32) + y_centers = y_centers * step + anchor_offset + x_centers = np.arange(grid_width).astype(np.float32) + x_centers = x_centers * step + anchor_offset + x_centers, y_centers = np.meshgrid(x_centers, y_centers) + + x_centers_shape = x_centers.shape + y_centers_shape = y_centers.shape + + widths_grid, x_centers_grid = np.meshgrid(widths, x_centers.reshape([-1])) + heights_grid, y_centers_grid = np.meshgrid(heights, y_centers.reshape([-1])) + + x_centers_grid = x_centers_grid.reshape(*x_centers_shape, -1) + y_centers_grid = y_centers_grid.reshape(*y_centers_shape, -1) + widths_grid = widths_grid.reshape(-1, *x_centers_shape) + heights_grid = heights_grid.reshape(-1, *y_centers_shape) + + + bbox_centers = np.stack([y_centers_grid, x_centers_grid], axis=3) + bbox_sizes = np.stack([heights_grid, widths_grid], axis=3) + bbox_centers = bbox_centers.reshape([-1, 2]) + bbox_sizes = bbox_sizes.reshape([-1, 2]) + bbox_corners = np.concatenate([bbox_centers - 0.5 * bbox_sizes, bbox_centers + 0.5 * bbox_sizes], axis=1) + self.bbox_corners = bbox_corners / np.array([*self.image_shape, *self.image_shape]).astype(np.float32) + self.bbox_centers = np.concatenate([bbox_centers, bbox_sizes], axis=1) + self.bbox_centers = self.bbox_centers / np.array([*self.image_shape, *self.image_shape]).astype(np.float32) + + print(self.bbox_centers.shape) + return self.bbox_centers, self.bbox_corners + + def generate_multi_levels(self, steps): + bbox_centers_list = [] + bbox_corners_list = [] + for step in steps: + bbox_centers, bbox_corners = self.generate(step) + bbox_centers_list.append(bbox_centers) + bbox_corners_list.append(bbox_corners) + + self.bbox_centers = np.concatenate(bbox_centers_list, axis=0) + self.bbox_corners = np.concatenate(bbox_corners_list, axis=0) + return self.bbox_centers, self.bbox_corners diff --git a/cv/detection/ssd/MindSpore/src/box_utils.py b/cv/detection/ssd/MindSpore/src/box_utils.py new file mode 100755 index 0000000000000000000000000000000000000000..f7509c18049a6df28d17d6c90c30957cf294958c --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/box_utils.py @@ -0,0 +1,170 @@ +# Copyright 2020 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. +# ============================================================================ + +"""Bbox utils""" + +import math +import itertools as it +import numpy as np +from src.model_utils.config import config +from .anchor_generator import GridAnchorGenerator + + +class GeneratDefaultBoxes(): + """ + Generate Default boxes for SSD, follows the order of (W, H, archor_sizes). + `self.default_boxes` has a shape of [archor_sizes, H, W, 4], the last dimension is [y, x, h, w]. + `self.default_boxes_tlbr` has a shape as `self.default_boxes`, the last dimension is [y1, x1, y2, x2]. + """ + def __init__(self): + fk = config.img_shape[0] / np.array(config.steps) + scale_rate = (config.max_scale - config.min_scale) / (len(config.num_default) - 1) + scales = [config.min_scale + scale_rate * i for i in range(len(config.num_default))] + [1.0] + self.default_boxes = [] + for idex, feature_size in enumerate(config.feature_size): + sk1 = scales[idex] + sk2 = scales[idex + 1] + sk3 = math.sqrt(sk1 * sk2) + if idex == 0 and not config.aspect_ratios[idex]: + w, h = sk1 * math.sqrt(2), sk1 / math.sqrt(2) + all_sizes = [(0.1, 0.1), (w, h), (h, w)] + else: + all_sizes = [(sk1, sk1)] + for aspect_ratio in config.aspect_ratios[idex]: + w, h = sk1 * math.sqrt(aspect_ratio), sk1 / math.sqrt(aspect_ratio) + all_sizes.append((w, h)) + all_sizes.append((h, w)) + all_sizes.append((sk3, sk3)) + + assert len(all_sizes) == config.num_default[idex] + + for i, j in it.product(range(feature_size), repeat=2): + for w, h in all_sizes: + cx, cy = (j + 0.5) / fk[idex], (i + 0.5) / fk[idex] + self.default_boxes.append([cy, cx, h, w]) + + def to_tlbr(cy, cx, h, w): + return cy - h / 2, cx - w / 2, cy + h / 2, cx + w / 2 + + # For IoU calculation + self.default_boxes_tlbr = np.array(tuple(to_tlbr(*i) for i in self.default_boxes), dtype='float32') + self.default_boxes = np.array(self.default_boxes, dtype='float32') + +if hasattr(config, 'use_anchor_generator') and config.use_anchor_generator: + generator = GridAnchorGenerator(config.img_shape, 4, 2, [1.0, 2.0, 0.5]) + default_boxes, default_boxes_tlbr = generator.generate_multi_levels(config.steps) +else: + default_boxes_tlbr = GeneratDefaultBoxes().default_boxes_tlbr + default_boxes = GeneratDefaultBoxes().default_boxes +y1, x1, y2, x2 = np.split(default_boxes_tlbr[:, :4], 4, axis=-1) +vol_anchors = (x2 - x1) * (y2 - y1) +matching_threshold = config.match_threshold + + +def ssd_bboxes_encode(boxes): + """ + Labels anchors with ground truth inputs. + + Args: + boxex: ground truth with shape [N, 5], for each row, it stores [y, x, h, w, cls]. + + Returns: + gt_loc: location ground truth with shape [num_anchors, 4]. + gt_label: class ground truth with shape [num_anchors, 1]. + num_matched_boxes: number of positives in an image. + """ + + def jaccard_with_anchors(bbox): + """Compute jaccard score a box and the anchors.""" + # Intersection bbox and volume. + ymin = np.maximum(y1, bbox[0]) + xmin = np.maximum(x1, bbox[1]) + ymax = np.minimum(y2, bbox[2]) + xmax = np.minimum(x2, bbox[3]) + w = np.maximum(xmax - xmin, 0.) + h = np.maximum(ymax - ymin, 0.) + + # Volumes. + inter_vol = h * w + union_vol = vol_anchors + (bbox[2] - bbox[0]) * (bbox[3] - bbox[1]) - inter_vol + jaccard = inter_vol / union_vol + return np.squeeze(jaccard) + + pre_scores = np.zeros((config.num_ssd_boxes), dtype=np.float32) + t_boxes = np.zeros((config.num_ssd_boxes, 4), dtype=np.float32) + t_label = np.zeros((config.num_ssd_boxes), dtype=np.int64) + for bbox in boxes: + label = int(bbox[4]) + scores = jaccard_with_anchors(bbox) + idx = np.argmax(scores) + scores[idx] = 2.0 + mask = (scores > matching_threshold) + mask = mask & (scores > pre_scores) + pre_scores = np.maximum(pre_scores, scores * mask) + t_label = mask * label + (1 - mask) * t_label + for i in range(4): + t_boxes[:, i] = mask * bbox[i] + (1 - mask) * t_boxes[:, i] + + index = np.nonzero(t_label) + + # Transform to tlbr. + bboxes = np.zeros((config.num_ssd_boxes, 4), dtype=np.float32) + bboxes[:, [0, 1]] = (t_boxes[:, [0, 1]] + t_boxes[:, [2, 3]]) / 2 + bboxes[:, [2, 3]] = t_boxes[:, [2, 3]] - t_boxes[:, [0, 1]] + + # Encode features. + bboxes_t = bboxes[index] + default_boxes_t = default_boxes[index] + bboxes_t[:, :2] = (bboxes_t[:, :2] - default_boxes_t[:, :2]) / (default_boxes_t[:, 2:] * config.prior_scaling[0]) + tmp = np.maximum(bboxes_t[:, 2:4] / default_boxes_t[:, 2:4], 0.000001) + bboxes_t[:, 2:4] = np.log(tmp) / config.prior_scaling[1] + bboxes[index] = bboxes_t + + num_match = np.array([len(np.nonzero(t_label)[0])], dtype=np.int32) + return bboxes, t_label.astype(np.int32), num_match + + +def ssd_bboxes_decode(boxes): + """Decode predict boxes to [y, x, h, w]""" + boxes_t = boxes.copy() + default_boxes_t = default_boxes.copy() + boxes_t[:, :2] = boxes_t[:, :2] * config.prior_scaling[0] * default_boxes_t[:, 2:] + default_boxes_t[:, :2] + boxes_t[:, 2:4] = np.exp(boxes_t[:, 2:4] * config.prior_scaling[1]) * default_boxes_t[:, 2:4] + + bboxes = np.zeros((len(boxes_t), 4), dtype=np.float32) + + bboxes[:, [0, 1]] = boxes_t[:, [0, 1]] - boxes_t[:, [2, 3]] / 2 + bboxes[:, [2, 3]] = boxes_t[:, [0, 1]] + boxes_t[:, [2, 3]] / 2 + + return np.clip(bboxes, 0, 1) + + +def intersect(box_a, box_b): + """Compute the intersect of two sets of boxes.""" + max_yx = np.minimum(box_a[:, 2:4], box_b[2:4]) + min_yx = np.maximum(box_a[:, :2], box_b[:2]) + inter = np.clip((max_yx - min_yx), a_min=0, a_max=np.inf) + return inter[:, 0] * inter[:, 1] + + +def jaccard_numpy(box_a, box_b): + """Compute the jaccard overlap of two sets of boxes.""" + inter = intersect(box_a, box_b) + area_a = ((box_a[:, 2] - box_a[:, 0]) * + (box_a[:, 3] - box_a[:, 1])) + area_b = ((box_b[2] - box_b[0]) * + (box_b[3] - box_b[1])) + union = area_a + area_b - inter + return inter / union diff --git a/cv/detection/ssd/MindSpore/src/dataset.py b/cv/detection/ssd/MindSpore/src/dataset.py new file mode 100755 index 0000000000000000000000000000000000000000..9e181e526e5ea26eec49629bef46b00eb5d94742 --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/dataset.py @@ -0,0 +1,458 @@ +# Copyright 2020-2022 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. +# ============================================================================ + +"""SSD dataset""" + +from __future__ import division + +import os +import json +import multiprocessing +import xml.etree.ElementTree as et +import numpy as np +import cv2 + +import mindspore.dataset as de +from mindspore.mindrecord import FileWriter +from src.model_utils.config import config +from .box_utils import jaccard_numpy, ssd_bboxes_encode + + +def _rand(a=0., b=1.): + """Generate random.""" + return np.random.rand() * (b - a) + a + + +def get_imageId_from_fileName(filename, id_iter): + """Get imageID from fileName if fileName is int, else return id_iter.""" + filename = os.path.splitext(filename)[0] + if filename.isdigit(): + return int(filename) + return id_iter + + +def random_sample_crop(image, boxes): + """Random Crop the image and boxes""" + height, width, _ = image.shape + min_iou = np.random.choice([None, 0.1, 0.3, 0.5, 0.7, 0.9]) + + if min_iou is None: + return image, boxes + + # max trails (50) + for _ in range(50): + image_t = image + + w = _rand(0.3, 1.0) * width + h = _rand(0.3, 1.0) * height + + # aspect ratio constraint b/t .5 & 2 + if h / w < 0.5 or h / w > 2: + continue + + left = _rand() * (width - w) + top = _rand() * (height - h) + + rect = np.array([int(top), int(left), int(top + h), int(left + w)]) + overlap = jaccard_numpy(boxes, rect) + + # dropout some boxes + drop_mask = overlap > 0 + if not drop_mask.any(): + continue + + if overlap[drop_mask].min() < min_iou and overlap[drop_mask].max() > (min_iou + 0.2): + continue + + image_t = image_t[rect[0]:rect[2], rect[1]:rect[3], :] + + centers = (boxes[:, :2] + boxes[:, 2:4]) / 2.0 + + m1 = (rect[0] < centers[:, 0]) * (rect[1] < centers[:, 1]) + m2 = (rect[2] > centers[:, 0]) * (rect[3] > centers[:, 1]) + + # mask in that both m1 and m2 are true + mask = m1 * m2 * drop_mask + + # have any valid boxes? try again if not + if not mask.any(): + continue + + # take only matching gt boxes + boxes_t = boxes[mask, :].copy() + + boxes_t[:, :2] = np.maximum(boxes_t[:, :2], rect[:2]) + boxes_t[:, :2] -= rect[:2] + boxes_t[:, 2:4] = np.minimum(boxes_t[:, 2:4], rect[2:4]) + boxes_t[:, 2:4] -= rect[:2] + + return image_t, boxes_t + return image, boxes + + +def preprocess_fn(img_id, image, box, is_training): + """Preprocess function for dataset.""" + cv2.setNumThreads(2) + + def _infer_data(image, input_shape): + img_h, img_w, _ = image.shape + input_h, input_w = input_shape + + image = cv2.resize(image, (input_w, input_h)) + + # When the channels of image is 1 + if len(image.shape) == 2: + image = np.expand_dims(image, axis=-1) + image = np.concatenate([image, image, image], axis=-1) + + return img_id, image, np.array((img_h, img_w), np.float32) + + def _data_aug(image, box, is_training, image_size=(300, 300)): + """Data augmentation function.""" + ih, iw, _ = image.shape + h, w = image_size + + if not is_training: + return _infer_data(image, image_size) + + # Random crop + box = box.astype(np.float32) + image, box = random_sample_crop(image, box) + ih, iw, _ = image.shape + + # Resize image + image = cv2.resize(image, (w, h)) + + # Flip image or not + flip = _rand() < .5 + if flip: + image = cv2.flip(image, 1, dst=None) + + # When the channels of image is 1 + if len(image.shape) == 2: + image = np.expand_dims(image, axis=-1) + image = np.concatenate([image, image, image], axis=-1) + + box[:, [0, 2]] = box[:, [0, 2]] / ih + box[:, [1, 3]] = box[:, [1, 3]] / iw + + if flip: + box[:, [1, 3]] = 1 - box[:, [3, 1]] + + box, label, num_match = ssd_bboxes_encode(box) + return image, box, label, num_match + + return _data_aug(image, box, is_training, image_size=config.img_shape) + + +def create_voc_label(is_training): + """Get image path and annotation from VOC.""" + voc_root = config.voc_root + cls_map = {name: i for i, name in enumerate(config.classes)} + sub_dir = 'train' if is_training else 'eval' + voc_dir = os.path.join(voc_root, sub_dir) + if not os.path.isdir(voc_dir): + raise ValueError(f'Cannot find {sub_dir} dataset path.') + + image_dir = anno_dir = voc_dir + if os.path.isdir(os.path.join(voc_dir, 'Images')): + image_dir = os.path.join(voc_dir, 'Images') + if os.path.isdir(os.path.join(voc_dir, 'Annotations')): + anno_dir = os.path.join(voc_dir, 'Annotations') + + if not is_training: + json_file = os.path.join(config.voc_root, config.voc_json) + file_dir = os.path.split(json_file)[0] + if not os.path.isdir(file_dir): + os.makedirs(file_dir) + json_dict = {"images": [], "type": "instances", "annotations": [], + "categories": []} + bnd_id = 1 + + image_files_dict = {} + image_anno_dict = {} + images = [] + id_iter = 0 + for anno_file in os.listdir(anno_dir): + print(anno_file) + if not anno_file.endswith('xml'): + continue + tree = et.parse(os.path.join(anno_dir, anno_file)) + root_node = tree.getroot() + file_name = root_node.find('filename').text + img_id = get_imageId_from_fileName(file_name, id_iter) + id_iter += 1 + image_path = os.path.join(image_dir, file_name) + print(image_path) + if not os.path.isfile(image_path): + print(f'Cannot find image {file_name} according to annotations.') + continue + + labels = [] + for obj in root_node.iter('object'): + cls_name = obj.find('name').text + if cls_name not in cls_map: + print(f'Label "{cls_name}" not in "{config.classes}"') + continue + bnd_box = obj.find('bndbox') + x_min = int(float(bnd_box.find('xmin').text)) - 1 + y_min = int(float(bnd_box.find('ymin').text)) - 1 + x_max = int(float(bnd_box.find('xmax').text)) - 1 + y_max = int(float(bnd_box.find('ymax').text)) - 1 + labels.append([y_min, x_min, y_max, x_max, cls_map[cls_name]]) + + if not is_training: + o_width = abs(x_max - x_min) + o_height = abs(y_max - y_min) + ann = {'area': o_width * o_height, 'iscrowd': 0, 'image_id': \ + img_id, 'bbox': [x_min, y_min, o_width, o_height], \ + 'category_id': cls_map[cls_name], 'id': bnd_id, \ + 'ignore': 0, \ + 'segmentation': []} + json_dict['annotations'].append(ann) + bnd_id = bnd_id + 1 + + if labels: + images.append(img_id) + image_files_dict[img_id] = image_path + image_anno_dict[img_id] = np.array(labels) + + if not is_training: + size = root_node.find("size") + width = int(size.find('width').text) + height = int(size.find('height').text) + image = {'file_name': file_name, 'height': height, 'width': width, + 'id': img_id} + json_dict['images'].append(image) + + if not is_training: + for cls_name, cid in cls_map.items(): + cat = {'supercategory': 'none', 'id': cid, 'name': cls_name} + json_dict['categories'].append(cat) + json_fp = open(json_file, 'w') + json_str = json.dumps(json_dict) + json_fp.write(json_str) + json_fp.close() + + return images, image_files_dict, image_anno_dict + + +def create_coco_label(is_training): + """Get image path and annotation from COCO.""" + from pycocotools.coco import COCO + + coco_root = os.path.join(config.data_path, config.coco_root) + data_type = config.val_data_type + if is_training: + data_type = config.train_data_type + + # Classes need to train or test. + train_cls = config.classes + train_cls_dict = {} + for i, cls in enumerate(train_cls): + train_cls_dict[cls] = i + + anno_json = os.path.join(coco_root, config.instances_set.format(data_type)) + + coco = COCO(anno_json) + classs_dict = {} + cat_ids = coco.loadCats(coco.getCatIds()) + for cat in cat_ids: + classs_dict[cat["id"]] = cat["name"] + + image_ids = coco.getImgIds() + images = [] + image_path_dict = {} + image_anno_dict = {} + + for img_id in image_ids: + image_info = coco.loadImgs(img_id) + file_name = image_info[0]["file_name"] + anno_ids = coco.getAnnIds(imgIds=img_id, iscrowd=None) + anno = coco.loadAnns(anno_ids) + image_path = os.path.join(coco_root, data_type, file_name) + annos = [] + iscrowd = False + for label in anno: + bbox = label["bbox"] + class_name = classs_dict[label["category_id"]] + iscrowd = iscrowd or label["iscrowd"] + if class_name in train_cls: + x_min, x_max = bbox[0], bbox[0] + bbox[2] + y_min, y_max = bbox[1], bbox[1] + bbox[3] + annos.append(list(map(round, [y_min, x_min, y_max, x_max])) + [train_cls_dict[class_name]]) + + if not is_training and iscrowd: + continue + if len(annos) >= 1: + images.append(img_id) + image_path_dict[img_id] = image_path + image_anno_dict[img_id] = np.array(annos) + + return images, image_path_dict, image_anno_dict + + +def anno_parser(annos_str): + """Parse annotation from string to list.""" + annos = [] + for anno_str in annos_str: + anno = list(map(int, anno_str.strip().split(','))) + annos.append(anno) + return annos + + +def filter_valid_data(image_dir, anno_path): + """Filter valid image file, which both in image_dir and anno_path.""" + images = [] + image_path_dict = {} + image_anno_dict = {} + if not os.path.isdir(image_dir): + raise RuntimeError("Path given is not valid.") + if not os.path.isfile(anno_path): + raise RuntimeError("Annotation file is not valid.") + + with open(anno_path, "rb") as f: + lines = f.readlines() + for img_id, line in enumerate(lines): + line_str = line.decode("utf-8").strip() + line_split = str(line_str).split(' ') + file_name = line_split[0] + image_path = os.path.join(image_dir, file_name) + if os.path.isfile(image_path): + images.append(img_id) + image_path_dict[img_id] = image_path + image_anno_dict[img_id] = anno_parser(line_split[1:]) + + return images, image_path_dict, image_anno_dict + + +def voc_data_to_mindrecord(mindrecord_dir, is_training, prefix="ssd.mindrecord", file_num=8): + """Create MindRecord file by image_dir and anno_path.""" + mindrecord_path = os.path.join(mindrecord_dir, prefix) + writer = FileWriter(mindrecord_path, file_num) + images, image_path_dict, image_anno_dict = create_voc_label(is_training) + + ssd_json = { + "img_id": {"type": "int32", "shape": [1]}, + "image": {"type": "bytes"}, + "annotation": {"type": "int32", "shape": [-1, 5]}, + } + writer.add_schema(ssd_json, "ssd_json") + + for img_id in images: + image_path = image_path_dict[img_id] + with open(image_path, 'rb') as f: + img = f.read() + annos = np.array(image_anno_dict[img_id], dtype=np.int32) + img_id = np.array([img_id], dtype=np.int32) + row = {"img_id": img_id, "image": img, "annotation": annos} + writer.write_raw_data([row]) + writer.commit() + + +def data_to_mindrecord_byte_image(dataset="coco", is_training=True, prefix="ssd.mindrecord", file_num=8): + """Create MindRecord file.""" + mindrecord_path = os.path.join(config.data_path, config.mindrecord_dir, prefix) + writer = FileWriter(mindrecord_path, file_num) + if dataset == "coco": + images, image_path_dict, image_anno_dict = create_coco_label(is_training) + else: + images, image_path_dict, image_anno_dict = filter_valid_data(config.image_dir, config.anno_path) + + ssd_json = { + "img_id": {"type": "int32", "shape": [1]}, + "image": {"type": "bytes"}, + "annotation": {"type": "int32", "shape": [-1, 5]}, + } + writer.add_schema(ssd_json, "ssd_json") + + for img_id in images: + image_path = image_path_dict[img_id] + with open(image_path, 'rb') as f: + img = f.read() + annos = np.array(image_anno_dict[img_id], dtype=np.int32) + img_id = np.array([img_id], dtype=np.int32) + row = {"img_id": img_id, "image": img, "annotation": annos} + writer.write_raw_data([row]) + writer.commit() + + +def create_ssd_dataset(mindrecord_file, batch_size=32, device_num=1, rank=0, + is_training=True, num_parallel_workers=6, use_multiprocessing=True): + """Create SSD dataset with MindDataset.""" + cores = multiprocessing.cpu_count() + if cores < num_parallel_workers: + print("The num_parallel_workers {} is set too large, now set it {}".format(num_parallel_workers, cores)) + num_parallel_workers = cores + ds = de.MindDataset(mindrecord_file, columns_list=["img_id", "image", "annotation"], num_shards=device_num, + shard_id=rank, num_parallel_workers=num_parallel_workers, shuffle=is_training) + decode = de.vision.Decode() + ds = ds.map(operations=decode, input_columns=["image"]) + change_swap_op = de.vision.HWC2CHW() + # Computed from random subset of ImageNet training images + normalize_op = de.vision.Normalize(mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + color_adjust_op = de.vision.RandomColorAdjust(brightness=0.4, contrast=0.4, saturation=0.4) + compose_map_func = (lambda img_id, image, annotation: preprocess_fn(img_id, image, annotation, is_training)) + if is_training: + output_columns = ["image", "box", "label", "num_match"] + trans = [color_adjust_op, normalize_op, change_swap_op] + else: + output_columns = ["img_id", "image", "image_shape"] + trans = [normalize_op, change_swap_op] + ds = ds.map(operations=compose_map_func, input_columns=["img_id", "image", "annotation"], + output_columns=output_columns, column_order=output_columns, + python_multiprocessing=use_multiprocessing, + num_parallel_workers=num_parallel_workers) + ds = ds.map(operations=trans, input_columns=["image"], python_multiprocessing=use_multiprocessing, + num_parallel_workers=num_parallel_workers) + ds = ds.batch(batch_size, drop_remainder=True) + return ds + + +def create_mindrecord(dataset="coco", prefix="ssd.mindrecord", is_training=True): + print("Start create dataset!") + + # It will generate mindrecord file in config.mindrecord_dir, + # and the file name is ssd.mindrecord0, 1, ... file_num. + + mindrecord_dir = os.path.join(config.data_path, config.mindrecord_dir) + mindrecord_file = os.path.join(mindrecord_dir, prefix + "0") + if not os.path.exists(mindrecord_file): + if not os.path.isdir(mindrecord_dir): + os.makedirs(mindrecord_dir) + if dataset == "coco": + coco_root = os.path.join(config.data_path, config.coco_root) + if os.path.isdir(coco_root): + print("Create Mindrecord.") + data_to_mindrecord_byte_image("coco", is_training, prefix) + print("Create Mindrecord Done, at {}".format(mindrecord_dir)) + else: + print("coco_root not exits.") + elif dataset == "voc": + if os.path.isdir(config.voc_root): + print("Create Mindrecord.") + voc_data_to_mindrecord(mindrecord_dir, is_training, prefix) + print("Create Mindrecord Done, at {}".format(mindrecord_dir)) + else: + print("voc_root not exits.") + else: + if os.path.isdir(config.image_dir) and os.path.exists(config.anno_path): + print("Create Mindrecord.") + data_to_mindrecord_byte_image("other", is_training, prefix) + print("Create Mindrecord Done, at {}".format(mindrecord_dir)) + else: + print("image_dir or anno_path not exits.") + return mindrecord_file diff --git a/cv/detection/ssd/MindSpore/src/eval_callback.py b/cv/detection/ssd/MindSpore/src/eval_callback.py new file mode 100755 index 0000000000000000000000000000000000000000..205fce0eaf9b4c07ed96170c8523a281f22524bc --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/eval_callback.py @@ -0,0 +1,90 @@ +# 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 callback when training""" + +import os +import stat +from mindspore import save_checkpoint +from mindspore import log as logger +from mindspore.train.callback import Callback + +class EvalCallBack(Callback): + """ + Evaluation callback when training. + + Args: + eval_function (function): evaluation function. + eval_param_dict (dict): evaluation parameters' configure dict. + 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`. + + Returns: + None + + Examples: + >>> EvalCallBack(eval_function, eval_param_dict) + """ + + 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"): + super(EvalCallBack, self).__init__() + self.eval_param_dict = eval_param_dict + self.eval_function = eval_function + self.eval_start_epoch = eval_start_epoch + if interval < 1: + raise ValueError("interval should >= 1.") + self.interval = interval + self.save_best_ckpt = save_best_ckpt + self.best_res = 0 + self.best_epoch = 0 + if not os.path.isdir(ckpt_directory): + os.makedirs(ckpt_directory) + self.bast_ckpt_path = os.path.join(ckpt_directory, besk_ckpt_name) + self.metrics_name = metrics_name + + def remove_ckpoint_file(self, file_name): + """Remove the specified checkpoint file from this checkpoint manager and also from the directory.""" + try: + os.chmod(file_name, stat.S_IWRITE) + os.remove(file_name) + except OSError: + logger.warning("OSError, failed to remove the older ckpt file %s.", file_name) + except ValueError: + logger.warning("ValueError, failed to remove the older ckpt file %s.", file_name) + + def epoch_end(self, run_context): + """Callback when epoch end.""" + 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) + print("epoch: {}, {}: {}".format(cur_epoch, self.metrics_name, res), flush=True) + if res >= self.best_res: + self.best_res = res + self.best_epoch = cur_epoch + print("update best result: {}".format(res), flush=True) + if self.save_best_ckpt: + if os.path.exists(self.bast_ckpt_path): + self.remove_ckpoint_file(self.bast_ckpt_path) + save_checkpoint(cb_params.train_network, self.bast_ckpt_path) + print("update best checkpoint at: {}".format(self.bast_ckpt_path), flush=True) + + def end(self, run_context): + print("End training, the best {0} is: {1}, the best {0} epoch is {2}".format(self.metrics_name, + self.best_res, + self.best_epoch), flush=True) diff --git a/cv/detection/ssd/MindSpore/src/eval_utils.py b/cv/detection/ssd/MindSpore/src/eval_utils.py new file mode 100755 index 0000000000000000000000000000000000000000..11ad00bdc096b7aed830306eb921881039452d08 --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/eval_utils.py @@ -0,0 +1,153 @@ +# Copyright 2022 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. +# ============================================================================ +"""Coco metrics utils""" + +import json +import numpy as np +from mindspore import Tensor +from pycocotools.coco import COCO +from pycocotools.cocoeval import COCOeval + +from src.model_utils.config import config + + +def apply_eval(eval_param_dict): + net = eval_param_dict["net"] + net.set_train(False) + ds = eval_param_dict["dataset"] + anno_json = eval_param_dict["anno_json"] + coco_metrics = COCOMetrics(anno_json=anno_json, + classes=config.classes, + num_classes=config.num_classes, + max_boxes=config.max_boxes, + nms_threshold=config.nms_threshold, + min_score=config.min_score) + for data in ds.create_dict_iterator(output_numpy=True, num_epochs=1): + img_id = data['img_id'] + img_np = data['image'] + image_shape = data['image_shape'] + + output = net(Tensor(img_np)) + + for batch_idx in range(img_np.shape[0]): + pred_batch = { + "boxes": output[0].asnumpy()[batch_idx], + "box_scores": output[1].asnumpy()[batch_idx], + "img_id": int(np.squeeze(img_id[batch_idx])), + "image_shape": image_shape[batch_idx] + } + coco_metrics.update(pred_batch) + eval_metrics = coco_metrics.get_metrics() + return eval_metrics + + +def apply_nms(all_boxes, all_scores, thres, max_boxes): + """Apply NMS to bboxes.""" + y1 = all_boxes[:, 0] + x1 = all_boxes[:, 1] + y2 = all_boxes[:, 2] + x2 = all_boxes[:, 3] + areas = (x2 - x1 + 1) * (y2 - y1 + 1) + + order = all_scores.argsort()[::-1] + keep = [] + + while order.size > 0: + i = order[0] + keep.append(i) + + if len(keep) >= max_boxes: + break + + xx1 = np.maximum(x1[i], x1[order[1:]]) + yy1 = np.maximum(y1[i], y1[order[1:]]) + xx2 = np.minimum(x2[i], x2[order[1:]]) + yy2 = np.minimum(y2[i], y2[order[1:]]) + + w = np.maximum(0.0, xx2 - xx1 + 1) + h = np.maximum(0.0, yy2 - yy1 + 1) + inter = w * h + + ovr = inter / (areas[i] + areas[order[1:]] - inter) + + inds = np.where(ovr <= thres)[0] + + order = order[inds + 1] + return keep + + +class COCOMetrics: + """Calculate mAP of predicted bboxes.""" + + def __init__(self, anno_json, classes, num_classes, min_score, nms_threshold, max_boxes): + self.num_classes = num_classes + self.classes = classes + self.min_score = min_score + self.nms_threshold = nms_threshold + self.max_boxes = max_boxes + + self.val_cls_dict = {i: cls for i, cls in enumerate(classes)} + self.coco_gt = COCO(anno_json) + cat_ids = self.coco_gt.loadCats(self.coco_gt.getCatIds()) + self.class_dict = {cat['name']: cat['id'] for cat in cat_ids} + + self.predictions = [] + self.img_ids = [] + + def update(self, batch): + pred_boxes = batch['boxes'] + box_scores = batch['box_scores'] + img_id = batch['img_id'] + h, w = batch['image_shape'] + + final_boxes = [] + final_label = [] + final_score = [] + self.img_ids.append(img_id) + + for c in range(1, self.num_classes): + class_box_scores = box_scores[:, c] + score_mask = class_box_scores > self.min_score + class_box_scores = class_box_scores[score_mask] + class_boxes = pred_boxes[score_mask] * [h, w, h, w] + + if score_mask.any(): + nms_index = apply_nms(class_boxes, class_box_scores, self.nms_threshold, self.max_boxes) + class_boxes = class_boxes[nms_index] + class_box_scores = class_box_scores[nms_index] + + final_boxes += class_boxes.tolist() + final_score += class_box_scores.tolist() + final_label += [self.class_dict[self.val_cls_dict[c]]] * len(class_box_scores) + + for loc, label, score in zip(final_boxes, final_label, final_score): + res = {} + res['image_id'] = img_id + res['bbox'] = [loc[1], loc[0], loc[3] - loc[1], loc[2] - loc[0]] + res['score'] = score + res['category_id'] = label + self.predictions.append(res) + + def get_metrics(self): + with open('predictions.json', 'w') as f: + json.dump(self.predictions, f) + + coco_dt = self.coco_gt.loadRes('predictions.json') + E = COCOeval(self.coco_gt, coco_dt, iouType='bbox') + E.params.imgIds = self.img_ids + E.evaluate() + E.accumulate() + E.summarize() + return E.stats[0] diff --git a/cv/detection/ssd/MindSpore/src/fpn.py b/cv/detection/ssd/MindSpore/src/fpn.py new file mode 100755 index 0000000000000000000000000000000000000000..83beebd70d949035d24856524f6ce004a1a71eaa --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/fpn.py @@ -0,0 +1,138 @@ +# Copyright 2020-2022 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 mindspore.ops as ops +import mindspore.nn as nn +from .mobilenet_v1 import conv_bn_relu, MobileNetV1 +from .resnet import resnet50 + + +class FpnTopDown(nn.Cell): + """ + Fpn to extract features + """ + def __init__(self, in_channel_list, out_channels): + super(FpnTopDown, self).__init__() + self.lateral_convs_list_ = [] + self.fpn_convs_ = [] + for channel in in_channel_list: + l_conv = nn.Conv2d(channel, out_channels, kernel_size=1, stride=1, + has_bias=True, padding=0, pad_mode='same') + fpn_conv = conv_bn_relu(out_channels, out_channels, kernel_size=3, stride=1, depthwise=False) + self.lateral_convs_list_.append(l_conv) + self.fpn_convs_.append(fpn_conv) + self.lateral_convs_list = nn.layer.CellList(self.lateral_convs_list_) + self.fpn_convs_list = nn.layer.CellList(self.fpn_convs_) + self.num_layers = len(in_channel_list) + + def construct(self, inputs): + image_features = () + for i, feature in enumerate(inputs): + image_features = image_features + (self.lateral_convs_list[i](feature),) + + features = (image_features[-1],) + for i in range(len(inputs) - 1): + top = len(inputs) - i - 1 + down = top - 1 + size = ops.shape(inputs[down]) + top_down = ops.ResizeBilinear((size[2], size[3]))(features[-1]) + top_down = top_down + image_features[down] + features = features + (top_down,) + + extract_features = () + num_features = len(features) + for i in range(num_features): + extract_features = extract_features + (self.fpn_convs_list[i](features[num_features - i - 1]),) + + return extract_features + + +class BottomUp(nn.Cell): + """ + Bottom Up feature extractor + """ + def __init__(self, levels, channels, kernel_size, stride): + super(BottomUp, self).__init__() + self.levels = levels + bottom_up_cells = [ + conv_bn_relu(channels, channels, kernel_size, stride, False) for x in range(self.levels) + ] + self.blocks = nn.CellList(bottom_up_cells) + + def construct(self, features): + for block in self.blocks: + features = features + (block(features[-1]),) + return features + + +class FeatureSelector(nn.Cell): + """ + Select specific layers from an entire feature list + """ + def __init__(self, feature_idxes): + super(FeatureSelector, self).__init__() + self.feature_idxes = feature_idxes + + def construct(self, feature_list): + selected = () + for i in self.feature_idxes: + selected = selected + (feature_list[i],) + return selected + + +class MobileNetV1Fpn(nn.Cell): + """ + MobileNetV1 with FPN as SSD backbone. + """ + def __init__(self, config): + super(MobileNetV1Fpn, self).__init__() + self.mobilenet_v1 = MobileNetV1(features_only=True) + + self.selector = FeatureSelector([10, 22, 26]) + + self.layer_indexs = [10, 22, 26] + self.fpn = FpnTopDown([256, 512, 1024], 256) + self.bottom_up = BottomUp(2, 256, 3, 2) + + def construct(self, x): + features = self.mobilenet_v1(x) + features = self.selector(features) + features = self.fpn(features) + features = self.bottom_up(features) + return features + +class ResNetV1Fpn(nn.Cell): + """ + ResNet with FPN as SSD backbone. + """ + def __init__(self, resnet): + super(ResNetV1Fpn, self).__init__() + self.resnet = resnet + self.fpn = FpnTopDown([512, 1024, 2048], 256) + self.bottom_up = BottomUp(2, 256, 3, 2) + + def construct(self, x): + _, _, c3, c4, c5 = self.resnet(x) + features = self.fpn((c3, c4, c5)) + features = self.bottom_up(features) + return features + + +def mobilenet_v1_fpn(config): + return MobileNetV1Fpn(config) + +def resnet50_fpn(): + resnet = resnet50() + return ResNetV1Fpn(resnet) diff --git a/cv/detection/ssd/MindSpore/src/init_params.py b/cv/detection/ssd/MindSpore/src/init_params.py new file mode 100755 index 0000000000000000000000000000000000000000..8ecd0f41bb06c7c2b893b22d0360360a14d46c9f --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/init_params.py @@ -0,0 +1,50 @@ +# Copyright 2020-2022 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. +# ============================================================================ +"""Parameters utils""" + +from mindspore.common.initializer import initializer, TruncatedNormal + +def init_net_param(network, initialize_mode='TruncatedNormal'): + """Init the parameters in net.""" + params = network.trainable_params() + for p in params: + if 'beta' not in p.name and 'gamma' not in p.name and 'bias' not in p.name: + if initialize_mode == 'TruncatedNormal': + p.set_data(initializer(TruncatedNormal(0.02), p.data.shape, p.data.dtype)) + else: + p.set_data(initialize_mode, p.data.shape, p.data.dtype) + + +def load_backbone_params(network, param_dict): + """Init the parameters from pre-train model, default is mobilenetv2.""" + for _, param in network.parameters_and_names(): + param_name = param.name.replace('network.backbone.', '') + name_split = param_name.split('.') + if 'features_1' in param_name: + param_name = param_name.replace('features_1', 'features') + if 'features_2' in param_name: + param_name = '.'.join(['features', str(int(name_split[1]) + 14)] + name_split[2:]) + if param_name in param_dict: + param.set_data(param_dict[param_name].data) + + +def filter_checkpoint_parameter_by_list(param_dict, filter_list): + """remove useless parameters according to filter_list""" + for key in list(param_dict.keys()): + for name in filter_list: + if name in key: + print("Delete parameter from checkpoint: ", key) + del param_dict[key] + break diff --git a/cv/detection/ssd/MindSpore/src/lr_schedule.py b/cv/detection/ssd/MindSpore/src/lr_schedule.py new file mode 100755 index 0000000000000000000000000000000000000000..4df26b39056c24e498d8ebb664e868674e4804ab --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/lr_schedule.py @@ -0,0 +1,56 @@ +# Copyright 2020 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. +# ============================================================================ + +"""Learning rate schedule""" + +import math +import numpy as np + + +def get_lr(global_step, lr_init, lr_end, lr_max, warmup_epochs, total_epochs, steps_per_epoch): + """ + generate learning rate array + + Args: + global_step(int): total steps of the training + lr_init(float): init learning rate + lr_end(float): end learning rate + lr_max(float): max learning rate + warmup_epochs(float): number of warmup epochs + total_epochs(int): total epoch of training + steps_per_epoch(int): steps of one epoch + + Returns: + np.array, learning rate array + """ + lr_each_step = [] + total_steps = steps_per_epoch * total_epochs + warmup_steps = steps_per_epoch * warmup_epochs + for i in range(total_steps): + if i < warmup_steps: + lr = lr_init + (lr_max - lr_init) * i / warmup_steps + else: + lr = lr_end + \ + (lr_max - lr_end) * \ + (1. + math.cos(math.pi * (i - warmup_steps) / (total_steps - warmup_steps))) / 2. + if lr < 0.0: + lr = 0.0 + lr_each_step.append(lr) + + current_step = global_step + lr_each_step = np.array(lr_each_step).astype(np.float32) + learning_rate = lr_each_step[current_step:] + + return learning_rate diff --git a/cv/detection/ssd/MindSpore/src/mobilenet_v1.py b/cv/detection/ssd/MindSpore/src/mobilenet_v1.py new file mode 100755 index 0000000000000000000000000000000000000000..da64db3001518da0a4343064e30a61d539f56f62 --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/mobilenet_v1.py @@ -0,0 +1,125 @@ +# Copyright 2021-2022 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 mindspore.nn as nn +import mindspore.ops as ops + +def conv_bn_relu(in_channel, out_channel, kernel_size, stride, depthwise, activation='relu6'): + output = [] + output.append(nn.Conv2d(in_channel, out_channel, kernel_size, stride, pad_mode="same", + group=1 if not depthwise else in_channel)) + output.append(nn.BatchNorm2d(out_channel)) + if activation: + output.append(nn.get_activation(activation)) + return nn.SequentialCell(output) + + +class MobileNetV1(nn.Cell): + """ + MobileNet V1 backbone + """ + def __init__(self, class_num=1001, features_only=False): + super(MobileNetV1, self).__init__() + self.features_only = features_only + cnn = [ + conv_bn_relu(3, 32, 3, 2, False), # Conv0 + + conv_bn_relu(32, 32, 3, 1, True), # Conv1_depthwise + conv_bn_relu(32, 64, 1, 1, False), # Conv1_pointwise + conv_bn_relu(64, 64, 3, 2, True), # Conv2_depthwise + conv_bn_relu(64, 128, 1, 1, False), # Conv2_pointwise + + conv_bn_relu(128, 128, 3, 1, True), # Conv3_depthwise + conv_bn_relu(128, 128, 1, 1, False), # Conv3_pointwise + conv_bn_relu(128, 128, 3, 2, True), # Conv4_depthwise + conv_bn_relu(128, 256, 1, 1, False), # Conv4_pointwise + + conv_bn_relu(256, 256, 3, 1, True), # Conv5_depthwise + conv_bn_relu(256, 256, 1, 1, False), # Conv5_pointwise + conv_bn_relu(256, 256, 3, 2, True), # Conv6_depthwise + conv_bn_relu(256, 512, 1, 1, False), # Conv6_pointwise + + conv_bn_relu(512, 512, 3, 1, True), # Conv7_depthwise + conv_bn_relu(512, 512, 1, 1, False), # Conv7_pointwise + conv_bn_relu(512, 512, 3, 1, True), # Conv8_depthwise + conv_bn_relu(512, 512, 1, 1, False), # Conv8_pointwise + conv_bn_relu(512, 512, 3, 1, True), # Conv9_depthwise + conv_bn_relu(512, 512, 1, 1, False), # Conv9_pointwise + conv_bn_relu(512, 512, 3, 1, True), # Conv10_depthwise + conv_bn_relu(512, 512, 1, 1, False), # Conv10_pointwise + conv_bn_relu(512, 512, 3, 1, True), # Conv11_depthwise + conv_bn_relu(512, 512, 1, 1, False), # Conv11_pointwise + + conv_bn_relu(512, 512, 3, 2, True), # Conv12_depthwise + conv_bn_relu(512, 1024, 1, 1, False), # Conv12_pointwise + conv_bn_relu(1024, 1024, 3, 1, True), # Conv13_depthwise + conv_bn_relu(1024, 1024, 1, 1, False), # Conv13_pointwise + ] + + if self.features_only: + self.network = nn.CellList(cnn) + else: + self.network = nn.SequentialCell(cnn) + self.fc = nn.Dense(1024, class_num) + + def construct(self, x): + output = x + if self.features_only: + features = () + for block in self.network: + output = block(output) + features = features + (output,) + return features + output = self.network(x) + output = ops.ReduceMean()(output, (2, 3)) + output = self.fc(output) + return output + +class FeatureSelector(nn.Cell): + """ + Select specific layers from an entire feature list + """ + def __init__(self, feature_idxes): + super(FeatureSelector, self).__init__() + self.feature_idxes = feature_idxes + + def construct(self, feature_list): + selected = () + for i in self.feature_idxes: + selected = selected + (feature_list[i],) + return selected + +class MobileNetV1Feature(nn.Cell): + """ + MobileNetV1 with FPN as SSD backbone. + """ + def __init__(self, config): + super(MobileNetV1Feature, self).__init__() + self.mobilenet_v1 = MobileNetV1(features_only=True) + + self.selector = FeatureSelector([14, 26]) + + self.layer_indexs = [14, 26] + + def construct(self, x): + features = self.mobilenet_v1(x) + features = self.selector(features) + return features + +def mobilenet_v1(class_num=1001): + return MobileNetV1(class_num) + +def mobilenet_v1_Feature(config): + return MobileNetV1Feature(config) diff --git a/cv/detection/ssd/MindSpore/src/model_utils/config.py b/cv/detection/ssd/MindSpore/src/model_utils/config.py new file mode 100755 index 0000000000000000000000000000000000000000..a00dd1174725aac3118de43d00476d52a0bf3aca --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/model_utils/config.py @@ -0,0 +1,130 @@ +# 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. +# ============================================================================ + +"""Parse arguments""" + +import os +import ast +import argparse +from pprint import pformat, pprint +import yaml + +_config_path = "./ssd300_config.yaml" + +class Config: + """ + Configuration namespace. Convert dictionary to members. + """ + def __init__(self, cfg_dict): + for k, v in cfg_dict.items(): + if isinstance(v, (list, tuple)): + setattr(self, k, [Config(x) if isinstance(x, dict) else x for x in v]) + else: + setattr(self, k, Config(v) if isinstance(v, dict) else v) + + def __str__(self): + return pformat(self.__dict__) + + def __repr__(self): + return self.__str__() + + +def parse_cli_to_yaml(parser, cfg, helper=None, choices=None, cfg_path="ssd300_config.yaml"): + """ + Parse command line arguments to the configuration according to the default yaml. + + Args: + parser: Parent parser. + cfg: Base configuration. + helper: Helper description. + cfg_path: Path to the default yaml config. + """ + parser = argparse.ArgumentParser(description="[REPLACE THIS at config.py]", + parents=[parser]) + helper = {} if helper is None else helper + choices = {} if choices is None else choices + for item in cfg: + if not isinstance(cfg[item], list) and not isinstance(cfg[item], dict): + help_description = helper[item] if item in helper else "Please reference to {}".format(cfg_path) + choice = choices[item] if item in choices else None + if isinstance(cfg[item], bool): + parser.add_argument("--" + item, type=ast.literal_eval, default=cfg[item], choices=choice, + help=help_description) + else: + parser.add_argument("--" + item, type=type(cfg[item]), default=cfg[item], choices=choice, + help=help_description) + args = parser.parse_args() + return args + + +def parse_yaml(yaml_path): + """ + Parse the yaml config file. + + Args: + yaml_path: Path to the yaml config. + """ + with open(yaml_path, 'r') as fin: + try: + cfgs = yaml.load_all(fin.read(), Loader=yaml.FullLoader) + cfgs = [x for x in cfgs] + if len(cfgs) == 1: + cfg_helper = {} + cfg = cfgs[0] + cfg_choices = {} + elif len(cfgs) == 2: + cfg, cfg_helper = cfgs + cfg_choices = {} + elif len(cfgs) == 3: + cfg, cfg_helper, cfg_choices = cfgs + else: + raise ValueError("At most 3 docs (config description for help, choices) are supported in config yaml") + print(cfg_helper) + except: + raise ValueError("Failed to parse yaml") + return cfg, cfg_helper, cfg_choices + + +def merge(args, cfg): + """ + Merge the base config from yaml file and command line arguments. + + Args: + args: Command line arguments. + cfg: Base configuration. + """ + args_var = vars(args) + for item in args_var: + cfg[item] = args_var[item] + return cfg + + +def get_config(): + """ + Get Config according to the yaml file and cli arguments. + """ + parser = argparse.ArgumentParser(description="default name", add_help=False) + current_dir = os.path.dirname(os.path.abspath(__file__)) + parser.add_argument("--config_path", type=str, default=os.path.join(current_dir, \ + "../../config/ssd300_config.yaml"), help="Config file path") + path_args, _ = parser.parse_known_args() + default, helper, choices = parse_yaml(path_args.config_path) + args = parse_cli_to_yaml(parser=parser, cfg=default, helper=helper, choices=choices, cfg_path=path_args.config_path) + final_config = merge(args, default) + pprint(final_config) + print("Please check the above information for the configurations", flush=True) + return Config(final_config) + +config = get_config() diff --git a/cv/detection/ssd/MindSpore/src/model_utils/device_adapter.py b/cv/detection/ssd/MindSpore/src/model_utils/device_adapter.py new file mode 100755 index 0000000000000000000000000000000000000000..9c3d21d5e47c22617170887df9da97beff668495 --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/model_utils/device_adapter.py @@ -0,0 +1,27 @@ +# 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. +# ============================================================================ + +"""Device adapter for ModelArts""" + +from src.model_utils.config import config + +if config.enable_modelarts: + from src.model_utils.moxing_adapter import get_device_id, get_device_num, get_rank_id, get_job_id +else: + from src.model_utils.local_adapter import get_device_id, get_device_num, get_rank_id, get_job_id + +__all__ = [ + "get_device_id", "get_device_num", "get_rank_id", "get_job_id" +] diff --git a/cv/detection/ssd/MindSpore/src/model_utils/local_adapter.py b/cv/detection/ssd/MindSpore/src/model_utils/local_adapter.py new file mode 100755 index 0000000000000000000000000000000000000000..769fa6dc78e59eb66dbc8e6773accdc1d08b649e --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/model_utils/local_adapter.py @@ -0,0 +1,36 @@ +# 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. +# ============================================================================ + +"""Local adapter""" + +import os + +def get_device_id(): + device_id = os.getenv('DEVICE_ID', '0') + return int(device_id) + + +def get_device_num(): + device_num = os.getenv('RANK_SIZE', '1') + return int(device_num) + + +def get_rank_id(): + global_rank_id = os.getenv('RANK_ID', '0') + return int(global_rank_id) + + +def get_job_id(): + return "Local Job" diff --git a/cv/detection/ssd/MindSpore/src/model_utils/moxing_adapter.py b/cv/detection/ssd/MindSpore/src/model_utils/moxing_adapter.py new file mode 100755 index 0000000000000000000000000000000000000000..72b124bd07b46f04de7575b604bcaa10a6588184 --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/model_utils/moxing_adapter.py @@ -0,0 +1,115 @@ +# Copyright 2021-2022 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. +# ============================================================================ + +"""Moxing adapter for ModelArts""" + +import os +import functools +import mindspore as ms +from src.model_utils.config import config + +_global_sync_count = 0 + +def get_device_id(): + device_id = os.getenv('DEVICE_ID', '0') + return int(device_id) + + +def get_device_num(): + device_num = os.getenv('RANK_SIZE', '1') + return int(device_num) + + +def get_rank_id(): + global_rank_id = os.getenv('RANK_ID', '0') + return int(global_rank_id) + + +def get_job_id(): + job_id = os.getenv('JOB_ID') + job_id = job_id if job_id != "" else "default" + return job_id + +def sync_data(from_path, to_path): + """ + Download data from remote obs to local directory if the first url is remote url and the second one is local path + Upload data from local directory to remote obs in contrast. + """ + import moxing as mox + import time + global _global_sync_count + sync_lock = "/tmp/copy_sync.lock" + str(_global_sync_count) + _global_sync_count += 1 + + # Each server contains 8 devices as most. + if get_device_id() % min(get_device_num(), 8) == 0 and not os.path.exists(sync_lock): + print("from path: ", from_path) + print("to path: ", to_path) + mox.file.copy_parallel(from_path, to_path) + print("===finish data synchronization===") + try: + os.mknod(sync_lock) + except IOError: + pass + print("===save flag===") + + while True: + if os.path.exists(sync_lock): + break + time.sleep(1) + + print("Finish sync data from {} to {}.".format(from_path, to_path)) + + +def moxing_wrapper(pre_process=None, post_process=None): + """ + Moxing wrapper to download dataset and upload outputs. + """ + def wrapper(run_func): + @functools.wraps(run_func) + def wrapped_func(*args, **kwargs): + # Download data from data_url + if config.enable_modelarts: + if config.data_url: + sync_data(config.data_url, config.data_path) + print("Dataset downloaded: ", os.listdir(config.data_path)) + if config.checkpoint_url: + sync_data(config.checkpoint_url, config.load_path) + print("Preload downloaded: ", os.listdir(config.load_path)) + if config.train_url: + sync_data(config.train_url, config.output_path) + print("Workspace downloaded: ", os.listdir(config.output_path)) + + ms.set_context(save_graphs_path=os.path.join(config.output_path, str(get_rank_id()))) + config.device_num = get_device_num() + config.device_id = get_device_id() + if not os.path.exists(config.output_path): + os.makedirs(config.output_path) + + if pre_process: + pre_process() + + run_func(*args, **kwargs) + + # Upload data to train_url + if config.enable_modelarts: + if post_process: + post_process() + + if config.train_url: + print("Start to copy output directory") + sync_data(config.output_path, config.train_url) + return wrapped_func + return wrapper diff --git a/cv/detection/ssd/MindSpore/src/resnet.py b/cv/detection/ssd/MindSpore/src/resnet.py new file mode 100755 index 0000000000000000000000000000000000000000..489cbff036165bf90b2fa5611d4bf8b9f2cf8957 --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/resnet.py @@ -0,0 +1,216 @@ +# Copyright 2021-2022 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. +# ============================================================================ +"""ResNet.""" +import mindspore.nn as nn +import mindspore.ops as ops + + +def _conv3x3(in_channel, out_channel, stride=1): + return nn.Conv2d(in_channel, out_channel, + kernel_size=3, stride=stride, padding=0, pad_mode='same') + + +def _conv1x1(in_channel, out_channel, stride=1): + return nn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride, padding=0, pad_mode='same') + + +def _conv7x7(in_channel, out_channel, stride=1): + return nn.Conv2d(in_channel, out_channel, kernel_size=7, stride=stride, padding=0, pad_mode='same') + + +def _bn(channel): + return nn.BatchNorm2d(channel, eps=1e-3, momentum=0.997, + gamma_init=1, beta_init=0, moving_mean_init=0, moving_var_init=1) + + +def _bn_last(channel): + return nn.BatchNorm2d(channel, eps=1e-3, momentum=0.997, + gamma_init=0, beta_init=0, moving_mean_init=0, moving_var_init=1) + +class ResidualBlock(nn.Cell): + """ + ResNet V1 residual block definition. + + Args: + in_channel (int): Input channel. + out_channel (int): Output channel. + stride (int): Stride size for the first convolutional layer. Default: 1. + + Returns: + Tensor, output tensor. + + Examples: + >>> ResidualBlock(3, 256, stride=2) + """ + expansion = 4 + + def __init__(self, + in_channel, + out_channel, + stride=1): + super(ResidualBlock, self).__init__() + self.stride = stride + channel = out_channel // self.expansion + self.conv1 = _conv1x1(in_channel, channel, stride=1) + self.bn1 = _bn(channel) + self.conv2 = _conv3x3(channel, channel, stride=stride) + self.bn2 = _bn(channel) + + self.conv3 = _conv1x1(channel, out_channel, stride=1) + self.bn3 = _bn_last(out_channel) + self.relu = nn.ReLU() + + self.down_sample = False + + if stride != 1 or in_channel != out_channel: + self.down_sample = True + self.down_sample_layer = None + + if self.down_sample: + self.down_sample_layer = nn.SequentialCell([_conv1x1(in_channel, out_channel, stride), _bn(out_channel)]) + self.add = ops.Add() + + def construct(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + out = self.conv3(out) + out = self.bn3(out) + + if self.down_sample: + identity = self.down_sample_layer(identity) + + out = self.add(out, identity) + out = self.relu(out) + + return out + + +class ResNet(nn.Cell): + """ + ResNet architecture. + + Args: + block (Cell): Block for network. + layer_nums (list): Numbers of block in different layers. + in_channels (list): Input channel in each layer. + out_channels (list): Output channel in each layer. + strides (list): Stride size in each layer. + num_classes (int): The number of classes that the training images are belonging to. + Returns: + Tensor, output tensor. + + Examples: + >>> ResNet(ResidualBlock, + >>> [3, 4, 6, 3], + >>> [64, 256, 512, 1024], + >>> [256, 512, 1024, 2048], + >>> [1, 2, 2, 2], + >>> 10) + """ + + def __init__(self, + block, + layer_nums, + in_channels, + out_channels, + strides): + super(ResNet, self).__init__() + + if not len(layer_nums) == len(in_channels) == len(out_channels) == 4: + raise ValueError("the length of layer_num, in_channels, out_channels list must be 4!") + self.conv1 = _conv7x7(3, 64, stride=2) + self.bn1 = _bn(64) + self.relu = ops.ReLU() + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, pad_mode="same") + self.layer1 = self._make_layer(block, + layer_nums[0], + in_channel=in_channels[0], + out_channel=out_channels[0], + stride=strides[0]) + self.layer2 = self._make_layer(block, + layer_nums[1], + in_channel=in_channels[1], + out_channel=out_channels[1], + stride=strides[1]) + self.layer3 = self._make_layer(block, + layer_nums[2], + in_channel=in_channels[2], + out_channel=out_channels[2], + stride=strides[2]) + self.layer4 = self._make_layer(block, + layer_nums[3], + in_channel=in_channels[3], + out_channel=out_channels[3], + stride=strides[3]) + + def _make_layer(self, block, layer_num, in_channel, out_channel, stride): + """ + Make stage network of ResNet. + + Args: + block (Cell): Resnet block. + layer_num (int): Layer number. + in_channel (int): Input channel. + out_channel (int): Output channel. + stride (int): Stride size for the first convolutional layer. + Returns: + SequentialCell, the output layer. + + Examples: + >>> _make_layer(ResidualBlock, 3, 128, 256, 2) + """ + layers = [] + + resnet_block = block(in_channel, out_channel, stride=stride) + layers.append(resnet_block) + for _ in range(1, layer_num): + resnet_block = block(out_channel, out_channel, stride=1) + layers.append(resnet_block) + return nn.SequentialCell(layers) + + def construct(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + c1 = self.maxpool(x) + + c2 = self.layer1(c1) + c3 = self.layer2(c2) + c4 = self.layer3(c3) + c5 = self.layer4(c4) + return c1, c2, c3, c4, c5 + + +def resnet50(): + """ + Get ResNet50 neural network. + + Returns: + Cell, cell instance of ResNet50 neural network. + + Examples: + >>> net = resnet50() + """ + return ResNet(ResidualBlock, + [3, 4, 6, 3], + [64, 256, 512, 1024], + [256, 512, 1024, 2048], + [1, 2, 2, 2]) diff --git a/cv/detection/ssd/MindSpore/src/ssd.py b/cv/detection/ssd/MindSpore/src/ssd.py new file mode 100755 index 0000000000000000000000000000000000000000..763eab96bada24181ee6376e63101b4ca8de6f9f --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/ssd.py @@ -0,0 +1,769 @@ +# Copyright 2020-2022 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. +# ============================================================================ + +"""SSD net based MobilenetV2.""" + +import mindspore as ms +import mindspore.nn as nn +from mindspore import Tensor +from mindspore.context import ParallelMode +from mindspore.parallel._auto_parallel_context import auto_parallel_context +from mindspore.communication.management import get_group_size +import mindspore.ops as ops +from .fpn import mobilenet_v1_fpn, resnet50_fpn +from .vgg16 import vgg16 +from .mobilenet_v1 import mobilenet_v1_Feature + + +def _make_divisible(v, divisor, min_value=None): + """nsures that all layers have a channel number that is divisible by 8.""" + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +def _conv2d(in_channel, out_channel, kernel_size=3, stride=1, pad_mod='same'): + return nn.Conv2d(in_channel, out_channel, kernel_size=kernel_size, stride=stride, + padding=0, pad_mode=pad_mod, has_bias=True) + + +def _bn(channel): + return nn.BatchNorm2d(channel, eps=1e-3, momentum=0.97, + gamma_init=1, beta_init=0, moving_mean_init=0, moving_var_init=1) + + +def _last_conv2d(in_channel, out_channel, kernel_size=3, stride=1, pad_mod='same', pad=0): + in_channels = in_channel + out_channels = in_channel + depthwise_conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, pad_mode='same', + padding=pad, group=in_channels) + conv = _conv2d(in_channel, out_channel, kernel_size=1) + return nn.SequentialCell([depthwise_conv, _bn(in_channel), nn.ReLU6(), conv]) + + +class ConvBNReLU(nn.Cell): + """ + Convolution/Depthwise fused with Batchnorm and ReLU block definition. + + Args: + in_planes (int): Input channel. + out_planes (int): Output channel. + kernel_size (int): Input kernel size. + stride (int): Stride size for the first convolutional layer. Default: 1. + groups (int): channel group. Convolution is 1 while Depthiwse is input channel. Default: 1. + shared_conv(Cell): Use the weight shared conv, default: None. + + Returns: + Tensor, output tensor. + + Examples: + >>> ConvBNReLU(16, 256, kernel_size=1, stride=1, groups=1) + """ + def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1, shared_conv=None): + super(ConvBNReLU, self).__init__() + padding = 0 + in_channels = in_planes + out_channels = out_planes + if shared_conv is None: + if groups == 1: + conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, pad_mode='same', padding=padding) + else: + out_channels = in_planes + conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, pad_mode='same', + padding=padding, group=in_channels) + layers = [conv, _bn(out_planes), nn.ReLU6()] + else: + layers = [shared_conv, _bn(out_planes), nn.ReLU6()] + self.features = nn.SequentialCell(layers) + + def construct(self, x): + output = self.features(x) + return output + + +class InvertedResidual(nn.Cell): + """ + Residual block definition. + + Args: + inp (int): Input channel. + oup (int): Output channel. + stride (int): Stride size for the first convolutional layer. Default: 1. + expand_ratio (int): expand ration of input channel + + Returns: + Tensor, output tensor. + + Examples: + >>> ResidualBlock(3, 256, 1, 1) + """ + def __init__(self, inp, oup, stride, expand_ratio, last_relu=False): + super(InvertedResidual, self).__init__() + assert stride in [1, 2] + + hidden_dim = int(round(inp * expand_ratio)) + self.use_res_connect = stride == 1 and inp == oup + + layers = [] + if expand_ratio != 1: + layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) + layers.extend([ + # dw + ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim), + # pw-linear + nn.Conv2d(hidden_dim, oup, kernel_size=1, stride=1, has_bias=False), + _bn(oup), + ]) + self.conv = nn.SequentialCell(layers) + self.cast = ops.Cast() + self.last_relu = last_relu + self.relu = nn.ReLU6() + + def construct(self, x): + identity = x + x = self.conv(x) + if self.use_res_connect: + x = identity + x + if self.last_relu: + x = self.relu(x) + return x + + +class FlattenConcat(nn.Cell): + """ + Concatenate predictions into a single tensor. + + Args: + config (dict): The default config of SSD. + + Returns: + Tensor, flatten predictions. + """ + def __init__(self, config): + super(FlattenConcat, self).__init__() + self.num_ssd_boxes = config.num_ssd_boxes + self.concat = ops.Concat(axis=1) + self.transpose = ops.Transpose() + def construct(self, inputs): + output = () + batch_size = ops.shape(inputs[0])[0] + for x in inputs: + x = self.transpose(x, (0, 2, 3, 1)) + output += (ops.reshape(x, (batch_size, -1)),) + res = self.concat(output) + return ops.reshape(res, (batch_size, self.num_ssd_boxes, -1)) + + +class MultiBox(nn.Cell): + """ + Multibox conv layers. Each multibox layer contains class conf scores and localization predictions. + + Args: + config (dict): The default config of SSD. + + Returns: + Tensor, localization predictions. + Tensor, class conf scores. + """ + def __init__(self, config): + super(MultiBox, self).__init__() + num_classes = config.num_classes + out_channels = config.extras_out_channels + num_default = config.num_default + + loc_layers = [] + cls_layers = [] + for k, out_channel in enumerate(out_channels): + loc_layers += [_last_conv2d(out_channel, 4 * num_default[k], + kernel_size=3, stride=1, pad_mod='same', pad=0)] + cls_layers += [_last_conv2d(out_channel, num_classes * num_default[k], + kernel_size=3, stride=1, pad_mod='same', pad=0)] + + self.multi_loc_layers = nn.layer.CellList(loc_layers) + self.multi_cls_layers = nn.layer.CellList(cls_layers) + self.flatten_concat = FlattenConcat(config) + + def construct(self, inputs): + loc_outputs = () + cls_outputs = () + for i in range(len(self.multi_loc_layers)): + loc_outputs += (self.multi_loc_layers[i](inputs[i]),) + cls_outputs += (self.multi_cls_layers[i](inputs[i]),) + return self.flatten_concat(loc_outputs), self.flatten_concat(cls_outputs) + + +class WeightSharedMultiBox(nn.Cell): + """ + Weight shared Multi-box conv layers. Each multi-box layer contains class conf scores and localization predictions. + All box predictors shares the same conv weight in different features. + + Args: + config (dict): The default config of SSD. + loc_cls_shared_addition(bool): Whether the location predictor and classifier prediction share the + same addition layer. + Returns: + Tensor, localization predictions. + Tensor, class conf scores. + """ + def __init__(self, config, loc_cls_shared_addition=False): + super(WeightSharedMultiBox, self).__init__() + num_classes = config.num_classes + out_channels = config.extras_out_channels[0] + num_default = config.num_default[0] + num_features = len(config.feature_size) + num_addition_layers = config.num_addition_layers + self.loc_cls_shared_addition = loc_cls_shared_addition + + if not loc_cls_shared_addition: + loc_convs = [ + _conv2d(out_channels, out_channels, 3, 1) for x in range(num_addition_layers) + ] + cls_convs = [ + _conv2d(out_channels, out_channels, 3, 1) for x in range(num_addition_layers) + ] + addition_loc_layer_list = [] + addition_cls_layer_list = [] + for _ in range(num_features): + addition_loc_layer = [ + ConvBNReLU(out_channels, out_channels, 3, 1, 1, loc_convs[x]) for x in range(num_addition_layers) + ] + addition_cls_layer = [ + ConvBNReLU(out_channels, out_channels, 3, 1, 1, cls_convs[x]) for x in range(num_addition_layers) + ] + addition_loc_layer_list.append(nn.SequentialCell(addition_loc_layer)) + addition_cls_layer_list.append(nn.SequentialCell(addition_cls_layer)) + self.addition_layer_loc = nn.CellList(addition_loc_layer_list) + self.addition_layer_cls = nn.CellList(addition_cls_layer_list) + else: + convs = [ + _conv2d(out_channels, out_channels, 3, 1) for x in range(num_addition_layers) + ] + addition_layer_list = [] + for _ in range(num_features): + addition_layers = [ + ConvBNReLU(out_channels, out_channels, 3, 1, 1, convs[x]) for x in range(num_addition_layers) + ] + addition_layer_list.append(nn.SequentialCell(addition_layers)) + self.addition_layer = nn.SequentialCell(addition_layer_list) + + loc_layers = [_conv2d(out_channels, 4 * num_default, + kernel_size=3, stride=1, pad_mod='same')] + cls_layers = [_conv2d(out_channels, num_classes * num_default, + kernel_size=3, stride=1, pad_mod='same')] + + self.loc_layers = nn.SequentialCell(loc_layers) + self.cls_layers = nn.SequentialCell(cls_layers) + self.flatten_concat = FlattenConcat(config) + + def construct(self, inputs): + loc_outputs = () + cls_outputs = () + num_heads = len(inputs) + for i in range(num_heads): + if self.loc_cls_shared_addition: + features = self.addition_layer[i](inputs[i]) + loc_outputs += (self.loc_layers(features),) + cls_outputs += (self.cls_layers(features),) + else: + features = self.addition_layer_loc[i](inputs[i]) + loc_outputs += (self.loc_layers(features),) + features = self.addition_layer_cls[i](inputs[i]) + cls_outputs += (self.cls_layers(features),) + return self.flatten_concat(loc_outputs), self.flatten_concat(cls_outputs) + + +class SSD300(nn.Cell): + """ + SSD300 Network. Default backbone is resnet34. + + Args: + backbone (Cell): Backbone Network. + config (dict): The default config of SSD. + + Returns: + Tensor, localization predictions. + Tensor, class conf scores. + + Examples:backbone + SSD300(backbone=resnet34(num_classes=None), + config=config). + """ + def __init__(self, backbone, config, is_training=True): + super(SSD300, self).__init__() + + self.backbone = backbone + in_channels = config.extras_in_channels + out_channels = config.extras_out_channels + ratios = config.extras_ratio + strides = config.extras_strides + residual_list = [] + for i in range(2, len(in_channels)): + residual = InvertedResidual(in_channels[i], out_channels[i], stride=strides[i], + expand_ratio=ratios[i], last_relu=True) + residual_list.append(residual) + self.multi_residual = nn.layer.CellList(residual_list) + self.multi_box = MultiBox(config) + self.is_training = is_training + if not is_training: + self.activation = ops.Sigmoid() + + def construct(self, x): + layer_out_13, output = self.backbone(x) + multi_feature = (layer_out_13, output) + feature = output + for residual in self.multi_residual: + feature = residual(feature) + multi_feature += (feature,) + pred_loc, pred_label = self.multi_box(multi_feature) + if not self.is_training: + pred_label = self.activation(pred_label) + pred_loc = ops.cast(pred_loc, ms.float32) + pred_label = ops.cast(pred_label, ms.float32) + return pred_loc, pred_label + + +class SsdMobilenetV1Fpn(nn.Cell): + """ + SSD Network using mobilenetV1 with fpn to extract features + + Args: + config (dict): The default config of SSD. + is_training (bool): Used for training, default is True. + + Returns: + Tensor, localization predictions. + Tensor, class conf scores. + + Examples:backbone + SsdMobilenetV1Fpn(config, True). + """ + def __init__(self, config): + super(SsdMobilenetV1Fpn, self).__init__() + self.multi_box = WeightSharedMultiBox(config) + self.activation = ops.Sigmoid() + self.feature_extractor = mobilenet_v1_fpn(config) + + def construct(self, x): + features = self.feature_extractor(x) + pred_loc, pred_label = self.multi_box(features) + if not self.training: + pred_label = self.activation(pred_label) + pred_loc = ops.cast(pred_loc, ms.float32) + pred_label = ops.cast(pred_label, ms.float32) + return pred_loc, pred_label + + +class SsdMobilenetV1Feature(nn.Cell): + """ + SSD Network using mobilenetV1 with fpn to extract features + + Args: + config (dict): The default config of SSD. + is_training (bool): Used for training, default is True. + + Returns: + Tensor, localization predictions. + Tensor, class conf scores. + + Examples:backbone + SsdMobilenetV1Feature(config, True). + """ + def __init__(self, config, is_training=True): + super(SsdMobilenetV1Feature, self).__init__() + self.multi_box = MultiBox(config) + self.activation = ops.Sigmoid() + self.feature_extractor = mobilenet_v1_Feature(config) + in_channels = config.extras_in_channels + out_channels = config.extras_out_channels + strides = config.extras_strides + residual_list = [] + for i in range(2, len(in_channels)): + residual = ConvBNReLU(in_channels[i], out_channels[i], stride=strides[i], + ) + residual_list.append(residual) + self.multi_residual = nn.layer.CellList(residual_list) + self.multi_box = MultiBox(config) + self.is_training = is_training + if not is_training: + self.activation = ops.Sigmoid() + + def construct(self, x): + feature, output = self.feature_extractor(x) + multi_feature = (feature, output) + feature = output + for residual in self.multi_residual: + feature = residual(feature) + multi_feature += (feature,) + pred_loc, pred_label = self.multi_box(multi_feature) + if not self.training: + pred_label = self.activation(pred_label) + pred_loc = ops.cast(pred_loc, ms.float32) + pred_label = ops.cast(pred_label, ms.float32) + return pred_loc, pred_label + + +class SsdResNet50Fpn(nn.Cell): + """ + SSD Network using ResNet50 with fpn to extract features + + Args: + config (dict): The default config of SSD. + + Returns: + Tensor, localization predictions. + Tensor, class conf scores. + + Examples:backbone + SsdResNet50Fpn(config). + """ + def __init__(self, config): + super(SsdResNet50Fpn, self).__init__() + self.multi_box = WeightSharedMultiBox(config) + self.activation = ops.Sigmoid() + self.feature_extractor = resnet50_fpn() + + def construct(self, x): + features = self.feature_extractor(x) + pred_loc, pred_label = self.multi_box(features) + if not self.training: + pred_label = self.activation(pred_label) + pred_loc = ops.cast(pred_loc, ms.float32) + pred_label = ops.cast(pred_label, ms.float32) + return pred_loc, pred_label + + +class SigmoidFocalClassificationLoss(nn.Cell): + """" + Sigmoid focal-loss for classification. + + Args: + gamma (float): Hyper-parameter to balance the easy and hard examples. Default: 2.0 + alpha (float): Hyper-parameter to balance the positive and negative example. Default: 0.25 + + Returns: + Tensor, the focal loss. + """ + def __init__(self, gamma=2.0, alpha=0.25): + super(SigmoidFocalClassificationLoss, self).__init__() + self.sigmiod_cross_entropy = ops.SigmoidCrossEntropyWithLogits() + self.sigmoid = ops.Sigmoid() + self.pow = ops.Pow() + self.onehot = ops.OneHot() + self.on_value = Tensor(1.0, ms.float32) + self.off_value = Tensor(0.0, ms.float32) + self.gamma = gamma + self.alpha = alpha + + def construct(self, logits, label): + label = self.onehot(label, ops.shape(logits)[-1], self.on_value, self.off_value) + sigmiod_cross_entropy = self.sigmiod_cross_entropy(logits, label) + sigmoid = self.sigmoid(logits) + label = ops.cast(label, ms.float32) + p_t = label * sigmoid + (1 - label) * (1 - sigmoid) + modulating_factor = self.pow(1 - p_t, self.gamma) + alpha_weight_factor = label * self.alpha + (1 - label) * (1 - self.alpha) + focal_loss = modulating_factor * alpha_weight_factor * sigmiod_cross_entropy + return focal_loss + + +class SSDWithLossCell(nn.Cell): + """" + Provide SSD training loss through network. + + Args: + network (Cell): The training network. + config (dict): SSD config. + + Returns: + Tensor, the loss of the network. + """ + def __init__(self, network, config): + super(SSDWithLossCell, self).__init__() + self.network = network + self.less = ops.Less() + self.tile = ops.Tile() + self.reduce_sum = ops.ReduceSum() + self.expand_dims = ops.ExpandDims() + self.class_loss = SigmoidFocalClassificationLoss(config.gamma, config.alpha) + self.loc_loss = nn.SmoothL1Loss() + + def construct(self, x, gt_loc, gt_label, num_matched_boxes): + pred_loc, pred_label = self.network(x) + mask = ops.cast(self.less(0, gt_label), ms.float32) + num_matched_boxes = self.reduce_sum(ops.cast(num_matched_boxes, ms.float32)) + + # Localization Loss + mask_loc = self.tile(self.expand_dims(mask, -1), (1, 1, 4)) + smooth_l1 = self.loc_loss(pred_loc, gt_loc) * mask_loc + loss_loc = self.reduce_sum(self.reduce_sum(smooth_l1, -1), -1) + + # Classification Loss + loss_cls = self.class_loss(pred_label, gt_label) + loss_cls = self.reduce_sum(loss_cls, (1, 2)) + + return self.reduce_sum((loss_cls + loss_loc) / num_matched_boxes) + + +grad_scale = ops.MultitypeFuncGraph("grad_scale") +@grad_scale.register("Tensor", "Tensor") +def tensor_grad_scale(scale, grad): + return grad * ops.Reciprocal()(scale) + + +class TrainingWrapper(nn.Cell): + """ + Encapsulation class of SSD network training. + + Append an optimizer to the training network after that the construct + function can be called to create the backward graph. + + Args: + network (Cell): The training network. Note that loss function should have been added. + optimizer (Optimizer): Optimizer for updating the weights. + sens (Number): The adjust parameter. Default: 1.0. + use_global_nrom(bool): Whether apply global norm before optimizer. Default: False + """ + def __init__(self, network, optimizer, sens=1.0, use_global_norm=False): + super(TrainingWrapper, self).__init__(auto_prefix=False) + self.network = network + self.network.set_grad() + self.weights = ms.ParameterTuple(network.trainable_params()) + self.optimizer = optimizer + self.grad = ops.GradOperation(get_by_list=True, sens_param=True) + self.sens = sens + self.reducer_flag = False + self.grad_reducer = None + self.use_global_norm = use_global_norm + self.parallel_mode = ms.get_auto_parallel_context("parallel_mode") + if self.parallel_mode in [ParallelMode.DATA_PARALLEL, ParallelMode.HYBRID_PARALLEL]: + self.reducer_flag = True + if self.reducer_flag: + mean = ms.get_auto_parallel_context("gradients_mean") + if auto_parallel_context().get_device_num_is_set(): + degree = ms.get_auto_parallel_context("device_num") + else: + degree = get_group_size() + self.grad_reducer = nn.DistributedGradReducer(optimizer.parameters, mean, degree) + self.hyper_map = ops.HyperMap() + + def construct(self, *args): + weights = self.weights + loss = self.network(*args) + sens = ops.Fill()(ops.DType()(loss), ops.Shape()(loss), self.sens) + grads = self.grad(self.network, weights)(*args, sens) + if self.reducer_flag: + # apply grad reducer on grads + grads = self.grad_reducer(grads) + if self.use_global_norm: + grads = self.hyper_map(ops.partial(grad_scale, ops.scalar_to_array(self.sens)), grads) + grads = ops.clip_by_global_norm(grads) + self.optimizer(grads) + return loss + + +class SSDWithMobileNetV2(nn.Cell): + """ + MobileNetV2 architecture for SSD backbone. + + Args: + width_mult (int): Channels multiplier for round to 8/16 and others. Default is 1. + inverted_residual_setting (list): Inverted residual settings. Default is None + round_nearest (list): Channel round to. Default is 8 + Returns: + Tensor, the 13th feature after ConvBNReLU in MobileNetV2. + Tensor, the last feature in MobileNetV2. + + Examples: + >>> SSDWithMobileNetV2() + """ + def __init__(self, width_mult=1.0, inverted_residual_setting=None, round_nearest=8): + super(SSDWithMobileNetV2, self).__init__() + block = InvertedResidual + input_channel = 32 + last_channel = 1280 + + if inverted_residual_setting is None: + inverted_residual_setting = [ + # t, c, n, s + [1, 16, 1, 1], + [6, 24, 2, 2], + [6, 32, 3, 2], + [6, 64, 4, 2], + [6, 96, 3, 1], + [6, 160, 3, 2], + [6, 320, 1, 1], + ] + if len(inverted_residual_setting[0]) != 4: + raise ValueError("inverted_residual_setting should be non-empty " + "or a 4-element list, got {}".format(inverted_residual_setting)) + + #building first layer + input_channel = _make_divisible(input_channel * width_mult, round_nearest) + self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), round_nearest) + features = [ConvBNReLU(3, input_channel, stride=2)] + # building inverted residual blocks + layer_index = 0 + for t, c, n, s in inverted_residual_setting: + output_channel = _make_divisible(c * width_mult, round_nearest) + for i in range(n): + if layer_index == 13: + hidden_dim = int(round(input_channel * t)) + self.expand_layer_conv_13 = ConvBNReLU(input_channel, hidden_dim, kernel_size=1) + stride = s if i == 0 else 1 + features.append(block(input_channel, output_channel, stride, expand_ratio=t)) + input_channel = output_channel + layer_index += 1 + # building last several layers + features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1)) + + self.features_1 = nn.SequentialCell(features[:14]) + self.features_2 = nn.SequentialCell(features[14:]) + + def construct(self, x): + out = self.features_1(x) + expand_layer_conv_13 = self.expand_layer_conv_13(out) + out = self.features_2(out) + return expand_layer_conv_13, out + + def get_out_channels(self): + return self.last_channel + + +class SsdInferWithDecoder(nn.Cell): + """ + SSD Infer wrapper to decode the bbox locations. + + Args: + network (Cell): the origin ssd infer network without bbox decoder. + default_boxes (Tensor): the default_boxes from anchor generator + config (dict): ssd config + Returns: + Tensor, the locations for bbox after decoder representing (y0,x0,y1,x1) + Tensor, the prediction labels. + + """ + def __init__(self, network, default_boxes, config): + super(SsdInferWithDecoder, self).__init__() + self.network = network + self.default_boxes = default_boxes + self.prior_scaling_xy = config.prior_scaling[0] + self.prior_scaling_wh = config.prior_scaling[1] + + def construct(self, x): + pred_loc, pred_label = self.network(x) + + default_bbox_xy = self.default_boxes[..., :2] + default_bbox_wh = self.default_boxes[..., 2:] + pred_xy = pred_loc[..., :2] * self.prior_scaling_xy * default_bbox_wh + default_bbox_xy + pred_wh = ops.Exp()(pred_loc[..., 2:] * self.prior_scaling_wh) * default_bbox_wh + + pred_xy_0 = pred_xy - pred_wh / 2.0 + pred_xy_1 = pred_xy + pred_wh / 2.0 + pred_xy = ops.Concat(-1)((pred_xy_0, pred_xy_1)) + pred_xy = ops.Maximum()(pred_xy, 0) + pred_xy = ops.Minimum()(pred_xy, 1) + return pred_xy, pred_label + + +def ssd_mobilenet_v1_fpn(**kwargs): + return SsdMobilenetV1Fpn(**kwargs) + +def ssd_mobilenet_v1(**kwargs): + return SsdMobilenetV1Feature(**kwargs) + +def ssd_resnet50_fpn(**kwargs): + return SsdResNet50Fpn(**kwargs) + +def ssd_mobilenet_v2(**kwargs): + return SSDWithMobileNetV2(**kwargs) + + +class SSD300VGG16(nn.Cell): + def __init__(self, config): + super(SSD300VGG16, self).__init__() + + # VGG16 backbone: block1~5 + self.backbone = vgg16() + + # SSD blocks: block6~7 + self.b6_1 = nn.Conv2d(in_channels=512, out_channels=1024, kernel_size=3, padding=6, dilation=6, pad_mode='pad') + self.b6_2 = nn.Dropout(0.5) + + self.b7_1 = nn.Conv2d(in_channels=1024, out_channels=1024, kernel_size=1) + self.b7_2 = nn.Dropout(0.5) + + # Extra Feature Layers: block8~11 + self.b8_1 = nn.Conv2d(in_channels=1024, out_channels=256, kernel_size=1, padding=1, pad_mode='pad') + self.b8_2 = nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=2, pad_mode='valid') + + self.b9_1 = nn.Conv2d(in_channels=512, out_channels=128, kernel_size=1, padding=1, pad_mode='pad') + self.b9_2 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=2, pad_mode='valid') + + self.b10_1 = nn.Conv2d(in_channels=256, out_channels=128, kernel_size=1) + self.b10_2 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, pad_mode='valid') + + self.b11_1 = nn.Conv2d(in_channels=256, out_channels=128, kernel_size=1) + self.b11_2 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, pad_mode='valid') + + # boxes + self.multi_box = MultiBox(config) + if not self.training: + self.activation = ops.Sigmoid() + + def construct(self, x): + # VGG16 backbone: block1~5 + block4, x = self.backbone(x) + + # SSD blocks: block6~7 + x = self.b6_1(x) # 1024 + x = self.b6_2(x) + + x = self.b7_1(x) # 1024 + x = self.b7_2(x) + block7 = x + + # Extra Feature Layers: block8~11 + x = self.b8_1(x) # 256 + x = self.b8_2(x) # 512 + block8 = x + + x = self.b9_1(x) # 128 + x = self.b9_2(x) # 256 + block9 = x + + x = self.b10_1(x) # 128 + x = self.b10_2(x) # 256 + block10 = x + + x = self.b11_1(x) # 128 + x = self.b11_2(x) # 256 + block11 = x + + # boxes + multi_feature = (block4, block7, block8, block9, block10, block11) + pred_loc, pred_label = self.multi_box(multi_feature) + if not self.training: + pred_label = self.activation(pred_label) + pred_loc = ops.cast(pred_loc, ms.float32) + pred_label = ops.cast(pred_label, ms.float32) + return pred_loc, pred_label + + +def ssd_vgg16(**kwargs): + return SSD300VGG16(**kwargs) diff --git a/cv/detection/ssd/MindSpore/src/vgg16.py b/cv/detection/ssd/MindSpore/src/vgg16.py new file mode 100755 index 0000000000000000000000000000000000000000..af02a823a839829bd08d5a331760d815f5eb839b --- /dev/null +++ b/cv/detection/ssd/MindSpore/src/vgg16.py @@ -0,0 +1,99 @@ +# 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. +# ============================================================================ + +"""VGG16 backbone for SSD""" + +from mindspore import nn +from src.model_utils.config import config + +pretrain_vgg_bn = config.pretrain_vgg_bn +ssd_vgg_bn = config.ssd_vgg_bn + + +def _get_key_mapper(): + vgg_key_num = [1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5] + size = len(vgg_key_num) + + pretrain_vgg_bn_false = [0, 2, 5, 7, 10, 12, 14, 17, 19, 21, 24, 26, 28] + pretrain_vgg_bn_true = [0, 3, 7, 10, 14, 17, 20, 24, 27, 30, 34, 37, 40] + ssd_vgg_bn_false = [0, 2, 0, 2, 0, 2, 4, 0, 2, 4, 0, 2, 4] + ssd_vgg_bn_true = [0, 3, 0, 3, 0, 3, 6, 0, 3, 6, 0, 3, 6] + + pretrain_vgg_keys = pretrain_vgg_bn_true if pretrain_vgg_bn else pretrain_vgg_bn_false + ssd_vgg_keys = ssd_vgg_bn_true if ssd_vgg_bn else ssd_vgg_bn_false + + pretrain_vgg_keys = ['layers.' + str(pretrain_vgg_keys[i]) for i in range(size)] + ssd_vgg_keys = ['b' + str(vgg_key_num[i]) + '.' + str(ssd_vgg_keys[i]) for i in range(size)] + + return {pretrain_vgg_keys[i]: ssd_vgg_keys[i] for i in range(size)} + + +ssd_vgg_key_mapper = _get_key_mapper() + + +def _make_layer(channels): + in_channels = channels[0] + layers = [] + for out_channels in channels[1:]: + layers.append(nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=3)) + if ssd_vgg_bn: + layers.append(nn.BatchNorm2d(out_channels)) + layers.append(nn.ReLU()) + in_channels = out_channels + return nn.SequentialCell(layers) + + +class VGG16(nn.Cell): + def __init__(self): + super(VGG16, self).__init__() + self.b1 = _make_layer([3, 64, 64]) + self.b2 = _make_layer([64, 128, 128]) + self.b3 = _make_layer([128, 256, 256, 256]) + self.b4 = _make_layer([256, 512, 512, 512]) + self.b5 = _make_layer([512, 512, 512, 512]) + + self.m1 = nn.MaxPool2d(kernel_size=2, stride=2, pad_mode='SAME') + self.m2 = nn.MaxPool2d(kernel_size=2, stride=2, pad_mode='SAME') + self.m3 = nn.MaxPool2d(kernel_size=2, stride=2, pad_mode='SAME') + self.m4 = nn.MaxPool2d(kernel_size=2, stride=2, pad_mode='SAME') + self.m5 = nn.MaxPool2d(kernel_size=3, stride=1, pad_mode='SAME') + + def construct(self, x): + # block1 + x = self.b1(x) + x = self.m1(x) + + # block2 + x = self.b2(x) + x = self.m2(x) + + # block3 + x = self.b3(x) + x = self.m3(x) + + # block4 + x = self.b4(x) + block4 = x + x = self.m4(x) + + # block5 + x = self.b5(x) + x = self.m5(x) + + return block4, x + + +def vgg16(): + return VGG16() diff --git a/cv/detection/ssd/MindSpore/train.py b/cv/detection/ssd/MindSpore/train.py new file mode 100755 index 0000000000000000000000000000000000000000..e18062d5f409ce7f16fa2ef35bd4e9465b110988 --- /dev/null +++ b/cv/detection/ssd/MindSpore/train.py @@ -0,0 +1,199 @@ +# Copyright 2020-2022 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 SSD and get checkpoint files.""" + +import os +import mindspore as ms +import mindspore.nn as nn +from mindspore import Tensor +from mindspore.communication.management import init, get_rank +from mindspore.train.callback import CheckpointConfig, ModelCheckpoint, LossMonitor, TimeMonitor +from mindspore.train import Model +from mindspore.context import ParallelMode +from mindspore.common import set_seed, dtype +from src.ssd import SSD300, SsdInferWithDecoder, SSDWithLossCell, TrainingWrapper, ssd_mobilenet_v2,\ + ssd_mobilenet_v1_fpn, ssd_mobilenet_v1, ssd_resnet50_fpn, ssd_vgg16 +from src.dataset import create_ssd_dataset, create_mindrecord +from src.lr_schedule import get_lr +from src.init_params import init_net_param, filter_checkpoint_parameter_by_list +from src.eval_callback import EvalCallBack +from src.eval_utils import apply_eval +from src.box_utils import default_boxes +from src.model_utils.config import config +from src.model_utils.moxing_adapter import moxing_wrapper + +set_seed(1) + +def ssd_model_build(): + if config.model_name == "ssd300": + backbone = ssd_mobilenet_v2() + ssd = SSD300(backbone=backbone, config=config) + init_net_param(ssd) + if config.freeze_layer == "backbone": + for param in backbone.feature_1.trainable_params(): + param.requires_grad = False + elif config.model_name == "ssd_mobilenet_v1_fpn": + ssd = ssd_mobilenet_v1_fpn(config=config) + init_net_param(ssd) + if config.feature_extractor_base_param != "": + param_dict = ms.load_checkpoint(config.feature_extractor_base_param) + for x in list(param_dict.keys()): + param_dict["network.feature_extractor.mobilenet_v1." + x] = param_dict[x] + del param_dict[x] + ms.load_param_into_net(ssd.feature_extractor.mobilenet_v1.network, param_dict) + elif config.model_name == "ssd_mobilenet_v1": + ssd = ssd_mobilenet_v1(config=config) + init_net_param(ssd) + if config.feature_extractor_base_param != "": + param_dict = ms.load_checkpoint(config.feature_extractor_base_param) + for x in list(param_dict.keys()): + param_dict["network.feature_extractor.mobilenet_v1." + x] = param_dict[x] + del param_dict[x] + ms.load_param_into_net(ssd.feature_extractor.mobilenet_v1.network, param_dict) + elif config.model_name == "ssd_resnet50_fpn": + ssd = ssd_resnet50_fpn(config=config) + init_net_param(ssd) + if config.feature_extractor_base_param != "": + param_dict = ms.load_checkpoint(config.feature_extractor_base_param) + for x in list(param_dict.keys()): + param_dict["network.feature_extractor.resnet." + x] = param_dict[x] + del param_dict[x] + ms.load_param_into_net(ssd.feature_extractor.resnet, param_dict) + elif config.model_name == "ssd_vgg16": + ssd = ssd_vgg16(config=config) + init_net_param(ssd) + if config.feature_extractor_base_param != "": + param_dict = ms.load_checkpoint(config.feature_extractor_base_param) + from src.vgg16 import ssd_vgg_key_mapper + for k in ssd_vgg_key_mapper: + v = ssd_vgg_key_mapper[k] + param_dict["network.backbone." + v + ".weight"] = param_dict[k + ".weight"] + del param_dict[k + ".weight"] + ms.load_param_into_net(ssd.backbone, param_dict) + else: + raise ValueError(f'config.model: {config.model_name} is not supported') + return ssd + +def set_graph_kernel_context(device_target, model): + if device_target == "GPU" and model == "ssd300": + # Enable graph kernel for default model ssd300 on GPU back-end. + ms.set_context(enable_graph_kernel=True, + graph_kernel_flags="--enable_parallel_fusion --enable_expand_ops=Conv2D") + if device_target == "GPU" and model == "ssd_mobilenet_v1": + # Enable graph kernel for default model ssd300 on GPU back-end. + ms.context.set_context(enable_graph_kernel=True, + graph_kernel_flags="--enable_parallel_fusion --enable_expand_ops=Conv2D") + +@moxing_wrapper() +def train_net(): + if hasattr(config, 'num_ssd_boxes') and config.num_ssd_boxes == -1: + num = 0 + h, w = config.img_shape + for i in range(len(config.steps)): + num += (h // config.steps[i]) * (w // config.steps[i]) * config.num_default[i] + config.num_ssd_boxes = num + + rank = 0 + device_num = 1 + loss_scale = float(config.loss_scale) + if config.device_target == "CPU": + loss_scale = 1.0 + ms.set_context(mode=ms.GRAPH_MODE, device_target="CPU") + else: + ms.set_context(mode=ms.GRAPH_MODE, device_target=config.device_target, device_id=config.device_id) + set_graph_kernel_context(config.device_target, config.model_name) + if config.run_distribute: + device_num = config.device_num + ms.reset_auto_parallel_context() + ms.set_auto_parallel_context(parallel_mode=ParallelMode.DATA_PARALLEL, gradients_mean=True, + device_num=device_num) + init() + if config.all_reduce_fusion_config: + ms.set_auto_parallel_context(all_reduce_fusion_config=config.all_reduce_fusion_config) + rank = get_rank() + + mindrecord_file = create_mindrecord(config.dataset, "ssd.mindrecord", True) + + if config.only_create_dataset: + return + + # When create MindDataset, using the fitst mindrecord file, such as ssd.mindrecord0. + use_multiprocessing = (config.device_target != "CPU") + dataset = create_ssd_dataset(mindrecord_file, batch_size=config.batch_size, + device_num=device_num, rank=rank, use_multiprocessing=use_multiprocessing) + + dataset_size = dataset.get_dataset_size() + print(f"Create dataset done! dataset size is {dataset_size}") + ssd = ssd_model_build() + if (hasattr(config, 'use_float16') and config.use_float16): + ssd.to_float(dtype.float16) + net = SSDWithLossCell(ssd, config) + + # checkpoint + ckpt_config = CheckpointConfig(save_checkpoint_steps=dataset_size * config.save_checkpoint_epochs) + ckpt_save_dir = config.output_path +'/ckpt_{}/'.format(rank) + ckpoint_cb = ModelCheckpoint(prefix="ssd", directory=ckpt_save_dir, config=ckpt_config) + + if config.pre_trained: + param_dict = ms.load_checkpoint(config.pre_trained) + if config.filter_weight: + filter_checkpoint_parameter_by_list(param_dict, config.checkpoint_filter_list) + ms.load_param_into_net(net, param_dict, True) + + lr = Tensor(get_lr(global_step=config.pre_trained_epoch_size * dataset_size, + lr_init=config.lr_init, lr_end=config.lr_end_rate * config.lr, lr_max=config.lr, + warmup_epochs=config.warmup_epochs, + total_epochs=config.epoch_size, + steps_per_epoch=dataset_size)) + + if hasattr(config, 'use_global_norm') and config.use_global_norm: + opt = nn.Momentum(filter(lambda x: x.requires_grad, net.get_parameters()), lr, + config.momentum, config.weight_decay, 1.0) + net = TrainingWrapper(net, opt, loss_scale, True) + else: + opt = nn.Momentum(filter(lambda x: x.requires_grad, net.get_parameters()), lr, + config.momentum, config.weight_decay, loss_scale) + net = TrainingWrapper(net, opt, loss_scale) + + callback = [TimeMonitor(data_size=dataset_size), LossMonitor(), ckpoint_cb] + if config.run_eval: + eval_net = SsdInferWithDecoder(ssd, Tensor(default_boxes), config) + eval_net.set_train(False) + mindrecord_file = create_mindrecord(config.dataset, "ssd_eval.mindrecord", False) + eval_dataset = create_ssd_dataset(mindrecord_file, batch_size=config.batch_size, + is_training=False, use_multiprocessing=False) + if config.dataset == "coco": + anno_json = os.path.join(config.coco_root, config.instances_set.format(config.val_data_type)) + elif config.dataset == "voc": + anno_json = os.path.join(config.voc_root, config.voc_json) + else: + raise ValueError('SSD eval only support dataset mode is coco and voc!') + eval_param_dict = {"net": eval_net, "dataset": eval_dataset, "anno_json": anno_json} + eval_cb = EvalCallBack(apply_eval, eval_param_dict, interval=config.eval_interval, + eval_start_epoch=config.eval_start_epoch, save_best_ckpt=True, + ckpt_directory=ckpt_save_dir, besk_ckpt_name="best_map.ckpt", + metrics_name="mAP") + callback.append(eval_cb) + model = Model(net) + dataset_sink_mode = False + if config.mode_sink == "sink" and config.device_target != "CPU": + print("In sink mode, one epoch return a loss.") + dataset_sink_mode = True + print("Start train SSD, the first epoch will be slower because of the graph compilation.") + model.train(config.epoch_size, dataset, callbacks=callback, dataset_sink_mode=dataset_sink_mode) + +if __name__ == '__main__': + train_net() diff --git a/cv/image_generation/dcgan/MindSpore/README.md b/cv/image_generation/dcgan/MindSpore/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a12eb335f1e114481c765d2c66edf3b46d522ba6 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/README.md @@ -0,0 +1,51 @@ + +# DCGAN +## Model description + +The deep convolutional generative adversarial networks (DCGANs) first introduced CNN into the GAN structure, and the strong feature extraction ability of convolution layer was used to improve the generation effect of GAN. + +[Paper](https://arxiv.org/pdf/1511.06434.pdf): Radford A, Metz L, Chintala S. Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks[J]. Computer ence, 2015. +## Step 1: Installing +``` +pip3 install -r requirements.txt +``` +## Step 2: Prepare Datasets + +Train DCGAN Dataset used: [Imagenet-1k]() + +- Dataset size: ~125G, 224*224 colorful images in 1000 classes + - Train: 120G, 1281167 images + - Test: 5G, 50000 images +- Data format: RGB images. + - Note: Data will be processed in src/dataset.py + +```path + +└─imagenet_original + └─train +``` +## Step 3: Training +### On single GPU +```bash +python3 train.py --device_id=2 --data_url=/home/datasets/cv/imagenet/train --train_url=./ --device_target=GPU +``` +### [Evaluation] + +```bash +python3 -u eval.py --device_id=$DEVICE_ID --img_url=$PATH1 --ckpt_url=$PATH2 --device_target=GPU +``` + +### [Evaluation result] +### 单卡性能数据:BI-V100 +![image](image2022-9-14_10-39-29.png) +![image](image2022-9-14_10-41-12.png) +### 单卡性能数据:NV-V100S +![image](image2022-9-13_13-5-52.png) +![image](image2022-9-13_13-12-42.png) + + + + + + + diff --git a/cv/image_generation/dcgan/MindSpore/ascend310_infer/CMakeLists.txt b/cv/image_generation/dcgan/MindSpore/ascend310_infer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d8c92f9b1d031943418145713e41fdda83bd73f0 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/ascend310_infer/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.14.1) +project(Ascend310Infer) +add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -std=c++17 -Werror -Wall -fPIE -Wl,--allow-shlib-undefined") +set(PROJECT_SRC_ROOT ${CMAKE_CURRENT_LIST_DIR}/) +option(MINDSPORE_PATH "mindspore install path" "") +include_directories(${MINDSPORE_PATH}) +include_directories(${MINDSPORE_PATH}/include) +include_directories(${PROJECT_SRC_ROOT}) +find_library(MS_LIB libmindspore.so ${MINDSPORE_PATH}/lib) +file(GLOB_RECURSE MD_LIB ${MINDSPORE_PATH}/_c_dataengine*) + +add_executable(main src/main.cc src/utils.cc) +find_package(gflags REQUIRED) +target_link_libraries(main ${MS_LIB} ${MD_LIB} gflags) + diff --git a/cv/image_generation/dcgan/MindSpore/ascend310_infer/build.sh b/cv/image_generation/dcgan/MindSpore/ascend310_infer/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..d803d4a1683240601d3028de2aba2ef4640af460 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/ascend310_infer/build.sh @@ -0,0 +1,22 @@ +#!/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. +# ============================================================================ +if [ ! -d out ]; then + mkdir out +fi +cd out || exit +cmake .. \ + -DMINDSPORE_PATH="`pip show mindspore-ascend | grep Location | awk '{print $2"/mindspore"}' | xargs realpath`" +make \ No newline at end of file diff --git a/cv/image_generation/dcgan/MindSpore/ascend310_infer/inc/utils.h b/cv/image_generation/dcgan/MindSpore/ascend310_infer/inc/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..52542080ea932266a8dc25d6080e4b564cce0881 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/ascend310_infer/inc/utils.h @@ -0,0 +1,36 @@ +/** + * 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 MINDSPORE_INFERENCE_UTILS_H_ +#define MINDSPORE_INFERENCE_UTILS_H_ + +#include +#include +#include +#include +#include +#include "include/api/types.h" + +std::vector GetAllFiles(std::string_view dirName); +DIR *OpenDir(std::string_view dirName); +std::string RealPath(std::string_view path); +mindspore::MSTensor ReadFileToTensor(const std::string &file); +int WriteResult(const std::string& imageFile, const std::vector &outputs, const std::string& mode); +std::vector GetAllFiles(std::string dir_name); +std::vector> GetAllInputData(std::string dir_name); + +#endif + diff --git a/cv/image_generation/dcgan/MindSpore/ascend310_infer/src/main.cc b/cv/image_generation/dcgan/MindSpore/ascend310_infer/src/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..336d9632d9f6f114b9a7daa007adf6047c63652b --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/ascend310_infer/src/main.cc @@ -0,0 +1,118 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../inc/utils.h" +#include "include/dataset/execute.h" +#include "include/dataset/transforms.h" +#include "include/dataset/vision.h" +#include "include/dataset/vision_ascend.h" +#include "include/api/types.h" +#include "include/api/model.h" +#include "include/api/serialization.h" +#include "include/api/context.h" + +using mindspore::Serialization; +using mindspore::Model; +using mindspore::Context; +using mindspore::Status; +using mindspore::ModelType; +using mindspore::Graph; +using mindspore::GraphCell; +using mindspore::kSuccess; +using mindspore::MSTensor; +using mindspore::DataType; +using mindspore::dataset::Execute; +using mindspore::dataset::TensorTransform; +using mindspore::dataset::vision::Decode; +using mindspore::dataset::vision::Resize; +using mindspore::dataset::vision::Normalize; +using mindspore::dataset::vision::HWC2CHW; + + +DEFINE_string(mindir_path, "", "model path"); +DEFINE_string(dataset_path, "", "dataset path"); +DEFINE_int32(device_id, 0, "device id"); +DEFINE_string(mode, "", "train or test"); + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + + Model model; + + std::vector model_inputs; + if (RealPath(FLAGS_mindir_path).empty()) { + std::cout << "Invalid mindir" << std::endl; + return -1; + } + + auto context = std::make_shared(); + auto ascend310 = std::make_shared(); + ascend310->SetDeviceID(FLAGS_device_id); + context->MutableDeviceInfo().push_back(ascend310); + mindspore::Graph graph; + Serialization::Load(FLAGS_mindir_path, ModelType::kMindIR, &graph); + + Status ret_build = model.Build(GraphCell(graph), context); + if (ret_build != kSuccess) { + std::cout << "ERROR: Build failed." << std::endl; + return -1; + } + + model_inputs = model.GetInputs(); + if (model_inputs.empty()) { + std::cout << "Invalid model, inputs is empty." << std::endl; + return -1; + } + + auto input0_files = GetAllFiles(FLAGS_dataset_path); + if (input0_files.empty()) { + std::cout << "ERROR: no input data." << std::endl; + return 1; + } + size_t size = input0_files.size(); + for (size_t i = 0; i < size; ++i) { + std::vector inputs; + std::vector outputs; + std::cout << "Start predict input files:" << input0_files[i] < +#include +#include +#include "inc/utils.h" + +using mindspore::MSTensor; +using mindspore::DataType; + +std::vector> GetAllInputData(std::string dir_name) { + std::vector> ret; + + DIR *dir = OpenDir(dir_name); + if (dir == nullptr) { + return {}; + } + struct dirent *filename; + /* read all the files in the dir ~ */ + std::vector sub_dirs; + while ((filename = readdir(dir)) != nullptr) { + std::string d_name = std::string(filename->d_name); + // get rid of "." and ".." + if (d_name == "." || d_name == ".." || d_name.empty()) { + continue; + } + std::string dir_path = RealPath(std::string(dir_name) + "/" + filename->d_name); + struct stat s; + lstat(dir_path.c_str(), &s); + if (!S_ISDIR(s.st_mode)) { + continue; + } + + sub_dirs.emplace_back(dir_path); + } + std::sort(sub_dirs.begin(), sub_dirs.end()); + + (void)std::transform(sub_dirs.begin(), sub_dirs.end(), std::back_inserter(ret), + [](const std::string &d) { return GetAllFiles(d); }); + + return ret; +} + +std::vector GetAllFiles(std::string dir_name) { + struct dirent *filename; + DIR *dir = OpenDir(dir_name); + if (dir == nullptr) { + return {}; + } + std::vector res; + while ((filename = readdir(dir)) != nullptr) { + std::string d_name = std::string(filename->d_name); + if (d_name == "." || d_name == ".." || d_name.size() <= 3) { + continue; + } + res.emplace_back(std::string(dir_name) + "/" + filename->d_name); + } + std::sort(res.begin(), res.end()); + return res; +} + +std::vector GetAllFiles(std::string_view dirName) { + std::cout << "string_view" << std::endl; + struct dirent *filename; + DIR *dir = OpenDir(dirName); + if (dir == nullptr) { + return {}; + } + std::vector res; + while ((filename = readdir(dir)) != nullptr) { + std::string dName = std::string(filename->d_name); + if (dName == "." || dName == ".." || filename->d_type != DT_REG) { + continue; + } + res.emplace_back(std::string(dirName) + "/" + filename->d_name); + } + std::sort(res.begin(), res.end()); + for (auto &f : res) { + std::cout << "image file: " << f << std::endl; + } + return res; +} + +int WriteResult(const std::string& imageFile, const std::vector &outputs, const std::string& mode) { + std::string homePath = "./result_Files_"+mode; + const int INVALID_POINTER = -1; + const int ERROR = -2; + for (size_t i = 0; i < outputs.size(); ++i) { + size_t outputSize; + std::shared_ptr netOutput; + netOutput = outputs[i].Data(); + outputSize = outputs[i].DataSize(); + int pos = imageFile.rfind('/'); + std::string fileName(imageFile, pos + 1); + fileName.replace(fileName.find('.'), fileName.size() - fileName.find('.'), "_"+mode+"_" + std::to_string(i) + + ".bin"); + std::string outFileName = homePath + "/" + fileName; + FILE *outputFile = fopen(outFileName.c_str(), "wb"); + if (outputFile == nullptr) { + std::cout << "open result file " << outFileName << " failed" << std::endl; + return INVALID_POINTER; + } + size_t size = fwrite(netOutput.get(), sizeof(char), outputSize, outputFile); + if (size != outputSize) { + fclose(outputFile); + outputFile = nullptr; + std::cout << "write result file " << outFileName << " failed, write size[" << size << + "] is smaller than output size[" << outputSize << "], maybe the disk is full." << std::endl; + return ERROR; + } + fclose(outputFile); + std::cout << "save result file " << outFileName << " success" << std::endl; + outputFile = nullptr; + } + return 0; +} + +mindspore::MSTensor ReadFileToTensor(const std::string &file) { + if (file.empty()) { + std::cout << "Pointer file is nullptr" << std::endl; + return mindspore::MSTensor(); + } + + std::ifstream ifs(file); + if (!ifs.good()) { + std::cout << "File: " << file << " is not exist" << std::endl; + return mindspore::MSTensor(); + } + + if (!ifs.is_open()) { + std::cout << "File: " << file << "open failed" << std::endl; + return mindspore::MSTensor(); + } + + ifs.seekg(0, std::ios::end); + size_t size = ifs.tellg(); + mindspore::MSTensor buffer(file, mindspore::DataType::kNumberTypeUInt8, + {static_cast(size)}, nullptr, size); + + ifs.seekg(0, std::ios::beg); + ifs.read(reinterpret_cast(buffer.MutableData()), size); + ifs.close(); + + return buffer; +} + +DIR *OpenDir(std::string_view dirName) { + if (dirName.empty()) { + std::cout << " dirName is null ! " << std::endl; + return nullptr; + } + std::string realPath = RealPath(dirName); + struct stat s; + lstat(realPath.c_str(), &s); + if (!S_ISDIR(s.st_mode)) { + std::cout << "dirName is not a valid directory !" << std::endl; + return nullptr; + } + DIR *dir; + dir = opendir(realPath.c_str()); + if (dir == nullptr) { + std::cout << "Can not open dir " << dirName << std::endl; + return nullptr; + } + std::cout << "Successfully opened the dir " << dirName << std::endl; + return dir; +} + +std::string RealPath(std::string_view path) { + char realPathMem[PATH_MAX] = {0}; + char *realPathRet = nullptr; + realPathRet = realpath(path.data(), realPathMem); + if (realPathRet == nullptr) { + std::cout << "File: " << path << " is not exist."; + return ""; + } + + std::string realPath(realPathMem); + std::cout << path << " realpath is: " << realPath << std::endl; + return realPath; +} diff --git a/cv/image_generation/dcgan/MindSpore/docker_start.sh b/cv/image_generation/dcgan/MindSpore/docker_start.sh new file mode 100644 index 0000000000000000000000000000000000000000..ff4ec545577096e72691d0cdbaa4403e2aacadca --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/docker_start.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# Copyright(C) 2022 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 +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 ~/ascend/log/npu/conf/slog/slog.conf:/var/log/npu/conf/slog/slog.conf \ + -v ~/ascend/log/npu/slog/:/var/log/npu/slog -v ~/ascend/log/npu/profiling/:/var/log/npu/profiling \ + -v ~/ascend/log/npu/dump/:/var/log/npu/dump -v ~/ascend/log/npu/:/usr/slog ${docker_image} \ + /bin/bash diff --git a/cv/image_generation/dcgan/MindSpore/eval.py b/cv/image_generation/dcgan/MindSpore/eval.py new file mode 100644 index 0000000000000000000000000000000000000000..ba2e93a90f1285926f09246f0d8d9068166596c1 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/eval.py @@ -0,0 +1,82 @@ +# Copyright 2021-2022 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. +# ============================================================================ +"""dcgan eval""" +import argparse +import numpy as np +from mindspore import context, Tensor, nn, load_checkpoint + +from src.config import dcgan_imagenet_cfg as cfg +from src.generator import Generator +from src.discriminator import Discriminator +from src.cell import WithLossCellD, WithLossCellG +from src.dcgan import DCGAN + + +def save_imgs(gen_imgs, img_url): + """save_imgs function""" + import matplotlib + matplotlib.use('Agg') + import matplotlib.pyplot as plt + for i in range(gen_imgs.shape[0]): + plt.subplot(4, 4, i + 1) + gen_imgs[i] = gen_imgs[i] * 127.5 + 127.5 + perm = (1, 2, 0) + show_imgs = np.transpose(gen_imgs[i], perm) + sdf = show_imgs.astype(int) + plt.imshow(sdf) + plt.axis("off") + plt.savefig(img_url + "/generate.png") + + +def load_dcgan(ckpt_url): + """load_dcgan function""" + netD = Discriminator() + netG = Generator() + + criterion = nn.BCELoss(reduction='mean') + + netD_with_criterion = WithLossCellD(netD, netG, criterion) + netG_with_criterion = WithLossCellG(netD, netG, criterion) + + optimizerD = nn.Adam(netD.trainable_params(), learning_rate=cfg.learning_rate, beta1=cfg.beta1) + optimizerG = nn.Adam(netG.trainable_params(), learning_rate=cfg.learning_rate, beta1=cfg.beta1) + + myTrainOneStepCellForD = nn.TrainOneStepCell(netD_with_criterion, optimizerD) + myTrainOneStepCellForG = nn.TrainOneStepCell(netG_with_criterion, optimizerG) + + dcgan = DCGAN(myTrainOneStepCellForD, myTrainOneStepCellForG) + load_checkpoint(ckpt_url, dcgan) + netG_trained = dcgan.myTrainOneStepCellForG.network.netG + return netG_trained + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='MindSpore dcgan training') + parser.add_argument('--device_target', type=str, default='Ascend', help='Ascend or GPU') + parser.add_argument('--device_id', type=int, default=0, help='device id of Ascend or GPU (Default: 0)') + parser.add_argument('--img_url', type=str, default=None, help='img save path') + parser.add_argument('--ckpt_url', type=str, default=None, help='checkpoint load path') + args = parser.parse_args() + + context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target) + context.set_context(device_id=args.device_id) + + fixed_noise = Tensor(np.random.normal(size=(16, cfg.latent_size, 1, 1)).astype("float32")) + + net_G = load_dcgan(args.ckpt_url) + fake = net_G(fixed_noise) + print("================saving images================") + save_imgs(fake.asnumpy(), args.img_url) + print("================success================") diff --git a/cv/image_generation/dcgan/MindSpore/export.py b/cv/image_generation/dcgan/MindSpore/export.py new file mode 100644 index 0000000000000000000000000000000000000000..617ac0883523961988a30e1efb3d36db0273d90f --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/export.py @@ -0,0 +1,172 @@ +# Copyright 2021-2022 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 checkpoint file into air, onnx, mindir models""" +import argparse +import ast +import os + +import numpy as np + +from mindspore import Tensor, nn, ops, context, load_checkpoint, export +import mindspore.common.dtype as mstype + +from src.cell import WithLossCellD, WithLossCellG +from src.dcgan import DCGAN +from src.discriminator import Discriminator +from src.generator import Generator +from src.config import dcgan_imagenet_cfg as cfg + + +def load_dcgan(ckpt_url): + """ + load dcgan from checkpoint file + """ + netD = Discriminator() + netG = Generator() + + criterion = nn.BCELoss(reduction='mean') + + netD_with_criterion = WithLossCellD(netD, netG, criterion) + netG_with_criterion = WithLossCellG(netD, netG, criterion) + + optimizerD = nn.Adam(netD.trainable_params(), learning_rate=cfg.learning_rate, beta1=cfg.beta1) + optimizerG = nn.Adam(netG.trainable_params(), learning_rate=cfg.learning_rate, beta1=cfg.beta1) + + myTrainOneStepCellForD = nn.TrainOneStepCell(netD_with_criterion, optimizerD) + myTrainOneStepCellForG = nn.TrainOneStepCell(netG_with_criterion, optimizerG) + + net = DCGAN(myTrainOneStepCellForD, myTrainOneStepCellForG) + load_checkpoint(ckpt_url, net) + net.set_train(False) + return net + + +def load_discriminator(dcgan_net): + """ + load discriminator layers from dcgan model + """ + netD_trained = dcgan_net.myTrainOneStepCellForD.network.netD + for m in netD_trained.discriminator.cells_and_names(): + if m[0] == '0': + print(m[0], m[1]) + conv_1 = m[1] + elif m[0] == '1': + print(m[0], m[1]) + leakyReLU_1 = m[1] + elif m[0] == '2': + print(m[0], m[1]) + conv_2 = m[1] + elif m[0] == '3': + print(m[0], m[1]) + bm_1 = m[1] + elif m[0] == '4': + print(m[0], m[1]) + leakyReLU_2 = m[1] + elif m[0] == '5': + print(m[0], m[1]) + conv_3 = m[1] + return conv_1, leakyReLU_1, conv_2, bm_1, leakyReLU_2, conv_3 + + +class DiscriminatorConvert(nn.Cell): + """ + Discriminator_convert + """ + + def __init__(self, conv1, leakyReLU1, conv2, bm1, leakyReLU2, conv3): + super(DiscriminatorConvert, self).__init__() + self.conv1 = conv1 + self.leakyReLU1 = leakyReLU1 + self.conv2 = conv2 + self.bm1 = bm1 + self.leakyReLU2 = leakyReLU2 + self.conv3 = conv3 + self.maxpool1 = nn.MaxPool2d(kernel_size=4, stride=4) + self.maxpool2 = nn.MaxPool2d(kernel_size=2, stride=2) + self.concat = ops.Concat(1) + self.reshape = ops.Reshape() + + def construct(self, x): + x = self.conv1(x) + output1 = self.maxpool1(x) + x = self.conv2(self.leakyReLU1(x)) + output2 = self.maxpool2(x) + x = self.conv3(self.leakyReLU2(self.bm1(x))) + output3 = x + result = self.concat((output1, output2, output3)) + result = self.reshape(result, (1, -1)) + return result + + +parser = argparse.ArgumentParser(description='dcgan export') +parser.add_argument("--run_modelart", type=ast.literal_eval, default=False, help="Run on modelArt, default is false.") +parser.add_argument('--device_target', type=str, default='Ascend', choices=('Ascend', 'GPU'), + help='device where the code will be implemented (default: Ascend)') +parser.add_argument("--device_id", type=int, default=0, help="Device id") +parser.add_argument("--batch_size", type=int, default=100, help="batch size") +parser.add_argument("--ckpt_url", default=None, help="Checkpoint file url.") +parser.add_argument("--ckpt_file", default=None, help="Checkpoint file name.") +parser.add_argument('--data_url', default=None, help='Directory contains dataset.') +parser.add_argument('--train_url', default=None, help='Directory contains checkpoint file') +parser.add_argument("--file_name", type=str, default="dcgan", help="output file name.") +parser.add_argument("--file_format", type=str, default="MINDIR", help="file format") +parser.add_argument("--load_netG", type=str, default=False, help="export netG, default is false.") +parser.add_argument("--load_netD", type=str, default=True, help="export netD for infer, default is True.") +parser.add_argument("--load_G_and_D", type=str, default=False, help="export netG and netD, default is false.") +args = parser.parse_args() + +if args.run_modelart: + local_ckpt_url = '/cache/train_outputs' + device_id = int(os.getenv('DEVICE_ID')) + context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", + save_graphs=False) + context.set_context(device_id=device_id) + import moxing as mox + mox.file.copy_parallel(src_url=args.ckpt_url, dst_url=local_ckpt_url) + local_ckpt_url = local_ckpt_url + args.ckpt_file +else: + local_ckpt_url = args.ckpt_file + device_target = args.device_target + device_id = args.device_id + context.set_context(mode=context.GRAPH_MODE, device_target=device_target, save_graphs=False, device_id=device_id) + + +if __name__ == '__main__': + if args.load_netD: + dcgan = load_dcgan(local_ckpt_url) + d_conv1, d_leakyReLU1, d_conv2, d_bm1, d_leakyReLU2, d_conv3 = load_discriminator(dcgan) + discriminator_convert = DiscriminatorConvert(conv1=d_conv1, leakyReLU1=d_leakyReLU1, conv2=d_conv2, bm1=d_bm1, + leakyReLU2=d_leakyReLU2, conv3=d_conv3) + discriminator_convert.set_train(False) + + inputs = Tensor(np.random.rand(args.batch_size, 3, 32, 32), mstype.float32) + export(discriminator_convert, inputs, file_name=args.file_name, file_format=args.file_format) + elif args.load_netG: + dcgan = load_dcgan(local_ckpt_url) + netG_trained = dcgan.myTrainOneStepCellForG.network.netG + netG_trained.set_train(False) + latent_code = Tensor(np.random.rand(args.batch_size, 100, 1, 1), mstype.float32) + export(netG_trained, latent_code, file_name=args.file_name, file_format=args.file_format) + else: + dcgan = load_dcgan(local_ckpt_url) + # inputs = Tensor(np.random.rand(args.batch_size, 3, 448, 448), mstype.float32) + real_data = Tensor(np.random.rand(args.batch_size, 3, 32, 32), mstype.float32) + latent_code = Tensor(np.random.rand(args.batch_size, 100, 1, 1), mstype.float32) + inputs = [real_data, latent_code] + export(dcgan, *inputs, file_name=args.file_name, file_format=args.file_format) + if args.run_modelart: + file_name = args.file_name + "." + args.file_format.lower() + mox.file.copy_parallel(src_url=file_name, + dst_url=os.path.join(args.ckpt_url, file_name)) diff --git a/cv/image_generation/dcgan/MindSpore/gpu_infer/CMakeLists.txt b/cv/image_generation/dcgan/MindSpore/gpu_infer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fe0084e53564dbbb9cf5281c86601a941a5a8324 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/gpu_infer/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.14.1) +project(GpuInfer) +add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -std=c++17 -Werror -Wall -fPIE -Wl,--allow-shlib-undefined") +set(PROJECT_SRC_ROOT ${CMAKE_CURRENT_LIST_DIR}/) +option(MINDSPORE_PATH "mindspore install path" "") +include_directories(${MINDSPORE_PATH}) +include_directories(${MINDSPORE_PATH}/include) +include_directories(${PROJECT_SRC_ROOT}) +find_library(MS_LIB libmindspore.so ${MINDSPORE_PATH}/lib) +file(GLOB_RECURSE MD_LIB ${MINDSPORE_PATH}/_c_dataengine*) + +add_executable(main src/main.cc src/utils.cc) +find_package(gflags REQUIRED) +target_link_libraries(main ${MS_LIB} ${MD_LIB} gflags) + diff --git a/cv/image_generation/dcgan/MindSpore/gpu_infer/build.sh b/cv/image_generation/dcgan/MindSpore/gpu_infer/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..cad23b6251e7cbe6bc7635b740626dd920c3963a --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/gpu_infer/build.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ +if [ -d out ]; then + rm -rf out +fi + +mkdir out +cd out || exit + +if [ -f "Makefile" ]; then + make clean +fi +cmake .. \ + -DMINDSPORE_PATH="`pip show mindspore-gpu | grep Location | awk '{print $2"/mindspore"}' | xargs realpath`" +make \ No newline at end of file diff --git a/cv/image_generation/dcgan/MindSpore/gpu_infer/inc/utils.h b/cv/image_generation/dcgan/MindSpore/gpu_infer/inc/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..492ce809487ce5c6e85e20b3cf071094988b1708 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/gpu_infer/inc/utils.h @@ -0,0 +1,36 @@ +/** + * Copyright 2022 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 MINDSPORE_INFERENCE_UTILS_H_ +#define MINDSPORE_INFERENCE_UTILS_H_ + +#include +#include +#include +#include +#include +#include "include/api/types.h" + +std::vector GetAllFiles(std::string_view dirName); +DIR *OpenDir(std::string_view dirName); +std::string RealPath(std::string_view path); +mindspore::MSTensor ReadFileToTensor(const std::string &file); +int WriteResult(const std::string& imageFile, const std::vector &outputs, const std::string& mode); +std::vector GetAllFiles(std::string dir_name); +std::vector> GetAllInputData(std::string dir_name); + +#endif + diff --git a/cv/image_generation/dcgan/MindSpore/gpu_infer/src/main.cc b/cv/image_generation/dcgan/MindSpore/gpu_infer/src/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..f083b0a5964e2bbc8266b6a554d2a8554e2502d9 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/gpu_infer/src/main.cc @@ -0,0 +1,116 @@ +/** + * Copyright 2022 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../inc/utils.h" +#include "include/dataset/execute.h" +#include "include/dataset/transforms.h" +#include "include/dataset/vision.h" +#include "include/api/types.h" +#include "include/api/model.h" +#include "include/api/serialization.h" +#include "include/api/context.h" + +using mindspore::Serialization; +using mindspore::Model; +using mindspore::Context; +using mindspore::Status; +using mindspore::ModelType; +using mindspore::Graph; +using mindspore::GraphCell; +using mindspore::kSuccess; +using mindspore::MSTensor; +using mindspore::DataType; +using mindspore::dataset::Execute; +using mindspore::dataset::TensorTransform; +using mindspore::dataset::vision::Decode; +using mindspore::dataset::vision::Resize; +using mindspore::dataset::vision::Normalize; +using mindspore::dataset::vision::HWC2CHW; + +DEFINE_string(mindir_path, "", "model path"); +DEFINE_string(dataset_path, "", "dataset path"); +DEFINE_int32(device_id, 0, "device id"); +DEFINE_string(mode, "", "train or test"); + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + + Model model; + + std::vector model_inputs; + if (RealPath(FLAGS_mindir_path).empty()) { + std::cout << "Invalid mindir" << std::endl; + return -1; + } + + auto context = std::make_shared(); + auto gpu_device_info = std::make_shared(); + gpu_device_info->SetDeviceID(FLAGS_device_id); + context->MutableDeviceInfo().push_back(gpu_device_info); + mindspore::Graph graph; + Serialization::Load(FLAGS_mindir_path, ModelType::kMindIR, &graph); + + Status ret_build = model.Build(GraphCell(graph), context); + if (ret_build != kSuccess) { + std::cout << "ERROR: Build failed." << std::endl; + return -1; + } + + model_inputs = model.GetInputs(); + if (model_inputs.empty()) { + std::cout << "Invalid model, inputs is empty." << std::endl; + return -1; + } + + auto input0_files = GetAllFiles(FLAGS_dataset_path); + if (input0_files.empty()) { + std::cout << "ERROR: no input data." << std::endl; + return 1; + } + size_t size = input0_files.size(); + for (size_t i = 0; i < size; ++i) { + std::vector inputs; + std::vector outputs; + std::cout << "Start predict input files:" << input0_files[i] < +#include +#include +#include "inc/utils.h" + +using mindspore::MSTensor; +using mindspore::DataType; + +std::vector> GetAllInputData(std::string dir_name) { + std::vector> ret; + + DIR *dir = OpenDir(dir_name); + if (dir == nullptr) { + return {}; + } + struct dirent *filename; + /* read all the files in the dir ~ */ + std::vector sub_dirs; + while ((filename = readdir(dir)) != nullptr) { + std::string d_name = std::string(filename->d_name); + // get rid of "." and ".." + if (d_name == "." || d_name == ".." || d_name.empty()) { + continue; + } + std::string dir_path = RealPath(std::string(dir_name) + "/" + filename->d_name); + struct stat s; + lstat(dir_path.c_str(), &s); + if (!S_ISDIR(s.st_mode)) { + continue; + } + + sub_dirs.emplace_back(dir_path); + } + std::sort(sub_dirs.begin(), sub_dirs.end()); + + (void)std::transform(sub_dirs.begin(), sub_dirs.end(), std::back_inserter(ret), + [](const std::string &d) { return GetAllFiles(d); }); + + return ret; +} + +std::vector GetAllFiles(std::string dir_name) { + struct dirent *filename; + DIR *dir = OpenDir(dir_name); + if (dir == nullptr) { + return {}; + } + std::vector res; + while ((filename = readdir(dir)) != nullptr) { + std::string d_name = std::string(filename->d_name); + if (d_name == "." || d_name == ".." || d_name.size() <= 3) { + continue; + } + res.emplace_back(std::string(dir_name) + "/" + filename->d_name); + } + std::sort(res.begin(), res.end()); + return res; +} + +std::vector GetAllFiles(std::string_view dirName) { + std::cout << "string_view" << std::endl; + struct dirent *filename; + DIR *dir = OpenDir(dirName); + if (dir == nullptr) { + return {}; + } + std::vector res; + while ((filename = readdir(dir)) != nullptr) { + std::string dName = std::string(filename->d_name); + if (dName == "." || dName == ".." || filename->d_type != DT_REG) { + continue; + } + res.emplace_back(std::string(dirName) + "/" + filename->d_name); + } + std::sort(res.begin(), res.end()); + for (auto &f : res) { + std::cout << "image file: " << f << std::endl; + } + return res; +} + +int WriteResult(const std::string& imageFile, const std::vector &outputs, const std::string& mode) { + std::string homePath = "./result_Files_"+mode; + const int INVALID_POINTER = -1; + const int ERROR = -2; + for (size_t i = 0; i < outputs.size(); ++i) { + size_t outputSize; + std::shared_ptr netOutput; + netOutput = outputs[i].Data(); + outputSize = outputs[i].DataSize(); + int pos = imageFile.rfind('/'); + std::string fileName(imageFile, pos + 1); + fileName.replace(fileName.find('.'), fileName.size() - fileName.find('.'), "_"+mode+"_" + std::to_string(i) + + ".bin"); + std::string outFileName = homePath + "/" + fileName; + FILE *outputFile = fopen(outFileName.c_str(), "wb"); + if (outputFile == nullptr) { + std::cout << "open result file " << outFileName << " failed" << std::endl; + return INVALID_POINTER; + } + size_t size = fwrite(netOutput.get(), sizeof(char), outputSize, outputFile); + if (size != outputSize) { + fclose(outputFile); + outputFile = nullptr; + std::cout << "write result file " << outFileName << " failed, write size[" << size << + "] is smaller than output size[" << outputSize << "], maybe the disk is full." << std::endl; + return ERROR; + } + fclose(outputFile); + std::cout << "save result file " << outFileName << " success" << std::endl; + outputFile = nullptr; + } + return 0; +} + +mindspore::MSTensor ReadFileToTensor(const std::string &file) { + if (file.empty()) { + std::cout << "Pointer file is nullptr" << std::endl; + return mindspore::MSTensor(); + } + + std::ifstream ifs(file); + if (!ifs.good()) { + std::cout << "File: " << file << " is not exist" << std::endl; + return mindspore::MSTensor(); + } + + if (!ifs.is_open()) { + std::cout << "File: " << file << "open failed" << std::endl; + return mindspore::MSTensor(); + } + + ifs.seekg(0, std::ios::end); + size_t size = ifs.tellg(); + mindspore::MSTensor buffer(file, mindspore::DataType::kNumberTypeUInt8, + {static_cast(size)}, nullptr, size); + + ifs.seekg(0, std::ios::beg); + ifs.read(reinterpret_cast(buffer.MutableData()), size); + ifs.close(); + + return buffer; +} + +DIR *OpenDir(std::string_view dirName) { + if (dirName.empty()) { + std::cout << " dirName is null ! " << std::endl; + return nullptr; + } + std::string realPath = RealPath(dirName); + struct stat s; + lstat(realPath.c_str(), &s); + if (!S_ISDIR(s.st_mode)) { + std::cout << "dirName is not a valid directory !" << std::endl; + return nullptr; + } + DIR *dir; + dir = opendir(realPath.c_str()); + if (dir == nullptr) { + std::cout << "Can not open dir " << dirName << std::endl; + return nullptr; + } + std::cout << "Successfully opened the dir " << dirName << std::endl; + return dir; +} + +std::string RealPath(std::string_view path) { + char realPathMem[PATH_MAX] = {0}; + char *realPathRet = nullptr; + realPathRet = realpath(path.data(), realPathMem); + if (realPathRet == nullptr) { + std::cout << "File: " << path << " is not exist."; + return ""; + } + + std::string realPath(realPathMem); + std::cout << path << " realpath is: " << realPath << std::endl; + return realPath; +} diff --git a/cv/image_generation/dcgan/MindSpore/image2022-9-13_13-12-42.png b/cv/image_generation/dcgan/MindSpore/image2022-9-13_13-12-42.png new file mode 100644 index 0000000000000000000000000000000000000000..36901977820dc2db4c1500b8e9eaf13349867479 Binary files /dev/null and b/cv/image_generation/dcgan/MindSpore/image2022-9-13_13-12-42.png differ diff --git a/cv/image_generation/dcgan/MindSpore/image2022-9-13_13-5-52.png b/cv/image_generation/dcgan/MindSpore/image2022-9-13_13-5-52.png new file mode 100644 index 0000000000000000000000000000000000000000..881479905ec808855d4064e0eb95384c3953fd28 Binary files /dev/null and b/cv/image_generation/dcgan/MindSpore/image2022-9-13_13-5-52.png differ diff --git a/cv/image_generation/dcgan/MindSpore/image2022-9-14_10-39-29.png b/cv/image_generation/dcgan/MindSpore/image2022-9-14_10-39-29.png new file mode 100644 index 0000000000000000000000000000000000000000..dd7e306ba3886020cdca2ba05a5dd5d78d382f3d Binary files /dev/null and b/cv/image_generation/dcgan/MindSpore/image2022-9-14_10-39-29.png differ diff --git a/cv/image_generation/dcgan/MindSpore/image2022-9-14_10-41-12.png b/cv/image_generation/dcgan/MindSpore/image2022-9-14_10-41-12.png new file mode 100644 index 0000000000000000000000000000000000000000..62cd6b7bf307ed47eb936da01c4d1bb548e9fb77 Binary files /dev/null and b/cv/image_generation/dcgan/MindSpore/image2022-9-14_10-41-12.png differ diff --git a/cv/image_generation/dcgan/MindSpore/infer/convert/convert_om.sh b/cv/image_generation/dcgan/MindSpore/infer/convert/convert_om.sh new file mode 100644 index 0000000000000000000000000000000000000000..dca25ecec209d651f37ccda5bab31eb4cb550dd0 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/convert/convert_om.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Copyright 2022 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. +# ============================================================================ + +# Parameter format +if [ $# -ne 2 ] +then + echo "Wrong parameter format." + echo "Usage:" + echo " bash $0 INPUT_AIR_PATH OUTPUT_OM_PATH_NAME" + echo "Example:" + echo " bash $0 ./dcgan_16_20220106.air ../models/DCGAN" + + exit 255 +fi + +# DCGAN model from .air to .om +AIR_PATH=$1 +OM_PATH=$2 +atc --input_format=NCHW \ +--framework=1 \ +--model="${AIR_PATH}" \ +--output="${OM_PATH}" \ +--soc_version=Ascend310 + +# Delete unnecessary files +rm fusion_result.json +rm -r kernel_meta/ + +# Modify file permissions +chmod +r+w "${OM_PATH}.om" diff --git a/cv/image_generation/dcgan/MindSpore/infer/data/config/DCGAN.pipeline b/cv/image_generation/dcgan/MindSpore/infer/data/config/DCGAN.pipeline new file mode 100644 index 0000000000000000000000000000000000000000..73dbf49550164c89be32da3e3d936769eac57e83 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/data/config/DCGAN.pipeline @@ -0,0 +1,29 @@ +{ + "DCGAN":{ + "stream_config": { + "deviceId": "0" + }, + "appsrc0":{ + "props": { + "blocksize": "409600" + }, + "factory": "appsrc", + "next": "mxpi_tensorinfer0" + }, + "mxpi_tensorinfer0":{ + "props": { + "dataSource":"appsrc0", + "singleBatchInfer":"1", + "modelPath":"../data/models/DCGAN.om" + }, + "factory": "mxpi_tensorinfer", + "next": "appsink0" + }, + "appsink0":{ + "props": { + "blocksize": "4096000" + }, + "factory": "appsink" + } + } +} \ No newline at end of file diff --git a/cv/image_generation/dcgan/MindSpore/infer/docker_start_infer.sh b/cv/image_generation/dcgan/MindSpore/infer/docker_start_infer.sh new file mode 100644 index 0000000000000000000000000000000000000000..f7d4ba9ef6dd1529085436081e579a5998f9f3e4 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/docker_start_infer.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Copyright 2022 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_infer.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/cv/image_generation/dcgan/MindSpore/infer/mxbase/CMakeLists.txt b/cv/image_generation/dcgan/MindSpore/infer/mxbase/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..94210cd63bd508793e53adaf7e620d32cfde3437 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/mxbase/CMakeLists.txt @@ -0,0 +1,68 @@ +#!/bin/bash + +# Copyright 2022 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. +# ============================================================================ + +cmake_minimum_required(VERSION 3.10.0) +project(DCGAN) +set(TARGET DCGAN) + +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(DEFINED ENV{MXSDK_OPENSOURCE_DIR}) + set(OPENSOURCE_DIR $ENV{MXSDK_OPENSOURCE_DIR}) +else() + set(OPENSOURCE_DIR ${MXBASE_ROOT_DIR}/opensource) +endif() +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/DCGAN.cpp) + +target_link_libraries(${TARGET} glog cpprest mxbase opencv_world stdc++fs) + +install(TARGETS ${TARGET} RUNTIME DESTINATION ${PROJECT_SOURCE_DIR}/) \ No newline at end of file diff --git a/cv/image_generation/dcgan/MindSpore/infer/mxbase/build.sh b/cv/image_generation/dcgan/MindSpore/infer/mxbase/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..6f28aa3b3894fd62ea9084b54321d2078b865c93 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/mxbase/build.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Copyright 2022 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. +# ============================================================================ + +# Parameter format +if [ $# -ne 3 ] +then + echo "Wrong parameter format." + echo "Usage:" + echo " bash $0 OM_PATH RESULT_PATH GEN_NUM" + echo "Example:" + echo " bash $0 ../data/models/DCGAN.om ./results 10" + + exit 255 +fi + + +# Rebuild build folder +rm core +rm -r build +mkdir -p build +# Enter build floder +cd build || exit + +# Cmake & make +if ! cmake ..; +then + echo "[ERROR] Cmake failed." + exit +fi +if ! (make); +then + echo "[ERROR] Make failed." + exit +fi +echo "[INFO] Build successfully." + +# Enter previous floder +cd - || exit +# Rebuild results folder +rm -r results +mkdir -p results + +# run +OM_PATH=$1 +RESULT_PATH=$2 +GEN_NUM=$3 +./build/DCGAN "${OM_PATH}" "${RESULT_PATH}" "${GEN_NUM}" \ No newline at end of file diff --git a/cv/image_generation/dcgan/MindSpore/infer/mxbase/src/DCGAN.cpp b/cv/image_generation/dcgan/MindSpore/infer/mxbase/src/DCGAN.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd0b30069cbce0ffc6073a20c5e05456f3e44e21 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/mxbase/src/DCGAN.cpp @@ -0,0 +1,226 @@ +/** + * Copyright 2022 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 +#include +#include +#include + +#include "DCGAN.h" + +InitParam initParam_; + +APP_ERROR DCGAN::Init(const InitParam &initParam) { + // Param init + initParam_ = initParam; + + // Device init + APP_ERROR ret = MxBase::DeviceManager::GetInstance()->InitDevices(); + if (ret != APP_ERR_OK) { + LogError << "Init devices failed, ret=" << ret << "."; + return ret; + } + + // Context init + ret = MxBase::TensorContext::GetInstance()->SetContext(initParam.deviceId); + if (ret != APP_ERR_OK) { + LogError << "Set context failed, ret=" << ret << "."; + return ret; + } + + // Model init + model_ = std::make_shared(); + ret = model_->Init(initParam.modelPath, modelDesc_); + if (ret != APP_ERR_OK) { + LogError << "ModelInferenceProcessor init failed, ret=" << ret << "."; + return ret; + } + + // create random number from normal distribution (mean=0.0, std=1.0) + std::mt19937 gen_{1213}; + std::normal_distribution dis_{0.0, 1.0}; + + return ret; +} + +APP_ERROR DCGAN::DeInit() { + model_->DeInit(); + MxBase::DeviceManager::GetInstance()->DestroyDevices(); + + return APP_ERR_OK; +} + +APP_ERROR DCGAN::CreateRandomTensorBase(std::vector &inputs) { + MxBase::TensorBase tensorBase; + size_t D0 = initParam_.batchSize, D1 = 100, D2 = 1, D3 = 1; // D0:batchsize + const uint32_t dataSize = D0 * D1 * D2 * D3 * FLOAT32_TYPE_BYTE_NUM; + + float *mat_data = new float[dataSize / FLOAT32_TYPE_BYTE_NUM]; + for (size_t d0 = 0; d0 < D0; d0++) { + for (size_t d1 = 0; d1 < D1; d1++) { + for (size_t d2 = 0; d2 < D2; d2++) { + for (size_t d3 = 0; d3 < D3; d3++) { + int i = d0 * D1 * D2 * D3 + d1 * D2 * D3 + d2 * D3 + d3; + mat_data[i] = dis_(gen_); + } + } + } + } + + MxBase::MemoryData memoryDataDst(dataSize, MxBase::MemoryData::MEMORY_DEVICE, initParam_.deviceId); + MxBase::MemoryData memoryDataSrc(reinterpret_cast(mat_data), + dataSize, MxBase::MemoryData::MEMORY_HOST_MALLOC); + + APP_ERROR ret = MxBase::MemoryHelper::MxbsMallocAndCopy(memoryDataDst, memoryDataSrc); + if (ret != APP_ERR_OK) { + LogError << GetError(ret) << "Memory malloc failed."; + return ret; + } + + std::vector shape = {static_cast(D0), static_cast(D1), + static_cast(D2), static_cast(D3)}; + tensorBase = MxBase::TensorBase(memoryDataDst, false, shape, MxBase::TENSOR_DTYPE_FLOAT32); + + inputs.push_back(tensorBase); + + return APP_ERR_OK; +} + +APP_ERROR DCGAN::Inference(const std::vector &inputs, + std::vector &outputs) { + // apply for output Tensor buffer + auto dtypes = model_->GetOutputDataType(); + for (size_t i = 0; i < modelDesc_.outputTensors.size(); ++i) { + // shape + std::vector shape = {}; + for (size_t j = 0; j < modelDesc_.outputTensors[i].tensorDims.size(); ++j) { + shape.push_back((uint32_t)modelDesc_.outputTensors[i].tensorDims[j]); + } + // define tensor + MxBase::TensorBase tensor(shape, dtypes[i], MxBase::MemoryData::MemoryType::MEMORY_DEVICE, initParam_.deviceId); + // request memory + APP_ERROR ret = MxBase::TensorBase::TensorBaseMalloc(tensor); + if (ret != APP_ERR_OK) { + LogError << "TensorBaseMalloc failed, ret=" << ret << "."; + return ret; + } + outputs.push_back(tensor); + } + + // dynamic information + MxBase::DynamicInfo dynamicInfo = {}; + dynamicInfo.dynamicType = MxBase::DynamicType::STATIC_BATCH; + + // do inferrnce + auto startTime = std::chrono::high_resolution_clock::now(); + APP_ERROR ret = model_->ModelInference(inputs, outputs, dynamicInfo); + auto endTime = std::chrono::high_resolution_clock::now(); + double costMs = std::chrono::duration(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 DCGAN::PostProcess(std::vector outputs, std::vector &resultMats) { + APP_ERROR ret; + ret = outputs[0].ToHost(); + if (ret != APP_ERR_OK) { + LogError << GetError(ret) << "tohost fail."; + return ret; + } + + float *outputPtr = reinterpret_cast(outputs[0].GetBuffer()); + + size_t H = initParam_.imageHeight, W = initParam_.imageWidth, C = CHANNEL; + + for (uint32_t b = 0; b < initParam_.batchSize; b++) { + cv::Mat resultMat(initParam_.imageHeight, initParam_.imageWidth, CV_8UC3); + for (size_t c = 0; c < C; c++) { + for (size_t h = 0; h < H; h++) { + for (size_t w = 0; w < W; w++) { + float *tmpLoc = outputPtr + b * C * H * W + (C - c - 1) * H * W + h * W + w; + // denormalize + float tmpNum = (*tmpLoc) * NORMALIZE_STD + NORMALIZE_MEAN; + // NCHW to NHWC + resultMat.at(h, w)[c] = static_cast(tmpNum); + } + } + } + resultMats.push_back(resultMat); + } + + return ret; +} + +APP_ERROR DCGAN::SaveResult(std::vector &resultMats, const std::string &imgName) { + DIR *dirPtr = opendir(initParam_.savePath.c_str()); + if (dirPtr == nullptr) { + std::string path = "mkdir -p " + initParam_.savePath; + system(path.c_str()); + } + for (uint32_t b = 0; b < initParam_.batchSize; b++) { + std::string file_path = initParam_.savePath + "/" + imgName + "-" + std::to_string(b) + ".jpg"; + cv::imwrite(file_path, resultMats[b]); + std::cout << "[INFO] image saved path: " << file_path << std::endl; + } + + return APP_ERR_OK; +} + +APP_ERROR DCGAN::Process(uint32_t gen_id) { + APP_ERROR ret; + + // create random tensor + std::vector inputs = {}; + std::vector outputs = {}; + ret = CreateRandomTensorBase(inputs); + if (ret != APP_ERR_OK) { + LogError << "CVMatToTensorBase failed, ret=" << ret << "."; + return ret; + } + + // do inference + ret = Inference(inputs, outputs); + if (ret != APP_ERR_OK) { + LogError << "Inference failed, ret=" << ret << "."; + return ret; + } + std::cout << "[INFO] Inference finished!" << std::endl; + + // do postprocess + std::vector resultMats = {}; + ret = PostProcess(outputs, resultMats); + if (ret != APP_ERR_OK) { + LogError << "PostProcess failed, ret=" << ret << "."; + return ret; + } + std::cout << "[INFO] Postprocess finished!" << std::endl; + + // save results + std::string imgName = std::to_string(gen_id); + ret = SaveResult(resultMats, imgName); + if (ret != APP_ERR_OK) { + LogError << "Save result failed, ret=" << ret << "."; + return ret; + } + std::cout << "[INFO] Result saved successfully!" << std::endl; + + return APP_ERR_OK; +} diff --git a/cv/image_generation/dcgan/MindSpore/infer/mxbase/src/DCGAN.h b/cv/image_generation/dcgan/MindSpore/infer/mxbase/src/DCGAN.h new file mode 100644 index 0000000000000000000000000000000000000000..fc9fe585821404be54be4154b2758542bfbce98f --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/mxbase/src/DCGAN.h @@ -0,0 +1,72 @@ +/** + * Copyright 2022 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_DCGAN_H +#define MXBASE_DCGAN_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "MxBase/DeviceManager/DeviceManager.h" +#include "MxBase/Log/Log.h" +#include "MxBase/DvppWrapper/DvppWrapper.h" +#include "MxBase/ModelInfer/ModelInferenceProcessor.h" +#include "MxBase/Tensor/TensorContext/TensorContext.h" + +extern std::vector g_inferCost; +const uint32_t FLOAT32_TYPE_BYTE_NUM = 4; +const float NORMALIZE_MEAN = 127.5; +const float NORMALIZE_STD = 127.5; +const uint32_t CHANNEL = 3; + +struct InitParam { + uint32_t deviceId; + bool checkTensor; + std::string modelPath; + std::string savePath; + uint32_t imageNum; + uint32_t imageWidth; + uint32_t imageHeight; + uint32_t batchSize; +}; + +class DCGAN { + public: + APP_ERROR Init(const InitParam &initParam); + APP_ERROR DeInit(); + APP_ERROR Inference(const std::vector &inputs, std::vector &outputs); + APP_ERROR PostProcess(std::vector outputs, std::vector &resultMats); + APP_ERROR Process(uint32_t gen_id); + APP_ERROR SaveResult(std::vector &resultMats, const std::string &imgName); + APP_ERROR CreateRandomTensorBase(std::vector &inputs); + + private: + std::shared_ptr model_; + MxBase::ModelDesc modelDesc_; + std::mt19937 gen_; + std::normal_distribution dis_; +}; +#endif diff --git a/cv/image_generation/dcgan/MindSpore/infer/mxbase/src/main.cpp b/cv/image_generation/dcgan/MindSpore/infer/mxbase/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa014dbbbcdb13a8c6a04636554b4737ccbeee23 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/mxbase/src/main.cpp @@ -0,0 +1,73 @@ +/** + * Copyright 2022 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 +#include +#include "DCGAN.h" +#include "MxBase/Log/Log.h" + +const uint32_t IMG_CARDINALITY = 1000; +std::vector g_inferCost; + +void InitDCGANParam(InitParam *initParam, char* argv[]) { + initParam->deviceId = 0; + initParam->checkTensor = true; + initParam->modelPath = argv[1]; + initParam->savePath = argv[2]; + initParam->imageNum = std::stoi(argv[3]); + initParam->imageWidth = 32; + initParam->imageHeight = 32; + initParam->batchSize = 16; +} + +int main(int argc, char* argv[]) { + InitParam initParam; + InitDCGANParam(&initParam, argv); + + // Create dcgan + auto dcgan = std::make_shared(); + + // do init + APP_ERROR ret = dcgan->Init(initParam); + if (ret != APP_ERR_OK) { + LogError << "DCGAN init failed, ret=" << ret << "."; + return ret; + } + LogInfo << "End to Init DCGAN."; + + for (uint32_t i = 0; i < initParam.imageNum; i++) { + // do process + LogInfo << "generate image " << i; + ret = dcgan->Process(i); + if (ret != APP_ERR_OK) { + LogError << "DCGAN process failed, ret=" << ret << "."; + dcgan->DeInit(); + return ret; + } + } + + // do deinit + dcgan->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() * IMG_CARDINALITY / costSum << " images/sec."; + return APP_ERR_OK; +} diff --git a/cv/image_generation/dcgan/MindSpore/infer/sdk/build.sh b/cv/image_generation/dcgan/MindSpore/infer/sdk/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..990896553c4ac59786533a4caf9f39022c304edb --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/sdk/build.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Copyright 2022 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. +# ============================================================================ + +# Parameter format +if [ $# -ne 2 ] +then + echo "Wrong parameter format." + echo "Usage:" + echo " bash $0 RESULT_PATH GEN_NUM" + echo "Example:" + echo " bash $0 ./results 10" + + exit 255 +fi + +# Rebuild results folder +rm -r results +mkdir -p results + +# Run main.py +RESULT_PATH=$1 +GEN_NUM=$2 +python3 main.py "${RESULT_PATH}" "${GEN_NUM}" \ No newline at end of file diff --git a/cv/image_generation/dcgan/MindSpore/infer/sdk/main.py b/cv/image_generation/dcgan/MindSpore/infer/sdk/main.py new file mode 100644 index 0000000000000000000000000000000000000000..febef58d0c3a5aa2a70d9d6062077705f3cb3935 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/infer/sdk/main.py @@ -0,0 +1,177 @@ +# Copyright 2022 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. +# ============================================================================ +""" Model Main """ + +import sys +import datetime +import numpy as np +import matplotlib.pyplot as plt +import MxpiDataType_pb2 as MxpiDataType +from StreamManagerApi import StreamManagerApi, InProtobufVector, MxProtobufIn, StringVector + +NORMALIZE_MEAN = 127.5 +NORMALIZE_STD = 127.5 +PLT_SHAPE = [4, 4] + +def create_input(): + """ + Create an random input tensor, size is (16, 100, 1, 1). + + Args: + None + + Returns: + input_data_(numpy.ndarray): An random tensor, size is (16, 100, 1, 1). + + """ + input_data_ = np.random.normal(size=(16, 100, 1, 1)).astype("float32") + + return input_data_ + + +def postprocess_image(result_np_): + """ + Do postprocess to the result of inference, include denormalize and convert from NCHW to NHWC. + + Args: + result_np_(numpy.ndarray): The result of inference, size is (3, 32, 32). + + Returns: + result_np_(numpy.ndarray): Postprocessed result, size is (32, 32, 3). + """ + # denormalize + result_np_ = (result_np_ * NORMALIZE_STD + NORMALIZE_MEAN).astype(np.uint8) + # NCHW to NHWC + result_np_ = result_np_.transpose((1, 2, 0)) + + return result_np_ + + +def save_images(result_np_, save_path_): + """ + Save results of inference to images in groups of 16 (which depends on the model shape) + + Args: + result_np_(numpy.ndarray): The result of inference, size is (16, 3, 32, 32). + save_path_(str): File path to save images in. + + Returns: + None + + """ + result_ = result_np_.copy() + + for ii in range(result_.shape[0]): + image = postprocess_image(result_[ii]) + plt.subplot(PLT_SHAPE[0], PLT_SHAPE[1], ii + 1) + plt.imshow(image) + plt.axis("off") + plt.savefig(save_path_) + + +if __name__ == '__main__': + # init stream manager + stream_manager_api = StreamManagerApi() + ret = stream_manager_api.InitManager() + if ret != 0: + print("[INFO] Failed to init Stream manager, ret=%s" % str(ret)) + exit() + print("[INFO] Init Stream manager successfully!") + + # create streams by pipeline config file + with open("../data/config/DCGAN.pipeline", 'rb') as f: + pipelineStr = f.read() + ret = stream_manager_api.CreateMultipleStreams(pipelineStr) + if ret != 0: + print("[INFO] Failed to create Stream, ret=%s" % str(ret)) + exit() + print("[INFO] Create Stream successfully!") + + res_dir_name = sys.argv[1] + n_epochs = int(sys.argv[2]) + + np.random.seed(1213) + for epoch in range(n_epochs): + print("[INFO] Epoch:", epoch) + + # Get input data + input_data = create_input() + + # Construct the input of the stream + mxpi_tensor_package_list = MxpiDataType.MxpiTensorPackageList() + tensor_package_vec = mxpi_tensor_package_list.tensorPackageVec.add() + + tensorVec = tensor_package_vec.tensorVec.add() + tensorVec.memType = 1 + tensorVec.deviceId = 0 + + tensorVec.tensorDataSize = int( + input_data.shape[0]*input_data.shape[1]*input_data.shape[2]*input_data.shape[3]*4) + tensorVec.tensorDataType = 0 # float32 + for i in input_data.shape: + tensorVec.tensorShape.append(i) + tensorVec.dataStr = input_data.tobytes() + + protobuf = MxProtobufIn() + protobuf.key = b'appsrc0' + protobuf.type = b'MxTools.MxpiTensorPackageList' + protobuf.protobuf = mxpi_tensor_package_list.SerializeToString() + + protobuf_vec = InProtobufVector() + protobuf_vec.push_back(protobuf) + + # Inputs data to a specified stream based on streamName. + stream_name = b'DCGAN' + in_plugin_id = 0 + + # Send data to stream + unique_id = stream_manager_api.SendProtobuf( + stream_name, in_plugin_id, protobuf_vec) + if unique_id < 0: + print("[INFO] Failed to send data to stream.") + exit() + print("[INFO] Send data to stream successfully!") + + # Obtain the inference result by specifying streamName and uniqueId. + keyVec = StringVector() + keyVec.push_back(b'mxpi_tensorinfer0') + start_time = datetime.datetime.now() + infer_result = stream_manager_api.GetProtobuf( + stream_name, unique_id, keyVec) + end_time = datetime.datetime.now() + print('[INFO] sdk run time: {}'.format( + (end_time - start_time).microseconds)) + if infer_result.size() == 0: + print("[INFO] infer result is null") + exit() + if infer_result[0].errorCode != 0: + print("[INFO] GetResultWithUniqueId error. errorCode=%d, errorMsg=%s" % + (infer_result[0].errorCode, infer_result[0].data.decode())) + exit() + print("[INFO] Get result successfully!") + + # Save result + result = MxpiDataType.MxpiTensorPackageList() + result.ParseFromString(infer_result[0].messageBuf) + + result_np = np.frombuffer( + result.tensorPackageVec[0].tensorVec[0].dataStr, dtype=np.float32) + result_np = result_np.reshape(16, 3, 32, 32) + + save_path = res_dir_name+"/"+str(epoch)+".jpg" + save_images(result_np, save_path) + + # destroy streams + stream_manager_api.DestroyAllStreams() diff --git a/cv/image_generation/dcgan/MindSpore/mindspore_hub_conf.py b/cv/image_generation/dcgan/MindSpore/mindspore_hub_conf.py new file mode 100644 index 0000000000000000000000000000000000000000..dab3b8277942f4f49ec4049e1f117d284f3ed9a2 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/mindspore_hub_conf.py @@ -0,0 +1,44 @@ +# 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. +# ============================================================================ +"""hub config.""" +from mindspore import nn + +from src.cell import WithLossCellD, WithLossCellG +from src.dcgan import DCGAN +from src.discriminator import Discriminator +from src.generator import Generator +from src.config import dcgan_imagenet_cfg as cfg + + +def create_network(name): + """create_network function""" + if name == "dcgan": + netD = Discriminator() + netG = Generator() + + criterion = nn.BCELoss(reduction='mean') + + netD_with_criterion = WithLossCellD(netD, netG, criterion) + netG_with_criterion = WithLossCellG(netD, netG, criterion) + + optimizerD = nn.Adam(netD.trainable_params(), learning_rate=cfg.learning_rate, beta1=cfg.beta1) + optimizerG = nn.Adam(netG.trainable_params(), learning_rate=cfg.learning_rate, beta1=cfg.beta1) + + myTrainOneStepCellForD = nn.TrainOneStepCell(netD_with_criterion, optimizerD) + myTrainOneStepCellForG = nn.TrainOneStepCell(netG_with_criterion, optimizerG) + + dcgan = DCGAN(myTrainOneStepCellForD, myTrainOneStepCellForG) + return dcgan + raise NotImplementedError(f"{name} is not implemented in the repo") diff --git a/cv/image_generation/dcgan/MindSpore/modelarts/train_start.py b/cv/image_generation/dcgan/MindSpore/modelarts/train_start.py new file mode 100644 index 0000000000000000000000000000000000000000..1a477c406c89a238edb5e4137ac62dce5162c883 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/modelarts/train_start.py @@ -0,0 +1,214 @@ +# Copyright 2022 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 DCGAN on ModelArts, get checkpoint files and air/onnx models.""" +import argparse +import os +import datetime +import numpy as np +import matplotlib.pyplot as plt +import matplotlib + +import mindspore.common.dtype as mstype +from mindspore import context +from mindspore import nn, Tensor, export +from mindspore.train.callback import CheckpointConfig, _InternalCallbackParam, ModelCheckpoint, RunContext +from mindspore.context import ParallelMode +from mindspore.communication.management import init, get_rank +import moxing as mox + +from src.dataset import create_dataset_imagenet +from src.config import dcgan_imagenet_cfg as cfg +from src.generator import Generator +from src.discriminator import Discriminator +from src.cell import WithLossCellD, WithLossCellG +from src.dcgan import DCGAN + + +NORMALIZE_MEAN = 127.5 +NORMALIZE_STD = 127.5 + +def save_imgs(gen_imgs, idx): + """ + Save images in 4 * 4 format when training on the modelarts + + Inputs: + - **gen_imgs** (array) - Images generated by the generator. + - **idx** (int) - Training epoch. + """ + matplotlib.use('Agg') + for index in range(gen_imgs.shape[0]): + plt.subplot(4, 4, index + 1) + gen_imgs[index] = gen_imgs[index] * NORMALIZE_STD + NORMALIZE_MEAN + perm = (1, 2, 0) + show_imgs = np.transpose(gen_imgs[index], perm) + sdf = show_imgs.astype(int) + plt.imshow(sdf) + plt.axis("off") + plt.savefig("/cache/images/{}.png".format(idx)) + + +def save_losses(G_losses_list, D_losses_list, idx): + """ + Save Loss visualization images when training on the modelarts + + Inputs: + - **G_losses_list** (list) - Generator loss list. + - **D_losses_list** (list) - Discriminator loss list. + - **idx** (int) - Training epoch. + """ + plt.figure(figsize=(10, 5)) + plt.title("Generator and Discriminator Loss During Training") + plt.plot(G_losses_list, label="G") + plt.plot(D_losses_list, label="D") + plt.xlabel("iterations") + plt.ylabel("Loss") + plt.legend() + plt.savefig("/cache/losses/{}.png".format(idx)) + + +parser = argparse.ArgumentParser(description='MindSpore dcgan training') +parser.add_argument('--data_url', default=None, + help='Directory contains ImageNet-1k dataset.') +parser.add_argument('--train_url', default=None, + help='Directory of training output.') +parser.add_argument('--images_url', default=None, + help='Location of images outputs.') +parser.add_argument('--losses_url', default=None, + help='Location of losses outputs.') +parser.add_argument("--file_format", type=str, + default="AIR", help="Format of export file.") +parser.add_argument("--file_name", type=str, + default="dcgan", help="Output file name.") +parser.add_argument('--epoch_size', type=int, + default=cfg.epoch_size, help='Epoch size of training.') +args = parser.parse_args() + +device_id = int(os.getenv('DEVICE_ID')) +device_num = int(os.getenv('RANK_SIZE')) +local_input_url = '/cache/data' + str(device_id) +local_output_url = '/cache/ckpt' + str(device_id) +local_images_url = '/cache/images' +local_losses_url = '/cache/losses' +context.set_context(mode=context.GRAPH_MODE, + device_target="Ascend", save_graphs=False) +context.set_context(device_id=device_id) + +if device_num > 1: + init() + context.set_auto_parallel_context(device_num=device_num, + global_rank=device_id, + parallel_mode=ParallelMode.DATA_PARALLEL, + gradients_mean=True) + rank = get_rank() +else: + rank = 0 + +mox.file.copy_parallel(src_url=args.data_url, dst_url=local_input_url) +mox.file.copy_parallel(src_url=args.images_url, dst_url=local_images_url) +mox.file.copy_parallel(src_url=args.losses_url, dst_url=local_losses_url) + +if __name__ == '__main__': + # Load Dataset + ds = create_dataset_imagenet(os.path.join( + local_input_url), num_parallel_workers=2) + + steps_per_epoch = ds.get_dataset_size() + + # Define Network + netD = Discriminator() + netG = Generator() + + criterion = nn.BCELoss(reduction='mean') + + netD_with_criterion = WithLossCellD(netD, netG, criterion) + netG_with_criterion = WithLossCellG(netD, netG, criterion) + + optimizerD = nn.Adam(netD.trainable_params(), + learning_rate=cfg.learning_rate, beta1=cfg.beta1) + optimizerG = nn.Adam(netG.trainable_params(), + learning_rate=cfg.learning_rate, beta1=cfg.beta1) + + myTrainOneStepCellForD = nn.TrainOneStepCell( + netD_with_criterion, optimizerD) + myTrainOneStepCellForG = nn.TrainOneStepCell( + netG_with_criterion, optimizerG) + + dcgan = DCGAN(myTrainOneStepCellForD, myTrainOneStepCellForG) + dcgan.set_train() + + # checkpoint save + ckpt_config = CheckpointConfig(save_checkpoint_steps=steps_per_epoch, + keep_checkpoint_max=args.epoch_size) + ckpt_cb = ModelCheckpoint( + config=ckpt_config, directory=local_output_url, prefix='dcgan') + + cb_params = _InternalCallbackParam() + cb_params.train_network = netG + cb_params.batch_num = steps_per_epoch + cb_params.epoch_num = args.epoch_size + # For each epoch + cb_params.cur_epoch_num = 0 + cb_params.cur_step_num = 0 + run_context = RunContext(cb_params) + ckpt_cb.begin(run_context) + + np.random.seed(1) + fixed_noise = Tensor(np.random.normal( + size=(16, cfg.latent_size, 1, 1)).astype("float32")) + + data_loader = ds.create_dict_iterator( + output_numpy=True, num_epochs=args.epoch_size) + G_losses = [] + D_losses = [] + # Start Training Loop + print("Starting Training Loop...") + for epoch in range(args.epoch_size): + # For each batch in the dataloader + for i, data in enumerate(data_loader): + real_data = Tensor(data['image']) + latent_code = Tensor(data["latent_code"]) + netD_loss, netG_loss = dcgan(real_data, latent_code) + if i % 50 == 0: + print("Date time: ", datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "\tepoch: ", epoch, "/", + args.epoch_size, "\tstep: ", i, "/", steps_per_epoch, "\tDloss: ", netD_loss, "\tGloss: ", + netG_loss) + D_losses.append(netD_loss.asnumpy()) + G_losses.append(netG_loss.asnumpy()) + cb_params.cur_step_num = cb_params.cur_step_num + 1 + cb_params.cur_epoch_num = cb_params.cur_epoch_num + 1 + print("================saving model===================") + if device_id == 0: + ckpt_cb.step_end(run_context) + fake = netG(fixed_noise) + print("================saving images===================") + save_imgs(fake.asnumpy(), epoch + 1) + print("================saving losses===================") + save_losses(G_losses, D_losses, epoch + 1) + mox.file.copy_parallel( + src_url=local_images_url, dst_url=args.images_url) + mox.file.copy_parallel( + src_url=local_losses_url, dst_url=args.losses_url) + mox.file.copy_parallel( + src_url=local_output_url, dst_url=args.train_url) + print("================success================") + + # export checkpoint file into air, onnx, mindir models + inputs = Tensor(np.random.rand(16, 100, 1, 1), mstype.float32) + export(netG, inputs, file_name=args.file_name, + file_format=args.file_format) + file_name = args.file_name + "." + args.file_format.lower() + mox.file.copy_parallel( + src_url=file_name, dst_url=os.path.join(args.train_url, file_name)) diff --git a/cv/image_generation/dcgan/MindSpore/preprocess.py b/cv/image_generation/dcgan/MindSpore/preprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..65b49db63e83751c3ae4c66f7dccc6f97720d063 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/preprocess.py @@ -0,0 +1,50 @@ +# 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. +# ============================================================================ +"""preprocess""" +import os +import argparse +import numpy as np +from src.config import dcgan_cifar10_cfg +from src.dataset import create_dataset_cifar10 + +parser = argparse.ArgumentParser('preprocess') +parser.add_argument('--dataset_name', type=str, default="cifar10") +parser.add_argument('--data_path', type=str, default='', help='eval data dir') + +args = parser.parse_args() +if __name__ == "__main__": + dataset_train = create_dataset_cifar10(args.data_path, num_parallel_workers=2, usage='train') + img_path_train = os.path.join('./preprocess_Result/', "train_data") + os.makedirs(img_path_train) + label_list = [] + for idx, data in enumerate(dataset_train.create_dict_iterator(output_numpy=True)): + file_name = "dcgan_data_bs" + str(dcgan_cifar10_cfg.batch_size) + "_" + str(idx) + ".bin" + file_path = os.path.join(img_path_train, file_name) + data["image"].tofile(file_path) + label_list.append(data["label"]) + np.save(os.path.join('./preprocess_Result/', "cifar10_label_ids_train.npy"), label_list) + print("=" * 20, "export bin files finished", "=" * 20) + + dataset_test = create_dataset_cifar10(args.data_path, num_parallel_workers=2, usage='test') + img_path_test = os.path.join('./preprocess_Result/', "test_data") + os.makedirs(img_path_test) + label_list = [] + for idx, data in enumerate(dataset_test.create_dict_iterator(output_numpy=True)): + file_name = "dcgan_data_bs" + str(dcgan_cifar10_cfg.batch_size) + "_" + str(idx) + ".bin" + file_path = os.path.join(img_path_test, file_name) + data["image"].tofile(file_path) + label_list.append(data["label"]) + np.save(os.path.join('./preprocess_Result/', "cifar10_label_ids_test.npy"), label_list) + print("=" * 20, "export bin files finished", "=" * 20) diff --git a/cv/image_generation/dcgan/MindSpore/requirements.txt b/cv/image_generation/dcgan/MindSpore/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8ba3246ed29ebf9ac7294238d22013d8ff2c619f --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/requirements.txt @@ -0,0 +1,3 @@ +numpy +matplotlib +scikit-learn \ No newline at end of file diff --git a/cv/image_generation/dcgan/MindSpore/scripts/run_distribute_train_ascend.sh b/cv/image_generation/dcgan/MindSpore/scripts/run_distribute_train_ascend.sh new file mode 100644 index 0000000000000000000000000000000000000000..9e8206fd4d311485f42b8b90c7263bec3d428eed --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/scripts/run_distribute_train_ascend.sh @@ -0,0 +1,90 @@ +#!/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. +# ============================================================================ + +if [ $# != 3 ] +then + echo "Usage: bash run_distribute_train_ascend.sh [RANK_TABLE_FILE] [DATA_URL] [TRAIN_URL]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} +PATH1=$(get_real_path $1) +PATH2=$(get_real_path $2) +PATH3=$(get_real_path $3) + +echo $PATH1 +echo $PATH2 +echo $PATH3 + +if [ ! -f $PATH1 ] +then + echo "error: RANK_TABLE_FILE=$PATH1 is not a file" +exit 1 +fi + +if [ ! -d $PATH2 ] +then + echo "error: DATA_URL=$PATH2 is not a directory" +exit 1 +fi + +if [ ! -d $PATH3 ] +then + echo "error: TRAIN_URL=$PATH3 is not a directory" +exit 1 +fi + +ulimit -u unlimited +export HCCL_CONNECT_TIMEOUT=600 +export DEVICE_NUM=8 +export RANK_SIZE=8 +export RANK_TABLE_FILE=$PATH1 + +echo 3 > /proc/sys/vm/drop_caches + +cpus=`cat /proc/cpuinfo| grep "processor"| wc -l` +avg=`expr $cpus \/ $DEVICE_NUM` +gap=`expr $avg \- 1` + +for((i=0; i<${DEVICE_NUM}; i++)) +do + start=`expr $i \* $avg` + end=`expr $start \+ $gap` + cmdopt=$start"-"$end + + export DEVICE_ID=$i + export RANK_ID=$i + rm -rf ./train_parallel$i + mkdir ./train_parallel$i + cp ../*.py ./train_parallel$i + cp *.sh ./train_parallel$i + cp -r ../src ./train_parallel$i + cd ./train_parallel$i || exit + echo "start training for rank $RANK_ID, device $DEVICE_ID" + env > env.log + taskset -c $cmdopt nohup python -u train.py --device_target=Ascend --device_id=$i --run_distribute=True \ + --data_url=$PATH2 --train_url=$PATH3 > distribute_train_log 2>&1 & + cd .. +done + + + diff --git a/cv/image_generation/dcgan/MindSpore/scripts/run_distribute_train_gpu.sh b/cv/image_generation/dcgan/MindSpore/scripts/run_distribute_train_gpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..0733f9884783f1502bdcc43c3b34d0a23dffcb34 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/scripts/run_distribute_train_gpu.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# Copyright 2022 Huawei Technologies Co., Ltd +# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +if [ $# != 4 ] +then + echo "Usage: bash run_distribute_train_gpu.sh [DEVICE_NUM] [CUDA_VISIBLE_DEVICES] [DATA_URL] [TRAIN_URL]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} +PATH1=$3 +PATH2=$(get_real_path $4) + +if [ $1 -lt 1 ] && [ $1 -gt 8 ] +then + echo "error: DEVICE_NUM=$1 is not in (1-8)" + exit 1 +fi + +if [ ! -d $PATH1 ] +then + echo "error: TRAIN_URL=$PATH1 is not a directory" +exit 1 +fi + +if [ ! -d $PATH2 ] +then + echo "error: DATA_URL=$PATH2 is not a directory" +exit 1 +fi + +echo "DEVICE_NUM:" $1 +echo "CUDA_VISIBLE_DEVICES:" $2 +echo "DATA_URL:" $PATH1 +echo "TRAIN_URL:" $PATH2 + +ulimit -c unlimited +export DEVICE_NUM=$1 +export RANK_SIZE=$1 +export CUDA_VISIBLE_DEVICES=$2 + +rm -rf ./train_parallel +mkdir ./train_parallel +mkdir ./train_parallel/scripts +cp ../*.py ./train_parallel +cp *.sh ./train_parallel/scripts +cp -r ../src ./train_parallel +cp -r ../gpu_infer ./train_parallel +cd ./train_parallel || exit +mpirun -n $DEVICE_NUM --output-filename log_output --merge-stderr-to-stdout --allow-run-as-root python3 train.py --device_target GPU --run_distribute True\ + --data_url $PATH1 --train_url $PATH2 > output.train.dis_log 2>&1 & +cd .. + + + + diff --git a/cv/image_generation/dcgan/MindSpore/scripts/run_eval_ascend.sh b/cv/image_generation/dcgan/MindSpore/scripts/run_eval_ascend.sh new file mode 100644 index 0000000000000000000000000000000000000000..4c25ecaed2103ea714c07826bb479ae51c268546 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/scripts/run_eval_ascend.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ + +if [ $# != 3 ] +then + echo "Usage: sh run_eval.sh [IMG_URL] [CKPT_URL] [DEVICE_ID]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +PATH1=$(get_real_path $1) +PATH2=$(get_real_path $2) + +if [ ! -d $PATH1 ] +then + echo "error: IMG_URL=$PATH1 is not a directory" +exit 1 +fi + +if [ ! -f $PATH2 ] +then + echo "error: CKPT_URL=$PATH2 is not a file" +exit 1 +fi + +ulimit -c unlimited +export DEVICE_NUM=1 +export RANK_SIZE=$DEVICE_NUM +export DEVICE_ID=$3 +export RANK_ID=$DEVICE_ID + + +if [ -d "eval" ]; +then + rm -rf ./eval +fi +mkdir ./eval +cp ../*.py ./eval +cp *.sh ./eval +cp -r ../src ./eval +cd ./eval || exit +env > env.log +echo "start evaluation for device $DEVICE_ID" +nohup python -u eval.py --device_id=$DEVICE_ID --img_url=$PATH1 --ckpt_url=$PATH2 --device_target=Ascend> eval_log 2>&1 & +cd .. + diff --git a/cv/image_generation/dcgan/MindSpore/scripts/run_eval_gpu.sh b/cv/image_generation/dcgan/MindSpore/scripts/run_eval_gpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..fc65499afac74e784214a853e7536b9426343746 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/scripts/run_eval_gpu.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ + +if [ $# != 3 ] +then + echo "Usage: sh run_eval.sh [IMG_URL] [CKPT_URL] [DEVICE_ID]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +PATH1=$(get_real_path $1) +PATH2=$(get_real_path $2) + +if [ ! -d $PATH1 ] +then + echo "error: IMG_URL=$PATH1 is not a directory" +exit 1 +fi + +if [ ! -f $PATH2 ] +then + echo "error: CKPT_URL=$PATH2 is not a file" +exit 1 +fi + +ulimit -c unlimited +export DEVICE_NUM=1 +export RANK_SIZE=$DEVICE_NUM +export DEVICE_ID=$3 +export RANK_ID=$DEVICE_ID +if [ -d "eval" ]; +then + rm -rf ./eval +fi +mkdir ./eval +cp ../*.py ./eval +cp *.sh ./eval +cp -r ../src ./eval +cd ./eval || exit +env > env.log +echo "start evaluation for device $DEVICE_ID" +nohup python -u eval.py --device_id=$DEVICE_ID --img_url=$PATH1 --ckpt_url=$PATH2 --device_target=GPU> eval_log 2>&1 & +cd .. + diff --git a/cv/image_generation/dcgan/MindSpore/scripts/run_infer_310.sh b/cv/image_generation/dcgan/MindSpore/scripts/run_infer_310.sh new file mode 100644 index 0000000000000000000000000000000000000000..f807c50f6cbc317fa0725e44b478bbed05a089df --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/scripts/run_infer_310.sh @@ -0,0 +1,130 @@ +#!/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. +# ============================================================================ +if [[ $# -lt 2 || $# -gt 3 ]]; then + echo "Usage: bash run_infer_310.sh [MINDIR_PATH] [DATA_URL] [DEVICE_ID] + DEVICE_ID is optional, it can be set by environment variable device_id, otherwise the value is zero" +exit 1 +fi +get_real_path(){ + + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} +model=$(get_real_path $1) +dataset_path=$(get_real_path $2) +device_id=0 +if [ $# == 3 ]; then + device_id=$3 +fi +echo "mindir name: "$model +echo "dataset path: "$dataset_path +echo "device id: "$device_id +export ASCEND_HOME=/usr/local/Ascend/ +if [ -d ${ASCEND_HOME}/ascend-toolkit ]; then + export PATH=$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/ascend-toolkit/latest/atc/bin:$PATH + export LD_LIBRARY_PATH=/usr/local/lib:$ASCEND_HOME/ascend-toolkit/latest/atc/lib64:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export TBE_IMPL_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe + export PYTHONPATH=${TBE_IMPL_PATH}:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp +else + export ASCEND_HOME=/usr/local/Ascend/latest/ + export PATH=$ASCEND_HOME/atc/ccec_compiler/bin:$ASCEND_HOME/atc/bin:$PATH + export LD_LIBRARY_PATH=/usr/local/lib:$ASCEND_HOME/atc/lib64:$ASCEND_HOME/acllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export PYTHONPATH=$ASCEND_HOME/atc/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/opp +fi +export ASCEND_HOME=/usr/local/Ascend +export PATH=$ASCEND_HOME/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/fwkacllib/bin:$ASCEND_HOME/toolkit/bin:$PATH +export LD_LIBRARY_PATH=/usr/local/lib/:/usr/local/fwkacllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:/usr/local/Ascend/toolkit/lib64:$LD_LIBRARY_PATH +export PYTHONPATH=$ASCEND_HOME/fwkacllib/python/site-packages +export PATH=/usr/local/python375/bin:$PATH +export NPU_HOST_LIB=/usr/local/Ascend/acllib/lib64/stub +export ASCEND_OPP_PATH=/usr/local/Ascend/opp +export ASCEND_AICPU_PATH=/usr/local/Ascend +export LD_LIBRARY_PATH=/usr/local/lib64/:$LD_LIBRARY_PATH +function preprocess_data() +{ + if [ -d preprocess_Result ]; then + rm -rf ./preprocess_Result + fi + mkdir preprocess_Result + python ../preprocess.py --data_path=$dataset_path +} +function compile_app() +{ + cd ../ascend310_infer/ || exit + bash build.sh &> build.log +} +function infer_train() +{ + cd - || exit + if [ -d result_Files_train ]; then + rm -rf ./result_Files_train + fi + if [ -d time_Result ]; then + rm -rf ./time_Result + fi + mkdir result_Files_train + mkdir time_Result + + ../ascend310_infer/out/main --mindir_path=$model --dataset_path=./preprocess_Result/train_data --device_id=$device_id --mode=train &> infer_train.log +} +function infer_test() +{ + if [ -d result_Files_test ]; then + rm -rf ./result_Files_test + fi + if [ -d time_Result ]; then + rm -rf ./time_Result + fi + mkdir result_Files_test + mkdir time_Result + + ../ascend310_infer/out/main --mindir_path=$model --dataset_path=./preprocess_Result/test_data --device_id=$device_id --mode=test &> infer_test.log +} +function post_process() +{ + nohup python -u ../verify.py >> verify_log 2>&1 & +} + +preprocess_data +if [ $? -ne 0 ]; then + echo "preprocess dataset failed" + exit 1 +fi +compile_app +if [ $? -ne 0 ]; then + echo "compile app code failed" + exit 1 +fi +infer_train +if [ $? -ne 0 ]; then + echo " execute inference train failed" + exit 1 +fi +infer_test +if [ $? -ne 0 ]; then + echo " execute inference test failed" + exit 1 +fi +post_process +if [ $? -ne 0 ]; then + echo " execute post_process failed" + exit 1 +fi \ No newline at end of file diff --git a/cv/image_generation/dcgan/MindSpore/scripts/run_infer_gpu.sh b/cv/image_generation/dcgan/MindSpore/scripts/run_infer_gpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..476913ee76a9d577ca0cc5dff5b960547833727e --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/scripts/run_infer_gpu.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ +if [[ $# -lt 2 || $# -gt 3 ]]; then + echo "Usage: bash run_infer_gpu.sh [MINDIR_PATH] [DATASET_PATH] [DEVICE_ID] + DEVICE_ID is optional, it can be set by environment variable device_id, otherwise the value is zero" +exit 1 +fi +get_real_path(){ + + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} +model=$(get_real_path $1) +dataset_path=$(get_real_path $2) +device_id=0 +if [ $# == 3 ]; then + device_id=$3 +fi +echo "mindir name: "$model +echo "dataset path: "$dataset_path +echo "device id: "$device_id +export LD_PRELOAD=/root/anaconda3/lib/libpython3.7m.so +export PYTHONPATH=/home/mindspore/.local/lib/python3.7/site-packages +export LD_LIBRARY_PATH=/usr/local/lib64/:$LD_LIBRARY_PATH +function preprocess_data() +{ + if [ -d preprocess_Result ]; then + rm -rf ./preprocess_Result + fi + mkdir preprocess_Result + python3.7 ../preprocess.py --data_path=$dataset_path +} +function compile_app() +{ + cd ../gpu_infer/ || exit + bash build.sh &> build.log +} +function infer_train() +{ + cd - || exit + if [ -d result_Files_train ]; then + rm -rf ./result_Files_train + fi + if [ -d time_Result ]; then + rm -rf ./time_Result + fi + mkdir result_Files_train + mkdir time_Result + + ../gpu_infer/out/main --mindir_path=$model --dataset_path=./preprocess_Result/train_data --device_id=$device_id --mode=train &> infer_train.log +} +function infer_test() +{ + if [ -d result_Files_test ]; then + rm -rf ./result_Files_test + fi + if [ -d time_Result ]; then + rm -rf ./time_Result + fi + mkdir result_Files_test + mkdir time_Result + + ../gpu_infer/out/main --mindir_path=$model --dataset_path=./preprocess_Result/test_data --device_id=$device_id --mode=test &> infer_test.log +} +function post_process() +{ + nohup python -u ../verify.py >> verify_log 2>&1 & +} + +preprocess_data +if [ $? -ne 0 ]; then + echo "preprocess dataset failed" + exit 1 +fi +compile_app +if [ $? -ne 0 ]; then + echo "compile app code failed" + exit 1 +fi +infer_train +if [ $? -ne 0 ]; then + echo " execute inference train failed" + exit 1 +fi +infer_test +if [ $? -ne 0 ]; then + echo " execute inference test failed" + exit 1 +fi +post_process +if [ $? -ne 0 ]; then + echo " execute post_process failed" + exit 1 +fi \ No newline at end of file diff --git a/cv/image_generation/dcgan/MindSpore/scripts/run_standalone_train_ascend.sh b/cv/image_generation/dcgan/MindSpore/scripts/run_standalone_train_ascend.sh new file mode 100644 index 0000000000000000000000000000000000000000..f5c47e7c8d390a45ca98306f7ee4586f556dc85e --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/scripts/run_standalone_train_ascend.sh @@ -0,0 +1,68 @@ +#!/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. +# ============================================================================ + +if [ $# != 3 ] +then + echo "Usage: bash run_standalone_train_ascend.sh [DEVICE_ID] [DATA_URL] [TRAIN_URL]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +ID=$1 +echo $ID +PATH1=$(get_real_path $2) +echo $PATH1 +PATH2=$(get_real_path $3) +echo $PATH2 + +if [ ! -d $PATH1 ] +then + echo "error: DATA_URL=$PATH1 is not a directory" +exit 1 +fi + +if [ ! -d $PATH2 ] +then + echo "error: TRAIN_URL=$PATH2 is not a directory" +exit 1 +fi + +ulimit -u unlimited +export DEVICE_NUM=1 +export DEVICE_ID=$ID +export RANK_ID=0 +export RANK_SIZE=1 + +if [ -d "train" ]; +then + rm -rf ./train +fi +mkdir ./train +cp ../*.py ./train +cp *.sh ./train +cp -r ../src ./train +cd ./train || exit +echo "start training for device $ID" +env > env.log +nohup python -u train.py --device_target=Ascend --device_id=$ID --data_url=$PATH1 --train_url=$PATH2 > train_log 2>&1 & +cd .. diff --git a/cv/image_generation/dcgan/MindSpore/scripts/run_standalone_train_gpu.sh b/cv/image_generation/dcgan/MindSpore/scripts/run_standalone_train_gpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..c02169f842e381cb03dc9502ded0f9819cb25477 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/scripts/run_standalone_train_gpu.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ + +if [ $# != 3 ] +then + echo "Usage: bash run_standalone_train_gpu.sh [DEVICE_ID] [DATA_URL] [TRAIN_URL]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +ID=$1 +echo $ID +PATH1=$2 +echo $PATH1 +PATH2=$(get_real_path $3) +echo $PATH2 + +if [ ! -d $PATH1 ] +then + echo "error: DATA_URL=$PATH1 is not a directory" +exit 1 +fi + +if [ ! -d $PATH2 ] +then + echo "error: TRAIN_URL=$PATH2 is not a directory" +exit 1 +fi + +ulimit -c unlimited +export DEVICE_NUM=1 +export DEVICE_ID=$ID +export RANK_ID=$ID +export RANK_SIZE=1 + +if [ -d "train" ]; +then + rm -rf ./train +fi +mkdir ./train +mkdir ./train/scripts +cp ../*.py ./train +cp *.sh ./train/scripts +cp -r ../src ./train +cp -r ../gpu_infer ./train +cd ./train || exit +echo "start training for device $ID" +env > env.log +nohup python3 -u train.py --device_target=GPU --device_id=$ID --data_url=$PATH1 --train_url=$PATH2 > output.train_log 2>&1 & +cd .. diff --git a/cv/image_generation/dcgan/MindSpore/src/cell.py b/cv/image_generation/dcgan/MindSpore/src/cell.py new file mode 100644 index 0000000000000000000000000000000000000000..b2312583b31286734cd837d894ac0700b5a31ed3 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/src/cell.py @@ -0,0 +1,99 @@ +# 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. +# ============================================================================ +"""dcgan cell""" +import numpy as np +from mindspore import nn, ops +from mindspore.ops import functional as F +from mindspore.common import dtype as mstype +from mindspore.common.initializer import Initializer, _assignment + + +class Reshape(nn.Cell): + """ + Reshapes input tensor with the same values based on a given shape tuple. + """ + + def __init__(self, shape, auto_prefix=True): + super().__init__(auto_prefix=auto_prefix) + self.shape = shape + + def construct(self, x): + return ops.operations.Reshape()(x, self.shape) + + +class Normal(Initializer): + """ + Draw random samples from a normal (Gaussian) distribution. + """ + def __init__(self, mean=0.0, sigma=0.01): + super(Normal, self).__init__() + self.sigma = sigma + self.mean = mean + + def _initialize(self, arr): + np.random.seed(999) + arr_normal = np.random.normal(self.mean, self.sigma, arr.shape) + _assignment(arr, arr_normal) + + +class WithLossCellD(nn.Cell): + """class WithLossCellD""" + def __init__(self, netD, netG, loss_fn): + super(WithLossCellD, self).__init__(auto_prefix=True) + self.netD = netD + self.netG = netG + self.loss_fn = loss_fn + + def construct(self, real_data, latent_code): + """class WithLossCellD construct""" + ones = ops.Ones() + zeros = ops.Zeros() + + out1 = self.netD(real_data) + label1 = ones(out1.shape, mstype.float32) + loss1 = self.loss_fn(out1, label1) + + fake_data = self.netG(latent_code) + fake_data = F.stop_gradient(fake_data) + out2 = self.netD(fake_data) + label2 = zeros(out2.shape, mstype.float32) + loss2 = self.loss_fn(out2, label2) + return loss1 + loss2 + + @property + def backbone_network(self): + """class WithLossCellD backbone_network""" + return self.netD + + +class WithLossCellG(nn.Cell): + """class WithLossCellG""" + def __init__(self, netD, netG, loss_fn): + super(WithLossCellG, self).__init__(auto_prefix=True) + self.netD = netD + self.netG = netG + self.loss_fn = loss_fn + + def construct(self, latent_code): + ones = ops.Ones() + fake_data = self.netG(latent_code) + out = self.netD(fake_data) + label = ones(out.shape, mstype.float32) + loss = self.loss_fn(out, label) + return loss + + @property + def backbone_network(self): + return self.netG diff --git a/cv/image_generation/dcgan/MindSpore/src/config.py b/cv/image_generation/dcgan/MindSpore/src/config.py new file mode 100644 index 0000000000000000000000000000000000000000..f79309021e7b44417946e28fb7db7b40cc2606e3 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/src/config.py @@ -0,0 +1,45 @@ +# 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. +# ============================================================================ +""" +network config setting, will be used in train.py +""" + +from easydict import EasyDict as edict + +dcgan_imagenet_cfg = edict({ + 'num_classes': 1000, + 'epoch_size': 20, + 'batch_size': 128, + 'latent_size': 100, + 'feature_size': 64, + 'channel_size': 3, + 'image_height': 32, + 'image_width': 32, + 'learning_rate': 0.0002, + 'beta1': 0.5 +}) + +dcgan_cifar10_cfg = edict({ + 'num_classes': 10, + 'ds_length': 60000, + 'batch_size': 100, + 'latent_size': 100, + 'feature_size': 64, + 'channel_size': 3, + 'image_height': 32, + 'image_width': 32, + 'learning_rate': 0.0002, + 'beta1': 0.5 +}) diff --git a/cv/image_generation/dcgan/MindSpore/src/dataset.py b/cv/image_generation/dcgan/MindSpore/src/dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..4e0c06772b3a1d9f2c809fb14b362057e866fb1d --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/src/dataset.py @@ -0,0 +1,128 @@ +# Copyright 2021-2022 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. +# ============================================================================ +"""dataset preprocessing""" +import os +import numpy as np + +import mindspore.common.dtype as mstype +import mindspore.dataset as ds +import mindspore.dataset.transforms as C +import mindspore.dataset.vision as vision +from src.config import dcgan_imagenet_cfg, dcgan_cifar10_cfg + + +def create_dataset_imagenet(dataset_path, num_parallel_workers=None): + """ + create a train or eval imagenet2012 dataset for dcgan + + Args: + dataset_path(string): the path of dataset. + + Returns: + dataset + """ + device_num, rank_id = _get_rank_info() + if device_num == 1: + data_set = ds.ImageFolderDataset(dataset_path, num_parallel_workers=num_parallel_workers) + else: + data_set = ds.ImageFolderDataset(dataset_path, num_parallel_workers=num_parallel_workers, + num_shards=device_num, shard_id=rank_id) + + assert dcgan_imagenet_cfg.image_height == dcgan_imagenet_cfg.image_width, "image_height not equal image_width" + image_size = dcgan_imagenet_cfg.image_height + + # define map operations + transform_img = [ + vision.Decode(), + vision.Resize(image_size), + vision.CenterCrop(image_size), + vision.HWC2CHW() + ] + + data_set = data_set.map(input_columns="image", num_parallel_workers=num_parallel_workers, operations=transform_img, + output_columns="image") + data_set = data_set.map(input_columns="image", num_parallel_workers=num_parallel_workers, + operations=lambda x: ((x - 127.5) / 127.5).astype("float32")) + data_set = data_set.map( + input_columns="image", + operations=lambda x: ( + x, + np.random.normal(size=(dcgan_imagenet_cfg.latent_size, 1, 1)).astype("float32") + ), + output_columns=["image", "latent_code"], + column_order=["image", "latent_code"], + num_parallel_workers=num_parallel_workers + ) + + data_set = data_set.batch(dcgan_imagenet_cfg.batch_size) + + return data_set + + +def create_dataset_cifar10(dataset_path, num_parallel_workers=None, usage=None): + """ + create a train or eval cifar10 dataset for dcgan + + Args: + dataset_path(string): the path of dataset. + + Returns: + dataset + """ + + device_num, rank_id = _get_rank_info() + + if device_num == 1: + data_set = ds.Cifar10Dataset(dataset_path, num_parallel_workers=num_parallel_workers, usage=usage) + else: + data_set = ds.Cifar10Dataset(dataset_path, num_parallel_workers=num_parallel_workers, + num_shards=device_num, shard_id=rank_id, usage=usage) + + assert dcgan_imagenet_cfg.image_height == dcgan_imagenet_cfg.image_width, "image_height not equal image_width" + image_size = dcgan_cifar10_cfg.image_height + # define map operations + transform_img = [ + # vision.Decode(), + vision.Resize(image_size), + vision.CenterCrop(image_size), + vision.HWC2CHW() + ] + + type_cast_op = C.TypeCast(mstype.int32) + data_set = data_set.map(input_columns="image", num_parallel_workers=num_parallel_workers, operations=transform_img, + output_columns="image") + data_set = data_set.map(input_columns="image", num_parallel_workers=num_parallel_workers, + operations=lambda x: ((x - 127.5) / 127.5).astype("float32")) + data_set = data_set.map(operations=type_cast_op, input_columns="label", num_parallel_workers=num_parallel_workers) + + data_set = data_set.shuffle(buffer_size=dcgan_cifar10_cfg.ds_length) + data_set = data_set.batch(dcgan_cifar10_cfg.batch_size) + + return data_set + + +def _get_rank_info(): + """ + get rank size and rank id + """ + rank_size = int(os.environ.get("RANK_SIZE", 1)) + + if rank_size > 1: + from mindspore.communication.management import get_rank, get_group_size + rank_size = get_group_size() + rank_id = get_rank() + else: + rank_size = rank_id = None + return rank_size, rank_id diff --git a/cv/image_generation/dcgan/MindSpore/src/dcgan.py b/cv/image_generation/dcgan/MindSpore/src/dcgan.py new file mode 100644 index 0000000000000000000000000000000000000000..1509349763f7d28544b6e9d95277a300d23ac767 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/src/dcgan.py @@ -0,0 +1,31 @@ +# 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. +# ============================================================================ +"""dcgan""" +from mindspore import nn + + +class DCGAN(nn.Cell): + """dcgan class""" + def __init__(self, myTrainOneStepCellForD, myTrainOneStepCellForG): + super(DCGAN, self).__init__(auto_prefix=True) + self.myTrainOneStepCellForD = myTrainOneStepCellForD + self.myTrainOneStepCellForG = myTrainOneStepCellForG + + def construct(self, real_data, latent_code): + output_D = self.myTrainOneStepCellForD(real_data, latent_code).view(-1) + netD_loss = output_D.mean() + output_G = self.myTrainOneStepCellForG(latent_code).view(-1) + netG_loss = output_G.mean() + return netD_loss, netG_loss diff --git a/cv/image_generation/dcgan/MindSpore/src/discriminator.py b/cv/image_generation/dcgan/MindSpore/src/discriminator.py new file mode 100644 index 0000000000000000000000000000000000000000..861bdae6bda95997c4f45e505e937473888bc9a0 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/src/discriminator.py @@ -0,0 +1,58 @@ +# 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. +# ============================================================================ +"""dcgan discriminator""" +from mindspore import nn + +from src.cell import Normal +from src.config import dcgan_imagenet_cfg as cfg + + +def conv(in_channels, out_channels, kernel_size, stride=1, padding=0, pad_mode="pad"): + weight_init = Normal(mean=0, sigma=0.02) + return nn.Conv2d(in_channels, out_channels, + kernel_size=kernel_size, stride=stride, padding=padding, + weight_init=weight_init, has_bias=False, pad_mode=pad_mode) + + +def bm(num_features): + gamma_init = Normal(mean=1, sigma=0.02) + return nn.BatchNorm2d(num_features=num_features, gamma_init=gamma_init) + + +class Discriminator(nn.Cell): + """ + DCGAN Discriminator + """ + + def __init__(self): + super(Discriminator, self).__init__() + self.discriminator = nn.SequentialCell() + # input is 3 x 32 x 32 + self.discriminator.append(conv(cfg.channel_size, cfg.feature_size * 2, 4, 2, 1)) + self.discriminator.append(nn.LeakyReLU(0.2)) + # state size. 128 x 16 x 16 + self.discriminator.append(conv(cfg.feature_size * 2, cfg.feature_size * 4, 4, 2, 1)) + self.discriminator.append(bm(cfg.feature_size * 4)) + self.discriminator.append(nn.LeakyReLU(0.2)) + # state size. 256 x 8 x 8 + self.discriminator.append(conv(cfg.feature_size * 4, cfg.feature_size * 8, 4, 2, 1)) + self.discriminator.append(bm(cfg.feature_size * 8)) + self.discriminator.append(nn.LeakyReLU(0.2)) + # state size. 512 x 4 x 4 + self.discriminator.append(conv(cfg.feature_size * 8, 1, 4, 1)) + self.discriminator.append(nn.Sigmoid()) + + def construct(self, x): + return self.discriminator(x) diff --git a/cv/image_generation/dcgan/MindSpore/src/generator.py b/cv/image_generation/dcgan/MindSpore/src/generator.py new file mode 100644 index 0000000000000000000000000000000000000000..47079df10a341d54724572c4f55fd5e3ba482151 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/src/generator.py @@ -0,0 +1,60 @@ +# 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. +# ============================================================================ +"""dcgan generator""" +from mindspore import nn + +from src.cell import Normal +from src.config import dcgan_imagenet_cfg as cfg + + +def convt(in_channels, out_channels, kernel_size, stride=1, padding=0, pad_mode="pad"): + weight_init = Normal(mean=0, sigma=0.02) + return nn.Conv2dTranspose(in_channels, out_channels, + kernel_size=kernel_size, stride=stride, padding=padding, + weight_init=weight_init, has_bias=False, pad_mode=pad_mode) + + +def bm(num_features): + gamma_init = Normal(mean=1, sigma=0.02) + return nn.BatchNorm2d(num_features=num_features, gamma_init=gamma_init) + + +class Generator(nn.Cell): + """ + DCGAN Generator + """ + + def __init__(self): + super(Generator, self).__init__() + self.generator = nn.SequentialCell() + # input is Z, going into a convolution + self.generator.append(convt(cfg.latent_size, cfg.feature_size * 8, 4, 1, 0)) + self.generator.append(bm(cfg.feature_size * 8)) + self.generator.append(nn.ReLU()) + # state size. 512 x 4 x 4 + self.generator.append(convt(cfg.feature_size * 8, cfg.feature_size * 4, 4, 2, 1)) + self.generator.append(bm(cfg.feature_size * 4)) + self.generator.append(nn.ReLU()) + # state size. 256 x 8 x 8 + self.generator.append(convt(cfg.feature_size * 4, cfg.feature_size * 2, 4, 2, 1)) + self.generator.append(bm(cfg.feature_size * 2)) + self.generator.append(nn.ReLU()) + # state size. 128 x 16 x 16 + self.generator.append(convt(cfg.feature_size * 2, cfg.channel_size, 4, 2, 1)) + self.generator.append(nn.Tanh()) + # state size. 3 x 32 x 32 + + def construct(self, x): + return self.generator(x) diff --git a/cv/image_generation/dcgan/MindSpore/train.py b/cv/image_generation/dcgan/MindSpore/train.py new file mode 100644 index 0000000000000000000000000000000000000000..90dbaeba9ccc40ade3709dea6468833f5077312c --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/train.py @@ -0,0 +1,228 @@ +# Copyright 2021-2022 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 DCGAN and get checkpoint files.""" +import argparse +import ast +import os +import time +import datetime +import numpy as np +from mindspore import context +from mindspore import nn, Tensor +from mindspore.train.callback import CheckpointConfig, _InternalCallbackParam, ModelCheckpoint, RunContext +from mindspore.context import ParallelMode +from mindspore.common import set_seed +from mindspore.communication.management import init, get_rank, get_group_size +from src.dataset import create_dataset_imagenet +from src.config import dcgan_imagenet_cfg as cfg +from src.generator import Generator +from src.discriminator import Discriminator +from src.cell import WithLossCellD, WithLossCellG +from src.dcgan import DCGAN +import matplotlib.pyplot as plt +import matplotlib +matplotlib.use('Agg') +set_seed(1) + +def save_imgs(gen_imgs, idx): + """ + Save images in 4 * 4 format when training on the modelarts + + Inputs: + - **gen_imgs** (array) - Images generated by the generator. + - **idx** (int) - Training epoch. + """ + for index in range(gen_imgs.shape[0]): + plt.subplot(4, 4, index + 1) + gen_imgs[index] = gen_imgs[index] * 127.5 + 127.5 + perm = (1, 2, 0) + show_imgs = np.transpose(gen_imgs[index], perm) + sdf = show_imgs.astype(int) + plt.imshow(sdf) + plt.axis("off") + plt.savefig("/cache/images/{}.png".format(idx)) + + +def save_losses(G_losses_list, D_losses_list, idx): + """ + Save Loss visualization images when training on the modelarts + + Inputs: + - **G_losses_list** (list) - Generator loss list. + - **D_losses_list** (list) - Discriminator loss list. + - **idx** (int) - Training epoch. + """ + plt.figure(figsize=(10, 5)) + plt.title("Generator and Discriminator Loss During Training") + plt.plot(G_losses_list, label="G") + plt.plot(D_losses_list, label="D") + plt.xlabel("iterations") + plt.ylabel("Loss") + plt.legend() + plt.savefig("/cache/losses/{}.png".format(idx)) + + +parser = argparse.ArgumentParser(description='MindSpore dcgan training') +parser.add_argument("--run_modelart", type=ast.literal_eval, default=False, + help="Run on modelArt, default is false.") +parser.add_argument("--run_distribute", type=ast.literal_eval, default=False, + help="Run distribute, default is false.") +parser.add_argument('--device_target', type=str, default='Ascend', help='GPU or Ascend') +parser.add_argument('--device_id', type=int, default=0, help='device id of Ascend (Default: 0)') +parser.add_argument('--data_url', default=None, help='Directory contains ImageNet-1k dataset.') +parser.add_argument('--train_url', default=None, help='Directory of training output.') +parser.add_argument('--images_url', default=None, help='Location of images outputs.') +parser.add_argument('--losses_url', default=None, help='Location of losses outputs.') +args = parser.parse_args() +run_modelart = args.run_modelart +if run_modelart: + device_id = int(os.getenv('DEVICE_ID')) + device_num = int(os.getenv('RANK_SIZE')) + local_input_url = '/cache/data' + str(device_id) + local_output_url = '/cache/ckpt' + str(device_id) + local_images_url = '/cache/images' + local_losses_url = '/cache/losses' + context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", + save_graphs=False) + context.set_context(device_id=device_id) + + if device_num > 1: + init() + context.set_auto_parallel_context(device_num=device_num, + global_rank=device_id, + parallel_mode=ParallelMode.DATA_PARALLEL, + gradients_mean=True) + rank = get_rank() + else: + rank = 0 + import moxing as mox + + mox.file.copy_parallel(src_url=args.data_url, dst_url=local_input_url) + mox.file.copy_parallel(src_url=args.images_url, dst_url=local_images_url) + mox.file.copy_parallel(src_url=args.losses_url, dst_url=local_losses_url) + +elif args.run_distribute: + if args.device_target == 'Ascend': + device_id = args.device_id + context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", save_graphs=False) + context.set_context(device_id=device_id) + init() + device_num = 1 + context.set_auto_parallel_context(device_num=device_num, parallel_mode=ParallelMode.DATA_PARALLEL, + gradients_mean=True) + local_input_url = args.data_url + local_output_url = args.train_url + rank = get_rank() + elif args.device_target == 'GPU': + context.set_context(mode=context.GRAPH_MODE, device_target='GPU', enable_graph_kernel=True) + init() + rank = get_rank() + device_num = get_group_size() + args.device_id = rank + context.set_auto_parallel_context(device_num=device_num, global_rank=rank, + parallel_mode=ParallelMode.DATA_PARALLEL, + gradients_mean=True) + local_input_url = args.data_url + local_output_url = args.train_url +else: + device_id = args.device_id + device_target = args.device_target + context.set_context(mode=context.GRAPH_MODE, device_target=device_target, save_graphs=False) + context.set_context(device_id=device_id) + rank = 0 + device_num = 1 + local_input_url = args.data_url + local_output_url = args.train_url + + +if __name__ == '__main__': + start = time.time() + # Load Dataset + ds = create_dataset_imagenet(os.path.join(local_input_url), num_parallel_workers=4) + + steps_per_epoch = ds.get_dataset_size() + + # Define Network + netD = Discriminator() + netG = Generator() + + criterion = nn.BCELoss(reduction='mean') + + netD_with_criterion = WithLossCellD(netD, netG, criterion) + netG_with_criterion = WithLossCellG(netD, netG, criterion) + + optimizerD = nn.Adam(netD.trainable_params(), learning_rate=cfg.learning_rate, beta1=cfg.beta1) + optimizerG = nn.Adam(netG.trainable_params(), learning_rate=cfg.learning_rate, beta1=cfg.beta1) + + myTrainOneStepCellForD = nn.TrainOneStepCell(netD_with_criterion, optimizerD) + myTrainOneStepCellForG = nn.TrainOneStepCell(netG_with_criterion, optimizerG) + + dcgan = DCGAN(myTrainOneStepCellForD, myTrainOneStepCellForG) + dcgan.set_train() + + # checkpoint save + ckpt_config = CheckpointConfig(save_checkpoint_steps=steps_per_epoch, + keep_checkpoint_max=cfg.epoch_size) + ckpt_cb = ModelCheckpoint(config=ckpt_config, directory=local_output_url, prefix='dcgan') + + cb_params = _InternalCallbackParam() + cb_params.train_network = dcgan + cb_params.batch_num = steps_per_epoch + cb_params.epoch_num = cfg.epoch_size + # For each epoch + cb_params.cur_epoch_num = 0 + cb_params.cur_step_num = 0 + run_context = RunContext(cb_params) + ckpt_cb.begin(run_context) + + np.random.seed(1) + fixed_noise = Tensor(np.random.normal(size=(16, cfg.latent_size, 1, 1)).astype("float32")) + + data_loader = ds.create_dict_iterator(output_numpy=True, num_epochs=cfg.epoch_size) + G_losses = [] + D_losses = [] + # Start Training Loop + print("Starting Training Loop...") + for epoch in range(cfg.epoch_size): + # For each batch in the dataloader + for i, data in enumerate(data_loader): + real_data = Tensor(data['image']) + latent_code = Tensor(data["latent_code"]) + netD_loss, netG_loss = dcgan(real_data, latent_code) + if i % 50 == 0: + print("Date time: ", datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "\tepoch: ", epoch, "/", + cfg.epoch_size, "\tstep: ", i, "/", steps_per_epoch, "\tDloss: ", netD_loss, "\tGloss: ", + netG_loss) + D_losses.append(netD_loss.asnumpy()) + G_losses.append(netG_loss.asnumpy()) + cb_params.cur_step_num = cb_params.cur_step_num + 1 + cb_params.cur_epoch_num = cb_params.cur_epoch_num + 1 + + if args.device_id == 0 or not args.run_distribute: + print("================saving model===================") + ckpt_cb.step_end(run_context) + if run_modelart: + fake = netG(fixed_noise) + print("================saving images===================") + save_imgs(fake.asnumpy(), epoch + 1) + print("================saving losses===================") + save_losses(G_losses, D_losses, epoch + 1) + mox.file.copy_parallel(src_url=local_images_url, dst_url=args.images_url) + mox.file.copy_parallel(src_url=local_losses_url, dst_url=args.losses_url) + mox.file.copy_parallel(src_url=local_output_url, dst_url=args.train_url) + print("================success================") + t = time.time() - start + print("train time:", t) diff --git a/cv/image_generation/dcgan/MindSpore/verify.py b/cv/image_generation/dcgan/MindSpore/verify.py new file mode 100644 index 0000000000000000000000000000000000000000..ff250d2e06efef27078bd1371ed7f6f6361ec1d2 --- /dev/null +++ b/cv/image_generation/dcgan/MindSpore/verify.py @@ -0,0 +1,85 @@ +# Copyright 2021-2022 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. +# ============================================================================ +"""verify by svm""" +import time +import numpy as np +from sklearn import svm +from sklearn.preprocessing import StandardScaler + + +def verify_cifar10(): + """ + verify on cifar10 dataset + """ + label_path_train = "./preprocess_Result/cifar10_label_ids_train.npy" + label_path_test = "./preprocess_Result/cifar10_label_ids_test.npy" + label_set_train = np.load(label_path_train, allow_pickle=True) + label_set_test = np.load(label_path_test, allow_pickle=True) + + result_set_train = [] + for i in range(0, 500): + result_file_train = './result_Files_train/dcgan_data_bs100_' + str(i) + "_train_0.bin" + result = np.fromfile(result_file_train, dtype=np.float32).reshape(-1, 14336) + result_set_train.append(result) + + result_set_train = np.array(result_set_train) + result_set_train = result_set_train.reshape(-1, 14336) + label_set_train = label_set_train.reshape(-1, 1) + label_set_train = label_set_train.flatten() + + result_set_test = [] + for i in range(0, 100): + result_file_test = './result_Files_test/dcgan_data_bs100_' + str(i) + "_test_0.bin" + result = np.fromfile(result_file_test, dtype=np.float32).reshape(-1, 14336) + result_set_test.append(result) + result_set_test = np.array(result_set_test) + result_set_test = result_set_test.reshape(-1, 14336) + label_set_test = label_set_test.reshape(-1, 1) + label_set_test = label_set_test.flatten() + + print("result_set_train.shape: ", result_set_train.shape) + print("label_set_train.shape: ", label_set_train.shape) + + print("result_set_test.shape: ", result_set_test.shape) + print("label_set_test.shape: ", label_set_test.shape) + + print("============================standradScaler") + standardScaler = StandardScaler() + standardScaler.fit(result_set_train) + result_set_train_standard = standardScaler.transform(result_set_train) + standardScaler.fit(result_set_test) + result_set_test_standard = standardScaler.transform(result_set_test) + + print("============================training") + clf = svm.SVC(max_iter=-1) + start = time.time() + print("result_set_train.shape: ", result_set_train_standard.shape) + print("label_set_train.shape: ", label_set_train.shape) + clf.fit(result_set_train_standard, label_set_train) + t = time.time() - start + print("train time:", t) + + print("============================testing") + # Test on Training data + print("result_set_test.shape: ", result_set_test_standard.shape) + print("label_set_test.shape: ", label_set_test.shape) + test_result = clf.predict(result_set_test_standard) + accuracy = sum(test_result == label_set_test) / label_set_test.shape[0] + print('Test accuracy: ', accuracy) + + +if __name__ == '__main__': + print("============================verify_cifar10") + verify_cifar10() diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/Dockerfile b/cv/semantic_segmentation/deeplabv3/MindSpore/Dockerfile new file mode 100755 index 0000000000000000000000000000000000000000..fcb31f207f23664ca2d60bda9a15463af1042dd9 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/Dockerfile @@ -0,0 +1,6 @@ +ARG FROM_IMAGE_NAME +FROM ${FROM_IMAGE_NAME} + +RUN apt install libgl1-mesa-glx -y +COPY requirements.txt . +RUN pip3.7 install -r requirements.txt diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/README.md b/cv/semantic_segmentation/deeplabv3/MindSpore/README.md new file mode 100755 index 0000000000000000000000000000000000000000..e293df7fb5058d62fcccbbf04cf470a5d8192545 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/README.md @@ -0,0 +1,65 @@ + +# DeepLabV3 +## Model description +DeepLab is a series of image semantic segmentation models, DeepLabV3 improves significantly over previous versions. Two keypoints of DeepLabV3: Its multi-grid atrous convolution makes it better to deal with segmenting objects at multiple scales, and augmented ASPP makes image-level features available to capture long range information. +This repository provides a script and recipe to DeepLabV3 model and achieve state-of-the-art performance. + +Refer to [this paper][1] for network details. +`Chen L C, Papandreou G, Schroff F, et al. Rethinking atrous convolution for semantic image segmentation[J]. arXiv preprint arXiv:1706.05587, 2017.` + +[1]: https://arxiv.org/abs/1706.05587 +## Step 1: Installing +``` +pip3 install -r requirements.txt +``` +## Step 2: Prepare Datasets + +Pascal VOC datasets [link](https://host.robots.ox.ac.uk/pascal/VOC/), and Semantic Boundaries Dataset: [link](https://www2.eecs.berkeley.edu/Research/Projects/CS/vision/grouping/semantic_contours/benchmark.tgz) + +- Download segmentation dataset. + +- Prepare the training data list file. The list file saves the relative path to image and annotation pairs. Lines are like: + +```shell +JPEGImages/00001.jpg SegmentationClassGray/00001.png +JPEGImages/00002.jpg SegmentationClassGray/00002.png +JPEGImages/00003.jpg SegmentationClassGray/00003.png +JPEGImages/00004.jpg SegmentationClassGray/00004.png +...... +``` + +You can also generate the list file automatically by run script: `python get_dataset_lst.py --data_root=/PATH/TO/DATA` + +- Configure and run build_data.sh to convert dataset to mindrecords. Arguments in scripts/build_data.sh: + + ```shell + --data_root root path of training data + --data_lst list of training data(prepared above) + --dst_path where mindrecords are saved + --num_shards number of shards of the mindrecords + --shuffle shuffle or not + ``` + +# [Pretrained models](#contents) +Please [resnet101](https://download.mindspore.cn/model_zoo/r1.2/resnet101_ascend_v120_imagenet2012_official_cv_bs32_acc78/) download resnet101 here + +## Step 3: Training +``` +python3 train.py --data_file=/home/dataset/deeplabv3/vocaug_train.mindrecord0 --train_dir=./ckpt --train_epochs=200 --batch_size=32 --crop_size=513 --base_lr=0.015 --lr_type=cos --min_scale=0.5 --max_scale=2.0 --ignore_label=255 --num_classes=21 --model=deeplab_v3_s16 --ckpt_pre_trained=./resnet101_ascend_v120_imagenet2012_official_cv_bs32_acc78.ckpt --save_steps=1500 --keep_checkpoint_max=200 --device_target=GPU +``` +### [Evaluation] +``` +python3 eval.py --data_root=deeplabv3/ --data_lst=voc_val_lst.txt --batch_size=32 --crop_size=513 --ignore_label=255 --num_classes=21 --model=deeplab_v3_s16 --scales_type=0 --freeze_bn=True --device_target=GPU --ckpt_path=deeplab_v3_s16-200_45.ckpt +``` +### [Evaluation result] +## Results on BI-V100 + +| GPUs | per step time | Miou | +|------|-------------- |--------| +| 1 | 1.465s | 0.7386 | + +## Results on NV-V100s + +| GPUs | per step time | MAP | +|------|-------------- |-------| +| 1 | 1.716s | 0.7453| diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/CMakeLists.txt b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/CMakeLists.txt new file mode 100755 index 0000000000000000000000000000000000000000..ee3c85447340e0449ff2b70ed24f60a17e07b2b6 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.14.1) +project(Ascend310Infer) +add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -std=c++17 -Werror -Wall -fPIE -Wl,--allow-shlib-undefined") +set(PROJECT_SRC_ROOT ${CMAKE_CURRENT_LIST_DIR}/) +option(MINDSPORE_PATH "mindspore install path" "") +include_directories(${MINDSPORE_PATH}) +include_directories(${MINDSPORE_PATH}/include) +include_directories(${PROJECT_SRC_ROOT}) +find_library(MS_LIB libmindspore.so ${MINDSPORE_PATH}/lib) +file(GLOB_RECURSE MD_LIB ${MINDSPORE_PATH}/_c_dataengine*) + +add_executable(main src/main.cc src/utils.cc) +target_link_libraries(main ${MS_LIB} ${MD_LIB} gflags) diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/build.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..713d7f657ddfa5f75b069351c55f8447f77c72d0 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/build.sh @@ -0,0 +1,29 @@ +#!/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. +# ============================================================================ +if [ -d out ]; then + rm -rf out +fi + +mkdir out +cd out || exit + +if [ -f "Makefile" ]; then + make clean +fi + +cmake .. \ + -DMINDSPORE_PATH="`pip show mindspore-ascend | grep Location | awk '{print $2"/mindspore"}' | xargs realpath`" +make diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/fusion_switch.cfg b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/fusion_switch.cfg new file mode 100755 index 0000000000000000000000000000000000000000..f5fadd59385362ae3ce752172c1226162776c447 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/fusion_switch.cfg @@ -0,0 +1 @@ +ConvBatchnormFusionPass:off \ No newline at end of file diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/inc/utils.h b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/inc/utils.h new file mode 100755 index 0000000000000000000000000000000000000000..f8d7c5b6ae6ce2cb1244b50cd6612dc592d42b08 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/inc/utils.h @@ -0,0 +1,33 @@ +/** + * 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 MINDSPORE_INFERENCE_UTILS_H_ +#define MINDSPORE_INFERENCE_UTILS_H_ + +#include +#include +#include +#include +#include +#include "include/api/types.h" + +std::vector GetAllFiles(std::string_view dirName); +std::vector GetImagesById(const std::string &idFile, const std::string &dirName); +DIR *OpenDir(std::string_view dirName); +std::string RealPath(std::string_view path); +mindspore::MSTensor ReadFileToTensor(const std::string &file); +int WriteResult(const std::string& imageFile, const std::vector &outputs); +#endif diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/src/main.cc b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/src/main.cc new file mode 100755 index 0000000000000000000000000000000000000000..5c47938ee02fa111b518940383b7a1738bb27c0a --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/src/main.cc @@ -0,0 +1,224 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/api/context.h" +#include "include/api/model.h" +#include "include/api/types.h" +#include "include/api/serialization.h" +#include "include/dataset/vision.h" +#include "include/dataset/execute.h" +#include "../inc/utils.h" + +using mindspore::Context; +using mindspore::Serialization; +using mindspore::Model; +using mindspore::Status; +using mindspore::ModelType; +using mindspore::GraphCell; +using mindspore::kSuccess; +using mindspore::MSTensor; +using mindspore::dataset::Execute; +using mindspore::dataset::TensorTransform; +using mindspore::dataset::vision::Resize; +using mindspore::dataset::vision::Pad; +using mindspore::dataset::vision::HWC2CHW; +using mindspore::dataset::vision::Normalize; +using mindspore::dataset::vision::SwapRedBlue; +using mindspore::dataset::vision::Decode; + + +DEFINE_string(mindir_path, "", "mindir path"); +DEFINE_string(image_list, "", "image list"); +DEFINE_string(dataset_path, ".", "dataset path"); +DEFINE_string(fusion_switch_path, ".", "fusion switch path"); +DEFINE_int32(device_id, 0, "device id"); + +int PadImage(const MSTensor &input, MSTensor *output) { + std::shared_ptr normalize(new Normalize({103.53, 116.28, 123.675}, + {57.375, 57.120, 58.395})); + Execute composeNormalize({normalize}); + std::vector shape = input.Shape(); + auto imgResize = MSTensor(); + auto imgNormalize = MSTensor(); + int paddingSize; + const int IMAGEWIDTH = 513; + const int IMAGEHEIGHT = 513; + float widthScale, heightScale; + widthScale = static_cast(IMAGEWIDTH) / shape[1]; + heightScale = static_cast(IMAGEHEIGHT) / shape[0]; + Status ret; + if (widthScale < heightScale) { + int heightSize = shape[0]*widthScale; + std::shared_ptr resize(new Resize({heightSize, IMAGEWIDTH})); + Execute composeResizeWidth({resize}); + ret = composeResizeWidth(input, &imgResize); + if (ret != kSuccess) { + std::cout << "ERROR: Resize Width failed." << std::endl; + return 1; + } + ret = composeNormalize(imgResize, &imgNormalize); + if (ret != kSuccess) { + std::cout << "ERROR: Normalize failed." << std::endl; + return 1; + } + paddingSize = IMAGEHEIGHT - heightSize; + std::shared_ptr pad(new Pad({0, 0, 0, paddingSize})); + Execute composePad({pad}); + ret = composePad(imgNormalize, output); + if (ret != kSuccess) { + std::cout << "ERROR: Height Pad failed." << std::endl; + return 1; + } + } else { + int widthSize = shape[1]*heightScale; + std::shared_ptr resize(new Resize({IMAGEHEIGHT, widthSize})); + Execute composeResizeHeight({resize}); + ret = composeResizeHeight(input, &imgResize); + if (ret != kSuccess) { + std::cout << "ERROR: Resize Height failed." << std::endl; + return 1; + } + ret = composeNormalize(imgResize, &imgNormalize); + if (ret != kSuccess) { + std::cout << "ERROR: Normalize failed." << std::endl; + return 1; + } + paddingSize = IMAGEWIDTH - widthSize; + std::shared_ptr pad(new Pad({0, 0, paddingSize, 0})); + Execute composePad({pad}); + ret = composePad(imgNormalize, output); + if (ret != kSuccess) { + std::cout << "ERROR: Width Pad failed." << std::endl; + return 1; + } + } + return 0; +} + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (RealPath(FLAGS_mindir_path).empty()) { + std::cout << "Invalid mindir" << std::endl; + return 1; + } + if (RealPath(FLAGS_fusion_switch_path).empty()) { + std::cout << "Invalid fusion switch path" << std::endl; + return 1; + } + auto context = std::make_shared(); + auto ascend310 = std::make_shared(); + ascend310->SetDeviceID(FLAGS_device_id); + context->MutableDeviceInfo().push_back(ascend310); + mindspore::Graph graph; + Serialization::Load(FLAGS_mindir_path, ModelType::kMindIR, &graph); + + if (!FLAGS_fusion_switch_path.empty()) { + ascend310->SetFusionSwitchConfigPath(FLAGS_fusion_switch_path); + } + Model model; + Status ret = model.Build(GraphCell(graph), context); + if (ret != kSuccess) { + std::cout << "ERROR: Build failed." << std::endl; + return 1; + } + std::vector model_inputs = model.GetInputs(); + if (model_inputs.empty()) { + std::cout << "Invalid model, inputs is empty." << std::endl; + return 1; + } + + auto all_files = GetImagesById(FLAGS_image_list, FLAGS_dataset_path); + if (all_files.empty()) { + std::cout << "ERROR: no input data." << std::endl; + return 1; + } + + std::map costTime_map; + size_t size = all_files.size(); + std::shared_ptr decode(new Decode()); + std::shared_ptr swapredblue(new SwapRedBlue()); + Execute composeDecode({decode, swapredblue}); + std::shared_ptr hwc2chw(new HWC2CHW()); + Execute composeTranspose({hwc2chw}); + + for (size_t i = 0; i < size; ++i) { + struct timeval start = {0}; + struct timeval end = {0}; + double startTimeMs; + double endTimeMs; + std::vector inputs; + std::vector outputs; + std::cout << "Start predict input files:" << all_files[i] << std::endl; + auto imgDecode = MSTensor(); + auto image = ReadFileToTensor(all_files[i]); + ret = composeDecode(image, &imgDecode); + if (ret != kSuccess) { + std::cout << "ERROR: Decode failed." << std::endl; + return 1; + } + auto imgPad = MSTensor(); + PadImage(imgDecode, &imgPad); + auto img = MSTensor(); + composeTranspose(imgPad, &img); + inputs.emplace_back(model_inputs[0].Name(), model_inputs[0].DataType(), model_inputs[0].Shape(), + img.Data().get(), img.DataSize()); + gettimeofday(&start, nullptr); + ret = model.Predict(inputs, &outputs); + gettimeofday(&end, nullptr); + if (ret != kSuccess) { + std::cout << "Predict " << all_files[i] << " failed." << std::endl; + return 1; + } + startTimeMs = (1.0 * start.tv_sec * 1000000 + start.tv_usec) / 1000; + endTimeMs = (1.0 * end.tv_sec * 1000000 + end.tv_usec) / 1000; + costTime_map.insert(std::pair(startTimeMs, endTimeMs)); + int rst = WriteResult(all_files[i], outputs); + if (rst != 0) { + std::cout << "write result failed." << std::endl; + return rst; + } + } + double average = 0.0; + int inferCount = 0; + + for (auto iter = costTime_map.begin(); iter != costTime_map.end(); iter++) { + double diff = 0.0; + diff = iter->second - iter->first; + average += diff; + inferCount++; + } + average = average / inferCount; + std::stringstream timeCost; + timeCost << "NN inference cost average time: "<< average << " ms of infer_count " << inferCount << std::endl; + std::cout << "NN inference cost average time: "<< average << "ms of infer_count " << inferCount << std::endl; + + std::string fileName = "./time_Result" + std::string("/test_perform_static.txt"); + std::ofstream fileStream(fileName.c_str(), std::ios::trunc); + fileStream << timeCost.str(); + fileStream.close(); + costTime_map.clear(); + return 0; +} diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/src/utils.cc b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/src/utils.cc new file mode 100755 index 0000000000000000000000000000000000000000..e4130611bc70a657e0146085d570a90a17408e58 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/ascend310_infer/src/utils.cc @@ -0,0 +1,161 @@ +/** + * 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 +#include +#include +#include "../inc/utils.h" + +using mindspore::MSTensor; +using mindspore::DataType; + +std::vector GetAllFiles(std::string_view dirName) { + struct dirent *filename; + DIR *dir = OpenDir(dirName); + if (dir == nullptr) { + return {}; + } + std::vector res; + while ((filename = readdir(dir)) != nullptr) { + std::string dName = std::string(filename->d_name); + if (dName == "." || dName == ".." || filename->d_type != DT_REG) { + continue; + } + res.emplace_back(std::string(dirName) + "/" + filename->d_name); + } + std::sort(res.begin(), res.end()); + for (auto &f : res) { + std::cout << "image file: " << f << std::endl; + } + return res; +} + +std::vector GetImagesById(const std::string &idFile, const std::string &dirName) { + std::ifstream readFile(idFile); + std::string line; + std::vector result; + + if (!readFile.is_open()) { + std::cout << "can not open image id txt file" << std::endl; + return result; + } + + while (getline(readFile, line)) { + std::size_t pos = line.find(" "); + std::string id = line.substr(0, pos); + result.emplace_back(dirName + "/" + id); + } + + return result; +} + +int WriteResult(const std::string& imageFile, const std::vector &outputs) { + std::string homePath = "./result_Files"; + const int INVALID_POINTER = -1; + const int ERROR = -2; + for (size_t i = 0; i < outputs.size(); ++i) { + size_t outputSize; + std::shared_ptr netOutput; + netOutput = outputs[i].Data(); + outputSize = outputs[i].DataSize(); + int pos = imageFile.rfind('/'); + std::string fileName(imageFile, pos + 1); + fileName.replace(fileName.find('.'), fileName.size() - fileName.find('.'), '_' + std::to_string(i) + ".bin"); + std::string outFileName = homePath + "/" + fileName; + FILE * outputFile = fopen(outFileName.c_str(), "wb"); + if (outputFile == nullptr) { + std::cout << "open result file " << outFileName << " failed" << std::endl; + return INVALID_POINTER; + } + size_t size = fwrite(netOutput.get(), sizeof(char), outputSize, outputFile); + if (size != outputSize) { + fclose(outputFile); + outputFile = nullptr; + std::cout << "write result file " << outFileName << " failed, write size[" << size << + "] is smaller than output size[" << outputSize << "], maybe the disk is full." << std::endl; + return ERROR; + } + fclose(outputFile); + outputFile = nullptr; + } + return 0; +} + +MSTensor ReadFileToTensor(const std::string &file) { + if (file.empty()) { + std::cout << "Pointer file is nullptr" << std::endl; + return MSTensor(); + } + + std::ifstream ifs(file); + if (!ifs.good()) { + std::cout << "File: " << file << " is not exist" << std::endl; + return MSTensor(); + } + + if (!ifs.is_open()) { + std::cout << "File: " << file << "open failed" << std::endl; + return MSTensor(); + } + + ifs.seekg(0, std::ios::end); + size_t size = ifs.tellg(); + MSTensor buffer(file, mindspore::DataType::kNumberTypeUInt8, {static_cast(size)}, nullptr, size); + + ifs.seekg(0, std::ios::beg); + ifs.read(reinterpret_cast(buffer.MutableData()), size); + ifs.close(); + + return buffer; +} + + +DIR *OpenDir(std::string_view dirName) { + if (dirName.empty()) { + std::cout << " dirName is null ! " << std::endl; + return nullptr; + } + std::string realPath = RealPath(dirName); + struct stat s; + lstat(realPath.c_str(), &s); + if (!S_ISDIR(s.st_mode)) { + std::cout << "dirName is not a valid directory !" << std::endl; + return nullptr; + } + DIR *dir; + dir = opendir(realPath.c_str()); + if (dir == nullptr) { + std::cout << "Can not open dir " << dirName << std::endl; + return nullptr; + } + std::cout << "Successfully opened the dir " << dirName << std::endl; + return dir; +} + +std::string RealPath(std::string_view path) { + char realPathMem[PATH_MAX] = {0}; + char *realPathRet = nullptr; + realPathRet = realpath(path.data(), realPathMem); + + if (realPathRet == nullptr) { + std::cout << "File: " << path << " is not exist."; + return ""; + } + + std::string realPath(realPathMem); + std::cout << path << " realpath is: " << realPath << std::endl; + return realPath; +} diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/default_config.yaml b/cv/semantic_segmentation/deeplabv3/MindSpore/default_config.yaml new file mode 100755 index 0000000000000000000000000000000000000000..92857627f17c9596ae7ddf462d775de234536954 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/default_config.yaml @@ -0,0 +1,118 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path" +device_target: "Ascend" # ['Ascend', 'CPU'] + +# ============================================================================== +# Training options +train_dir: "/cache/train/ckpt" + +# dataset +need_modelarts_dataset_unzip: True +data_file: "" +batch_size: 32 +crop_size: 513 +image_mean: [103.53, 116.28, 123.675] +image_std: [57.375, 57.120, 58.395] +min_scale: 0.5 +max_scale: 2.0 +ignore_label: 255 +num_classes: 21 + +# optimizer +train_epochs: 300 +lr_type: "cos" +base_lr: 0.015 +lr_decay_step: 40000 +lr_decay_rate: 0.1 +loss_scale: 3072.0 + +# model +model: "deeplab_v3_s16" +freeze_bn: False +ckpt_pre_trained: "" +filter_weight: False + +# train +is_distributed: False +rank: 0 +group_size: 1 +save_steps: 3000 +keep_checkpoint_max: 1 + +# eval param +data_root: "" +data_lst: "" +scales: [1.0,] +scales_list: [[1.0,], [0.5, 0.75, 1.0, 1.25, 1.75]] +scales_type: 0 +flip: False +ckpt_path: "" +input_format: "NCHW" # ["NCHW", "NHWC"] + +# export param +device_id: 0 +export_batch_size: 1 +input_size: 513 +ckpt_file: "" +file_name: "deeplabv3" +file_format: "MINDIR" +export_model: "deeplab_v3_s8" + +--- + +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Url for modelarts" +train_url: "Url for modelarts" +data_path: "The location of the input data." +output_path: "The location of the output file." +device_target: 'Target device type' +train_dir: "where training log and ckpts saved" +data_file: "path and name of one mindrecord file" +batch_size: "batch size" +crop_size: "crop size" +image_mean: "image mean" +image_std: "image std" +min_scale: "minimum scale of data argumentation" +max_scale: "maximum scale of data argumentation" +ignore_label: "ignore label" +num_classes: "number of classes" +train_epochs: "epoch" +lr_type: "type of learning rate" +base_lr: "base learning rate" +lr_decay_step: "learning rate decay step" +lr_decay_rate: "learning rate decay rate" +loss_scale: "loss scale" +model: "select model" +freeze_bn: "freeze bn, need to set to True when run without onnx" +ckpt_pre_trained: "pretrained model" +filter_weight: "Filter the last weight parameters, default is False." +is_distributed: "distributed training" +rank: "local rank of distributed" +group_size: "world size of distributed" +save_steps: "steps interval for saving" +keep_checkpoint_max: "max checkpoint for saving" + +data_root: "root path of val data" +data_lst: "list of val data" +scales: "scales of evaluation" +flip: "perform left-right flip" +ckpt_path: "model to evaluat" +input_format: "NCHW or NHWC" + +# export param +device_id: "Device id" +export_batch_size: "batch size for export" +input_size: "input_size" +ckpt_file: "Checkpoint file path." +file_name: "output file name." +file_format: "file format, choices in ['AIR', 'MINDIR']" +export_model: "Select model structure (Default: deeplab_v3_s8), choices in ['deeplab_v3_s16', 'deeplab_v3_s8']" \ No newline at end of file diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/eval.py b/cv/semantic_segmentation/deeplabv3/MindSpore/eval.py new file mode 100755 index 0000000000000000000000000000000000000000..5a407a6013bf7e991682ccb4be139a59b1d071e5 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/eval.py @@ -0,0 +1,253 @@ +# Copyright 2022 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. +# ============================================================================ +"""eval deeplabv3.""" + +import os +import time +import numpy as np +import cv2 +from mindspore import Tensor +import mindspore.common.dtype as mstype +import mindspore.nn as nn +import mindspore.ops as ops +from mindspore import context +from mindspore.train.serialization import load_checkpoint, load_param_into_net +from src.nets import net_factory + +from model_utils.config import config +from model_utils.moxing_adapter import moxing_wrapper +from model_utils.device_adapter import get_device_id, get_device_num, get_rank_id + + +def cal_hist(a, b, n): + k = (a >= 0) & (a < n) + return np.bincount(n * a[k].astype(np.int32) + b[k], minlength=n ** 2).reshape(n, n) + + +def resize_long(img, long_size=513): + h, w, _ = img.shape + if h > w: + new_h = long_size + new_w = int(1.0 * long_size * w / h) + else: + new_w = long_size + new_h = int(1.0 * long_size * h / w) + imo = cv2.resize(img, (new_w, new_h)) + return imo + + +class BuildEvalNetwork(nn.Cell): + def __init__(self, network, input_format="NCHW"): + super(BuildEvalNetwork, self).__init__() + self.network = network + self.softmax = nn.Softmax(axis=1) + self.transpose = ops.Transpose() + self.format = input_format + + def construct(self, input_data): + if self.format == "NHWC": + input_data = self.transpose(input_data, (0, 3, 1, 2)) + output = self.network(input_data) + output = self.softmax(output) + return output + + +def pre_process(args, img_, crop_size=513): + # resize + img_ = resize_long(img_, crop_size) + resize_h, resize_w, _ = img_.shape + + # mean, std + image_mean = np.array(args.image_mean) + image_std = np.array(args.image_std) + img_ = (img_ - image_mean) / image_std + + # pad to crop_size + pad_h = crop_size - img_.shape[0] + pad_w = crop_size - img_.shape[1] + if pad_h > 0 or pad_w > 0: + img_ = cv2.copyMakeBorder(img_, 0, pad_h, 0, pad_w, cv2.BORDER_CONSTANT, value=0) + # hwc to chw + img_ = img_.transpose((2, 0, 1)) + return img_, resize_h, resize_w + + +def eval_batch(args, eval_net, img_lst, crop_size=513, flip=True): + result_lst = [] + batch_size = len(img_lst) + batch_img = np.zeros((args.batch_size, 3, crop_size, crop_size), dtype=np.float32) + resize_hw = [] + for l in range(batch_size): + img_ = img_lst[l] + img_, resize_h, resize_w = pre_process(args, img_, crop_size) + batch_img[l] = img_ + resize_hw.append([resize_h, resize_w]) + + batch_img = np.ascontiguousarray(batch_img) + net_out = eval_net(Tensor(batch_img, mstype.float32)) + net_out = net_out.asnumpy() + + if flip: + batch_img = batch_img[:, :, :, ::-1] + net_out_flip = eval_net(Tensor(batch_img, mstype.float32)) + net_out += net_out_flip.asnumpy()[:, :, :, ::-1] + + for bs in range(batch_size): + probs_ = net_out[bs][:, :resize_hw[bs][0], :resize_hw[bs][1]].transpose((1, 2, 0)) + ori_h, ori_w = img_lst[bs].shape[0], img_lst[bs].shape[1] + probs_ = cv2.resize(probs_, (ori_w, ori_h)) + result_lst.append(probs_) + + return result_lst + + +def eval_batch_scales(args, eval_net, img_lst, scales, + base_crop_size=513, flip=True): + sizes_ = [int((base_crop_size - 1) * sc) + 1 for sc in scales] + probs_lst = eval_batch(args, eval_net, img_lst, crop_size=sizes_[0], flip=flip) + print(sizes_) + for crop_size_ in sizes_[1:]: + probs_lst_tmp = eval_batch(args, eval_net, img_lst, crop_size=crop_size_, flip=flip) + for pl, _ in enumerate(probs_lst): + probs_lst[pl] += probs_lst_tmp[pl] + + result_msk = [] + for i in probs_lst: + result_msk.append(i.argmax(axis=2)) + return result_msk + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + def unzip(zip_file, save_dir): + import zipfile + s_time = time.time() + if not os.path.exists(os.path.join(save_dir, "vocaug")): + zip_isexist = zipfile.is_zipfile(zip_file) + if zip_isexist: + fz = zipfile.ZipFile(zip_file, 'r') + data_num = len(fz.namelist()) + print("Extract Start...") + print("unzip file num: {}".format(data_num)) + i = 0 + for file in fz.namelist(): + if i % int(data_num / 100) == 0: + print("unzip percent: {}%".format(i / int(data_num / 100)), flush=True) + i += 1 + fz.extract(file, save_dir) + print("cost time: {}min:{}s.".format(int((time.time() - s_time) / 60), + int(int(time.time() - s_time) % 60))) + print("Extract Done.") + else: + print("This is not zip.") + else: + print("Zip has been extracted.") + + if config.need_modelarts_dataset_unzip: + zip_file_1 = os.path.join(config.data_path, "vocaug.zip") + save_dir_1 = os.path.join(config.data_path) + + sync_lock = "/tmp/unzip_sync.lock" + + # Each server contains 8 devices as most. + if get_device_id() % min(get_device_num(), 8) == 0 and not os.path.exists(sync_lock): + print("Zip file path: ", zip_file_1) + print("Unzip file save dir: ", save_dir_1) + unzip(zip_file_1, save_dir_1) + print("===Finish extract data synchronization===") + try: + os.mknod(sync_lock) + except IOError: + pass + + while True: + if os.path.exists(sync_lock): + break + time.sleep(1) + + print("Device: {}, Finish sync unzip data from {} to {}.".format(get_device_id(), zip_file_1, save_dir_1)) + + config.train_dir = os.path.join(config.output_path, str(get_rank_id()), config.train_dir) + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def net_eval(): + config.scales = config.scales_list[config.scales_type] + args = config + + context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target, save_graphs=False, + device_id=get_device_id()) + + # data list + with open(args.data_lst) as f: + img_lst = f.readlines() + + # network + if args.model == 'deeplab_v3_s16': + network = net_factory.nets_map[args.model]('eval', args.num_classes, 16, args.freeze_bn) + elif args.model == 'deeplab_v3_s8': + network = net_factory.nets_map[args.model]('eval', args.num_classes, 8, args.freeze_bn) + else: + raise NotImplementedError('model [{:s}] not recognized'.format(args.model)) + + eval_net = BuildEvalNetwork(network, args.input_format) + + # load model + param_dict = load_checkpoint(args.ckpt_path) + load_param_into_net(eval_net, param_dict) + eval_net.set_train(False) + + # evaluate + hist = np.zeros((args.num_classes, args.num_classes)) + batch_img_lst = [] + batch_msk_lst = [] + bi = 0 + image_num = 0 + for i, line in enumerate(img_lst): + img_path, msk_path = line.strip().split(' ') + img_path = os.path.join(args.data_root, img_path) + msk_path = os.path.join(args.data_root, msk_path) + img_ = cv2.imread(img_path) + msk_ = cv2.imread(msk_path, cv2.IMREAD_GRAYSCALE) + batch_img_lst.append(img_) + batch_msk_lst.append(msk_) + bi += 1 + if bi == args.batch_size: + batch_res = eval_batch_scales(args, eval_net, batch_img_lst, scales=args.scales, + base_crop_size=args.crop_size, flip=args.flip) + for mi in range(args.batch_size): + hist += cal_hist(batch_msk_lst[mi].flatten(), batch_res[mi].flatten(), args.num_classes) + + bi = 0 + batch_img_lst = [] + batch_msk_lst = [] + print('processed {} images'.format(i+1)) + image_num = i + + if bi > 0: + batch_res = eval_batch_scales(args, eval_net, batch_img_lst, scales=args.scales, + base_crop_size=args.crop_size, flip=args.flip) + for mi in range(bi): + hist += cal_hist(batch_msk_lst[mi].flatten(), batch_res[mi].flatten(), args.num_classes) + print('processed {} images'.format(image_num + 1)) + + print(hist) + iu = np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist)) + print('per-class IoU', iu) + print('mean IoU', np.nanmean(iu)) + + +if __name__ == '__main__': + net_eval() diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/eval_onnx.py b/cv/semantic_segmentation/deeplabv3/MindSpore/eval_onnx.py new file mode 100755 index 0000000000000000000000000000000000000000..5098e21e6a621338947fdc5a6eb39e48cc5ba29f --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/eval_onnx.py @@ -0,0 +1,135 @@ +# Copyright 2022 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. +# ============================================================================ +"""Eval deeplabv3 exported to ONNX""" + +import os +import numpy as np +import onnxruntime as ort +import cv2 + +from model_utils.config import config + +from eval import pre_process, cal_hist + + +def create_session(checkpoint_path, target_device): + """Load ONNX model and create ORT session""" + if target_device == 'GPU': + providers = ['CUDAExecutionProvider'] + elif target_device == 'CPU': + providers = ['CPUExecutionProvider'] + else: + raise ValueError(f"Unsupported target device '{target_device}'. Expected one of: 'CPU', 'GPU'") + return ort.InferenceSession(checkpoint_path, providers=providers) + + +def eval_batch(args, eval_net, img_lst, crop_size=513, flip=True): + """Run eval on batch""" + result_lst = [] + batch_size = len(img_lst) + batch_img = np.zeros((args.batch_size, 3, crop_size, crop_size), dtype=np.float32) + resize_hw = [] + for l in range(batch_size): + img_ = img_lst[l] + img_, resize_h, resize_w = pre_process(args, img_, crop_size) + batch_img[l] = img_ + resize_hw.append([resize_h, resize_w]) + + [input_name] = [x.name for x in eval_net.get_inputs()] + + batch_img = np.ascontiguousarray(batch_img) + [net_out] = eval_net.run(None, {input_name: batch_img}) + + if flip: + batch_img = batch_img[:, :, :, ::-1] + [net_out_flip] = eval_net.run(None, {input_name: batch_img}) + net_out += net_out_flip[:, :, :, ::-1] + + for bs in range(batch_size): + probs_ = net_out[bs][:, :resize_hw[bs][0], :resize_hw[bs][1]].transpose((1, 2, 0)) + ori_h, ori_w = img_lst[bs].shape[0], img_lst[bs].shape[1] + probs_ = cv2.resize(probs_, (ori_w, ori_h)) + result_lst.append(probs_) + + return result_lst + + +def eval_batch_scales(args, eval_net, img_lst, scales, + base_crop_size=513, flip=True): + """Run eval on batch with different scales""" + sizes_ = [int((base_crop_size - 1) * sc) + 1 for sc in scales] + probs_lst = eval_batch(args, eval_net, img_lst, crop_size=sizes_[0], flip=flip) + for crop_size_ in sizes_[1:]: + probs_lst_tmp = eval_batch(args, eval_net, img_lst, crop_size=crop_size_, flip=flip) + for pl, _ in enumerate(probs_lst): + probs_lst[pl] += probs_lst_tmp[pl] + + result_msk = [] + for i in probs_lst: + result_msk.append(i.argmax(axis=2)) + return result_msk + + +def net_eval(): + """Run evaluation""" + config.scales = config.scales_list[config.scales_type] + + with open(config.data_lst, encoding='utf-8') as f: + img_lst = f.readlines() + + session = create_session(config.file_name, config.device_target) + + # evaluate + hist = np.zeros((config.num_classes, config.num_classes)) + batch_img_lst = [] + batch_msk_lst = [] + bi = 0 + image_num = 0 + for i, line in enumerate(img_lst): + img_path, msk_path = line.strip().split(' ') + img_path = os.path.join(config.data_root, img_path) + msk_path = os.path.join(config.data_root, msk_path) + img_ = cv2.imread(img_path) + msk_ = cv2.imread(msk_path, cv2.IMREAD_GRAYSCALE) + batch_img_lst.append(img_) + batch_msk_lst.append(msk_) + bi += 1 + if bi == config.batch_size: + batch_res = eval_batch_scales(config, session, batch_img_lst, scales=config.scales, + base_crop_size=config.crop_size, flip=config.flip) + for mi in range(config.batch_size): + hist += cal_hist(batch_msk_lst[mi].flatten(), batch_res[mi].flatten(), config.num_classes) + + bi = 0 + batch_img_lst = [] + batch_msk_lst = [] + print(f'processed {i + 1} images') + image_num = i + + if bi > 0: + batch_res = eval_batch_scales(config, session, batch_img_lst, scales=config.scales, + base_crop_size=config.crop_size, flip=config.flip) + for mi in range(bi): + hist += cal_hist(batch_msk_lst[mi].flatten(), batch_res[mi].flatten(), config.num_classes) + print(f'processed {image_num + 1} images') + + print(hist) + iu = np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist)) + print('per-class IoU', iu) + print('mean IoU', np.nanmean(iu)) + + +if __name__ == '__main__': + net_eval() diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/export.py b/cv/semantic_segmentation/deeplabv3/MindSpore/export.py new file mode 100755 index 0000000000000000000000000000000000000000..0375e4048ad452ec37ad32128fe52bc08e4a0aa8 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/export.py @@ -0,0 +1,74 @@ +# Copyright 2022 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 checkpoint file into air, onnx, mindir models""" +import os +import numpy as np + +import mindspore.nn as nn +import mindspore.ops as ops +from mindspore import Tensor, context, load_checkpoint, load_param_into_net, export +from src.nets import net_factory + +from model_utils.config import config +from model_utils.moxing_adapter import moxing_wrapper + +class BuildEvalNetwork(nn.Cell): + def __init__(self, net, input_format="NCHW"): + super(BuildEvalNetwork, self).__init__() + self.network = net + self.softmax = nn.Softmax(axis=1) + self.transpose = ops.Transpose() + self.format = input_format + + def construct(self, x): + if self.format == "NHWC": + x = self.transpose(x, (0, 3, 1, 2)) + output = self.network(x) + output = self.softmax(output) + return output + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + config.file_name = os.path.join(config.output_path, config.file_name) + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_export(): + '''run export.''' + context.set_context(mode=context.GRAPH_MODE, device_target=config.device_target) + if config.device_target == "Ascend": + context.set_context(device_id=config.device_id) + + if config.export_model == 'deeplab_v3_s16': + network = net_factory.nets_map['deeplab_v3_s16']('eval', config.num_classes, 16, config.freeze_bn) + else: + network = net_factory.nets_map['deeplab_v3_s8']('eval', config.num_classes, 8, config.freeze_bn) + network = BuildEvalNetwork(network, config.input_format) + param_dict = load_checkpoint(config.ckpt_file) + + # load the parameter into net + load_param_into_net(network, param_dict) + if config.input_format == "NHWC": + input_data = Tensor( + np.ones([config.export_batch_size, config.input_size, config.input_size, 3]).astype(np.float32)) + else: + input_data = Tensor( + np.ones([config.export_batch_size, 3, config.input_size, config.input_size]).astype(np.float32)) + export(network, input_data, file_name=config.file_name, file_format=config.file_format) + + +if __name__ == '__main__': + run_export() diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/Dockerfile b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/Dockerfile new file mode 100755 index 0000000000000000000000000000000000000000..487c5864ffbdc30aa951e5226a32114056e1fe11 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/Dockerfile @@ -0,0 +1,5 @@ +ARG FROM_IMAGE_NAME +FROM ${FROM_IMAGE_NAME} + +COPY requirements.txt . +RUN pip3 install -r requirements.txt diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/convert/convert_om.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/convert/convert_om.sh new file mode 100755 index 0000000000000000000000000000000000000000..39cd519422de53f0d5174f7dc49eab9d834c333a --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/convert/convert_om.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 3.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. +# ============================================================================ + +if [ $# -ne 2 ] +then + echo "Wrong parameter format." + echo "Usage:" + echo " bash $0 [INPUT_AIR_PATH] [OUTPUT_OM_PATH_NAME]" + echo "Example: " + echo " bash convert_om.sh xxx.air xx" + + exit 1 +fi + +model_path=$1 +output_model_name=$2 + +atc --framework=1 \ + --input_format=NHWC \ + --model=$model_path \ + --output=$output_model_name \ + --fusion_switch_file=fusion_switch.cfg \ + --op_select_implmode=high_precision \ + --soc_version=Ascend310 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/convert/fusion_switch.cfg b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/convert/fusion_switch.cfg new file mode 100755 index 0000000000000000000000000000000000000000..f5fadd59385362ae3ce752172c1226162776c447 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/convert/fusion_switch.cfg @@ -0,0 +1 @@ +ConvBatchnormFusionPass:off \ No newline at end of file diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/data/config/deeplabv3.cfg b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/data/config/deeplabv3.cfg new file mode 100755 index 0000000000000000000000000000000000000000..e3b88b8e51c643ae351bc996bb85940271f93db2 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/data/config/deeplabv3.cfg @@ -0,0 +1,4 @@ +CLASS_NUM=21 +CHECK_MODEL=true +MODEL_TYPE=1 +FRAMEWORK_TYPE=2 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/data/config/deeplabv3.pipeline b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/data/config/deeplabv3.pipeline new file mode 100755 index 0000000000000000000000000000000000000000..af4c94d6808ecc4a81b9e2b033f28a7caf18a2f5 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/data/config/deeplabv3.pipeline @@ -0,0 +1,85 @@ +{ + "segmentation": { + "stream_config": { + "deviceId": "0" + }, + "mxpi_imagedecoder0": { + "props": { + "handleMethod": "opencv" + }, + "factory": "mxpi_imagedecoder", + "next": "mxpi_imageresize0" + }, + "mxpi_imageresize0": { + "props": { + "handleMethod": "opencv", + "resizeType": "Resizer_KeepAspectRatio_Long", + "scaleValue": "513" + }, + "factory": "mxpi_imageresize", + "next": "opencv_normalize0" + }, + "opencv_normalize0": { + "props": { + "alpha": "123.675, 116.28, 103.53", + "beta": "58.395, 57.120, 57.375", + "dataType": "FLOAT32" + }, + "factory": "mxpi_imagenormalize", + "next": "mxpi_imageresize1" + }, + "mxpi_imageresize1": { + "props": { + "handleMethod": "opencv", + "resizeType": "Resizer_OnlyPadding", + "scaleValue": "513", + "paddingType": "Padding_RightDown", + "paddingHeight": "513", + "paddingWidth": "513", + "paddingColorB": "0", + "paddingColorG": "0", + "paddingColorR": "0" + }, + "factory": "mxpi_imageresize", + "next": "mxpi_tensorinfer0" + }, + "mxpi_tensorinfer0": { + "props": { + "dataSource": "mxpi_imageresize1", + "modelPath": "../data/model/deeplabv3.om" + }, + "factory": "mxpi_modelinfer", + "next": "mxpi_semanticsegpostprocessor0" + }, + "mxpi_semanticsegpostprocessor0": { + "props": { + "dataSource": "mxpi_tensorinfer0", + "postProcessConfigPath": "../data/config/deeplabv3.cfg", + "labelPath": "../data/config/deeplabv3.names", + "postProcessLibPath": "libdeeplabv3post.so" + }, + "factory": "mxpi_semanticsegpostprocessor", + "next": "mxpi_dataserialize0" + }, + "mxpi_dataserialize0": { + "props": { + "outputDataKeys": "mxpi_semanticsegpostprocessor0" + }, + "factory": "mxpi_dataserialize", + "next": "appsink0" + }, + "appsrc0": { + "props": { + "blocksize": "409600" + }, + "factory": "appsrc", + "next": "mxpi_imagedecoder0" + }, + "appsink0": { + "props": { + "blocksize": "4096000" + }, + "factory": "appsink" + } + } +} diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/docker_start_infer.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/docker_start_infer.sh new file mode 100755 index 0000000000000000000000000000000000000000..a21c49aab109b43d6b462d2882cc4b76358ebc5b --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/docker_start_infer.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 3.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 +code_dir=$3 + +function show_help() { + echo "Usage: docker_start.sh docker_image data_dir code_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 + + if [ -z "${code_dir}" ]; then + echo "please input code_dir" + show_help + exit 1 + fi +} + +param_check + +docker run -it -u root \ + --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} \ + -v ${code_dir}:${code_dir} \ + ${docker_image} \ + /bin/bash diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/CMakeLists.txt b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/CMakeLists.txt new file mode 100755 index 0000000000000000000000000000000000000000..a6d0b5cf406497c6203966f5fb538ced122816d0 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.10.0) +project(deeplabv3) + +set(TARGET deeplabv3) + +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(DEFINED ENV{MXSDK_OPENSOURCE_DIR}) + set(OPENSOURCE_DIR $ENV{MXSDK_OPENSOURCE_DIR}) +else() + set(OPENSOURCE_DIR ${MXBASE_ROOT_DIR}/opensource) +endif() +include_directories(${MXBASE_POST_LIB_DIR}) +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/DeeplabV3.cpp) +target_link_libraries(${TARGET} glog cpprest mxbase deeplabv3post opencv_world stdc++fs) + +install(TARGETS ${TARGET} RUNTIME DESTINATION ${PROJECT_SOURCE_DIR}/) diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/build.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..2a96dde6603d734947f4261976fa54eb01d9e3ff --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/build.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 3.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. +# ============================================================================ + + +path_cur=$(dirname $0) + +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_deeplabv3() +{ + cd $path_cur + rm -rf build + mkdir -p build + cd build + cmake .. + make + ret=$? + if [ ${ret} -ne 0 ]; then + echo "Failed to build deeplabv3." + exit ${ret} + fi + make install +} + +check_env +build_deeplabv3 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/src/DeeplabV3.cpp b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/src/DeeplabV3.cpp new file mode 100755 index 0000000000000000000000000000000000000000..caa747f831989f291fb20122545afdd521225d5d --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/src/DeeplabV3.cpp @@ -0,0 +1,264 @@ +/* + * Copyright 2022 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 +#include +#include +#include +#include "MxBase/DeviceManager/DeviceManager.h" +#include "MxBase/Log/Log.h" +#include "DeeplabV3.h" + + +APP_ERROR DeeplabV3::Init(const InitParam &initParam) { + deviceId_ = initParam.deviceId; + 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; + } + + model_ = std::make_shared(); + ret = model_->Init(initParam.modelPath, modelDesc_); + if (ret != APP_ERR_OK) { + LogError << "ModelInferenceProcessor init failed, ret=" << ret << "."; + return ret; + } + MxBase::ConfigData configData; + configData.SetJsonValue("CLASS_NUM", std::to_string(initParam.classNum)); + configData.SetJsonValue("MODEL_TYPE", std::to_string(initParam.modelType)); + configData.SetJsonValue("CHECK_MODEL", initParam.checkModel); + configData.SetJsonValue("FRAMEWORK_TYPE", std::to_string(initParam.frameworkType)); + + auto jsonStr = configData.GetCfgJson().serialize(); + std::map> config; + config["postProcessConfigContent"] = std::make_shared(jsonStr); + config["labelPath"] = std::make_shared(initParam.labelPath); + post_ = std::make_shared(); + ret = post_->Init(config); + if (ret != APP_ERR_OK) { + LogError << "Deeplabv3PostProcess init failed, ret=" << ret << "."; + return ret; + } + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::DeInit() { + model_->DeInit(); + post_->DeInit(); + MxBase::DeviceManager::GetInstance()->DestroyDevices(); + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::ReadImage(const std::string &imgPath, cv::Mat &imageMat) { + imageMat = cv::imread(imgPath, cv::IMREAD_COLOR); + cv::cvtColor(imageMat, imageMat, cv::COLOR_BGR2RGB); + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::ResizeImage(const cv::Mat &srcImageMat, cv::Mat &dstImageMat, + MxBase::ResizedImageInfo &resizedImageInfo) { + constexpr float scaleValue = 513; + float scale = scaleValue / std::max(srcImageMat.rows, srcImageMat.cols); + int dstWidth = srcImageMat.cols * scale; + int dstHeight = srcImageMat.rows * scale; + cv::resize(srcImageMat, dstImageMat, cv::Size(dstWidth, dstHeight)); + + resizedImageInfo.heightOriginal = srcImageMat.rows; + resizedImageInfo.heightResize = dstWidth; + resizedImageInfo.widthOriginal = srcImageMat.cols; + resizedImageInfo.widthResize = dstHeight; + resizedImageInfo.resizeType = MxBase::RESIZER_MS_KEEP_ASPECT_RATIO; + + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::Normalize(const cv::Mat &srcImageMat, cv::Mat &dstImageMat) { + constexpr size_t ALPHA_AND_BETA_SIZE = 3; + cv::Mat float32Mat; + srcImageMat.convertTo(float32Mat, CV_32FC3); + + std::vector tmp; + cv::split(float32Mat, tmp); + + const std::vector mean = {123.675, 116.28, 103.53}; + const std::vector std = {58.395, 57.120, 57.375}; + for (size_t i = 0; i < ALPHA_AND_BETA_SIZE; ++i) { + tmp[i].convertTo(tmp[i], CV_32FC3, 1 / std[i], - mean[i] / std[i]); + } + cv::merge(tmp, dstImageMat); + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::Padding(const cv::Mat &srcImageMat, cv::Mat &dstImageMat) { + constexpr int32_t paddingHeight = 513; + constexpr int32_t paddingWidth = 513; + int padH = paddingHeight - srcImageMat.rows; + int padW = paddingWidth - srcImageMat.cols; + if (padH > 0 || padW > 0) { + cv::Scalar value(0, 0, 0); + cv::copyMakeBorder(srcImageMat, dstImageMat, 0, padH, 0, padW, cv::BORDER_CONSTANT, value); + } + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::CVMatToTensorBase(const cv::Mat &imageMat, MxBase::TensorBase &tensorBase) { + const uint32_t dataSize = imageMat.cols * imageMat.rows * imageMat.elemSize(); + MxBase::MemoryData memoryDataDst(dataSize, MxBase::MemoryData::MEMORY_DEVICE, deviceId_); + MxBase::MemoryData memoryDataSrc(imageMat.data, dataSize, MxBase::MemoryData::MEMORY_HOST); + + APP_ERROR ret = MxBase::MemoryHelper::MxbsMallocAndCopy(memoryDataDst, memoryDataSrc); + if (ret != APP_ERR_OK) { + LogError << GetError(ret) << "Memory malloc failed."; + return ret; + } + + std::vector shape = { + static_cast(imageMat.rows), + static_cast(imageMat.cols), + static_cast(imageMat.channels())}; + tensorBase = MxBase::TensorBase(memoryDataDst, false, shape, MxBase::TENSOR_DTYPE_FLOAT32); + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::Inference(const std::vector &inputs, + std::vector &outputs) { + auto dtypes = model_->GetOutputDataType(); + for (size_t i = 0; i < modelDesc_.outputTensors.size(); ++i) { + std::vector shape = {}; + for (size_t j = 0; j < modelDesc_.outputTensors[i].tensorDims.size(); ++j) { + shape.push_back((uint32_t)modelDesc_.outputTensors[i].tensorDims[j]); + } + MxBase::TensorBase tensor(shape, dtypes[i], MxBase::MemoryData::MEMORY_DEVICE, 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; + APP_ERROR ret = model_->ModelInference(inputs, outputs, dynamicInfo); + if (ret != APP_ERR_OK) { + LogError << "ModelInference failed, ret=" << ret << "."; + return ret; + } + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::PostProcess(const std::vector &inputs, + std::vector &segInfo, const std::vector &resizedInfo) { + APP_ERROR ret = post_->Process(inputs, segInfo, resizedInfo); + if (ret != APP_ERR_OK) { + LogError << "Process failed, ret=" << ret << "."; + return ret; + } + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::Process(const std::string &imgPath) { + cv::Mat imageMat; + APP_ERROR ret = ReadImage(imgPath, imageMat); + if (ret != APP_ERR_OK) { + LogError << "ReadImage failed, ret=" << ret << "."; + return ret; + } + + MxBase::ResizedImageInfo resizedImageInfo; + ResizeImage(imageMat, imageMat, resizedImageInfo); + Normalize(imageMat, imageMat); + Padding(imageMat, imageMat); + + MxBase::TensorBase tensorBase; + ret = CVMatToTensorBase(imageMat, tensorBase); + if (ret != APP_ERR_OK) { + LogError << "CVMatToTensorBase failed, ret=" << ret << "."; + return ret; + } + + std::vector inputs = {}; + std::vector outputs = {}; + inputs.push_back(tensorBase); + ret = Inference(inputs, outputs); + if (ret != APP_ERR_OK) { + LogError << "Inference failed, ret=" << ret << "."; + return ret; + } + std::vector semanticSegInfos = {}; + std::vector resizedImageInfos = {resizedImageInfo}; + ret = PostProcess(outputs, semanticSegInfos, resizedImageInfos); + if (ret != APP_ERR_OK) { + LogError << "PostProcess failed, ret=" << ret << "."; + return ret; + } + + std::string resultPath = imgPath; + size_t pos = resultPath.find_last_of("."); + resultPath.replace(resultPath.begin() + pos, resultPath.end(), "_infer.png"); + SaveResultToImage(semanticSegInfos[0], resultPath); + return APP_ERR_OK; +} + +APP_ERROR DeeplabV3::SaveResultToImage(const MxBase::SemanticSegInfo &segInfo, const std::string &filePath) { + cv::Mat imageMat(segInfo.pixels.size(), segInfo.pixels[0].size(), CV_8UC1); + for (size_t x = 0; x < segInfo.pixels.size(); ++x) { + for (size_t y = 0; y < segInfo.pixels[x].size(); ++y) { + uint8_t gray = segInfo.pixels[x][y]; + imageMat.at(x, y) = gray; + } + } + cv::Mat imageGrayC3 = cv::Mat::zeros(imageMat.rows, imageMat.cols, CV_8UC3); + std::vector planes; + for (int i = 0; i < 3; i++) { + planes.push_back(imageMat); + } + cv::merge(planes, imageGrayC3); + uchar rgbColorMap[256*3] = { + 0, 0, 0, + 128, 0, 0, + 0, 128, 0, + 128, 128, 0, + 0, 0, 128, + 128, 0, 128, + 0, 128, 128, + 128, 128, 128, + 64, 0, 0, + 192, 0, 0, + 64, 128, 0, + 192, 128, 0, + 64, 0, 128, + 192, 0, 128, + 64, 128, 128, + 192, 128, 128, + 0, 64, 0, + 128, 64, 0, + 0, 192, 0, + 128, 192, 0, + 0, 64, 128, + }; + cv::Mat lut(1, 256, CV_8UC3, rgbColorMap); + + cv::Mat imgColor; + cv::LUT(imageGrayC3, lut, imgColor); + cv::cvtColor(imgColor, imgColor, cv::COLOR_RGB2BGR); + cv::imwrite(filePath, imgColor); + return APP_ERR_OK; +} diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/src/DeeplabV3.h b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/src/DeeplabV3.h new file mode 100755 index 0000000000000000000000000000000000000000..8211f8f669d0e990169fdb508adeeee9f34a0d9c --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/src/DeeplabV3.h @@ -0,0 +1,61 @@ +/* + * Copyright 2022 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 DEEPLABV3_H +#define DEEPLABV3_H + +#include +#include +#include +#include +#include "MxBase/ModelInfer/ModelInferenceProcessor.h" +#include "SegmentPostProcessors/Deeplabv3Post.h" +#include "MxBase/PostProcessBases/PostProcessDataType.h" +#include "MxBase/Tensor/TensorContext/TensorContext.h" + +struct InitParam { + uint32_t deviceId; + std::string labelPath; + std::string modelPath; + uint32_t classNum; + uint32_t modelType; + std::string checkModel; + uint32_t frameworkType; +}; + +class DeeplabV3 { + public: + APP_ERROR Init(const InitParam &initParam); + APP_ERROR DeInit(); + APP_ERROR ReadImage(const std::string &imgPath, cv::Mat &imageMat); + APP_ERROR ResizeImage(const cv::Mat &srcImageMat, cv::Mat &dstImageMat, MxBase::ResizedImageInfo &resizedImageInfo); + APP_ERROR Normalize(const cv::Mat &srcImageMat, cv::Mat &dstImageMat); + APP_ERROR Padding(const cv::Mat &srcImageMat, cv::Mat &dstImageMat); + APP_ERROR CVMatToTensorBase(const cv::Mat &imageMat, MxBase::TensorBase &tensorBase); + APP_ERROR Inference(const std::vector &inputs, std::vector &outputs); + APP_ERROR PostProcess(const std::vector &inputs, + std::vector &segInfo, const std::vector &resizedInfo); + APP_ERROR Process(const std::string &imgPath); + APP_ERROR SaveResultToImage(const MxBase::SemanticSegInfo &segInfo, const std::string &filePath); + + private: + std::shared_ptr model_; + std::shared_ptr post_; + std::string outputDataPath_ = "./result"; + MxBase::ModelDesc modelDesc_; + uint32_t deviceId_ = 0; +}; + +#endif diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/src/main.cpp b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/src/main.cpp new file mode 100755 index 0000000000000000000000000000000000000000..63af7dc7dda78f9723eb7cf8b2150df44dabf3b8 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/mxbase/src/main.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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 "DeeplabV3.h" +#include "MxBase/Log/Log.h" + +namespace { + const uint32_t CLASS_NUM = 21; + const uint32_t MODEL_TYPE = 1; + const uint32_t FRAMEWORK_TYPE = 2; +} + +int main(int argc, char *argv[]) { + if (argc <= 1) { + LogWarn << "Please input image path, such as './deeplabv3 test.jpg'."; + return APP_ERR_OK; + } + + InitParam initParam = {}; + initParam.deviceId = 0; + initParam.classNum = CLASS_NUM; + initParam.modelType = MODEL_TYPE; + initParam.labelPath = "../data/config/deeplabv3.names"; + initParam.modelPath = "../data/model/deeplabv3.om"; + initParam.checkModel = "true"; + initParam.frameworkType = FRAMEWORK_TYPE; + + DeeplabV3 deeplabV3; + APP_ERROR ret = deeplabV3.Init(initParam); + if (ret != APP_ERR_OK) { + LogError << "DeeplabV3 init failed, ret=" << ret << "."; + return ret; + } + + std::string imgPath = argv[1]; + ret = deeplabV3.Process(imgPath); + if (ret != APP_ERR_OK) { + LogError << "DeeplabV3 process failed, ret=" << ret << "."; + deeplabV3.DeInit(); + return ret; + } + deeplabV3.DeInit(); + return APP_ERR_OK; +} diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/requirements.txt b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/requirements.txt new file mode 100755 index 0000000000000000000000000000000000000000..3868fb16b89087faa05d065f86198c026e0d05eb --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/requirements.txt @@ -0,0 +1 @@ +pillow diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/sdk/get_dataset_colormap.py b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/sdk/get_dataset_colormap.py new file mode 100755 index 0000000000000000000000000000000000000000..7f01c395252982fe5d85325f87f476ee27c85284 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/sdk/get_dataset_colormap.py @@ -0,0 +1,49 @@ +# coding=utf-8 +# Copyright 2022 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 +# +# less 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. +# ============================================================================ + +from six.moves import range +import numpy as np + + +_DATASET_MAX_ENTRY = 512 + + +def bit_get(val, idx): + return (val >> idx) & 1 + + +def create_pascal_label_colormap(): + colormap = np.zeros((_DATASET_MAX_ENTRY, 3), dtype=int) + ind = np.arange(_DATASET_MAX_ENTRY, dtype=int) + + for shift in reversed(list(range(8))): + for channel in range(3): + colormap[:, channel] |= bit_get(ind, channel) << shift + ind >>= 3 + + return colormap + + +def label_to_color_image(label): + if label.ndim != 2: + raise ValueError('Expect 2-D input label, Got {}'.format(label.shape)) + + if np.max(label) >= _DATASET_MAX_ENTRY: + raise ValueError('label value too large: {} >= {}'.format( + np.max(label), _DATASET_MAX_ENTRY)) + + colormap = create_pascal_label_colormap() + return colormap[label] diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/sdk/main.py b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/sdk/main.py new file mode 100755 index 0000000000000000000000000000000000000000..31c434af2291000e453504003d5fb2dfd86d00f2 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/sdk/main.py @@ -0,0 +1,121 @@ +# coding=utf-8 +# Copyright 2022 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 +# +# less 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 base64 +import json +import os +import cv2 +import numpy as np +from StreamManagerApi import MxDataInput +from StreamManagerApi import StreamManagerApi +from get_dataset_colormap import label_to_color_image + +PIPELINE_PATH = "../data/config/deeplabv3.pipeline" +INFER_RESULT_DIR = "./result" + + +def _parse_args(): + # val data + parser = argparse.ArgumentParser('mindspore deeplabv3 eval') + parser.add_argument('data_root', type=str, default='', help='root path of val data') + parser.add_argument('data_lst', type=str, default='', help='list of val data') + parser.add_argument('num_classes', type=int, default=21, help='number of classes') + args, _ = parser.parse_known_args() + return args + + +def _cal_hist(a, b, n): + k = (a >= 0) & (a < n) + return np.bincount(n * a[k].astype(np.int32) + b[k], minlength=n ** 2).reshape(n, n) + + +def _init_stream(pipeline_path): + stream_manager_api = StreamManagerApi() + ret = stream_manager_api.InitManager() + if ret != 0: + raise RuntimeError(f"Failed to init stream manager, ret={ret}") + + with open(pipeline_path, 'rb') as f: + pipeline_str = f.read() + + ret = stream_manager_api.CreateMultipleStreams(pipeline_str) + if ret != 0: + raise RuntimeError(f"Failed to create stream, ret={ret}") + return stream_manager_api + + +def _do_infer(stream_manager_api, data_input): + stream_name = b'segmentation' + unique_id = stream_manager_api.SendDataWithUniqueId( + stream_name, 0, data_input) + if unique_id < 0: + raise RuntimeError("Failed to send data to stream.") + + timeout = 3000 + infer_result = stream_manager_api.GetResultWithUniqueId( + stream_name, unique_id, timeout) + if infer_result.errorCode != 0: + raise RuntimeError( + "GetResultWithUniqueId error, errorCode=%d, errorMsg=%s" % ( + infer_result.errorCode, infer_result.data.decode())) + + load_dict = json.loads(infer_result.data.decode()) + image_mask = load_dict["MxpiImageMask"][0] + data_str = base64.b64decode(image_mask['dataStr']) + shape = image_mask['shape'] + return np.frombuffer(data_str, dtype=np.uint8).reshape(shape) + + +def main(): + args = _parse_args() + # init stream manager + stream_manager_api = _init_stream(PIPELINE_PATH) + if not stream_manager_api: + exit(1) + + with open(args.data_lst) as f: + img_lst = f.readlines() + os.makedirs(INFER_RESULT_DIR, exist_ok=True) + data_input = MxDataInput() + hist = np.zeros((args.num_classes, args.num_classes)) + for _, line in enumerate(img_lst): + img_path, msk_path = line.strip().split(' ') + img_path = os.path.join(args.data_root, img_path) + msk_path = os.path.join(args.data_root, msk_path) + msk_ = cv2.imread(msk_path, cv2.IMREAD_GRAYSCALE) + with open(img_path, 'rb') as f: + data_input.data = f.read() + each_array = _do_infer(stream_manager_api, data_input) + + hist += _cal_hist( + msk_.flatten(), each_array.flatten(), args.num_classes) + color_mask_res = label_to_color_image(each_array) + color_mask_res = cv2.cvtColor(color_mask_res.astype(np.uint8), + cv2.COLOR_RGBA2BGR) + result_path = os.path.join( + INFER_RESULT_DIR, + f"{img_path.split('/')[-1].split('.')[0]}.png") + cv2.imwrite(result_path, color_mask_res) + iou = np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist)) + print("per-class IoU", iou) + print("mean IoU", np.nanmean(iou)) + + stream_manager_api.DestroyAllStreams() + + +if __name__ == '__main__': + main() diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/sdk/run.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/sdk/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..eb9edef9b0d2e34d772c19bd2ff1ecd3bf73c72e --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/sdk/run.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 3.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. +# ============================================================================ + +data_root=$1 +data_lst=$2 +num_classes=$3 + +set -e + +# Simple log helper functions +info() { echo -e "\033[1;34m[INFO ][MxStream] $1\033[1;37m" ; } +warn() { echo >&2 -e "\033[1;31m[WARN ][MxStream] $1\033[1;37m" ; } + +export GST_PLUGIN_SCANNER=${MX_SDK_HOME}/opensource/libexec/gstreamer-1.0/gst-plugin-scanner +export GST_PLUGIN_PATH=${MX_SDK_HOME}/opensource/lib/gstreamer-1.0:${MX_SDK_HOME}/lib/plugins + +#to set PYTHONPATH, import the StreamManagerApi.py +export PYTHONPATH=$PYTHONPATH:${MX_SDK_HOME}/python + +python3 main.py $data_root $data_lst $num_classes + +exit 0 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/infer/util/get_vocval_lst.py b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/util/get_vocval_lst.py new file mode 100755 index 0000000000000000000000000000000000000000..13a0982a5c4108a52a06651c5edf734edb08ba43 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/infer/util/get_vocval_lst.py @@ -0,0 +1,68 @@ +# coding=utf-8 +""" +Copyright 2022 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 numpy as np +from PIL import Image + + +def _parse_args(): + parser = argparse.ArgumentParser('dataset list generator') + parser.add_argument("--data_dir", type=str, default='./', + help='where dataset stored.') + return parser.parse_args() + + +def _get_data_list(data_list_file): + with open(data_list_file, mode='r') as f: + return f.readlines() + + +def main(): + args = _parse_args() + data_dir = args.data_dir + voc_img_dir = os.path.join(data_dir, 'VOCdevkit', 'VOC2012', 'JPEGImages') + voc_anno_dir = os.path.join(data_dir, 'VOCdevkit', 'VOC2012', + 'SegmentationClass') + voc_anno_gray_dir = os.path.join(data_dir, 'VOCdevkit', 'VOC2012', + 'SegmentationClassGray') + voc_val_txt = os.path.join(data_dir, 'VOCdevkit', 'VOC2012', 'ImageSets', + 'Segmentation', 'val.txt') + voc_val_lst_txt = os.path.join(data_dir, 'voc_val_lst.txt') + + print('converting voc color png to gray png ...') + os.makedirs(voc_anno_gray_dir, exist_ok=True) + for ann in os.listdir(voc_anno_dir): + ann_im = Image.open(os.path.join(voc_anno_dir, ann)) + ann_im = Image.fromarray(np.array(ann_im)) + ann_im.save(os.path.join(voc_anno_gray_dir, ann)) + print('converting done.') + + voc_val_data_lst = _get_data_list(voc_val_txt) + with open(voc_val_lst_txt, mode='w') as f: + for id_ in voc_val_data_lst: + id_ = id_.strip() + img_ = os.path.join(voc_img_dir, id_ + '.jpg') + anno_ = os.path.join(voc_anno_gray_dir, id_ + '.png') + f.write(img_ + ' ' + anno_ + '\n') + print('generating voc val list success.') + + +if __name__ == '__main__': + main() diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/mindspore_hub_conf.py b/cv/semantic_segmentation/deeplabv3/MindSpore/mindspore_hub_conf.py new file mode 100755 index 0000000000000000000000000000000000000000..cf5d716600984d76ffc99c10a27972d37048ac43 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/mindspore_hub_conf.py @@ -0,0 +1,28 @@ +# Copyright 2020 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. +# ============================================================================ +"""hub config.""" +from src.nets import net_factory + +def create_network(name, *args, **kwargs): + freeze_bn = True + num_classes = kwargs["num_classes"] + if name == 'deeplab_v3_s16': + deeplab_v3_s16_network = net_factory.nets_map["deeplab_v3_s16"](num_classes, 16) + return deeplab_v3_s16_network + if name == 'deeplab_v3_s8': + # deeplab_v3_s8_network = net_factory.nets_map["deeplab_v3_s8"]('eval', num_classes, 8, freeze_bn) + deeplab_v3_s8_network = net_factory.nets_map["deeplab_v3_s8"](num_classes, 8) + return deeplab_v3_s8_network + raise NotImplementedError(f"{name} is not implemented in the repo") diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/__init__.py b/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/config.py b/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/config.py new file mode 100755 index 0000000000000000000000000000000000000000..2895b1e4a4fa69551be64485d8716b08fa3956a2 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/config.py @@ -0,0 +1,128 @@ +# 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. +# ============================================================================ + +"""Parse arguments""" + +import os +import ast +import argparse +from pprint import pprint, pformat +import yaml + +class Config: + """ + Configuration namespace. Convert dictionary to members. + """ + def __init__(self, cfg_dict): + for k, v in cfg_dict.items(): + if isinstance(v, (list, tuple)): + setattr(self, k, [Config(x) if isinstance(x, dict) else x for x in v]) + else: + setattr(self, k, Config(v) if isinstance(v, dict) else v) + + def __str__(self): + return pformat(self.__dict__) + + def __repr__(self): + return self.__str__() + + +def parse_cli_to_yaml(parser, cfg, helper=None, choices=None, cfg_path="default_config.yaml"): + """ + Parse command line arguments to the configuration according to the default yaml. + + Args: + parser: Parent parser. + cfg: Base configuration. + helper: Helper description. + cfg_path: Path to the default yaml config. + """ + parser = argparse.ArgumentParser(description="[REPLACE THIS at config.py]", + parents=[parser]) + helper = {} if helper is None else helper + choices = {} if choices is None else choices + for item in cfg: + if not isinstance(cfg[item], list) and not isinstance(cfg[item], dict): + help_description = helper[item] if item in helper else "Please reference to {}".format(cfg_path) + choice = choices[item] if item in choices else None + if isinstance(cfg[item], bool): + parser.add_argument("--" + item, type=ast.literal_eval, default=cfg[item], choices=choice, + help=help_description) + else: + parser.add_argument("--" + item, type=type(cfg[item]), default=cfg[item], choices=choice, + help=help_description) + args = parser.parse_args() + return args + + +def parse_yaml(yaml_path): + """ + Parse the yaml config file. + + Args: + yaml_path: Path to the yaml config. + """ + with open(yaml_path, 'r') as fin: + try: + cfgs = yaml.load_all(fin.read(), Loader=yaml.FullLoader) + cfgs = [x for x in cfgs] + if len(cfgs) == 1: + cfg_helper = {} + cfg = cfgs[0] + cfg_choices = {} + elif len(cfgs) == 2: + cfg, cfg_helper = cfgs + cfg_choices = {} + elif len(cfgs) == 3: + cfg, cfg_helper, cfg_choices = cfgs + else: + raise ValueError("At most 3 docs (config, description for help, choices) are supported in config yaml") + print(cfg_helper) + except: + raise ValueError("Failed to parse yaml") + return cfg, cfg_helper, cfg_choices + + +def merge(args, cfg): + """ + Merge the base config from yaml file and command line arguments. + + Args: + args: Command line arguments. + cfg: Base configuration. + """ + args_var = vars(args) + for item in args_var: + cfg[item] = args_var[item] + return cfg + + +def get_config(): + """ + Get Config according to the yaml file and cli arguments. + """ + parser = argparse.ArgumentParser(description="default name", add_help=False) + current_dir = os.path.dirname(os.path.abspath(__file__)) + parser.add_argument("--config_path", type=str, default=os.path.join(current_dir, "../default_config.yaml"), + help="Config file path") + path_args, _ = parser.parse_known_args() + default, helper, choices = parse_yaml(path_args.config_path) + args = parse_cli_to_yaml(parser=parser, cfg=default, helper=helper, choices=choices, cfg_path=path_args.config_path) + final_config = merge(args, default) + pprint(final_config) + print("Please check the above information for the configurations", flush=True) + return Config(final_config) + +config = get_config() diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/device_adapter.py b/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/device_adapter.py new file mode 100755 index 0000000000000000000000000000000000000000..7c5d7f837ddaa8f53cf8dc5573cac0e36881e7b1 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/device_adapter.py @@ -0,0 +1,27 @@ +# 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. +# ============================================================================ + +"""Device adapter for ModelArts""" + +from .config import config + +if config.enable_modelarts: + from .moxing_adapter import get_device_id, get_device_num, get_rank_id, get_job_id +else: + from .local_adapter import get_device_id, get_device_num, get_rank_id, get_job_id + +__all__ = [ + "get_device_id", "get_device_num", "get_rank_id", "get_job_id" +] diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/local_adapter.py b/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/local_adapter.py new file mode 100755 index 0000000000000000000000000000000000000000..769fa6dc78e59eb66dbc8e6773accdc1d08b649e --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/local_adapter.py @@ -0,0 +1,36 @@ +# 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. +# ============================================================================ + +"""Local adapter""" + +import os + +def get_device_id(): + device_id = os.getenv('DEVICE_ID', '0') + return int(device_id) + + +def get_device_num(): + device_num = os.getenv('RANK_SIZE', '1') + return int(device_num) + + +def get_rank_id(): + global_rank_id = os.getenv('RANK_ID', '0') + return int(global_rank_id) + + +def get_job_id(): + return "Local Job" diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/moxing_adapter.py b/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/moxing_adapter.py new file mode 100755 index 0000000000000000000000000000000000000000..25838a7da99a27a1bb744684c1f75f80f5704688 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/model_utils/moxing_adapter.py @@ -0,0 +1,116 @@ +# 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. +# ============================================================================ + +"""Moxing adapter for ModelArts""" + +import os +import functools +from mindspore import context +from .config import config + +_global_sync_count = 0 + +def get_device_id(): + device_id = os.getenv('DEVICE_ID', '0') + return int(device_id) + + +def get_device_num(): + device_num = os.getenv('RANK_SIZE', '1') + return int(device_num) + + +def get_rank_id(): + global_rank_id = os.getenv('RANK_ID', '0') + return int(global_rank_id) + + +def get_job_id(): + job_id = os.getenv('JOB_ID') + job_id = job_id if job_id != "" else "default" + return job_id + +def sync_data(from_path, to_path): + """ + Download data from remote obs to local directory if the first url is remote url and the second one is local path + Upload data from local directory to remote obs in contrast. + """ + import moxing as mox + import time + global _global_sync_count + sync_lock = "/tmp/copy_sync.lock" + str(_global_sync_count) + _global_sync_count += 1 + + # Each server contains 8 devices as most. + if get_device_id() % min(get_device_num(), 8) == 0 and not os.path.exists(sync_lock): + print("from path: ", from_path) + print("to path: ", to_path) + mox.file.copy_parallel(from_path, to_path) + print("===finish data synchronization===") + try: + os.mknod(sync_lock) + except IOError: + pass + print("===save flag===") + + while True: + if os.path.exists(sync_lock): + break + time.sleep(1) + + print("Finish sync data from {} to {}.".format(from_path, to_path)) + + +def moxing_wrapper(pre_process=None, post_process=None): + """ + Moxing wrapper to download dataset and upload outputs. + """ + def wrapper(run_func): + @functools.wraps(run_func) + def wrapped_func(*args, **kwargs): + # Download data from data_url + if config.enable_modelarts: + if config.data_url: + sync_data(config.data_url, config.data_path) + print("Dataset downloaded: ", os.listdir(config.data_path)) + if config.checkpoint_url: + sync_data(config.checkpoint_url, config.load_path) + print("Preload downloaded: ", os.listdir(config.load_path)) + if config.train_url: + sync_data(config.train_url, config.output_path) + print("Workspace downloaded: ", os.listdir(config.output_path)) + + context.set_context(save_graphs_path=os.path.join(config.output_path, str(get_rank_id()))) + config.device_num = get_device_num() + config.device_id = get_device_id() + if not os.path.exists(config.output_path): + os.makedirs(config.output_path) + + if pre_process: + pre_process() + + # Run the main function + run_func(*args, **kwargs) + + # Upload data to train_url + if config.enable_modelarts: + if post_process: + post_process() + + if config.train_url: + print("Start to copy output directory") + sync_data(config.output_path, config.train_url) + return wrapped_func + return wrapper diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/modelarts/train_start.py b/cv/semantic_segmentation/deeplabv3/MindSpore/modelarts/train_start.py new file mode 100755 index 0000000000000000000000000000000000000000..0a0f3f3e4c29ef61b44a312e6e0d42ac42adb0a6 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/modelarts/train_start.py @@ -0,0 +1,302 @@ +# Copyright 2022 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 deeplabv3.""" + +import os +import glob +import argparse +import moxing as mox +import numpy as np +from mindspore import context, export, Tensor +from mindspore.train.model import Model +from mindspore.context import ParallelMode +import mindspore.nn as nn +import mindspore.ops as ops +from mindspore.train.callback import ModelCheckpoint, CheckpointConfig +from mindspore.train.serialization import load_checkpoint, load_param_into_net +from mindspore.communication.management import init, get_rank, get_group_size +from mindspore.train.callback import LossMonitor, TimeMonitor +from mindspore.train.loss_scale_manager import FixedLossScaleManager +from mindspore.common import set_seed +from src.data import dataset as data_generator +from src.loss import loss +from src.nets import net_factory +from src.utils import learning_rates + +set_seed(1) + + +_CACHE_DATA_URL = "/cache/data_url" +_CACHE_TRAIN_URL = "/cache/train_url" + + +class BuildTrainNetwork(nn.Cell): + def __init__(self, network, criterion): + super(BuildTrainNetwork, self).__init__() + self.network = network + self.criterion = criterion + + def construct(self, input_data, label): + output = self.network(input_data) + net_loss = self.criterion(output, label) + return net_loss + + +class BuildEvalNetwork(nn.Cell): + def __init__(self, net, input_format="NCHW"): + super(BuildEvalNetwork, self).__init__() + self.network = net + self.softmax = nn.Softmax(axis=1) + self.transpose = ops.Transpose() + self.format = input_format + + def construct(self, x): + if self.format == "NHWC": + x = self.transpose(x, (0, 3, 1, 2)) + output = self.network(x) + output = self.softmax(output) + return output + + +def _parse_args(): + parser = argparse.ArgumentParser('mindspore deeplabv3 training') + # dataset + parser.add_argument('--train_url', type=str, default='', + help='where training log and ckpts saved') + parser.add_argument('--data_url', type=str, default='', + help='path of dataset') + parser.add_argument('--file_name', type=str, default='vocaug_mindrecord0', + help='mindrecord file name of dataset') + parser.add_argument('--batch_size', type=int, default=32, + help='batch size') + parser.add_argument('--crop_size', type=int, default=513, help='crop size') + parser.add_argument('--min_scale', type=float, default=0.5, + help='minimum scale of data argumentation') + parser.add_argument('--max_scale', type=float, default=2.0, + help='maximum scale of data argumentation') + parser.add_argument('--ignore_label', type=int, default=255, + help='ignore label') + parser.add_argument('--num_classes', type=int, default=21, + help='number of classes') + parser.add_argument('--image_mean', type=int, default=(103.53, 116.28, 123.675), + help='image mean') + parser.add_argument('--image_std', type=int, default=(57.375, 57.120, 58.395), + help='image std') + + + # optimizer + parser.add_argument('--train_epochs', type=int, default=300, help='epoch') + parser.add_argument('--lr_type', type=str, default='cos', + help='type of learning rate') + parser.add_argument('--base_lr', type=float, default=0.015, + help='base learning rate') + parser.add_argument('--lr_decay_step', type=int, default=40000, + help='learning rate decay step') + parser.add_argument('--lr_decay_rate', type=float, default=0.1, + help='learning rate decay rate') + parser.add_argument('--loss_scale', type=float, default=3072.0, + help='loss scale') + + # model + parser.add_argument('--model', type=str, default='deeplab_v3_s16', + help='select model') + parser.add_argument('--export_model', type=str, default='deeplab_v3_s16', + help='choices in [deeplab_v3_s16, deeplab_v3_s8]') + parser.add_argument('--freeze_bn', action='store_false', help='freeze bn') + parser.add_argument('--ckpt_pre_trained', type=str, default='', + help='pretrained model') + parser.add_argument('--input_format', type=str, default='NCHW', + help='NCHW or NHWC') + parser.add_argument('--export_batch_size', type=int, default=1, + help='batch size for export') + parser.add_argument('--input_size', type=int, default=513, + help='input size') + parser.add_argument('--export_name', type=str, default='deeplabv3', + help='output file name') + parser.add_argument('--file_format', type=str, default='AIR', + help='file format, choices in [AIR, MINDIR]') + + # train + parser.add_argument('--device_target', type=str, default='Ascend', + choices=['Ascend', 'CPU'], + help='device where the code will be implemented. ' + '(Default: Ascend)') + parser.add_argument('--is_distributed', action='store_false', + help='distributed training') + 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('--save_steps', type=int, default=1500, + help='steps interval for saving') + parser.add_argument('--keep_checkpoint_max', type=int, default=200, + help='max checkpoint for saving') + parser.add_argument('--filter_weight', type=str, default="", + help="filter weight") + + args, _ = parser.parse_known_args() + return args + + +def get_device_id(): + device_id = os.getenv('DEVICE_ID', '0') + return int(device_id) + + +def train(args, train_url, data_file, ckpt_pre_trained): + if args.device_target == "CPU": + context.set_context(mode=context.GRAPH_MODE, save_graphs=False, device_target="CPU") + else: + context.set_context(mode=context.GRAPH_MODE, save_graphs=False, + device_target="Ascend", device_id=get_device_id()) + + # init multicards training + if args.is_distributed: + init() + args.rank = get_rank() + args.group_size = get_group_size() + + parallel_mode = ParallelMode.DATA_PARALLEL + context.set_auto_parallel_context(parallel_mode=parallel_mode, gradients_mean=True, device_num=args.group_size) + + # dataset + dataset = data_generator.SegDataset(image_mean=args.image_mean, + image_std=args.image_std, + data_file=data_file, + batch_size=args.batch_size, + crop_size=args.crop_size, + max_scale=args.max_scale, + min_scale=args.min_scale, + ignore_label=args.ignore_label, + num_classes=args.num_classes, + num_readers=2, + num_parallel_calls=4, + shard_id=args.rank, + shard_num=args.group_size) + dataset = dataset.get_dataset(repeat=1) + + # network + if args.model == 'deeplab_v3_s16': + network = net_factory.nets_map[args.model]('train', args.num_classes, 16, args.freeze_bn) + elif args.model == 'deeplab_v3_s8': + network = net_factory.nets_map[args.model]('train', args.num_classes, 8, args.freeze_bn) + else: + raise NotImplementedError('model [{:s}] not recognized'.format(args.model)) + + # loss + loss_ = loss.SoftmaxCrossEntropyLoss(args.num_classes, args.ignore_label) + loss_.add_flags_recursive(fp32=True) + train_net = BuildTrainNetwork(network, loss_) + + # load pretrained model + if args.ckpt_pre_trained: + param_dict = load_checkpoint(ckpt_pre_trained) + if args.filter_weight: + filter_list = ["network.aspp.conv2.weight", "network.aspp.conv2.bias"] + for key in list(param_dict.keys()): + for filter_key in filter_list: + if filter_key not in key: + continue + print('filter {}'.format(key)) + del param_dict[key] + load_param_into_net(train_net, param_dict) + print('load_model {} success'.format(args.ckpt_pre_trained)) + else: + trans_param_dict = {} + for key, val in param_dict.items(): + key = key.replace("down_sample_layer", "downsample") + trans_param_dict[f"network.resnet.{key}"] = val + load_param_into_net(train_net, trans_param_dict) + print('load_model {} success'.format(args.ckpt_pre_trained)) + + # optimizer + iters_per_epoch = dataset.get_dataset_size() + total_train_steps = iters_per_epoch * args.train_epochs + if args.lr_type == 'cos': + lr_iter = learning_rates.cosine_lr(args.base_lr, total_train_steps, total_train_steps) + elif args.lr_type == 'poly': + lr_iter = learning_rates.poly_lr(args.base_lr, total_train_steps, total_train_steps, end_lr=0.0, power=0.9) + elif args.lr_type == 'exp': + lr_iter = learning_rates.exponential_lr(args.base_lr, args.lr_decay_step, args.lr_decay_rate, + total_train_steps, staircase=True) + else: + raise ValueError('unknown learning rate type') + opt = nn.Momentum(params=train_net.trainable_params(), learning_rate=lr_iter, momentum=0.9, weight_decay=0.0001, + loss_scale=args.loss_scale) + + # loss scale + manager_loss_scale = FixedLossScaleManager(args.loss_scale, drop_overflow_update=False) + amp_level = "O0" if args.device_target == "CPU" else "O3" + model = Model(train_net, optimizer=opt, amp_level=amp_level, loss_scale_manager=manager_loss_scale) + + # callback for saving ckpts + time_cb = TimeMonitor(data_size=iters_per_epoch) + loss_cb = LossMonitor() + cbs = [time_cb, loss_cb] + + if args.rank == 0: + config_ck = CheckpointConfig(save_checkpoint_steps=args.save_steps, + keep_checkpoint_max=args.keep_checkpoint_max) + ckpoint_cb = ModelCheckpoint(prefix=args.model, directory=train_url, config=config_ck) + cbs.append(ckpoint_cb) + + model.train(args.train_epochs, dataset, callbacks=cbs, dataset_sink_mode=(args.device_target != "CPU")) + + +def export_air(args, train_url): + '''run export.''' + context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target) + ckpt_list = glob.glob(train_url + "/*.ckpt") + ckpt_list.sort(key=os.path.getmtime) + ckpt_model = ckpt_list[-1] + + if args.export_model == 'deeplab_v3_s16': + network = net_factory.nets_map['deeplab_v3_s16']('eval', args.num_classes, 16, True) + else: + network = net_factory.nets_map['deeplab_v3_s8']('eval', args.num_classes, 8, True) + network = BuildEvalNetwork(network, args.input_format) + param_dict = load_checkpoint(ckpt_model) + + # load the parameter into net + load_param_into_net(network, param_dict) + if args.input_format == "NHWC": + input_data = Tensor( + np.ones([args.export_batch_size, args.input_size, args.input_size, 3]).astype(np.float32)) + else: + input_data = Tensor( + np.ones([args.export_batch_size, 3, args.input_size, args.input_size]).astype(np.float32)) + export(network, input_data, file_name=args.export_name, file_format=args.file_format) + export_file = args.export_name+"."+args.file_format.lower() + mox.file.copy(export_file, os.path.join(train_url, export_file)) + + +def main(): + args = _parse_args() + os.makedirs(_CACHE_TRAIN_URL, exist_ok=True) + os.makedirs(_CACHE_DATA_URL, exist_ok=True) + mox.file.copy_parallel(args.data_url, _CACHE_DATA_URL) + train_url = _CACHE_TRAIN_URL + data_url = _CACHE_DATA_URL + ckpt_pre_trained = os.path.join(_CACHE_DATA_URL, + args.ckpt_pre_trained) \ + if args.ckpt_pre_trained else "" + data_file = os.path.join(data_url, args.file_name) + train(args, train_url, data_file, ckpt_pre_trained) + export_air(args, train_url) + mox.file.copy_parallel(_CACHE_TRAIN_URL, args.train_url) + + +if __name__ == '__main__': + main() diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/postprocess.py b/cv/semantic_segmentation/deeplabv3/MindSpore/postprocess.py new file mode 100755 index 0000000000000000000000000000000000000000..5a31defac363fb086c1f0133b3bc7687e652b7b5 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/postprocess.py @@ -0,0 +1,77 @@ +# 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. +# ============================================================================ +"""post process for 310 inference""" +import os +import argparse +import numpy as np +from PIL import Image +import cv2 + +parser = argparse.ArgumentParser(description="deeplabv3 accuracy calculation") +parser.add_argument('--data_root', type=str, default='', help='root path of val data') +parser.add_argument('--data_lst', type=str, default='', help='list of val data') +parser.add_argument('--crop_size', type=int, default=513, help='crop size') +parser.add_argument('--num_classes', type=int, default=21, help='number of classes') +parser.add_argument('--result_path', type=str, default='./result_Files', help='result Files path') +args, _ = parser.parse_known_args() + +def get_img_size(file_name): + img = Image.open(file_name) + return img.size + +def get_resized_size(org_h, org_w, long_size=513): + if org_h > org_w: + new_h = long_size + new_w = int(1.0 * long_size * org_w / org_h) + else: + new_w = long_size + new_h = int(1.0 * long_size * org_h / org_w) + + return new_h, new_w + +def cal_hist(a, b, n): + k = (a >= 0) & (a < n) + return np.bincount(n * a[k].astype(np.int32) + b[k], minlength=n ** 2).reshape(n, n) + +def acc_cal(): + hist = np.zeros((args.num_classes, args.num_classes)) + with open(args.data_lst) as f: + img_lst = f.readlines() + + for line in enumerate(img_lst): + img_path, msk_path = line[1].strip().split(' ') + img_file_path = os.path.join(args.data_root, img_path) + org_width, org_height = get_img_size(img_file_path) + resize_h, resize_w = get_resized_size(org_height, org_width) + + result_file = os.path.join(args.result_path, os.path.basename(img_path).split('.jpg')[0] + '_0.bin') + net_out = np.fromfile(result_file, np.float32).reshape(args.num_classes, args.crop_size, args.crop_size) + probs_ = net_out[:, :resize_h, :resize_w].transpose((1, 2, 0)) + probs_ = cv2.resize(probs_, (org_width, org_height)) + + result_msk = probs_.argmax(axis=2) + + msk_path = os.path.join(args.data_root, msk_path) + mask = cv2.imread(msk_path, cv2.IMREAD_GRAYSCALE) + + hist += cal_hist(mask.flatten(), result_msk.flatten(), args.num_classes) + + print(hist) + iu = np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist)) + print('per-class IoU', iu) + print('mean IoU', np.nanmean(iu)) + +if __name__ == '__main__': + acc_cal() diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/requirements.txt b/cv/semantic_segmentation/deeplabv3/MindSpore/requirements.txt new file mode 100755 index 0000000000000000000000000000000000000000..23006a8a2cbeccddd038bd0bc90d05c597cac3ed --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/requirements.txt @@ -0,0 +1,7 @@ +onnxruntime-gpu +opencv-python >= 4.1.2.30 +pillow >= 6.2.0 +numpy +scipy +pyyaml +torch diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/build_data.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/build_data.sh new file mode 100755 index 0000000000000000000000000000000000000000..f94777c18651b14ce558720704465e489b84196a --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/build_data.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright 2020 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 DEVICE_ID=7 +EXECUTE_PATH=$(pwd) + +python ${EXECUTE_PATH}/../src/data/build_seg_data.py --data_root=/PATH/TO/DATA_ROOT \ + --data_lst=/PATH/TO/DATA_lst.txt \ + --dst_path=/PATH/TO/MINDRECORED_NAME.mindrecord \ + --num_shards=8 \ + --shuffle=True \ No newline at end of file diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/docker_start.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/docker_start.sh new file mode 100755 index 0000000000000000000000000000000000000000..eff8f5260602cd3ed58ee589d4123e4048dedd50 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/docker_start.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright 2022 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 -u root --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 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_distribute_train_s16_r1.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_distribute_train_s16_r1.sh new file mode 100755 index 0000000000000000000000000000000000000000..3bf974ee1ff1092da05b4612c3ed4a6bf0efc883 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_distribute_train_s16_r1.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +if [ $# != 1 ] +then + echo "Usage: sh run_distribute_train_base.sh [RANK_TABLE_FILE]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +PATH1=$(get_real_path $1) +echo $PATH1 + +if [ ! -f $PATH1 ] +then + echo "error: RANK_TABLE_FILE=$PATH1 is not a file" +exit 1 +fi + +ulimit -c unlimited +EXECUTE_PATH=$(pwd) +train_path=${EXECUTE_PATH}/s16_aug_train +export SLOG_PRINT_TO_STDOUT=0 +export RANK_TABLE_FILE=$PATH1 +export RANK_SIZE=8 +export RANK_START_ID=0 + +if [ -d ${train_path} ]; then + rm -rf ${train_path} +fi +mkdir -p ${train_path} +mkdir ${train_path}/ckpt + +for((i=0;i<=$RANK_SIZE-1;i++)); +do + export RANK_ID=${i} + export DEVICE_ID=$((i + RANK_START_ID)) + echo 'start rank='${i}', device id='${DEVICE_ID}'...' + mkdir ${train_path}/device${DEVICE_ID} + cd ${train_path}/device${DEVICE_ID} || exit + python ${EXECUTE_PATH}/../train.py --train_dir=${train_path}/ckpt \ + --data_file=/PATH_TO_DATA/vocaug/vocaug_mindrecord/vocaug_mindrecord0 \ + --train_epochs=300 \ + --batch_size=32 \ + --crop_size=513 \ + --base_lr=0.08 \ + --lr_type=cos \ + --min_scale=0.5 \ + --max_scale=2.0 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s16 \ + --ckpt_pre_trained=/PATH/TO/PRETRAIN_MODEL \ + --is_distributed=True \ + --save_steps=410 \ + --keep_checkpoint_max=1 >log 2>&1 & +done diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_distribute_train_s8_r1.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_distribute_train_s8_r1.sh new file mode 100755 index 0000000000000000000000000000000000000000..c0cc37df5b028b8e386c725960ec00acd36c2087 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_distribute_train_s8_r1.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +if [ $# != 1 ] +then + echo "Usage: sh run_distribute_train_base.sh [RANK_TABLE_FILE]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +PATH1=$(get_real_path $1) +echo $PATH1 + +if [ ! -f $PATH1 ] +then + echo "error: RANK_TABLE_FILE=$PATH1 is not a file" +exit 1 +fi + +ulimit -c unlimited +EXECUTE_PATH=$(pwd) +train_path=${EXECUTE_PATH}/s8_aug_train +export SLOG_PRINT_TO_STDOUT=0 +export RANK_TABLE_FILE=$PATH1 +export RANK_SIZE=8 +export RANK_START_ID=0 + +if [ -d ${train_path} ]; then + rm -rf ${train_path} +fi +mkdir -p ${train_path} +mkdir ${train_path}/ckpt + +for((i=0;i<=$RANK_SIZE-1;i++)); +do + export RANK_ID=${i} + export DEVICE_ID=$((i + RANK_START_ID)) + echo 'start rank='${i}', device id='${DEVICE_ID}'...' + mkdir ${train_path}/device${DEVICE_ID} + cd ${train_path}/device${DEVICE_ID} || exit + python ${EXECUTE_PATH}/../train.py --train_dir=${train_path}/ckpt \ + --data_file=/PATH_TO_DATA/vocaug/vocaug_mindrecord/vocaug_mindrecord0 \ + --train_epochs=800 \ + --batch_size=16 \ + --crop_size=513 \ + --base_lr=0.02 \ + --lr_type=cos \ + --min_scale=0.5 \ + --max_scale=2.0 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s8 \ + --loss_scale=2048 \ + --ckpt_pre_trained=/PATH/TO/PRETRAIN_MODEL \ + --is_distributed=True \ + --save_steps=820 \ + --keep_checkpoint_max=1 >log 2>&1 & +done diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_distribute_train_s8_r2.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_distribute_train_s8_r2.sh new file mode 100755 index 0000000000000000000000000000000000000000..bdcfb44477aaad8ffc985f400707a84c1bf1ddb5 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_distribute_train_s8_r2.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +if [ $# != 1 ] +then + echo "Usage: sh run_distribute_train_base.sh [RANK_TABLE_FILE]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +PATH1=$(get_real_path $1) +echo $PATH1 + +if [ ! -f $PATH1 ] +then + echo "error: RANK_TABLE_FILE=$PATH1 is not a file" +exit 1 +fi + +ulimit -c unlimited +EXECUTE_PATH=$(pwd) +train_path=${EXECUTE_PATH}/s8_voc_train +export SLOG_PRINT_TO_STDOUT=0 +export RANK_TABLE_FILE=$PATH1 +export RANK_SIZE=8 +export RANK_START_ID=0 + +if [ -d ${train_path} ]; then + rm -rf ${train_path} +fi +mkdir -p ${train_path} +mkdir ${train_path}/ckpt + +for((i=0;i<=$RANK_SIZE-1;i++)); +do + export RANK_ID=${i} + export DEVICE_ID=$((i + RANK_START_ID)) + echo 'start rank='${i}', device id='${DEVICE_ID}'...' + mkdir ${train_path}/device${DEVICE_ID} + cd ${train_path}/device${DEVICE_ID} || exit + python ${EXECUTE_PATH}/../train.py --train_dir=${train_path}/ckpt \ + --data_file=/PATH_TO_DATA/vocaug/voctrain_mindrecord/voctrain_mindrecord00 \ + --train_epochs=300 \ + --batch_size=16 \ + --crop_size=513 \ + --base_lr=0.008 \ + --lr_type=cos \ + --min_scale=0.5 \ + --max_scale=2.0 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s8 \ + --loss_scale=2048 \ + --ckpt_pre_trained=/PATH/TO/PRETRAIN_MODEL \ + --is_distributed=True \ + --save_steps=110 \ + --keep_checkpoint_max=1 >log 2>&1 & +done diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_onnx.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_onnx.sh new file mode 100755 index 0000000000000000000000000000000000000000..d86aeed1da75a9c99d0e6807980530377676be80 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_onnx.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ + +if [ $# != 3 ] +then + echo "Usage: bash run_eval_onnx.sh [DATA_ROOT] [DATA_LST] [FILE_NAME]" +exit 1 +fi + +export DEVICE_ID=0 +EXECUTE_PATH=$(pwd) +eval_path=${EXECUTE_PATH}/onnx_eval + +if [ -d ${eval_path} ]; then + rm -rf ${eval_path} +fi +mkdir -p ${eval_path} + +python ${EXECUTE_PATH}/../eval_onnx.py --data_root=$1 \ + --data_lst=$2 \ + --batch_size=32 \ + --device_target GPU \ + --file_name=$3 >${eval_path}/eval_log 2>&1 & diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s16.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s16.sh new file mode 100755 index 0000000000000000000000000000000000000000..88946755dfaef394b1fc12191ef2c62526c0cd70 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s16.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Copyright 2020 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 DEVICE_ID=0 +export SLOG_PRINT_TO_STDOUT=0 +EXECUTE_PATH=$(pwd) +eval_path=${EXECUTE_PATH}/s16_eval + +if [ -d ${eval_path} ]; then + rm -rf ${eval_path} +fi +mkdir -p ${eval_path} + +python ${EXECUTE_PATH}/../eval.py --data_root=/PATH_TO_DATA/vocaug \ + --data_lst=/PATH_TO_DATA/vocaug/voc_val_lst.txt \ + --batch_size=32 \ + --crop_size=513 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s16 \ + --scales_type=0 \ + --freeze_bn=True \ + --ckpt_path=/PATH/TO/PRETRAIN_MODEL >${eval_path}/eval_log 2>&1 & + diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s16_gpu.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s16_gpu.sh new file mode 100755 index 0000000000000000000000000000000000000000..0415327acd4009dba62936419f654c6dc5f5f3a1 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s16_gpu.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Copyright 2020 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 DEVICE_ID=0 +export SLOG_PRINT_TO_STDOUT=0 +EXECUTE_PATH=$(pwd) +eval_path=${EXECUTE_PATH}/s16_eval + +if [ -d ${eval_path} ]; then + rm -rf ${eval_path} +fi +mkdir -p ${eval_path} + +python3 ${EXECUTE_PATH}/../eval.py --data_root=${1} \ + --data_lst=${1}/voc_val_lst.txt \ + --batch_size=32 \ + --crop_size=513 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s16 \ + --scales_type=0 \ + --freeze_bn=True \ + --device_target="GPU" \ + --ckpt_path=${2}/deeplab_v3_s16-200_45.ckpt >${eval_path}/eval_log 2>&1 & + diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s8.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s8.sh new file mode 100755 index 0000000000000000000000000000000000000000..98e5f4c95874c1d866b4f14ec3792de2bb2b5f8a --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s8.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Copyright 2020 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 DEVICE_ID=1 +export SLOG_PRINT_TO_STDOUT=0 +EXECUTE_PATH=$(pwd) +eval_path=${EXECUTE_PATH}/s8_eval + +if [ -d ${eval_path} ]; then + rm -rf ${eval_path} +fi +mkdir -p ${eval_path} + +python ${EXECUTE_PATH}/../eval.py --data_root=/PATH_TO_DATA/vocaug \ + --data_lst=/PATH_TO_DATA/vocaug/voc_val_lst.txt \ + --batch_size=16 \ + --crop_size=513 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s8 \ + --scales_type=0 \ + --freeze_bn=True \ + --ckpt_path=/PATH/TO/PRETRAIN_MODEL >${eval_path}/eval_log 2>&1 & + diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s8_multiscale.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s8_multiscale.sh new file mode 100755 index 0000000000000000000000000000000000000000..1a8ec21f4a6cb9a8ea5c9ab47114f423797f5778 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s8_multiscale.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Copyright 2020 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 DEVICE_ID=2 +export SLOG_PRINT_TO_STDOUT=0 +EXECUTE_PATH=$(pwd) +eval_path=${EXECUTE_PATH}/multiscale_eval + +if [ -d ${eval_path} ]; then + rm -rf ${eval_path} +fi +mkdir -p ${eval_path} + +python ${EXECUTE_PATH}/../eval.py --data_root=/PATH_TO_DATA/vocaug \ + --data_lst=/PATH_TO_DATA/vocaug/voc_val_lst.txt \ + --batch_size=16 \ + --crop_size=513 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s8 \ + --scales_type=1 \ + --freeze_bn=True \ + --ckpt_path=/PATH/TO/PRETRAIN_MODEL >${eval_path}/eval_log 2>&1 & + diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s8_multiscale_flip.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s8_multiscale_flip.sh new file mode 100755 index 0000000000000000000000000000000000000000..b0834c65d76d670fc0304f1a708f5d5416406690 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_eval_s8_multiscale_flip.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Copyright 2020 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 DEVICE_ID=3 +export SLOG_PRINT_TO_STDOUT=0 +EXECUTE_PATH=$(pwd) +eval_path=${EXECUTE_PATH}/multiscale_flip_eval + +if [ -d ${eval_path} ]; then + rm -rf ${eval_path} +fi +mkdir -p ${eval_path} + +python ${EXECUTE_PATH}/../eval.py --data_root=/PATH_TO_DATA/vocaug \ + --data_lst=/PATH_TO_DATA/vocaug/voc_val_lst.txt \ + --batch_size=16 \ + --crop_size=513 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s8 \ + --scales_type=1 \ + --flip=True \ + --freeze_bn=True \ + --ckpt_path=/PATH/TO/PRETRAIN_MODEL >${eval_path}/eval_log 2>&1 & + diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_infer_310.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_infer_310.sh new file mode 100755 index 0000000000000000000000000000000000000000..d7181137fc9619da9b46d2b457c10448b789acd5 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_infer_310.sh @@ -0,0 +1,101 @@ +#!/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. +# ============================================================================ + +if [[ $# -lt 4 || $# -gt 5 ]]; then + echo "Usage: bash run_infer_310.sh [MINDIR_PATH] [DATA_PATH] [DATA_ROOT] [DATA_LIST] [DEVICE_ID] + DEVICE_ID is optional, it can be set by environment variable device_id, otherwise the value is zero" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} +model=$(get_real_path $1) +data_path=$(get_real_path $2) +data_root=$(get_real_path $3) +data_list_path=$(get_real_path $4) + + +device_id=0 +if [ $# == 5 ]; then + device_id=$5 +fi + +echo "mindir name: "$model +echo "dataset path: "$data_path +echo "data root path: "$data_root +echo "data list path: "$data_list_path +echo "device id: "$device_id + +export ASCEND_HOME=/usr/local/Ascend/ +if [ -d ${ASCEND_HOME}/ascend-toolkit ]; then + export PATH=$ASCEND_HOME/fwkacllib/bin:$ASCEND_HOME/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/ascend-toolkit/latest/atc/bin:$PATH + export LD_LIBRARY_PATH=$ASCEND_HOME/fwkacllib/lib64:/usr/local/lib:$ASCEND_HOME/ascend-toolkit/latest/atc/lib64:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export TBE_IMPL_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe + export PYTHONPATH=$ASCEND_HOME/fwkacllib/python/site-packages:${TBE_IMPL_PATH}:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp +else + export ASCEND_HOME=/usr/local/Ascend/latest/ + export PATH=$ASCEND_HOME/fwkacllib/bin:$ASCEND_HOME/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/atc/ccec_compiler/bin:$ASCEND_HOME/atc/bin:$PATH + export LD_LIBRARY_PATH=$ASCEND_HOME/fwkacllib/lib64:/usr/local/lib:$ASCEND_HOME/atc/lib64:$ASCEND_HOME/acllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export PYTHONPATH=$ASCEND_HOME/fwkacllib/python/site-packages:$ASCEND_HOME/atc/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/opp +fi + +function compile_app() +{ + cd ../ascend310_infer || exit + bash build.sh &> build.log +} + +function infer() +{ + cd - || exit + if [ -d result_Files ]; then + rm -rf ./result_Files + fi + if [ -d time_Result ]; then + rm -rf ./time_Result + fi + mkdir result_Files + mkdir time_Result + ../ascend310_infer/out/main --mindir_path=$model --dataset_path=$data_root --image_list=$data_list_path --device_id=$device_id --fusion_switch_path=../ascend310_infer/fusion_switch.cfg &> infer.log +} + +function cal_acc() +{ + python ../postprocess.py --data_root=$data_root --data_lst=$data_list_path --scales=1.0 --result_path=./result_Files &> acc.log & +} + +compile_app +if [ $? -ne 0 ]; then + echo "compile app code failed" + exit 1 +fi +infer +if [ $? -ne 0 ]; then + echo " execute inference failed" + exit 1 +fi +cal_acc +if [ $? -ne 0 ]; then + echo "calculate accuracy failed" + exit 1 +fi \ No newline at end of file diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_standalone_train.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_standalone_train.sh new file mode 100755 index 0000000000000000000000000000000000000000..c3fd3c15c14c4bfb9288959ebe7b413f51af87e0 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_standalone_train.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Copyright 2020 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 DEVICE_ID=0 +export SLOG_PRINT_TO_STDOUT=0 +EXECUTE_PATH=$(pwd) +train_path=${EXECUTE_PATH}/s16_aug_train_1p + +if [ -d ${train_path} ]; then + rm -rf ${train_path} +fi +mkdir -p ${train_path} +mkdir ${train_path}/device${DEVICE_ID} +mkdir ${train_path}/ckpt +cd ${train_path}/device${DEVICE_ID} || exit + +python ${EXECUTE_PATH}/../train.py --data_file=/PATH_TO_DATA/vocaug/vocaug_mindrecord/vocaug_mindrecord0 \ + --train_dir=${train_path}/ckpt \ + --train_epochs=200 \ + --batch_size=32 \ + --crop_size=513 \ + --base_lr=0.015 \ + --lr_type=cos \ + --min_scale=0.5 \ + --max_scale=2.0 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s16 \ + --ckpt_pre_trained=/PATH/TO/PRETRAIN_MODEL \ + --save_steps=1500 \ + --keep_checkpoint_max=200 >log 2>&1 & \ No newline at end of file diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_standalone_train_cpu.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_standalone_train_cpu.sh new file mode 100755 index 0000000000000000000000000000000000000000..4fc0d6ac21af4e8db87160466b11acb9af144e19 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_standalone_train_cpu.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright 2020 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 DEVICE_ID=0 +export SLOG_PRINT_TO_STDOUT=0 +EXECUTE_PATH=$(pwd) +train_path=${EXECUTE_PATH}/s16_aug_train_cpu + +if [ -d ${train_path} ]; then + rm -rf ${train_path} +fi +mkdir -p ${train_path} +mkdir ${train_path}/device${DEVICE_ID} +mkdir ${train_path}/ckpt +cd ${train_path}/device${DEVICE_ID} || exit + +python ${EXECUTE_PATH}/../train.py --data_file=/PATH_TO_DATA/vocaug/vocaug_mindrecord/vocaug_mindrecord0 \ + --device_target=CPU \ + --train_dir=${train_path}/ckpt \ + --train_epochs=200 \ + --batch_size=32 \ + --crop_size=513 \ + --base_lr=0.015 \ + --lr_type=cos \ + --min_scale=0.5 \ + --max_scale=2.0 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s16 \ + --ckpt_pre_trained=/PATH/TO/PRETRAIN_MODEL \ + --save_steps=1500 \ + --keep_checkpoint_max=200 >log 2>&1 & diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_standalone_train_gpu.sh b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_standalone_train_gpu.sh new file mode 100755 index 0000000000000000000000000000000000000000..f909c67f68485171e2f7eacc14815e9a8d908446 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/scripts/run_standalone_train_gpu.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright 2020 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 DEVICE_ID=0 +export SLOG_PRINT_TO_STDOUT=0 +EXECUTE_PATH=$(pwd) +train_path=${EXECUTE_PATH}/s16_aug_train_1p + +if [ -d ${train_path} ]; then + rm -rf ${train_path} +fi +mkdir -p ${train_path} +mkdir ${train_path}/device${DEVICE_ID} +mkdir ${train_path}/ckpt +cd ${train_path}/device${DEVICE_ID} || exit + +python3 ${EXECUTE_PATH}/../train.py --data_file=$1/vocaug_train.mindrecord0 \ + --train_dir=${train_path}/ckpt \ + --device_target="GPU" \ + --train_epochs=200 \ + --batch_size=32 \ + --crop_size=513 \ + --base_lr=0.015 \ + --lr_type=cos \ + --min_scale=0.5 \ + --max_scale=2.0 \ + --ignore_label=255 \ + --num_classes=21 \ + --model=deeplab_v3_s16 \ + --ckpt_pre_trained=$1/resnet101_ascend_v120_imagenet2012_official_cv_bs32_acc78.ckpt \ + --save_steps=1500 \ + --keep_checkpoint_max=200 >log 2>&1 & diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/__init__.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/__init__.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/build_seg_data.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/build_seg_data.py new file mode 100755 index 0000000000000000000000000000000000000000..a29f2f62e10d3b62c1a5929fe2da35f94603c8ea --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/build_seg_data.py @@ -0,0 +1,72 @@ +# Copyright 2020 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 os +import argparse +import numpy as np +from mindspore.mindrecord import FileWriter + + +seg_schema = {"file_name": {"type": "string"}, "label": {"type": "bytes"}, "data": {"type": "bytes"}} + + +def parse_args(): + parser = argparse.ArgumentParser('mindrecord') + + parser.add_argument('--data_root', type=str, default='', help='root path of data') + parser.add_argument('--data_lst', type=str, default='', help='list of data') + parser.add_argument('--dst_path', type=str, default='', help='save path of mindrecords') + parser.add_argument('--num_shards', type=int, default=8, help='number of shards') + parser.add_argument('--shuffle', type=bool, default=True, help='shuffle or not') + + parser_args, _ = parser.parse_known_args() + return parser_args + + +if __name__ == '__main__': + args = parse_args() + + data = [] + with open(args.data_lst) as f: + lines = f.readlines() + if args.shuffle: + np.random.shuffle(lines) + + dst_dir = '/'.join(args.dst_path.split('/')[:-1]) + if not os.path.exists(dst_dir): + os.makedirs(dst_dir) + + print('number of samples:', len(lines)) + writer = FileWriter(file_name=args.dst_path, shard_num=args.num_shards) + writer.add_schema(seg_schema, "seg_schema") + cnt = 0 + for l in lines: + img_path, label_path = l.strip().split(' ') + sample_ = {"file_name": img_path.split('/')[-1]} + with open(os.path.join(args.data_root, img_path), 'rb') as f: + sample_['data'] = f.read() + with open(os.path.join(args.data_root, label_path), 'rb') as f: + sample_['label'] = f.read() + data.append(sample_) + cnt += 1 + if cnt % 1000 == 0: + writer.write_raw_data(data) + print('number of samples written:', cnt) + data = [] + + if data: + writer.write_raw_data(data) + writer.commit() + print('number of samples written:', cnt) diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/dataset.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/dataset.py new file mode 100755 index 0000000000000000000000000000000000000000..e49a30ae9ceec486a1c04283a359ce4f95da71b7 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/dataset.py @@ -0,0 +1,94 @@ +# Copyright 2020 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 numpy as np +import cv2 +import mindspore.dataset as de +cv2.setNumThreads(0) + + +class SegDataset: + def __init__(self, + image_mean, + image_std, + data_file='', + batch_size=32, + crop_size=512, + max_scale=2.0, + min_scale=0.5, + ignore_label=255, + num_classes=21, + num_readers=2, + num_parallel_calls=4, + shard_id=None, + shard_num=None): + + self.data_file = data_file + self.batch_size = batch_size + self.crop_size = crop_size + self.image_mean = np.array(image_mean, dtype=np.float32) + self.image_std = np.array(image_std, dtype=np.float32) + self.max_scale = max_scale + self.min_scale = min_scale + self.ignore_label = ignore_label + self.num_classes = num_classes + self.num_readers = num_readers + self.num_parallel_calls = num_parallel_calls + self.shard_id = shard_id + self.shard_num = shard_num + assert max_scale > min_scale + + def preprocess_(self, image, label): + # bgr image + image_out = cv2.imdecode(np.frombuffer(image, dtype=np.uint8), cv2.IMREAD_COLOR) + label_out = cv2.imdecode(np.frombuffer(label, dtype=np.uint8), cv2.IMREAD_GRAYSCALE) + + sc = np.random.uniform(self.min_scale, self.max_scale) + new_h, new_w = int(sc * image_out.shape[0]), int(sc * image_out.shape[1]) + image_out = cv2.resize(image_out, (new_w, new_h), interpolation=cv2.INTER_CUBIC) + label_out = cv2.resize(label_out, (new_w, new_h), interpolation=cv2.INTER_NEAREST) + + image_out = (image_out - self.image_mean) / self.image_std + h_, w_ = max(new_h, self.crop_size), max(new_w, self.crop_size) + pad_h, pad_w = h_ - new_h, w_ - new_w + if pad_h > 0 or pad_w > 0: + image_out = cv2.copyMakeBorder(image_out, 0, pad_h, 0, pad_w, cv2.BORDER_CONSTANT, value=0) + label_out = cv2.copyMakeBorder(label_out, 0, pad_h, 0, pad_w, cv2.BORDER_CONSTANT, value=self.ignore_label) + offset_h = np.random.randint(0, h_ - self.crop_size + 1) + offset_w = np.random.randint(0, w_ - self.crop_size + 1) + image_out = image_out[offset_h: offset_h + self.crop_size, offset_w: offset_w + self.crop_size, :] + label_out = label_out[offset_h: offset_h + self.crop_size, offset_w: offset_w+self.crop_size] + + if np.random.uniform(0.0, 1.0) > 0.5: + image_out = image_out[:, ::-1, :] + label_out = label_out[:, ::-1] + + image_out = image_out.transpose((2, 0, 1)) + image_out = image_out.copy() + label_out = label_out.copy() + return image_out, label_out + + def get_dataset(self, repeat=1): + data_set = de.MindDataset(self.data_file, columns_list=["data", "label"], + shuffle=True, num_parallel_workers=self.num_readers, + num_shards=self.shard_num, shard_id=self.shard_id) + transforms_list = self.preprocess_ + data_set = data_set.map(operations=transforms_list, input_columns=["data", "label"], + output_columns=["data", "label"], + num_parallel_workers=self.num_parallel_calls) + data_set = data_set.shuffle(buffer_size=self.batch_size * 10) + data_set = data_set.batch(self.batch_size, drop_remainder=True) + data_set = data_set.repeat(repeat) + return data_set diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/get_dataset_lst.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/get_dataset_lst.py new file mode 100755 index 0000000000000000000000000000000000000000..66dd3920e755429fdd277dbcdf10d0b055b16369 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/src/data/get_dataset_lst.py @@ -0,0 +1,142 @@ +import argparse +import os + +import numpy as np +import scipy.io +from PIL import Image + +parser = argparse.ArgumentParser('dataset list generator') +parser.add_argument("--data_root", type=str, default='./', help='where dataset stored.') + +args, _ = parser.parse_known_args() + +data_root = args.data_root +print("Data dir is:", data_root) + +# +VOC_IMG_DIR = os.path.join(data_root, 'VOCdevkit/VOC2012/JPEGImages') +VOC_ANNO_DIR = os.path.join(data_root, 'VOCdevkit/VOC2012/SegmentationClass') +VOC_ANNO_GRAY_DIR = os.path.join(data_root, 'VOCdevkit/VOC2012/SegmentationClassGray') +VOC_TRAIN_TXT = os.path.join(data_root, 'VOCdevkit/VOC2012/ImageSets/Segmentation/train.txt') +VOC_VAL_TXT = os.path.join(data_root, 'VOCdevkit/VOC2012/ImageSets/Segmentation/val.txt') + +SBD_ANNO_DIR = os.path.join(data_root, 'benchmark_RELEASE/dataset/cls') +SBD_IMG_DIR = os.path.join(data_root, 'benchmark_RELEASE/dataset/img') +SBD_ANNO_PNG_DIR = os.path.join(data_root, 'benchmark_RELEASE/dataset/cls_png') +SBD_ANNO_GRAY_DIR = os.path.join(data_root, 'benchmark_RELEASE/dataset/cls_png_gray') +SBD_TRAIN_TXT = os.path.join(data_root, 'benchmark_RELEASE/dataset/train.txt') +SBD_VAL_TXT = os.path.join(data_root, 'benchmark_RELEASE/dataset/val.txt') + +VOC_TRAIN_LST_TXT = os.path.join(data_root, 'voc_train_lst.txt') +VOC_VAL_LST_TXT = os.path.join(data_root, 'voc_val_lst.txt') +VOC_AUG_TRAIN_LST_TXT = os.path.join(data_root, 'vocaug_train_lst.txt') + + +def __get_data_list(data_list_file): + with open(data_list_file, mode='r') as f: + return f.readlines() + + +def conv_voc_colorpng_to_graypng(): + if not os.path.exists(VOC_ANNO_GRAY_DIR): + os.makedirs(VOC_ANNO_GRAY_DIR) + + for ann in os.listdir(VOC_ANNO_DIR): + ann_im = Image.open(os.path.join(VOC_ANNO_DIR, ann)) + ann_im = Image.fromarray(np.array(ann_im)) + ann_im.save(os.path.join(VOC_ANNO_GRAY_DIR, ann)) + + +def __gen_palette(cls_nums=256): + palette = np.zeros((cls_nums, 3), dtype=np.uint8) + for i in range(cls_nums): + lbl = i + j = 0 + while lbl: + palette[i, 0] |= (((lbl >> 0) & 1) << (7 - j)) + palette[i, 1] |= (((lbl >> 1) & 1) << (7 - j)) + palette[i, 2] |= (((lbl >> 2) & 1) << (7 - j)) + lbl >>= 3 + j += 1 + return palette.flatten() + + +def conv_sbd_mat_to_png(): + if not os.path.exists(SBD_ANNO_PNG_DIR): + os.makedirs(SBD_ANNO_PNG_DIR) + if not os.path.exists(SBD_ANNO_GRAY_DIR): + os.makedirs(SBD_ANNO_GRAY_DIR) + + palette = __gen_palette() + for an in os.listdir(SBD_ANNO_DIR): + img_id = an[:-4] + mat = scipy.io.loadmat(os.path.join(SBD_ANNO_DIR, an)) + anno = mat['GTcls'][0]['Segmentation'][0].astype(np.uint8) + anno_png = Image.fromarray(anno) + # save to gray png + anno_png.save(os.path.join(SBD_ANNO_GRAY_DIR, img_id + '.png')) + # save to color png use palette + anno_png.putpalette(palette) + anno_png.save(os.path.join(SBD_ANNO_PNG_DIR, img_id + '.png')) + + +def create_voc_train_lst_txt(): + voc_train_data_lst = __get_data_list(VOC_TRAIN_TXT) + with open(VOC_TRAIN_LST_TXT, mode='w') as f: + for id_ in voc_train_data_lst: + id_ = id_.strip() + img_ = os.path.join(VOC_IMG_DIR, id_ + '.jpg') + anno_ = os.path.join(VOC_ANNO_GRAY_DIR, id_ + '.png') + f.write(img_ + ' ' + anno_ + '\n') + + +def create_voc_val_lst_txt(): + voc_val_data_lst = __get_data_list(VOC_VAL_TXT) + with open(VOC_VAL_LST_TXT, mode='w') as f: + for id_ in voc_val_data_lst: + id_ = id_.strip() + img_ = os.path.join(VOC_IMG_DIR, id_ + '.jpg') + anno_ = os.path.join(VOC_ANNO_GRAY_DIR, id_ + '.png') + f.write(img_ + ' ' + anno_ + '\n') + + +def create_voc_train_aug_lst_txt(): + voc_train_data_lst = __get_data_list(VOC_TRAIN_TXT) + voc_val_data_lst = __get_data_list(VOC_VAL_TXT) + + sbd_train_data_lst = __get_data_list(SBD_TRAIN_TXT) + sbd_val_data_lst = __get_data_list(SBD_VAL_TXT) + + with open(VOC_AUG_TRAIN_LST_TXT, mode='w') as f: + for id_ in sbd_train_data_lst + sbd_val_data_lst: + if id_ in voc_train_data_lst + voc_val_data_lst: + continue + id_ = id_.strip() + img_ = os.path.join(SBD_IMG_DIR, id_ + '.jpg') + anno_ = os.path.join(SBD_ANNO_GRAY_DIR, id_ + '.png') + f.write(img_ + ' ' + anno_ + '\n') + + for id_ in voc_train_data_lst: + id_ = id_.strip() + img_ = os.path.join(VOC_IMG_DIR, id_ + '.jpg') + anno_ = os.path.join(VOC_ANNO_GRAY_DIR, id_ + '.png') + f.write(img_ + ' ' + anno_ + '\n') + + +if __name__ == '__main__': + print('converting voc color png to gray png ...') + conv_voc_colorpng_to_graypng() + print('converting done.') + + create_voc_train_lst_txt() + print('generating voc train list success.') + + create_voc_val_lst_txt() + print('generating voc val list success.') + + print('converting sbd annotations to png ...') + conv_sbd_mat_to_png() + print('converting done') + + create_voc_train_aug_lst_txt() + print('generating voc train aug list success.') diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/loss/__init__.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/loss/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/loss/loss.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/loss/loss.py new file mode 100755 index 0000000000000000000000000000000000000000..e3f62c937ecbc5cdbaf49136abea556d96070a0c --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/src/loss/loss.py @@ -0,0 +1,50 @@ +# Copyright 2020 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. +# ============================================================================ + +from mindspore import Tensor +import mindspore.common.dtype as mstype +import mindspore.nn as nn +from mindspore.ops import operations as P + + +class SoftmaxCrossEntropyLoss(nn.Cell): + def __init__(self, num_cls=21, ignore_label=255): + super(SoftmaxCrossEntropyLoss, self).__init__() + self.one_hot = P.OneHot(axis=-1) + self.on_value = Tensor(1.0, mstype.float32) + self.off_value = Tensor(0.0, mstype.float32) + self.cast = P.Cast() + self.ce = nn.SoftmaxCrossEntropyWithLogits() + self.not_equal = P.NotEqual() + self.num_cls = num_cls + self.ignore_label = ignore_label + self.mul = P.Mul() + self.sum = P.ReduceSum(False) + self.div = P.RealDiv() + self.transpose = P.Transpose() + self.reshape = P.Reshape() + + def construct(self, logits, labels): + labels_int = self.cast(labels, mstype.int32) + labels_int = self.reshape(labels_int, (-1,)) + logits_ = self.transpose(logits, (0, 2, 3, 1)) # NCHW->NHWC + logits_ = self.reshape(logits_, (-1, self.num_cls)) + weights = self.not_equal(labels_int, self.ignore_label) + weights = self.cast(weights, mstype.float32) + one_hot_labels = self.one_hot(labels_int, self.num_cls, self.on_value, self.off_value) + loss = self.ce(logits_, one_hot_labels) + loss = self.mul(weights, loss) + loss = self.div(self.sum(loss), self.sum(weights)) + return loss diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/__init__.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/deeplab_v3/__init__.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/deeplab_v3/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/deeplab_v3/deeplab_v3.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/deeplab_v3/deeplab_v3.py new file mode 100755 index 0000000000000000000000000000000000000000..e41de7df37c001909dc96c15e9eb970fc5ab3b08 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/deeplab_v3/deeplab_v3.py @@ -0,0 +1,219 @@ +# Copyright 2020 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 mindspore.nn as nn +from mindspore.ops import operations as P + + +def conv1x1(in_planes, out_planes, stride=1): + return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, weight_init='xavier_uniform') + + +def conv3x3(in_planes, out_planes, stride=1, dilation=1, padding=1): + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, pad_mode='pad', padding=padding, + dilation=dilation, weight_init='xavier_uniform') + + +class Resnet(nn.Cell): + def __init__(self, block, block_num, output_stride, use_batch_statistics=True): + super(Resnet, self).__init__() + self.inplanes = 64 + self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, pad_mode='pad', padding=3, + weight_init='xavier_uniform') + self.bn1 = nn.BatchNorm2d(self.inplanes, use_batch_statistics=use_batch_statistics) + self.relu = nn.ReLU() + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, pad_mode='same') + self.layer1 = self._make_layer(block, 64, block_num[0], use_batch_statistics=use_batch_statistics) + self.layer2 = self._make_layer(block, 128, block_num[1], stride=2, use_batch_statistics=use_batch_statistics) + + if output_stride == 16: + self.layer3 = self._make_layer(block, 256, block_num[2], stride=2, + use_batch_statistics=use_batch_statistics) + self.layer4 = self._make_layer(block, 512, block_num[3], stride=1, base_dilation=2, grids=[1, 2, 4], + use_batch_statistics=use_batch_statistics) + elif output_stride == 8: + self.layer3 = self._make_layer(block, 256, block_num[2], stride=1, base_dilation=2, + use_batch_statistics=use_batch_statistics) + self.layer4 = self._make_layer(block, 512, block_num[3], stride=1, base_dilation=4, grids=[1, 2, 4], + use_batch_statistics=use_batch_statistics) + + def _make_layer(self, block, planes, blocks, stride=1, base_dilation=1, grids=None, use_batch_statistics=True): + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.SequentialCell([ + conv1x1(self.inplanes, planes * block.expansion, stride), + nn.BatchNorm2d(planes * block.expansion, use_batch_statistics=use_batch_statistics) + ]) + + if grids is None: + grids = [1] * blocks + + layers = [ + block(self.inplanes, planes, stride, downsample, dilation=base_dilation * grids[0], + use_batch_statistics=use_batch_statistics) + ] + self.inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append( + block(self.inplanes, planes, dilation=base_dilation * grids[i], + use_batch_statistics=use_batch_statistics)) + + return nn.SequentialCell(layers) + + def construct(self, x): + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.maxpool(out) + + out = self.layer1(out) + out = self.layer2(out) + out = self.layer3(out) + out = self.layer4(out) + return out + + +class Bottleneck(nn.Cell): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None, dilation=1, use_batch_statistics=True): + super(Bottleneck, self).__init__() + self.conv1 = conv1x1(inplanes, planes) + self.bn1 = nn.BatchNorm2d(planes, use_batch_statistics=use_batch_statistics) + + self.conv2 = conv3x3(planes, planes, stride, dilation, dilation) + self.bn2 = nn.BatchNorm2d(planes, use_batch_statistics=use_batch_statistics) + + self.conv3 = conv1x1(planes, planes * self.expansion) + self.bn3 = nn.BatchNorm2d(planes * self.expansion, use_batch_statistics=use_batch_statistics) + + self.relu = nn.ReLU() + self.downsample = downsample + + self.add = P.Add() + + def construct(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out = self.add(out, identity) + out = self.relu(out) + return out + + +class ASPP(nn.Cell): + def __init__(self, atrous_rates, phase='train', in_channels=2048, num_classes=21, + use_batch_statistics=True): + super(ASPP, self).__init__() + self.phase = phase + out_channels = 256 + self.aspp1 = ASPPConv(in_channels, out_channels, atrous_rates[0], use_batch_statistics=use_batch_statistics) + self.aspp2 = ASPPConv(in_channels, out_channels, atrous_rates[1], use_batch_statistics=use_batch_statistics) + self.aspp3 = ASPPConv(in_channels, out_channels, atrous_rates[2], use_batch_statistics=use_batch_statistics) + self.aspp4 = ASPPConv(in_channels, out_channels, atrous_rates[3], use_batch_statistics=use_batch_statistics) + self.aspp_pooling = ASPPPooling(in_channels, out_channels, use_batch_statistics=use_batch_statistics) + self.conv1 = nn.Conv2d(out_channels * (len(atrous_rates) + 1), out_channels, kernel_size=1, + weight_init='xavier_uniform') + self.bn1 = nn.BatchNorm2d(out_channels, use_batch_statistics=use_batch_statistics) + self.relu = nn.ReLU() + self.conv2 = nn.Conv2d(out_channels, num_classes, kernel_size=1, weight_init='xavier_uniform', has_bias=True) + self.concat = P.Concat(axis=1) + self.drop = nn.Dropout(0.3) + + def construct(self, x): + x1 = self.aspp1(x) + x2 = self.aspp2(x) + x3 = self.aspp3(x) + x4 = self.aspp4(x) + x5 = self.aspp_pooling(x) + + x = self.concat((x1, x2)) + x = self.concat((x, x3)) + x = self.concat((x, x4)) + x = self.concat((x, x5)) + + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + if self.phase == 'train': + x = self.drop(x) + x = self.conv2(x) + return x + + +class ASPPPooling(nn.Cell): + def __init__(self, in_channels, out_channels, use_batch_statistics=True): + super(ASPPPooling, self).__init__() + self.conv = nn.SequentialCell([ + nn.Conv2d(in_channels, out_channels, kernel_size=1, weight_init='xavier_uniform'), + nn.BatchNorm2d(out_channels, use_batch_statistics=use_batch_statistics), + nn.ReLU() + ]) + self.shape = P.Shape() + + def construct(self, x): + size = self.shape(x) + out = nn.AvgPool2d(size[2])(x) + out = self.conv(out) + out = P.ResizeNearestNeighbor((size[2], size[3]), True)(out) + return out + + +class ASPPConv(nn.Cell): + def __init__(self, in_channels, out_channels, atrous_rate=1, use_batch_statistics=True): + super(ASPPConv, self).__init__() + if atrous_rate == 1: + conv = nn.Conv2d(in_channels, out_channels, kernel_size=1, has_bias=False, weight_init='xavier_uniform') + else: + conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, pad_mode='pad', padding=atrous_rate, + dilation=atrous_rate, weight_init='xavier_uniform') + bn = nn.BatchNorm2d(out_channels, use_batch_statistics=use_batch_statistics) + relu = nn.ReLU() + self.aspp_conv = nn.SequentialCell([conv, bn, relu]) + + def construct(self, x): + out = self.aspp_conv(x) + return out + + +class DeepLabV3(nn.Cell): + def __init__(self, phase='train', num_classes=21, output_stride=16, freeze_bn=False): + super(DeepLabV3, self).__init__() + use_batch_statistics = not freeze_bn + self.resnet = Resnet(Bottleneck, [3, 4, 23, 3], output_stride=output_stride, + use_batch_statistics=use_batch_statistics) + self.aspp = ASPP([1, 6, 12, 18], phase, 2048, num_classes, + use_batch_statistics=use_batch_statistics) + self.shape = P.Shape() + + def construct(self, x): + size = self.shape(x) + out = self.resnet(x) + out = self.aspp(out) + out = P.ResizeBilinear((size[2], size[3]), True)(out) + return out diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/net_factory.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/net_factory.py new file mode 100755 index 0000000000000000000000000000000000000000..1f5c50a6ac49a34f871a297b89668079faf41837 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/src/nets/net_factory.py @@ -0,0 +1,18 @@ +# Copyright 2020 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. +# ============================================================================ + +from src.nets.deeplab_v3 import deeplab_v3 +nets_map = {'deeplab_v3_s8': deeplab_v3.DeepLabV3, + 'deeplab_v3_s16': deeplab_v3.DeepLabV3} diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/tools/__init__.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/tools/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/tools/get_multicards_json.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/tools/get_multicards_json.py new file mode 100755 index 0000000000000000000000000000000000000000..5a508731a5b40a8ef1b0f09c3ffd7a0ebb896495 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/src/tools/get_multicards_json.py @@ -0,0 +1,66 @@ +# Copyright 2020 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 os +import sys + + +def get_multicards_json(server_id): + hccn_configs = open('/etc/hccn.conf', 'r').readlines() + device_ips = {} + for hccn_item in hccn_configs: + hccn_item = hccn_item.strip() + if hccn_item.startswith('address_'): + device_id, device_ip = hccn_item.split('=') + device_id = device_id.split('_')[1] + device_ips[device_id] = device_ip + print('device_id:{}, device_ip:{}'.format(device_id, device_ip)) + hccn_table = {'board_id': '0x0000', 'chip_info': '910', 'deploy_mode': 'lab', 'group_count': '1', 'group_list': []} + instance_list = [] + usable_dev = '' + for instance_id in range(8): + instance = {'devices': []} + device_id = str(instance_id) + device_ip = device_ips[device_id] + usable_dev += str(device_id) + instance['devices'].append({ + 'device_id': device_id, + 'device_ip': device_ip, + }) + instance['rank_id'] = str(instance_id) + instance['server_id'] = server_id + instance_list.append(instance) + hccn_table['group_list'].append({ + 'device_num': '8', + 'server_num': '1', + 'group_name': '', + 'instance_count': '8', + 'instance_list': instance_list, + }) + hccn_table['para_plane_nic_location'] = 'device' + hccn_table['para_plane_nic_name'] = [] + for instance_id in range(8): + hccn_table['para_plane_nic_name'].append('eth{}'.format(instance_id)) + hccn_table['para_plane_nic_num'] = '8' + hccn_table['status'] = 'completed' + import json + table_fn = os.path.join(os.getcwd(), 'rank_table_8p.json') + print(table_fn) + with open(table_fn, 'w') as table_fp: + json.dump(hccn_table, table_fp, indent=4) + + +host_server_id = sys.argv[1] +get_multicards_json(host_server_id) diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/utils/__init__.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/utils/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/src/utils/learning_rates.py b/cv/semantic_segmentation/deeplabv3/MindSpore/src/utils/learning_rates.py new file mode 100755 index 0000000000000000000000000000000000000000..2267b1b6a464c7197b157908ed7952819777f412 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/src/utils/learning_rates.py @@ -0,0 +1,37 @@ +# Copyright 2020 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 numpy as np + + +def cosine_lr(base_lr, decay_steps, total_steps): + for i in range(total_steps): + step_ = min(i, decay_steps) + yield base_lr * 0.5 * (1 + np.cos(np.pi * step_ / decay_steps)) + + +def poly_lr(base_lr, decay_steps, total_steps, end_lr=0.0001, power=0.9): + for i in range(total_steps): + step_ = min(i, decay_steps) + yield (base_lr - end_lr) * ((1.0 - step_ / decay_steps) ** power) + end_lr + + +def exponential_lr(base_lr, decay_steps, decay_rate, total_steps, staircase=False): + for i in range(total_steps): + if staircase: + power_ = i // decay_steps + else: + power_ = float(i) / decay_steps + yield base_lr * (decay_rate ** power_) diff --git a/cv/semantic_segmentation/deeplabv3/MindSpore/train.py b/cv/semantic_segmentation/deeplabv3/MindSpore/train.py new file mode 100755 index 0000000000000000000000000000000000000000..10951a8b96fa5224ea12345b80493ca411c025e2 --- /dev/null +++ b/cv/semantic_segmentation/deeplabv3/MindSpore/train.py @@ -0,0 +1,214 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""train deeplabv3.""" + +import os +import time +from mindspore import context +from mindspore.train.model import Model +from mindspore.context import ParallelMode +import mindspore.nn as nn +from mindspore.train.callback import ModelCheckpoint, CheckpointConfig +from mindspore.train.serialization import load_checkpoint, load_param_into_net +from mindspore.communication.management import init, get_rank, get_group_size +from mindspore.train.callback import LossMonitor, TimeMonitor +from mindspore.train.loss_scale_manager import FixedLossScaleManager +from mindspore.common import set_seed +from src.data import dataset as data_generator +from src.loss import loss +from src.nets import net_factory +from src.utils import learning_rates +from model_utils.config import config +from model_utils.moxing_adapter import moxing_wrapper +from model_utils.device_adapter import get_device_id, get_device_num, get_rank_id + +set_seed(1) + + +class BuildTrainNetwork(nn.Cell): + def __init__(self, network, criterion): + super(BuildTrainNetwork, self).__init__() + self.network = network + self.criterion = criterion + + def construct(self, input_data, label): + output = self.network(input_data) + net_loss = self.criterion(output, label) + return net_loss + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + def unzip(zip_file, save_dir): + import zipfile + s_time = time.time() + if not os.path.exists(os.path.join(save_dir, "vocaug")): + zip_isexist = zipfile.is_zipfile(zip_file) + if zip_isexist: + fz = zipfile.ZipFile(zip_file, 'r') + data_num = len(fz.namelist()) + print("Extract Start...") + print("unzip file num: {}".format(data_num)) + i = 0 + for file in fz.namelist(): + if i % int(data_num / 100) == 0: + print("unzip percent: {}%".format(i / int(data_num / 100)), flush=True) + i += 1 + fz.extract(file, save_dir) + print("cost time: {}min:{}s.".format(int((time.time() - s_time) / 60), + int(int(time.time() - s_time) % 60))) + print("Extract Done.") + else: + print("This is not zip.") + else: + print("Zip has been extracted.") + + if config.need_modelarts_dataset_unzip: + zip_file_1 = os.path.join(config.data_path, "vocaug.zip") + save_dir_1 = os.path.join(config.data_path) + + sync_lock = "/tmp/unzip_sync.lock" + + # Each server contains 8 devices as most. + if get_device_id() % min(get_device_num(), 8) == 0 and not os.path.exists(sync_lock): + print("Zip file path: ", zip_file_1) + print("Unzip file save dir: ", save_dir_1) + unzip(zip_file_1, save_dir_1) + print("===Finish extract data synchronization===") + try: + os.mknod(sync_lock) + except IOError: + pass + + while True: + if os.path.exists(sync_lock): + break + time.sleep(1) + + print("Device: {}, Finish sync unzip data from {} to {}.".format(get_device_id(), zip_file_1, save_dir_1)) + + config.train_dir = os.path.join(config.output_path, str(get_rank_id()), config.train_dir) + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def train(): + args = config + + if args.device_target == "CPU": + context.set_context(mode=context.GRAPH_MODE, save_graphs=False, device_target="CPU") + elif args.device_target == "GPU": + context.set_context(mode=context.GRAPH_MODE, save_graphs=False, + device_target="GPU", device_id=get_device_id()) + else: + context.set_context(mode=context.GRAPH_MODE, save_graphs=False, + device_target="Ascend", device_id=get_device_id()) + + # init multicards training + if args.is_distributed: + init() + args.rank = get_rank() + args.group_size = get_group_size() + + parallel_mode = ParallelMode.DATA_PARALLEL + context.set_auto_parallel_context(parallel_mode=parallel_mode, gradients_mean=True, device_num=args.group_size) + + # dataset + dataset = data_generator.SegDataset(image_mean=args.image_mean, + image_std=args.image_std, + data_file=args.data_file, + batch_size=args.batch_size, + crop_size=args.crop_size, + max_scale=args.max_scale, + min_scale=args.min_scale, + ignore_label=args.ignore_label, + num_classes=args.num_classes, + num_readers=2, + num_parallel_calls=4, + shard_id=args.rank, + shard_num=args.group_size) + dataset = dataset.get_dataset(repeat=1) + + # network + if args.model == 'deeplab_v3_s16': + network = net_factory.nets_map[args.model]('train', args.num_classes, 16, args.freeze_bn) + elif args.model == 'deeplab_v3_s8': + network = net_factory.nets_map[args.model]('train', args.num_classes, 8, args.freeze_bn) + else: + raise NotImplementedError('model [{:s}] not recognized'.format(args.model)) + + # loss + loss_ = loss.SoftmaxCrossEntropyLoss(args.num_classes, args.ignore_label) + loss_.add_flags_recursive(fp32=True) + train_net = BuildTrainNetwork(network, loss_) + + # load pretrained model + if args.ckpt_pre_trained: + param_dict = load_checkpoint(args.ckpt_pre_trained) + if args.filter_weight: + filter_list = ["network.aspp.conv2.weight", "network.aspp.conv2.bias"] + for key in list(param_dict.keys()): + for filter_key in filter_list: + if filter_key not in key: + continue + print('filter {}'.format(key)) + del param_dict[key] + load_param_into_net(train_net, param_dict) + print('load_model {} success'.format(args.ckpt_pre_trained)) + else: + trans_param_dict = {} + for key, val in param_dict.items(): + key = key.replace("down_sample_layer", "downsample") + trans_param_dict[f"network.resnet.{key}"] = val + load_param_into_net(train_net, trans_param_dict) + print('load_model {} success'.format(args.ckpt_pre_trained)) + + # optimizer + iters_per_epoch = dataset.get_dataset_size() + total_train_steps = iters_per_epoch * args.train_epochs + if args.lr_type == 'cos': + lr_iter = learning_rates.cosine_lr(args.base_lr, total_train_steps, total_train_steps) + elif args.lr_type == 'poly': + lr_iter = learning_rates.poly_lr(args.base_lr, total_train_steps, total_train_steps, end_lr=0.0, power=0.9) + elif args.lr_type == 'exp': + lr_iter = learning_rates.exponential_lr(args.base_lr, args.lr_decay_step, args.lr_decay_rate, + total_train_steps, staircase=True) + else: + raise ValueError('unknown learning rate type') + opt = nn.Momentum(params=train_net.trainable_params(), learning_rate=lr_iter, momentum=0.9, weight_decay=0.0001, + loss_scale=args.loss_scale) + + # loss scale + manager_loss_scale = FixedLossScaleManager(args.loss_scale, drop_overflow_update=False) + amp_level = "O0" if args.device_target != "Ascend" else "O3" + model = Model(train_net, optimizer=opt, amp_level=amp_level, loss_scale_manager=manager_loss_scale) + + # callback for saving ckpts + time_cb = TimeMonitor(data_size=iters_per_epoch) + loss_cb = LossMonitor() + cbs = [time_cb, loss_cb] + + if args.rank == 0: + config_ck = CheckpointConfig(save_checkpoint_steps=args.save_steps, + keep_checkpoint_max=args.keep_checkpoint_max) + ckpoint_cb = ModelCheckpoint(prefix=args.model, directory=args.train_dir, config=config_ck) + cbs.append(ckpoint_cb) + + model.train(args.train_epochs, dataset, callbacks=cbs, dataset_sink_mode=(args.device_target != "CPU")) + + +if __name__ == '__main__': + train() diff --git a/gnn/GCN/README.md b/gnn/GCN/README.md new file mode 100755 index 0000000000000000000000000000000000000000..f3d096bab12615c8602b10e1387488b3350f7ca0 --- /dev/null +++ b/gnn/GCN/README.md @@ -0,0 +1,45 @@ +# GCN +## Model description +GCN(Graph Convolutional Networks) was proposed in 2016 and designed to do semi-supervised learning on graph-structured data. A scalable approach based on an efficient variant of convolutional neural networks which operate directly on graphs was presented. The model scales linearly in the number of graph edges and learns hidden layer representations that encode both local graph structure and features of nodes. + +[Paper](https://arxiv.org/abs/1609.02907): Thomas N. Kipf, Max Welling. 2016. Semi-Supervised Classification with Graph Convolutional Networks. In ICLR 2016. + +## Step 1: Installing +``` +pip3 install -r requirements.txt +pip3 install easydict +``` + +## Step 2: Prepare Datasets + +Note that you can run the scripts based on the dataset mentioned in original paper or widely used in relevant domain/network architecture. In the following sections, we will introduce how to run the scripts using the related dataset below. + +| Dataset | Type | Nodes | Edges | Classes | Features | Label rate | +| ------- | ---------------: |-----: | ----: | ------: |--------: | ---------: | +| Cora | Citation network | 2708 | 5429 | 7 | 1433 | 0.052 | +| Citeseer| Citation network | 3327 | 4732 | 6 | 3703 | 0.036 | + +## Step 3: Training +``` +cd scripts +bash train_gcn_1p.sh +``` +### [Evaluation] + +```bash +cd .. +python3 eval.py --data_dir=scripts/data_mr/cora --device_target="GPU" --model_ckpt scripts/train/ckpt/ckpt_gcn-200_1.ckpt &> eval.log & +``` +### [Evaluation result] +### 性能数据:BI +## Results on BI-V100 + +| GPUs | per step time | Acc | +|------|-------------- |-------| +| 1 | 4.454 | 0.8711| +### 性能数据:NV +## Results on NV-V100s + +| GPUs | per step time | Acc | +|------|-------------- |-------| +| 1 | 5.278 | 0.8659| diff --git a/gnn/GCN/ascend310_infer/CMakeLists.txt b/gnn/GCN/ascend310_infer/CMakeLists.txt new file mode 100755 index 0000000000000000000000000000000000000000..ee3c85447340e0449ff2b70ed24f60a17e07b2b6 --- /dev/null +++ b/gnn/GCN/ascend310_infer/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.14.1) +project(Ascend310Infer) +add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -std=c++17 -Werror -Wall -fPIE -Wl,--allow-shlib-undefined") +set(PROJECT_SRC_ROOT ${CMAKE_CURRENT_LIST_DIR}/) +option(MINDSPORE_PATH "mindspore install path" "") +include_directories(${MINDSPORE_PATH}) +include_directories(${MINDSPORE_PATH}/include) +include_directories(${PROJECT_SRC_ROOT}) +find_library(MS_LIB libmindspore.so ${MINDSPORE_PATH}/lib) +file(GLOB_RECURSE MD_LIB ${MINDSPORE_PATH}/_c_dataengine*) + +add_executable(main src/main.cc src/utils.cc) +target_link_libraries(main ${MS_LIB} ${MD_LIB} gflags) diff --git a/gnn/GCN/ascend310_infer/build.sh b/gnn/GCN/ascend310_infer/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..713d7f657ddfa5f75b069351c55f8447f77c72d0 --- /dev/null +++ b/gnn/GCN/ascend310_infer/build.sh @@ -0,0 +1,29 @@ +#!/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. +# ============================================================================ +if [ -d out ]; then + rm -rf out +fi + +mkdir out +cd out || exit + +if [ -f "Makefile" ]; then + make clean +fi + +cmake .. \ + -DMINDSPORE_PATH="`pip show mindspore-ascend | grep Location | awk '{print $2"/mindspore"}' | xargs realpath`" +make diff --git a/gnn/GCN/ascend310_infer/inc/utils.h b/gnn/GCN/ascend310_infer/inc/utils.h new file mode 100755 index 0000000000000000000000000000000000000000..efebe03a8c1179f5a1f9d5f7ee07e0352a9937c6 --- /dev/null +++ b/gnn/GCN/ascend310_infer/inc/utils.h @@ -0,0 +1,32 @@ +/** + * 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 MINDSPORE_INFERENCE_UTILS_H_ +#define MINDSPORE_INFERENCE_UTILS_H_ + +#include +#include +#include +#include +#include +#include "include/api/types.h" + +std::vector GetAllFiles(std::string_view dirName); +DIR *OpenDir(std::string_view dirName); +std::string RealPath(std::string_view path); +mindspore::MSTensor ReadFileToTensor(const std::string &file); +int WriteResult(const std::string& imageFile, const std::vector &outputs); +#endif diff --git a/gnn/GCN/ascend310_infer/src/main.cc b/gnn/GCN/ascend310_infer/src/main.cc new file mode 100755 index 0000000000000000000000000000000000000000..e3ccc14afc7c8f67fabb03d804f97c4cff1e00cc --- /dev/null +++ b/gnn/GCN/ascend310_infer/src/main.cc @@ -0,0 +1,135 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/api/model.h" +#include "include/api/context.h" +#include "include/api/types.h" +#include "include/api/serialization.h" +#include "include/dataset/execute.h" +#include "include/dataset/vision.h" +#include "inc/utils.h" + +using mindspore::Context; +using mindspore::Serialization; +using mindspore::Model; +using mindspore::Status; +using mindspore::MSTensor; +using mindspore::dataset::Execute; +using mindspore::ModelType; +using mindspore::GraphCell; +using mindspore::kSuccess; + +DEFINE_string(mindir_path, "", "mindir path"); +DEFINE_string(input0_path, ".", "input0 path"); +DEFINE_string(input1_path, ".", "input1 path"); +DEFINE_int32(device_id, 0, "device id"); + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (RealPath(FLAGS_mindir_path).empty()) { + std::cout << "Invalid mindir" << std::endl; + return 1; + } + + auto context = std::make_shared(); + auto ascend310 = std::make_shared(); + ascend310->SetDeviceID(FLAGS_device_id); + context->MutableDeviceInfo().push_back(ascend310); + mindspore::Graph graph; + Serialization::Load(FLAGS_mindir_path, ModelType::kMindIR, &graph); + + Model model; + Status ret = model.Build(GraphCell(graph), context); + if (ret != kSuccess) { + std::cout << "ERROR: Build failed." << std::endl; + return 1; + } + + std::vector model_inputs = model.GetInputs(); + if (model_inputs.empty()) { + std::cout << "Invalid model, inputs is empty." << std::endl; + return 1; + } + + auto input0_files = GetAllFiles(FLAGS_input0_path); + auto input1_files = GetAllFiles(FLAGS_input1_path); + + if (input0_files.empty() || input1_files.empty()) { + std::cout << "ERROR: input data empty." << std::endl; + return 1; + } + + std::map costTime_map; + size_t size = input0_files.size(); + + for (size_t i = 0; i < size; ++i) { + struct timeval start = {0}; + struct timeval end = {0}; + double startTimeMs; + double endTimeMs; + std::vector inputs; + std::vector outputs; + std::cout << "Start predict input files:" << input0_files[i] << std::endl; + + auto input0 = ReadFileToTensor(input0_files[i]); + auto input1 = ReadFileToTensor(input1_files[i]); + inputs.emplace_back(model_inputs[0].Name(), model_inputs[0].DataType(), model_inputs[0].Shape(), + input0.Data().get(), input0.DataSize()); + inputs.emplace_back(model_inputs[1].Name(), model_inputs[1].DataType(), model_inputs[1].Shape(), + input1.Data().get(), input1.DataSize()); + + gettimeofday(&start, nullptr); + ret = model.Predict(inputs, &outputs); + gettimeofday(&end, nullptr); + if (ret != kSuccess) { + std::cout << "Predict " << input0_files[i] << " failed." << std::endl; + return 1; + } + startTimeMs = (1.0 * start.tv_sec * 1000000 + start.tv_usec) / 1000; + endTimeMs = (1.0 * end.tv_sec * 1000000 + end.tv_usec) / 1000; + costTime_map.insert(std::pair(startTimeMs, endTimeMs)); + WriteResult(input0_files[i], outputs); + } + double average = 0.0; + int inferCount = 0; + + for (auto iter = costTime_map.begin(); iter != costTime_map.end(); iter++) { + double diff = 0.0; + diff = iter->second - iter->first; + average += diff; + inferCount++; + } + average = average / inferCount; + std::stringstream timeCost; + timeCost << "NN inference cost average time: "<< average << " ms of infer_count " << inferCount << std::endl; + std::cout << "NN inference cost average time: "<< average << "ms of infer_count " << inferCount << std::endl; + std::string fileName = "./time_Result" + std::string("/test_perform_static.txt"); + std::ofstream fileStream(fileName.c_str(), std::ios::trunc); + fileStream << timeCost.str(); + fileStream.close(); + costTime_map.clear(); + return 0; +} diff --git a/gnn/GCN/ascend310_infer/src/utils.cc b/gnn/GCN/ascend310_infer/src/utils.cc new file mode 100755 index 0000000000000000000000000000000000000000..c947e4d5f451b90bd4728aa3a92c4cfab174f5e6 --- /dev/null +++ b/gnn/GCN/ascend310_infer/src/utils.cc @@ -0,0 +1,129 @@ +/** + * 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 +#include +#include +#include "inc/utils.h" + +using mindspore::MSTensor; +using mindspore::DataType; + +std::vector GetAllFiles(std::string_view dirName) { + struct dirent *filename; + DIR *dir = OpenDir(dirName); + if (dir == nullptr) { + return {}; + } + std::vector res; + while ((filename = readdir(dir)) != nullptr) { + std::string dName = std::string(filename->d_name); + if (dName == "." || dName == ".." || filename->d_type != DT_REG) { + continue; + } + res.emplace_back(std::string(dirName) + "/" + filename->d_name); + } + std::sort(res.begin(), res.end()); + for (auto &f : res) { + std::cout << "image file: " << f << std::endl; + } + return res; +} + +int WriteResult(const std::string& imageFile, const std::vector &outputs) { + std::string homePath = "./result_Files"; + for (size_t i = 0; i < outputs.size(); ++i) { + size_t outputSize; + std::shared_ptr netOutput; + netOutput = outputs[i].Data(); + outputSize = outputs[i].DataSize(); + int pos = imageFile.rfind('/'); + std::string fileName(imageFile, pos + 1); + fileName.replace(fileName.find('.'), fileName.size() - fileName.find('.'), '_' + std::to_string(i) + ".bin"); + std::string outFileName = homePath + "/" + fileName; + FILE * outputFile = fopen(outFileName.c_str(), "wb"); + fwrite(netOutput.get(), outputSize, sizeof(char), outputFile); + fclose(outputFile); + outputFile = nullptr; + } + return 0; +} + +mindspore::MSTensor ReadFileToTensor(const std::string &file) { + if (file.empty()) { + std::cout << "Pointer file is nullptr" << std::endl; + return mindspore::MSTensor(); + } + + std::ifstream ifs(file); + if (!ifs.good()) { + std::cout << "File: " << file << " is not exist" << std::endl; + return mindspore::MSTensor(); + } + + if (!ifs.is_open()) { + std::cout << "File: " << file << "open failed" << std::endl; + return mindspore::MSTensor(); + } + + ifs.seekg(0, std::ios::end); + size_t size = ifs.tellg(); + mindspore::MSTensor buffer(file, mindspore::DataType::kNumberTypeUInt8, {static_cast(size)}, nullptr, size); + + ifs.seekg(0, std::ios::beg); + ifs.read(reinterpret_cast(buffer.MutableData()), size); + ifs.close(); + + return buffer; +} + + +DIR *OpenDir(std::string_view dirName) { + if (dirName.empty()) { + std::cout << " dirName is null ! " << std::endl; + return nullptr; + } + std::string realPath = RealPath(dirName); + struct stat s; + lstat(realPath.c_str(), &s); + if (!S_ISDIR(s.st_mode)) { + std::cout << "dirName is not a valid directory !" << std::endl; + return nullptr; + } + DIR *dir; + dir = opendir(realPath.c_str()); + if (dir == nullptr) { + std::cout << "Can not open dir " << dirName << std::endl; + return nullptr; + } + std::cout << "Successfully opened the dir " << dirName << std::endl; + return dir; +} + +std::string RealPath(std::string_view path) { + char realPathMem[PATH_MAX] = {0}; + char *realPathRet = nullptr; + realPathRet = realpath(path.data(), realPathMem); + + if (realPathRet == nullptr) { + std::cout << "File: " << path << " is not exist."; + return ""; + } + + std::string realPath(realPathMem); + std::cout << path << " realpath is: " << realPath << std::endl; + return realPath; +} diff --git a/gnn/GCN/default_config.yaml b/gnn/GCN/default_config.yaml new file mode 100755 index 0000000000000000000000000000000000000000..72a87962e3d15d0a3729c06b25579c3d9e41ba00 --- /dev/null +++ b/gnn/GCN/default_config.yaml @@ -0,0 +1,33 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path" +device_target: "Ascend" +need_modelarts_dataset_unzip: False +modelarts_dataset_unzip_name: "" + +# ============================================================================== +# train options +data_dir: "./data/cora/cora_mr" +train_nodes_num: 140 +eval_nodes_num: 500 +test_nodes_num: 1000 +save_TSNE: False +save_ckptpath: "ckpts/" +train_with_eval: False + + +--- + +# Help description for each configuration +data_dir: "Dataset directory" +train_nodes_num: "Nodes numbers for training" +eval_nodes_num: "Nodes numbers for evaluation" +test_nodes_num: "Nodes numbers for test" +save_TSNE: "Whether to save t-SNE graph" \ No newline at end of file diff --git a/gnn/GCN/eval.py b/gnn/GCN/eval.py new file mode 100755 index 0000000000000000000000000000000000000000..d2cb724835fdabecc3c9d463253a55f08f36a69a --- /dev/null +++ b/gnn/GCN/eval.py @@ -0,0 +1,70 @@ +# Copyright 2020 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. +# ============================================================================ + +""" +GCN eval script. +""" + +import argparse +import numpy as np +import mindspore.nn as nn +import mindspore.dataset as ds +import mindspore.common.dtype as mstype +from mindspore.train.serialization import load_checkpoint +from mindspore import Tensor +from mindspore import Model, context + +from src.config import ConfigGCN +from src.dataset import get_adj_features_labels, get_mask +from src.metrics import Loss_Gpu +from src.gcn import GCN_GPU + +def run_gcn_infer(): + """ + Run gcn infer + """ + parser = argparse.ArgumentParser(description='GCN') + parser.add_argument('--data_dir', type=str, default='./data/cora/cora_mr', help='Dataset directory') + parser.add_argument('--test_nodes_num', type=int, default=1000, help='Nodes numbers for test') + parser.add_argument("--model_ckpt", type=str, required=True, + help="existed checkpoint address.") + parser.add_argument('--device_target', type=str, default="Ascend", choices=['Ascend', 'GPU'], + help='device where the code will be implemented (default: Ascend)') + args_opt = parser.parse_args() + + context.set_context(mode=context.GRAPH_MODE, + device_target=args_opt.device_target, save_graphs=False) + config = ConfigGCN() + adj, feature, label_onehot, _ = get_adj_features_labels(args_opt.data_dir) + feature_d = np.expand_dims(feature, axis=0) + label_onehot_d = np.expand_dims(label_onehot, axis=0) + data = {"feature": feature_d, "label": label_onehot_d} + dataset = ds.NumpySlicesDataset(data=data) + adj = Tensor(adj, dtype=mstype.float32) + feature = Tensor(feature) + nodes_num = label_onehot.shape[0] + test_mask = get_mask(nodes_num, nodes_num - args_opt.test_nodes_num, nodes_num) + class_num = label_onehot.shape[1] + input_dim = feature.shape[1] + gcn_net_test = GCN_GPU(config, input_dim, class_num, adj) + load_checkpoint(args_opt.model_ckpt, net=gcn_net_test) + eval_metrics = {'Acc': nn.Accuracy()} + criterion = Loss_Gpu(test_mask, config.weight_decay, gcn_net_test.trainable_params()[0]) + model = Model(gcn_net_test, loss_fn=criterion, metrics=eval_metrics) + res = model.eval(dataset, dataset_sink_mode=True) + print(res) + +if __name__ == '__main__': + run_gcn_infer() diff --git a/gnn/GCN/export.py b/gnn/GCN/export.py new file mode 100755 index 0000000000000000000000000000000000000000..612fe3eedab14b76bdf15b70e680534fb769d963 --- /dev/null +++ b/gnn/GCN/export.py @@ -0,0 +1,58 @@ +# Copyright 2020 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 checkpoint file into air models""" +import argparse +import numpy as np + +from mindspore import Tensor, context, load_checkpoint, export + +from src.gcn import GCN +from src.config import ConfigGCN + +parser = argparse.ArgumentParser(description="GCN export") +parser.add_argument("--device_id", type=int, default=0, help="Device id") +parser.add_argument("--ckpt_file", type=str, required=True, help="Checkpoint file path.") +parser.add_argument("--dataset", type=str, default="cora", choices=["cora", "citeseer"], help="Dataset.") +parser.add_argument("--file_name", type=str, default="gcn", help="output file name.") +parser.add_argument("--file_format", type=str, choices=["AIR", "ONNX", "MINDIR"], default="MINDIR", help="file format") +parser.add_argument("--device_target", type=str, default="Ascend", + choices=["Ascend", "GPU", "CPU"], help="device target (default: Ascend)") +args = parser.parse_args() + +context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target) +if args.device_target == "Ascend": + context.set_context(device_id=args.device_id) + +if __name__ == "__main__": + config = ConfigGCN() + + if args.dataset == "cora": + input_dim = 1433 + class_num = 7 + adj = Tensor(np.zeros((2708, 2708), np.float64)) + feature = Tensor(np.zeros((2708, 1433), np.float32)) + else: + input_dim = 3703 + class_num = 6 + adj = Tensor(np.zeros((3312, 3312), np.float64)) + feature = Tensor(np.zeros((3312, 3703), np.float32)) + + gcn_net = GCN(config, input_dim, class_num) + + gcn_net.set_train(False) + load_checkpoint(args.ckpt_file, net=gcn_net) + gcn_net.add_flags_recursive(fp16=True) + + export(gcn_net, adj, feature, file_name=args.file_name, file_format=args.file_format) diff --git a/gnn/GCN/mindspore_hub_conf.py b/gnn/GCN/mindspore_hub_conf.py new file mode 100755 index 0000000000000000000000000000000000000000..b4bb80ef6e25002065b0da13ccc949fbae04f5a7 --- /dev/null +++ b/gnn/GCN/mindspore_hub_conf.py @@ -0,0 +1,25 @@ +# Copyright 2020 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. +# ============================================================================ +"""hub config.""" +from src.gcn import GCN + +def gcn(*args, **kwargs): + return GCN(*args, **kwargs) + + +def create_network(name, *args, **kwargs): + if name == "gcn": + return gcn(*args, **kwargs) + raise NotImplementedError(f"{name} is not implemented in the repo") diff --git a/gnn/GCN/model_utils/__init__.py b/gnn/GCN/model_utils/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gnn/GCN/model_utils/config.py b/gnn/GCN/model_utils/config.py new file mode 100755 index 0000000000000000000000000000000000000000..ad0d7497a8e5996a42c54a0cda599f84576fdc8e --- /dev/null +++ b/gnn/GCN/model_utils/config.py @@ -0,0 +1,126 @@ +# 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. +# ============================================================================ + +"""Parse arguments""" + +import os +import ast +import argparse +from pprint import pformat +import yaml + +class Config: + """ + Configuration namespace. Convert dictionary to members. + """ + def __init__(self, cfg_dict): + for k, v in cfg_dict.items(): + if isinstance(v, (list, tuple)): + setattr(self, k, [Config(x) if isinstance(x, dict) else x for x in v]) + else: + setattr(self, k, Config(v) if isinstance(v, dict) else v) + + def __str__(self): + return pformat(self.__dict__) + + def __repr__(self): + return self.__str__() + + +def parse_cli_to_yaml(parser, cfg, helper=None, choices=None, cfg_path="default_config.yaml"): + """ + Parse command line arguments to the configuration according to the default yaml. + + Args: + parser: Parent parser. + cfg: Base configuration. + helper: Helper description. + cfg_path: Path to the default yaml config. + """ + parser = argparse.ArgumentParser(description="[REPLACE THIS at config.py]", + parents=[parser]) + helper = {} if helper is None else helper + choices = {} if choices is None else choices + for item in cfg: + if not isinstance(cfg[item], list) and not isinstance(cfg[item], dict): + help_description = helper[item] if item in helper else "Please reference to {}".format(cfg_path) + choice = choices[item] if item in choices else None + if isinstance(cfg[item], bool): + parser.add_argument("--" + item, type=ast.literal_eval, default=cfg[item], choices=choice, + help=help_description) + else: + parser.add_argument("--" + item, type=type(cfg[item]), default=cfg[item], choices=choice, + help=help_description) + args = parser.parse_args() + return args + + +def parse_yaml(yaml_path): + """ + Parse the yaml config file. + + Args: + yaml_path: Path to the yaml config. + """ + with open(yaml_path, 'r') as fin: + try: + cfgs = yaml.load_all(fin.read(), Loader=yaml.FullLoader) + cfgs = [x for x in cfgs] + if len(cfgs) == 1: + cfg_helper = {} + cfg = cfgs[0] + cfg_choices = {} + elif len(cfgs) == 2: + cfg, cfg_helper = cfgs + cfg_choices = {} + elif len(cfgs) == 3: + cfg, cfg_helper, cfg_choices = cfgs + else: + raise ValueError("At most 3 docs (config, description for help, choices) are supported in config yaml") + print(cfg_helper) + except: + raise ValueError("Failed to parse yaml") + return cfg, cfg_helper, cfg_choices + + +def merge(args, cfg): + """ + Merge the base config from yaml file and command line arguments. + + Args: + args: Command line arguments. + cfg: Base configuration. + """ + args_var = vars(args) + for item in args_var: + cfg[item] = args_var[item] + return cfg + + +def get_config(): + """ + Get Config according to the yaml file and cli arguments. + """ + parser = argparse.ArgumentParser(description="default name", add_help=False) + current_dir = os.path.dirname(os.path.abspath(__file__)) + parser.add_argument("--config_path", type=str, default=os.path.join(current_dir, "../default_config.yaml"), + help="Config file path") + path_args, _ = parser.parse_known_args() + default, helper, choices = parse_yaml(path_args.config_path) + args = parse_cli_to_yaml(parser=parser, cfg=default, helper=helper, choices=choices, cfg_path=path_args.config_path) + final_config = merge(args, default) + return Config(final_config) + +config = get_config() diff --git a/gnn/GCN/model_utils/device_adapter.py b/gnn/GCN/model_utils/device_adapter.py new file mode 100755 index 0000000000000000000000000000000000000000..7c5d7f837ddaa8f53cf8dc5573cac0e36881e7b1 --- /dev/null +++ b/gnn/GCN/model_utils/device_adapter.py @@ -0,0 +1,27 @@ +# 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. +# ============================================================================ + +"""Device adapter for ModelArts""" + +from .config import config + +if config.enable_modelarts: + from .moxing_adapter import get_device_id, get_device_num, get_rank_id, get_job_id +else: + from .local_adapter import get_device_id, get_device_num, get_rank_id, get_job_id + +__all__ = [ + "get_device_id", "get_device_num", "get_rank_id", "get_job_id" +] diff --git a/gnn/GCN/model_utils/local_adapter.py b/gnn/GCN/model_utils/local_adapter.py new file mode 100755 index 0000000000000000000000000000000000000000..769fa6dc78e59eb66dbc8e6773accdc1d08b649e --- /dev/null +++ b/gnn/GCN/model_utils/local_adapter.py @@ -0,0 +1,36 @@ +# 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. +# ============================================================================ + +"""Local adapter""" + +import os + +def get_device_id(): + device_id = os.getenv('DEVICE_ID', '0') + return int(device_id) + + +def get_device_num(): + device_num = os.getenv('RANK_SIZE', '1') + return int(device_num) + + +def get_rank_id(): + global_rank_id = os.getenv('RANK_ID', '0') + return int(global_rank_id) + + +def get_job_id(): + return "Local Job" diff --git a/gnn/GCN/model_utils/moxing_adapter.py b/gnn/GCN/model_utils/moxing_adapter.py new file mode 100755 index 0000000000000000000000000000000000000000..25838a7da99a27a1bb744684c1f75f80f5704688 --- /dev/null +++ b/gnn/GCN/model_utils/moxing_adapter.py @@ -0,0 +1,116 @@ +# 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. +# ============================================================================ + +"""Moxing adapter for ModelArts""" + +import os +import functools +from mindspore import context +from .config import config + +_global_sync_count = 0 + +def get_device_id(): + device_id = os.getenv('DEVICE_ID', '0') + return int(device_id) + + +def get_device_num(): + device_num = os.getenv('RANK_SIZE', '1') + return int(device_num) + + +def get_rank_id(): + global_rank_id = os.getenv('RANK_ID', '0') + return int(global_rank_id) + + +def get_job_id(): + job_id = os.getenv('JOB_ID') + job_id = job_id if job_id != "" else "default" + return job_id + +def sync_data(from_path, to_path): + """ + Download data from remote obs to local directory if the first url is remote url and the second one is local path + Upload data from local directory to remote obs in contrast. + """ + import moxing as mox + import time + global _global_sync_count + sync_lock = "/tmp/copy_sync.lock" + str(_global_sync_count) + _global_sync_count += 1 + + # Each server contains 8 devices as most. + if get_device_id() % min(get_device_num(), 8) == 0 and not os.path.exists(sync_lock): + print("from path: ", from_path) + print("to path: ", to_path) + mox.file.copy_parallel(from_path, to_path) + print("===finish data synchronization===") + try: + os.mknod(sync_lock) + except IOError: + pass + print("===save flag===") + + while True: + if os.path.exists(sync_lock): + break + time.sleep(1) + + print("Finish sync data from {} to {}.".format(from_path, to_path)) + + +def moxing_wrapper(pre_process=None, post_process=None): + """ + Moxing wrapper to download dataset and upload outputs. + """ + def wrapper(run_func): + @functools.wraps(run_func) + def wrapped_func(*args, **kwargs): + # Download data from data_url + if config.enable_modelarts: + if config.data_url: + sync_data(config.data_url, config.data_path) + print("Dataset downloaded: ", os.listdir(config.data_path)) + if config.checkpoint_url: + sync_data(config.checkpoint_url, config.load_path) + print("Preload downloaded: ", os.listdir(config.load_path)) + if config.train_url: + sync_data(config.train_url, config.output_path) + print("Workspace downloaded: ", os.listdir(config.output_path)) + + context.set_context(save_graphs_path=os.path.join(config.output_path, str(get_rank_id()))) + config.device_num = get_device_num() + config.device_id = get_device_id() + if not os.path.exists(config.output_path): + os.makedirs(config.output_path) + + if pre_process: + pre_process() + + # Run the main function + run_func(*args, **kwargs) + + # Upload data to train_url + if config.enable_modelarts: + if post_process: + post_process() + + if config.train_url: + print("Start to copy output directory") + sync_data(config.output_path, config.train_url) + return wrapped_func + return wrapper diff --git a/gnn/GCN/postprocess.py b/gnn/GCN/postprocess.py new file mode 100755 index 0000000000000000000000000000000000000000..cd43dd9a0149c2d7be0e08f2739f11946a447b08 --- /dev/null +++ b/gnn/GCN/postprocess.py @@ -0,0 +1,58 @@ +# 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. +# ============================================================================ + +""" +postprocess. +""" +import os +import argparse + +import numpy as np + +def Accuracy(label, mask, preds): + """Accuracy with masking.""" + preds = preds.astype(np.float32) + correct_prediction = np.equal(np.argmax(preds, axis=1), np.argmax(label, axis=1)) + accuracy_all = correct_prediction.astype(np.float32) + mask = mask.astype(np.float32) + mask_reduce = np.mean(mask) + mask = mask / mask_reduce + accuracy_all *= mask + return np.mean(accuracy_all) + + +def get_acc(): + """get infer Accuracy.""" + parser = argparse.ArgumentParser(description='postprocess') + parser.add_argument('--dataset_name', type=str, default='cora', choices=['cora', 'citeseer'], help='dataset name') + parser.add_argument('--result_path', type=str, default='./result_Files', help='result Files') + parser.add_argument('--label_path', type=str, default='', help='y_test npy Files') + parser.add_argument('--mask_path', type=str, default='', help='test_mask npy Files') + args_opt = parser.parse_args() + + label_onehot = np.load(args_opt.label_path) + test_mask = np.load(args_opt.mask_path) + + pred = np.fromfile(os.path.join(args_opt.result_path, 'adj_0.bin'), np.float16) + if args_opt.dataset_name == 'cora': + pred = pred.reshape(2708, 7) + else: + pred = pred.reshape(3312, 6) + + acc = Accuracy(label_onehot, test_mask, pred) + print("Test set results:", "accuracy=", "{:.5f}".format(acc)) + +if __name__ == '__main__': + get_acc() diff --git a/gnn/GCN/preprocess.py b/gnn/GCN/preprocess.py new file mode 100755 index 0000000000000000000000000000000000000000..f0baedf191ffd605234c455f598a9e94dba3065d --- /dev/null +++ b/gnn/GCN/preprocess.py @@ -0,0 +1,47 @@ +# 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. +# ============================================================================ + +""" +preprocess. +""" +import os +import argparse + +import numpy as np +from src.dataset import get_adj_features_labels, get_mask + + +def generate_bin(): + """Generate bin files.""" + parser = argparse.ArgumentParser(description='preprocess') + parser.add_argument('--data_dir', type=str, default='./data/cora/cora_mr', help='Dataset directory') + parser.add_argument('--test_nodes_num', type=int, default=1000, help='Nodes numbers for test') + parser.add_argument('--result_path', type=str, default='./preprocess_Result/', help='Result path') + args_opt = parser.parse_args() + + adj, feature, label_onehot, _ = get_adj_features_labels(args_opt.data_dir) + nodes_num = label_onehot.shape[0] + test_mask = get_mask(nodes_num, nodes_num - args_opt.test_nodes_num, nodes_num) + adj_path = os.path.join(args_opt.result_path, "00_data") + feature_path = os.path.join(args_opt.result_path, "01_data") + os.makedirs(adj_path) + os.makedirs(feature_path) + adj.tofile(os.path.join(adj_path, "adj.bin")) + feature.tofile(os.path.join(feature_path, "feature.bin")) + np.save(os.path.join(args_opt.result_path, 'label_onehot.npy'), label_onehot) + np.save(os.path.join(args_opt.result_path, 'test_mask.npy'), test_mask) + +if __name__ == '__main__': + generate_bin() diff --git a/gnn/GCN/requirements.txt b/gnn/GCN/requirements.txt new file mode 100755 index 0000000000000000000000000000000000000000..f67390b3c3fbc629147820ff32ee207ab72a3778 --- /dev/null +++ b/gnn/GCN/requirements.txt @@ -0,0 +1,4 @@ +numpy +scipy +sklearn +pyyaml diff --git a/gnn/GCN/scripts/run_distribute_train_gpu.sh b/gnn/GCN/scripts/run_distribute_train_gpu.sh new file mode 100755 index 0000000000000000000000000000000000000000..c06830bcdceb22a3f43a6bd2865ee01165fa0bec --- /dev/null +++ b/gnn/GCN/scripts/run_distribute_train_gpu.sh @@ -0,0 +1,53 @@ +#!/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. +# ============================================================================ + +if [ $# != 3 ] +then + echo "Usage: sh run_distribute_train_gpu.sh [DATASET_PATH] [DATASET_NAME] [RANK_SIZE]" +exit 1 +fi + +DATASET_PATH=$1 +DATASET_NAME=$2 +echo $DATASET_NAME + +if [ -d "train" ]; +then + rm -rf ./train +fi +mkdir ./train +cp ../*.py ./train +cp ../*.yaml ./train +cp *.sh ./train +cp -r ../src ./train +cp -r ../model_utils ./train +cd ./train || exit +env > env.log +echo "start training for standalone GPU" + + +if [ $DATASET_NAME == cora ] +then +mpirun --allow-run-as-root -n $3 --output-filename log_output --merge-stderr-to-stdout \ + python3 train.py --data_dir=$DATASET_PATH --train_nodes_num=140 --device_target="GPU" &> log & +fi + +if [ $DATASET_NAME == citeseer ] +then +mpirun --allow-run-as-root -n $3 --output-filename log_output --merge-stderr-to-stdout \ + python3 train.py --data_dir=$DATASET_PATH --train_nodes_num=120 --device_target="GPU" &> log & +fi +cd .. diff --git a/gnn/GCN/scripts/run_eval_gpu.sh b/gnn/GCN/scripts/run_eval_gpu.sh new file mode 100755 index 0000000000000000000000000000000000000000..6f52e3e4361085e17c685dff3e0edfa4f7b7f681 --- /dev/null +++ b/gnn/GCN/scripts/run_eval_gpu.sh @@ -0,0 +1,60 @@ +#!/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. +# ============================================================================ + +if [ $# != 3 ] +then + echo "Usage: sh run_eval_gpu.sh [DATASET_PATH] [DATASET_NAME] [CKPT]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +DATASET_PATH=$1 +DATASET_NAME=$2 +echo $DATASET_NAME +MODEL_CKPT=$(get_real_path $3) +echo $MODEL_CKPT + + +if [ -d "eval" ]; +then + rm -rf ./eval +fi +mkdir ./eval +cp ../*.py ./eval +cp ../*.yaml ./eval +cp *.sh ./eval +cp -r ../src ./eval +cp -r ../model_utils ./eval +cd ./eval || exit +echo "start eval on standalone GPU" + +if [ $DATASET_NAME == cora ] +then + python eval.py --data_dir=$DATASET_PATH --device_target="GPU" --model_ckpt $MODEL_CKPT &> log & +fi + +if [ $DATASET_NAME == citeseer ] +then + python eval.py --data_dir=$DATASET_PATH --device_target="GPU" --model_ckpt $MODEL_CKPT &> log & +fi +cd .. diff --git a/gnn/GCN/scripts/run_infer_310.sh b/gnn/GCN/scripts/run_infer_310.sh new file mode 100755 index 0000000000000000000000000000000000000000..89e9382e2771e848e3165b731b041ae1ebe78d04 --- /dev/null +++ b/gnn/GCN/scripts/run_infer_310.sh @@ -0,0 +1,132 @@ +#!/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. +# ============================================================================ + +if [[ $# -lt 4 || $# -gt 5 ]]; then + echo "Usage: bash run_infer_310.sh [MINDIR_PATH] [DATASET_NAME] [DATASET_PATH] [NEED_PREPROCESS] [DEVICE_ID] + DATASET_NAME must be in ['cora', 'citeseer']. + NEED_PREPROCESS means weather need preprocess or not, it's value is 'y' or 'n'. + DEVICE_ID is optional, it can be set by environment variable device_id, otherwise the value is zero" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} +model=$(get_real_path $1) +if [ "$2" == "cora" ] || [ "$2" == "citeseer" ];then + dataset_name=$2 +else + echo "dataset must be in ['cora', 'citeseer']" + exit 1 +fi + +dataset_path=$(get_real_path $3) + +if [ "$4" == "y" ] || [ "$4" == "n" ];then + need_preprocess=$4 +else + echo "weather need preprocess or not, it's value must be in [y, n]" + exit 1 +fi + +device_id=0 +if [ $# == 5 ]; then + device_id=$5 +fi + +echo "mindir name: "$model +echo "dataset name: "$dataset_name +echo "dataset path: "$dataset_path +echo "need preprocess: "$need_preprocess +echo "device id: "$device_id + +export ASCEND_HOME=/usr/local/Ascend/ +if [ -d ${ASCEND_HOME}/ascend-toolkit ]; then + export PATH=$ASCEND_HOME/fwkacllib/bin:$ASCEND_HOME/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/ascend-toolkit/latest/atc/bin:$PATH + export LD_LIBRARY_PATH=$ASCEND_HOME/fwkacllib/lib64:/usr/local/lib:$ASCEND_HOME/ascend-toolkit/latest/atc/lib64:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export TBE_IMPL_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe + export PYTHONPATH=$ASCEND_HOME/fwkacllib/python/site-packages:${TBE_IMPL_PATH}:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp +else + export ASCEND_HOME=/usr/local/Ascend/latest/ + export PATH=$ASCEND_HOME/fwkacllib/bin:$ASCEND_HOME/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/atc/ccec_compiler/bin:$ASCEND_HOME/atc/bin:$PATH + export LD_LIBRARY_PATH=$ASCEND_HOME/fwkacllib/lib64:/usr/local/lib:$ASCEND_HOME/atc/lib64:$ASCEND_HOME/acllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export PYTHONPATH=$ASCEND_HOME/fwkacllib/python/site-packages:$ASCEND_HOME/atc/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/opp +fi + +function preprocess_data() +{ + if [ -d preprocess_Result ]; then + rm -rf ./preprocess_Result + fi + mkdir preprocess_Result + python ../preprocess.py --data_dir=$dataset_path --result_path=./preprocess_Result/ +} + +function compile_app() +{ + cd ../ascend310_infer || exit + bash build.sh &> build.log +} + +function infer() +{ + cd - || exit + if [ -d result_Files ]; then + rm -rf ./result_Files + fi + if [ -d time_Result ]; then + rm -rf ./time_Result + fi + mkdir result_Files + mkdir time_Result + + ../ascend310_infer/out/main --mindir_path=$model --input0_path=./preprocess_Result/00_data --input1_path=./preprocess_Result/01_data --device_id=$device_id &> infer.log + +} + +function cal_acc() +{ + python ../postprocess.py --result_path=./result_Files --dataset_name=$dataset_name --label_path=./preprocess_Result/label_onehot.npy --mask_path=./preprocess_Result/test_mask.npy &> acc.log +} + +if [ $need_preprocess == "y" ]; then + preprocess_data + if [ $? -ne 0 ]; then + echo "preprocess dataset failed" + exit 1 + fi +fi +compile_app +if [ $? -ne 0 ]; then + echo "compile app code failed" + exit 1 +fi +infer +if [ $? -ne 0 ]; then + echo " execute inference failed" + exit 1 +fi +cal_acc +if [ $? -ne 0 ]; then + echo "calculate accuracy failed" + exit 1 +fi \ No newline at end of file diff --git a/gnn/GCN/scripts/run_process_data.sh b/gnn/GCN/scripts/run_process_data.sh new file mode 100755 index 0000000000000000000000000000000000000000..ef02169b8af1426c97a7b3529f9daca43d76e557 --- /dev/null +++ b/gnn/GCN/scripts/run_process_data.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Copyright 2020 Huawei Technologies Co., Ltd +# +# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +if [ $# != 2 ] +then + echo "Usage: sh run_train.sh [SRC_PATH] [DATASET_NAME]" +exit 1 +fi + +get_real_path(){ + if [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} +SRC_PATH=$(get_real_path $1) +echo $SRC_PATH + +DATASET_NAME=$2 +echo $DATASET_NAME + +if [ ! -d data_mr ]; then + mkdir data_mr +else + echo data_mr exist +fi +MINDRECORD_PATH=`pwd`/data_mr + +rm -f $MINDRECORD_PATH/$DATASET_NAME +rm -f $MINDRECORD_PATH/$DATASET_NAME.db + +cd ../utils/graph_to_mindrecord || exit + +python3 writer.py --mindrecord_script $DATASET_NAME \ +--mindrecord_file "$MINDRECORD_PATH/$DATASET_NAME" \ +--mindrecord_partitions 1 \ +--mindrecord_header_size_by_bit 18 \ +--mindrecord_page_size_by_bit 20 \ +--graph_api_args "$SRC_PATH" + +cd - || exit diff --git a/gnn/GCN/scripts/run_train.sh b/gnn/GCN/scripts/run_train.sh new file mode 100755 index 0000000000000000000000000000000000000000..9fccfb42f8df9a371400b03957906440ad9cc2b8 --- /dev/null +++ b/gnn/GCN/scripts/run_train.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +if [[ $# != 2 && $# != 3 ]] +then + echo "Usage: sh run_train.sh [DATASET_PATH] [DATASET_NAME] [DEVICE_ID](optional)" +exit 1 +fi + +DATASET_PATH=$1 +DATASET_NAME=$2 +echo $DATASET_NAME + +ulimit -u unlimited +export DEVICE_NUM=1 +export RANK_SIZE=$DEVICE_NUM +if [ -z $3 ] +then + export DEVICE_ID=0 +else + export DEVICE_ID=$3 +fi +export RANK_ID=0 + +if [ -d "train" ]; +then + rm -rf ./train +fi +mkdir ./train +cp ../*.py ./train +cp ../*.yaml ./train +cp *.sh ./train +cp -r ../src ./train +cp -r ../model_utils ./train +cd ./train || exit +env > env.log +echo "start training for device $DEVICE_ID" + + +if [ $DATASET_NAME == cora ] +then + python3 train.py --data_dir=$DATASET_PATH --train_nodes_num=140 &> log & +fi + +if [ $DATASET_NAME == citeseer ] +then + python train.py --data_dir=$DATASET_PATH --train_nodes_num=120 &> log & +fi +cd .. + diff --git a/gnn/GCN/scripts/run_train_gpu.sh b/gnn/GCN/scripts/run_train_gpu.sh new file mode 100755 index 0000000000000000000000000000000000000000..3b6d4ffa503874ac6a085de9b6ec0b2b9c766ad8 --- /dev/null +++ b/gnn/GCN/scripts/run_train_gpu.sh @@ -0,0 +1,51 @@ +#!/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. +# ============================================================================ + +if [ $# != 2 ] +then + echo "Usage: sh run_train_gpu.sh [DATASET_PATH] [DATASET_NAME]" +exit 1 +fi + +DATASET_PATH=$1 +DATASET_NAME=$2 +echo $DATASET_NAME + +if [ -d "train" ]; +then + rm -rf ./train +fi +mkdir ./train +cp ../*.py ./train +cp ../*.yaml ./train +cp *.sh ./train +cp -r ../src ./train +cp -r ../model_utils ./train +cd ./train || exit +env > env.log +echo "start training for standalone GPU" + + +if [ $DATASET_NAME == cora ] +then + python3 train.py --data_dir=$DATASET_PATH --train_nodes_num=140 --device_target="GPU" &> log & +fi + +if [ $DATASET_NAME == citeseer ] +then + python3 train.py --data_dir=$DATASET_PATH --train_nodes_num=120 --device_target="GPU" &> log & +fi +cd .. diff --git a/gnn/GCN/scripts/train_gcn_1p.sh b/gnn/GCN/scripts/train_gcn_1p.sh new file mode 100755 index 0000000000000000000000000000000000000000..c30293a2b37cedf0b16374fffee31178010814b7 --- /dev/null +++ b/gnn/GCN/scripts/train_gcn_1p.sh @@ -0,0 +1,21 @@ +# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +if [ ! -d data_mr ]; then + git clone https://github.com/kimiyoung/planetoid.git + bash run_process_data.sh planetoid/data cora +fi +bash run_train_gpu.sh ../data_mr/cora cora diff --git a/gnn/GCN/src/config.py b/gnn/GCN/src/config.py new file mode 100755 index 0000000000000000000000000000000000000000..fc9b99814c3fdb0caafb36d4ed835b87546c2b48 --- /dev/null +++ b/gnn/GCN/src/config.py @@ -0,0 +1,35 @@ +# Copyright 2020 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. +# ============================================================================ +""" +network config setting, will be used in train.py +""" + + +class ConfigGCN(): + """Configuration for GCN""" + learning_rate = 0.01 + epochs = 200 + hidden1 = 16 + dropout = 0.5 + weight_decay = 5e-4 + early_stopping = 50 + save_ckpt_steps = 549 + keep_ckpt_max = 10 + ckpt_dir = './ckpt' + best_ckpt_dir = './best_ckpt' + best_ckpt_name = 'best.ckpt' + eval_start_epoch = 100 + save_best_ckpt = True + eval_interval = 1 diff --git a/gnn/GCN/src/dataset.py b/gnn/GCN/src/dataset.py new file mode 100755 index 0000000000000000000000000000000000000000..2402b66c4b055fc6095926e07d161b5c4e497dcd --- /dev/null +++ b/gnn/GCN/src/dataset.py @@ -0,0 +1,74 @@ +# Copyright 2020 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. +# ============================================================================ +""" +create adjacency matrix, node features, labels, and mask for training. +Cora and Citeseer datasets are supported by our example, the original versions of these datasets are as follows: +@inproceedings{nr, + title={The Network Data Repository with Interactive Graph Analytics and Visualization}, + author={Ryan A. Rossi and Nesreen K. Ahmed}, + booktitle={AAAI}, + url={http://networkrepository.com}, + year={2015} +} +In this example, we use dataset splits provided by https://github.com/kimiyoung/planetoid (Zhilin Yang, William W. Cohen, Ruslan Salakhutdinov, [Revisiting Semi-Supervised Learning with Graph Embeddings](https://arxiv.org/abs/1603.08861), ICML 2016). +""" +import numpy as np +import scipy.sparse as sp +import mindspore.dataset as ds + + +def normalize_adj(adj): + """Symmetrically normalize adjacency matrix.""" + rowsum = np.array(adj.sum(1)) + d_inv_sqrt = np.power(rowsum, -0.5).flatten() + d_inv_sqrt[np.isinf(d_inv_sqrt)] = 0. + d_mat_inv_sqrt = sp.diags(d_inv_sqrt) + return adj.dot(d_mat_inv_sqrt).transpose().dot(d_mat_inv_sqrt).tocoo() + + +def get_adj_features_labels(data_dir): + """Get adjacency matrix, node features and labels from dataset.""" + g = ds.GraphData(data_dir) + nodes = g.get_all_nodes(0) + nodes_list = nodes.tolist() + row_tensor = g.get_node_feature(nodes_list, [1, 2]) + features = row_tensor[0] + labels = row_tensor[1] + + nodes_num = labels.shape[0] + class_num = labels.max() + 1 + labels_onehot = np.eye(nodes_num, class_num)[labels].astype(np.float32) + + neighbor = g.get_all_neighbors(nodes_list, 0) + node_map = {node_id: index for index, node_id in enumerate(nodes_list)} + adj = np.zeros([nodes_num, nodes_num], dtype=np.float32) + for index, value in np.ndenumerate(neighbor): + # The first column of neighbor is node_id, second column to last column are neighbors of the first column. + # So we only care index[1] > 1. + # If the node does not have that many neighbors, -1 is padded. So if value < 0, we will not deal with it. + if value >= 0 and index[1] > 0: + adj[node_map[neighbor[index[0], 0]], node_map[value]] = 1 + adj = sp.coo_matrix(adj) + adj = adj + adj.T.multiply(adj.T > adj) + sp.eye(nodes_num) + nor_adj = normalize_adj(adj) + nor_adj = np.array(nor_adj.todense()) + return nor_adj, features, labels_onehot, labels + + +def get_mask(total, begin, end): + """Generate mask.""" + mask = np.zeros([total]).astype(np.float32) + mask[begin:end] = 1 + return mask diff --git a/gnn/GCN/src/eval_callback.py b/gnn/GCN/src/eval_callback.py new file mode 100755 index 0000000000000000000000000000000000000000..64166c36e4731fe6de2d47a899382d92f30a4522 --- /dev/null +++ b/gnn/GCN/src/eval_callback.py @@ -0,0 +1,92 @@ +# 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 callback when training""" + +import os +import stat +from mindspore import save_checkpoint +from mindspore import log as logger +from mindspore.train.callback import Callback + +class EvalCallBack(Callback): + """ + Evaluation callback when training. + + Args: + eval_function (function): evaluation function. + eval_param_dict (dict): evaluation parameters' configure dict. + 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`. + + Returns: + None + + Examples: + >>> EvalCallBack(eval_function, eval_param_dict) + """ + + 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"): + super(EvalCallBack, self).__init__() + self.eval_param_dict = eval_param_dict + self.eval_function = eval_function + self.eval_start_epoch = eval_start_epoch + if interval < 1: + raise ValueError("interval should >= 1.") + self.interval = interval + self.save_best_ckpt = save_best_ckpt + self.best_res = 0 + self.best_epoch = 0 + if not os.path.isdir(ckpt_directory): + os.makedirs(ckpt_directory) + self.bast_ckpt_path = os.path.join(ckpt_directory, besk_ckpt_name) + self.metrics_name = metrics_name + self.save_TSNE = save_TSNE + + def remove_ckpoint_file(self, file_name): + """Remove the specified checkpoint file from this checkpoint manager and also from the directory.""" + try: + os.chmod(file_name, stat.S_IWRITE) + os.remove(file_name) + except OSError: + logger.warning("OSError, failed to remove the older ckpt file %s.", file_name) + except ValueError: + logger.warning("ValueError, failed to remove the older ckpt file %s.", file_name) + + def epoch_end(self, run_context): + """Callback when epoch end.""" + 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) + print("epoch: {}, {}: {}".format(cur_epoch, self.metrics_name, res), flush=True) + if res >= self.best_res: + self.best_res = res + self.best_epoch = cur_epoch + print("update best result: {}".format(res), flush=True) + if self.save_best_ckpt: + if os.path.exists(self.bast_ckpt_path): + self.remove_ckpoint_file(self.bast_ckpt_path) + save_checkpoint(cb_params.train_network, self.bast_ckpt_path) + print("update best checkpoint at: {}".format(self.bast_ckpt_path), flush=True) + + def end(self, run_context): + print("End training, the best {0} is: {1}, the best {0} epoch is {2}".format(self.metrics_name, + self.best_res, + self.best_epoch), flush=True) + \ No newline at end of file diff --git a/gnn/GCN/src/gcn.py b/gnn/GCN/src/gcn.py new file mode 100755 index 0000000000000000000000000000000000000000..adb5748a9e4d58ea5a0b1b956dcf889894157576 --- /dev/null +++ b/gnn/GCN/src/gcn.py @@ -0,0 +1,125 @@ +# Copyright 2020 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. +# ============================================================================ +"""GCN.""" +import numpy as np +from mindspore import nn +from mindspore.ops import operations as P +from mindspore import Tensor +from mindspore.nn.layer.activation import get_activation + + +def glorot(shape): + init_range = np.sqrt(6.0/(shape[0]+shape[1])) + initial = np.random.uniform(-init_range, init_range, shape).astype(np.float32) + return Tensor(initial) + + +class GraphConvolution(nn.Cell): + """ + GCN graph convolution layer. + + Args: + feature_in_dim (int): The input feature dimension. + feature_out_dim (int): The output feature dimension. + dropout_ratio (float): Dropout ratio for the dropout layer. Default: None. + activation (str): Activation function applied to the output of the layer, eg. 'relu'. Default: None. + + Inputs: + - **adj** (Tensor) - Tensor of shape :math:`(N, N)`. + - **input_feature** (Tensor) - Tensor of shape :math:`(N, C)`. + + Outputs: + Tensor, output tensor. + """ + + def __init__(self, + feature_in_dim, + feature_out_dim, + dropout_ratio=None, + activation=None): + super(GraphConvolution, self).__init__() + self.in_dim = feature_in_dim + self.out_dim = feature_out_dim + self.weight_init = glorot([self.out_dim, self.in_dim]) + self.fc = nn.Dense(self.in_dim, + self.out_dim, + weight_init=self.weight_init, + has_bias=False) + self.dropout_ratio = dropout_ratio + if self.dropout_ratio is not None: + self.dropout = nn.Dropout(keep_prob=1-self.dropout_ratio) + self.dropout_flag = self.dropout_ratio is not None + self.activation = get_activation(activation) + self.activation_flag = self.activation is not None + self.matmul = P.MatMul() + + def construct(self, adj, input_feature): + """ + GCN graph convolution layer. + """ + dropout = input_feature + if self.dropout_flag: + dropout = self.dropout(dropout) + + fc = self.fc(dropout) + output_feature = self.matmul(adj, fc) + + if self.activation_flag: + output_feature = self.activation(output_feature) + return output_feature + + +class GCN(nn.Cell): + """ + GCN architecture. + + Args: + config (ConfigGCN): Configuration for GCN. + adj (numpy.ndarray): Numbers of block in different layers. + feature (numpy.ndarray): Input channel in each layer. + output_dim (int): The number of output channels, equal to classes num. + """ + + def __init__(self, config, input_dim, output_dim): + super(GCN, self).__init__() + self.layer0 = GraphConvolution(input_dim, config.hidden1, activation="relu", dropout_ratio=config.dropout) + self.layer1 = GraphConvolution(config.hidden1, output_dim, dropout_ratio=None) + + def construct(self, adj, feature): + output0 = self.layer0(adj, feature) + output1 = self.layer1(adj, output0) + return output1 + +class GCN_GPU(nn.Cell): + """ + GCN GPU architecture. + + Args: + config (ConfigGCN): Configuration for GCN. + adj (numpy.ndarray): Numbers of block in different layers. + feature (numpy.ndarray): Input channel in each layer. + output_dim (int): The number of output channels, equal to classes num. + """ + + def __init__(self, config, input_dim, output_dim, adj): + super(GCN_GPU, self).__init__() + self.layer0 = GraphConvolution(input_dim, config.hidden1, activation="relu", dropout_ratio=config.dropout) + self.layer1 = GraphConvolution(config.hidden1, output_dim, dropout_ratio=None) + self.adj = adj + def construct(self, feature): + output0 = self.layer0(self.adj, feature) + output1 = self.layer1(self.adj, output0) + return output1 + \ No newline at end of file diff --git a/gnn/GCN/src/metrics.py b/gnn/GCN/src/metrics.py new file mode 100755 index 0000000000000000000000000000000000000000..bd99ee614ffbd5127b6fb79946c866bc5fbebdcd --- /dev/null +++ b/gnn/GCN/src/metrics.py @@ -0,0 +1,216 @@ +# Copyright 2020 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. +# ============================================================================ +"""Loss and accuracy.""" +from mindspore import nn +from mindspore import Tensor +from mindspore.common import dtype as mstype +from mindspore.ops import operations as P +from mindspore.common.parameter import ParameterTuple +from mindspore.ops import composite as C +from mindspore.ops import functional as F + + +class Loss(nn.Cell): + """Softmax cross-entropy loss with masking.""" + def __init__(self, label, mask, weight_decay, param): + super(Loss, self).__init__(auto_prefix=False) + self.label = Tensor(label) + self.mask = Tensor(mask) + self.loss = P.SoftmaxCrossEntropyWithLogits() + self.one = Tensor(1.0, mstype.float32) + self.zero = Tensor(0.0, mstype.float32) + self.mean = P.ReduceMean() + self.cast = P.Cast() + self.l2_loss = P.L2Loss() + self.reduce_sum = P.ReduceSum() + self.weight_decay = weight_decay + self.param = param + + def construct(self, preds): + """Calculate loss""" + param = self.l2_loss(self.param) + loss = self.weight_decay * param + preds = self.cast(preds, mstype.float32) + loss = loss + self.loss(preds, self.label)[0] + mask = self.cast(self.mask, mstype.float32) + mask_reduce = self.mean(mask) + mask = mask / mask_reduce + loss = loss * mask + loss = self.mean(loss) + return loss + + +class Accuracy(nn.Cell): + """Accuracy with masking.""" + def __init__(self, label, mask): + super(Accuracy, self).__init__(auto_prefix=False) + self.label = Tensor(label) + self.mask = Tensor(mask) + self.equal = P.Equal() + self.argmax = P.Argmax() + self.cast = P.Cast() + self.mean = P.ReduceMean() + + def construct(self, preds): + preds = self.cast(preds, mstype.float32) + correct_prediction = self.equal(self.argmax(preds), self.argmax(self.label)) + accuracy_all = self.cast(correct_prediction, mstype.float32) + mask = self.cast(self.mask, mstype.float32) + mask_reduce = self.mean(mask) + mask = mask / mask_reduce + accuracy_all *= mask + return self.mean(accuracy_all) + + +class LossAccuracyWrapper(nn.Cell): + """ + Wraps the GCN model with loss and accuracy cell. + + Args: + network (Cell): GCN network. + label (numpy.ndarray): Dataset labels. + mask (numpy.ndarray): Mask for training, evaluation or test. + weight_decay (float): Weight decay parameter for weight of the first convolution layer. + """ + + def __init__(self, network, label, mask, weight_decay): + super(LossAccuracyWrapper, self).__init__(auto_prefix=False) + self.network = network + self.loss = Loss(label, mask, weight_decay, network.trainable_params()[0]) + self.accuracy = Accuracy(label, mask) + + def construct(self, adj, feature): + preds = self.network(adj, feature) + loss = self.loss(preds) + accuracy = self.accuracy(preds) + return loss, accuracy + + +class LossWrapper(nn.Cell): + """ + Wraps the GCN model with loss. + + Args: + network (Cell): GCN network. + label (numpy.ndarray): Dataset labels. + mask (numpy.ndarray): Mask for training. + weight_decay (float): Weight decay parameter for weight of the first convolution layer. + """ + + def __init__(self, network, label, mask, weight_decay): + super(LossWrapper, self).__init__(auto_prefix=False) + self.network = network + self.loss = Loss(label, mask, weight_decay, network.trainable_params()[0]) + + def construct(self, adj, feature): + preds = self.network(adj, feature) + loss = self.loss(preds) + return loss + + +class TrainOneStepCell(nn.Cell): + r""" + Network training package class. + + Wraps the network with an optimizer. The resulting Cell be trained without inputs. + Backward graph will be created in the construct function to do parameter updating. Different + parallel modes are available to run the training. + + Args: + network (Cell): The training network. + optimizer (Cell): Optimizer for updating the weights. + sens (Number): The scaling number to be filled as the input of backpropagation. Default value is 1.0. + + Outputs: + Tensor, a scalar Tensor with shape :math:`()`. + + Examples: + >>> net = Net() + >>> loss_fn = nn.SoftmaxCrossEntropyWithLogits() + >>> optim = nn.Momentum(net.trainable_params(), learning_rate=0.1, momentum=0.9) + >>> loss_net = nn.WithLossCell(net, loss_fn) + >>> train_net = nn.TrainOneStepCell(loss_net, optim) + """ + + def __init__(self, network, optimizer, sens=1.0): + super(TrainOneStepCell, self).__init__(auto_prefix=False) + self.network = network + self.network.set_grad() + self.network.add_flags(defer_inline=True) + self.weights = ParameterTuple(network.trainable_params()) + self.optimizer = optimizer + self.grad = C.GradOperation(get_by_list=True, sens_param=True) + self.sens = sens + + def construct(self, adj, feature): + weights = self.weights + loss = self.network(adj, feature) + sens = P.Fill()(P.DType()(loss), P.Shape()(loss), self.sens) + grads = self.grad(self.network, weights)(adj, feature, sens) + return F.depend(loss, self.optimizer(grads)) + + +class TrainNetWrapper(nn.Cell): + """ + Wraps the GCN model with optimizer. + + Args: + network (Cell): GCN network. + label (numpy.ndarray): Dataset labels. + mask (numpy.ndarray): Mask for training, evaluation or test. + config (ConfigGCN): Configuration for GCN. + """ + + def __init__(self, network, label, mask, config): + super(TrainNetWrapper, self).__init__(auto_prefix=False) + self.network = network + loss_net = LossWrapper(network, label, mask, config.weight_decay) + optimizer = nn.Adam(loss_net.trainable_params(), + learning_rate=config.learning_rate) + self.loss_train_net = TrainOneStepCell(loss_net, optimizer) + self.accuracy = Accuracy(label, mask) + + def construct(self, adj, feature): + loss = self.loss_train_net(adj, feature) + accuracy = self.accuracy(self.network(adj, feature)) + return loss, accuracy + +class Loss_Gpu(nn.Cell): + """Softmax cross-entropy loss with masking.""" + def __init__(self, mask, weight_decay, param): + super(Loss_Gpu, self).__init__(auto_prefix=False) + self.mask = Tensor(mask) + self.loss = P.SoftmaxCrossEntropyWithLogits() + self.one = Tensor(1.0, mstype.float32) + self.zero = Tensor(0.0, mstype.float32) + self.mean = P.ReduceMean() + self.cast = P.Cast() + self.l2_loss = P.L2Loss() + self.reduce_sum = P.ReduceSum() + self.weight_decay = weight_decay + self.param = param + + def construct(self, preds, label): + """Calculate loss""" + param = self.l2_loss(self.param) + loss = self.weight_decay * param + preds = self.cast(preds, mstype.float32) + loss = loss + self.loss(preds, label)[0] + mask = self.cast(self.mask, mstype.float32) + mask_reduce = self.mean(mask) + mask = mask / mask_reduce + loss = loss * mask + loss = self.mean(loss) + return loss diff --git a/gnn/GCN/t-SNE_visualization_on_Cora.gif b/gnn/GCN/t-SNE_visualization_on_Cora.gif new file mode 100755 index 0000000000000000000000000000000000000000..ae5aada9eb121a2ed6dead826da3a708fba81444 Binary files /dev/null and b/gnn/GCN/t-SNE_visualization_on_Cora.gif differ diff --git a/gnn/GCN/train.py b/gnn/GCN/train.py new file mode 100755 index 0000000000000000000000000000000000000000..c0c92c3a55b4e4b6816f8b05ed4abd59339c3165 --- /dev/null +++ b/gnn/GCN/train.py @@ -0,0 +1,242 @@ +# Copyright 2020 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. +# ============================================================================ + +""" +GCN training script. +""" +import os +import time + +import numpy as np +from matplotlib import pyplot as plt +from matplotlib import animation +from sklearn import manifold +from mindspore import Tensor +import mindspore.dataset as ds +import mindspore.nn as nn +import mindspore.common.dtype as mstype +from mindspore.train.serialization import save_checkpoint, load_checkpoint +from mindspore.train.callback import ModelCheckpoint, CheckpointConfig, TimeMonitor, LossMonitor +from mindspore import Model, context + +from src.gcn import GCN, GCN_GPU +from src.metrics import LossAccuracyWrapper, TrainNetWrapper, Loss_Gpu +from src.config import ConfigGCN +from src.dataset import get_adj_features_labels, get_mask + +from model_utils.config import config as default_args +from model_utils.moxing_adapter import moxing_wrapper +from model_utils.device_adapter import get_device_id, get_device_num + + +def t_SNE(out_feature, dim): + t_sne = manifold.TSNE(n_components=dim, init='pca', random_state=0) + return t_sne.fit_transform(out_feature) + + +def update_graph(i, data, scat, plot): + scat.set_offsets(data[i]) + plt.title('t-SNE visualization of Epoch:{0}'.format(i)) + return scat, plot + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + def unzip(zip_file, save_dir): + import zipfile + s_time = time.time() + if not os.path.exists(os.path.join(save_dir, default_args.modelarts_dataset_unzip_name)): + zip_isexist = zipfile.is_zipfile(zip_file) + if zip_isexist: + fz = zipfile.ZipFile(zip_file, 'r') + data_num = len(fz.namelist()) + print("Extract Start...") + print("unzip file num: {}".format(data_num)) + data_print = int(data_num / 100) if data_num > 100 else 1 + i = 0 + for file in fz.namelist(): + if i % data_print == 0: + print("unzip percent: {}%".format(int(i * 100 / data_num)), flush=True) + i += 1 + fz.extract(file, save_dir) + print("cost time: {}min:{}s.".format(int((time.time() - s_time) / 60), + int(int(time.time() - s_time) % 60))) + print("Extract Done.") + else: + print("This is not zip.") + else: + print("Zip has been extracted.") + + if default_args.need_modelarts_dataset_unzip: + zip_file_1 = os.path.join(default_args.data_path, default_args.modelarts_dataset_unzip_name + ".zip") + save_dir_1 = os.path.join(default_args.data_path) + + sync_lock = "/tmp/unzip_sync.lock" + + # Each server contains 8 devices as most. + if default_args.device_target == "Ascend" or default_args.device_target == "GPU": + device_id = get_device_id() + device_num = get_device_num() + else: + raise ValueError("Not support device_target.") + + if device_id % min(device_num, 8) == 0 and not os.path.exists(sync_lock): + print("Zip file path: ", zip_file_1) + print("Unzip file save dir: ", save_dir_1) + unzip(zip_file_1, save_dir_1) + print("===Finish extract data synchronization===") + try: + os.mknod(sync_lock) + except IOError: + pass + + while True: + if os.path.exists(sync_lock): + break + time.sleep(1) + + print("Device: {}, Finish sync unzip data from {} to {}.".format(device_id, zip_file_1, save_dir_1)) + + default_args.save_ckptpath = os.path.join(default_args.output_path, default_args.save_ckptpath) + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_gpu_train(): + """Train model.""" + context.set_context(mode=context.GRAPH_MODE, + device_target=default_args.device_target, save_graphs=False) + config = ConfigGCN() + if not os.path.exists(config.ckpt_dir): + os.mkdir(config.ckpt_dir) + adj, feature, label_onehot, _ = get_adj_features_labels(default_args.data_dir) + feature_d = np.expand_dims(feature, axis=0) + label_onehot_d = np.expand_dims(label_onehot, axis=0) + data = {"feature": feature_d, "label": label_onehot_d} + dataset = ds.NumpySlicesDataset(data=data) + nodes_num = label_onehot.shape[0] + eval_mask = get_mask(nodes_num, default_args.train_nodes_num, + default_args.train_nodes_num + default_args.eval_nodes_num) + class_num = label_onehot.shape[1] + input_dim = feature.shape[1] + adj = Tensor(adj, dtype=mstype.float32) + ckpt_config = CheckpointConfig(save_checkpoint_steps=config.save_ckpt_steps, + keep_checkpoint_max=config.keep_ckpt_max) + ckpoint_cb = ModelCheckpoint(prefix='ckpt_gcn', + directory=config.ckpt_dir, + config=ckpt_config) + gcn_net = GCN_GPU(config, input_dim, class_num, adj) + cb = [TimeMonitor(), LossMonitor(), ckpoint_cb] + opt = nn.Adam(gcn_net.trainable_params(), learning_rate=config.learning_rate) + criterion = Loss_Gpu(eval_mask, config.weight_decay, gcn_net.trainable_params()[0]) + model = Model(gcn_net, loss_fn=criterion, optimizer=opt, amp_level="O3") + if default_args.train_with_eval: + GCN_metric = GCNAccuracy(eval_mask) + eval_model = Model(gcn_net, loss_fn=criterion, metrics={'GCNAccuracy': GCN_metric}) + eval_param_dict = {"model": eval_model, "dataset": dataset, "metrics_name": "GCNAccuracy"} + eval_cb = EvalCallBack(apply_eval, eval_param_dict, interval=config.eval_interval, + eval_start_epoch=default_args.eval_start_epoch, save_best_ckpt=config.save_best_ckpt, + ckpt_directory=config.best_ckpt_dir, besk_ckpt_name=config.best_ckpt_name, + metrics_name="GCNAccuracy") + cb.append(eval_cb) + model.train(config.epochs, dataset, callbacks=cb, dataset_sink_mode=True) + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_train(): + """Train model.""" + context.set_context(mode=context.GRAPH_MODE, + device_target=default_args.device_target, save_graphs=False) + config = ConfigGCN() + adj, feature, label_onehot, label = get_adj_features_labels(default_args.data_dir) + + nodes_num = label_onehot.shape[0] + train_mask = get_mask(nodes_num, 0, default_args.train_nodes_num) + eval_mask = get_mask(nodes_num, default_args.train_nodes_num, + default_args.train_nodes_num + default_args.eval_nodes_num) + test_mask = get_mask(nodes_num, nodes_num - default_args.test_nodes_num, nodes_num) + + class_num = label_onehot.shape[1] + input_dim = feature.shape[1] + gcn_net = GCN(config, input_dim, class_num) + gcn_net.add_flags_recursive(fp16=True) + + adj = Tensor(adj) + feature = Tensor(feature) + + eval_net = LossAccuracyWrapper(gcn_net, label_onehot, eval_mask, config.weight_decay) + train_net = TrainNetWrapper(gcn_net, label_onehot, train_mask, config) + + loss_list = [] + + if default_args.save_TSNE: + out_feature = gcn_net() + tsne_result = t_SNE(out_feature.asnumpy(), 2) + graph_data = [] + graph_data.append(tsne_result) + fig = plt.figure() + scat = plt.scatter(tsne_result[:, 0], tsne_result[:, 1], s=2, c=label, cmap='rainbow') + plt.title('t-SNE visualization of Epoch:0', fontsize='large', fontweight='bold', verticalalignment='center') + + for epoch in range(config.epochs): + t = time.time() + + train_net.set_train() + train_result = train_net(adj, feature) + train_loss = train_result[0].asnumpy() + train_accuracy = train_result[1].asnumpy() + + eval_net.set_train(False) + eval_result = eval_net(adj, feature) + eval_loss = eval_result[0].asnumpy() + eval_accuracy = eval_result[1].asnumpy() + + loss_list.append(eval_loss) + print("Epoch:", '%04d' % (epoch + 1), "train_loss=", "{:.5f}".format(train_loss), + "train_acc=", "{:.5f}".format(train_accuracy), "val_loss=", "{:.5f}".format(eval_loss), + "val_acc=", "{:.5f}".format(eval_accuracy), "time=", "{:.5f}".format(time.time() - t)) + + if default_args.save_TSNE: + out_feature = gcn_net() + tsne_result = t_SNE(out_feature.asnumpy(), 2) + graph_data.append(tsne_result) + + if epoch > config.early_stopping and loss_list[-1] > np.mean(loss_list[-(config.early_stopping+1):-1]): + print("Early stopping...") + break + if not os.path.isdir(default_args.save_ckptpath): + os.makedirs(default_args.save_ckptpath) + ckpt_path = os.path.join(default_args.save_ckptpath, "gcn.ckpt") + save_checkpoint(gcn_net, ckpt_path) + gcn_net_test = GCN(config, input_dim, class_num) + load_checkpoint(ckpt_path, net=gcn_net_test) + gcn_net_test.add_flags_recursive(fp16=True) + + test_net = LossAccuracyWrapper(gcn_net_test, label_onehot, test_mask, config.weight_decay) + t_test = time.time() + test_net.set_train(False) + test_result = test_net(adj, feature) + test_loss = test_result[0].asnumpy() + test_accuracy = test_result[1].asnumpy() + print("Test set results:", "loss=", "{:.5f}".format(test_loss), + "accuracy=", "{:.5f}".format(test_accuracy), "time=", "{:.5f}".format(time.time() - t_test)) + + if default_args.save_TSNE: + ani = animation.FuncAnimation(fig, update_graph, frames=range(config.epochs + 1), fargs=(graph_data, scat, plt)) + ani.save('t-SNE_visualization.gif', writer='imagemagick') + + +if __name__ == '__main__': + if default_args.device_target == "GPU": + run_gpu_train() + else: + run_train() diff --git a/gnn/GCN/utils/graph_to_mindrecord/cora/__init__.py b/gnn/GCN/utils/graph_to_mindrecord/cora/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gnn/GCN/utils/graph_to_mindrecord/cora/mr_api.py b/gnn/GCN/utils/graph_to_mindrecord/cora/mr_api.py new file mode 100644 index 0000000000000000000000000000000000000000..aeeb0e04dedfeea22e5cd00134fe60eef7d79c3a --- /dev/null +++ b/gnn/GCN/utils/graph_to_mindrecord/cora/mr_api.py @@ -0,0 +1,105 @@ +# Copyright 2020 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. +# ============================================================================== +""" +User-defined API for MindRecord GNN writer. +""" +import os + +import pickle as pkl +import numpy as np +import scipy.sparse as sp + +# parse args from command line parameter 'graph_api_args' +# args delimiter is ':' +args = os.environ['graph_api_args'].split(':') +CORA_PATH = args[0] +dataset_str = 'cora' + +# profile: (num_features, feature_data_types, feature_shapes) +node_profile = (2, ["float32", "int32"], [[-1], [-1]]) +edge_profile = (0, [], []) + + +def _normalize_cora_features(features): + row_sum = np.array(features.sum(1)) + r_inv = np.power(row_sum * 1.0, -1).flatten() + r_inv[np.isinf(r_inv)] = 0. + r_mat_inv = sp.diags(r_inv) + features = r_mat_inv.dot(features) + return features + + +def _parse_index_file(filename): + """Parse index file.""" + index = [] + for line in open(filename): + index.append(int(line.strip())) + return index + + +def yield_nodes(task_id=0): + """ + Generate node data + + Yields: + data (dict): data row which is dict. + """ + print("Node task is {}".format(task_id)) + + names = ['tx', 'ty', 'allx', 'ally'] + objects = [] + for name in names: + with open("{}/ind.{}.{}".format(CORA_PATH, dataset_str, name), 'rb') as f: + objects.append(pkl.load(f, encoding='latin1')) + tx, ty, allx, ally = tuple(objects) + test_idx_reorder = _parse_index_file( + "{}/ind.{}.test.index".format(CORA_PATH, dataset_str)) + test_idx_range = np.sort(test_idx_reorder) + + features = sp.vstack((allx, tx)).tolil() + features[test_idx_reorder, :] = features[test_idx_range, :] + features = _normalize_cora_features(features) + features = features.A + + labels = np.vstack((ally, ty)) + labels[test_idx_reorder, :] = labels[test_idx_range, :] + + line_count = 0 + for i, label in enumerate(labels): + node = {'id': i, 'type': 0, 'feature_1': features[i].tolist(), + 'feature_2': label.tolist().index(1)} + line_count += 1 + yield node + print('Processed {} lines for nodes.'.format(line_count)) + + +def yield_edges(task_id=0): + """ + Generate edge data + + Yields: + data (dict): data row which is dict. + """ + print("Edge task is {}".format(task_id)) + with open("{}/ind.{}.graph".format(CORA_PATH, dataset_str), 'rb') as f: + graph = pkl.load(f, encoding='latin1') + line_count = 0 + for i in graph: + for dst_id in graph[i]: + edge = {'id': line_count, + 'src_id': i, 'dst_id': dst_id, 'type': 0} + line_count += 1 + yield edge + print('Processed {} lines for edges.'.format(line_count)) diff --git a/gnn/GCN/utils/graph_to_mindrecord/graph_map_schema.py b/gnn/GCN/utils/graph_to_mindrecord/graph_map_schema.py new file mode 100644 index 0000000000000000000000000000000000000000..1ee1fa343ff7a6224b5f368543118d03924a9d09 --- /dev/null +++ b/gnn/GCN/utils/graph_to_mindrecord/graph_map_schema.py @@ -0,0 +1,164 @@ +# Copyright 2019 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. +# ============================================================================== +""" +Graph data convert tool for MindRecord. +""" +import numpy as np +from mindspore import log as logger + +__all__ = ['GraphMapSchema'] + + +class GraphMapSchema: + """ + Class is for transformation from graph data to MindRecord. + """ + + def __init__(self): + """ + init + """ + self.num_node_features = 0 + self.num_edge_features = 0 + self.union_schema_in_mindrecord = { + "first_id": {"type": "int64"}, + "second_id": {"type": "int64"}, + "third_id": {"type": "int64"}, + "type": {"type": "int32"}, + "weight": {"type": "float32"}, + "attribute": {"type": "string"}, # 'n' for ndoe, 'e' for edge + "node_feature_index": {"type": "int32", "shape": [-1]}, + "edge_feature_index": {"type": "int32", "shape": [-1]} + } + + @property + def get_schema(self): + """ + Get schema + """ + return self.union_schema_in_mindrecord + + def set_node_feature_profile(self, num_features, features_data_type, features_shape): + """ + Set node features profile + """ + if num_features != len(features_data_type) or num_features != len(features_shape): + logger.info("Node feature profile is not match.") + raise ValueError("Node feature profile is not match.") + + self.num_node_features = num_features + for i in range(num_features): + k = i + 1 + field_key = 'node_feature_' + str(k) + field_value = {"type": features_data_type[i], "shape": features_shape[i]} + self.union_schema_in_mindrecord[field_key] = field_value + + def set_edge_feature_profile(self, num_features, features_data_type, features_shape): + """ + Set edge features profile + """ + if num_features != len(features_data_type) or num_features != len(features_shape): + logger.info("Edge feature profile is not match.") + raise ValueError("Edge feature profile is not match.") + + self.num_edge_features = num_features + for i in range(num_features): + k = i + 1 + field_key = 'edge_feature_' + str(k) + field_value = {"type": features_data_type[i], "shape": features_shape[i]} + self.union_schema_in_mindrecord[field_key] = field_value + + def transform_node(self, node): + """ + Executes transformation from node data to union format. + Args: + node(schema): node's data + Returns: + graph data with union schema + """ + if node is None: + logger.info("node cannot be None.") + raise ValueError("node cannot be None.") + + node_graph = {"first_id": node["id"], "second_id": 0, "third_id": 0, "weight": 1.0, "attribute": 'n', + "type": node["type"], "node_feature_index": []} + if "weight" in node: + node_graph["weight"] = node["weight"] + + for i in range(self.num_node_features): + k = i + 1 + node_field_key = 'feature_' + str(k) + graph_field_key = 'node_feature_' + str(k) + graph_field_type = self.union_schema_in_mindrecord[graph_field_key]["type"] + if node_field_key in node: + node_graph["node_feature_index"].append(k) + node_graph[graph_field_key] = np.reshape(np.array(node[node_field_key], dtype=graph_field_type), [-1]) + else: + node_graph[graph_field_key] = np.reshape(np.array([0], dtype=graph_field_type), [-1]) + + if node_graph["node_feature_index"]: + node_graph["node_feature_index"] = np.array(node_graph["node_feature_index"], dtype="int32") + else: + node_graph["node_feature_index"] = np.array([-1], dtype="int32") + + node_graph["edge_feature_index"] = np.array([-1], dtype="int32") + for i in range(self.num_edge_features): + k = i + 1 + graph_field_key = 'edge_feature_' + str(k) + graph_field_type = self.union_schema_in_mindrecord[graph_field_key]["type"] + node_graph[graph_field_key] = np.reshape(np.array([0], dtype=graph_field_type), [-1]) + return node_graph + + def transform_edge(self, edge): + """ + Executes transformation from edge data to union format. + Args: + edge(schema): edge's data + Returns: + graph data with union schema + """ + if edge is None: + logger.info("edge cannot be None.") + raise ValueError("edge cannot be None.") + + edge_graph = {"first_id": edge["id"], "second_id": edge["src_id"], "third_id": edge["dst_id"], "weight": 1.0, + "attribute": 'e', "type": edge["type"], "edge_feature_index": []} + + if "weight" in edge: + edge_graph["weight"] = edge["weight"] + + for i in range(self.num_edge_features): + k = i + 1 + edge_field_key = 'feature_' + str(k) + graph_field_key = 'edge_feature_' + str(k) + graph_field_type = self.union_schema_in_mindrecord[graph_field_key]["type"] + if edge_field_key in edge: + edge_graph["edge_feature_index"].append(k) + edge_graph[graph_field_key] = np.reshape(np.array(edge[edge_field_key], dtype=graph_field_type), [-1]) + else: + edge_graph[graph_field_key] = np.reshape(np.array([0], dtype=graph_field_type), [-1]) + + if edge_graph["edge_feature_index"]: + edge_graph["edge_feature_index"] = np.array(edge_graph["edge_feature_index"], dtype="int32") + else: + edge_graph["edge_feature_index"] = np.array([-1], dtype="int32") + + edge_graph["node_feature_index"] = np.array([-1], dtype="int32") + for i in range(self.num_node_features): + k = i + 1 + graph_field_key = 'node_feature_' + str(k) + graph_field_type = self.union_schema_in_mindrecord[graph_field_key]["type"] + edge_graph[graph_field_key] = np.array([0], dtype=graph_field_type) + return edge_graph diff --git a/gnn/GCN/utils/graph_to_mindrecord/writer.py b/gnn/GCN/utils/graph_to_mindrecord/writer.py new file mode 100644 index 0000000000000000000000000000000000000000..b0272b7d0dd24bd7ef790a4c50c74d998cd9313f --- /dev/null +++ b/gnn/GCN/utils/graph_to_mindrecord/writer.py @@ -0,0 +1,185 @@ +# Copyright 2020 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. +# ============================================================================== +""" +######################## write mindrecord example ######################## +Write mindrecord by data dictionary: +python writer.py --mindrecord_script /YourScriptPath ... +""" +import argparse +import os +import time +from importlib import import_module +from multiprocessing import Pool + +from mindspore.mindrecord import FileWriter +from graph_map_schema import GraphMapSchema + + +def exec_task(task_id, parallel_writer=True): + """ + Execute task with specified task id + """ + print("exec task {}, parallel: {} ...".format(task_id, parallel_writer)) + imagenet_iter = mindrecord_dict_data(task_id) + batch_size = 512 + transform_count = 0 + while True: + data_list = [] + try: + for _ in range(batch_size): + data = imagenet_iter.__next__() + if 'dst_id' in data: + data = graph_map_schema.transform_edge(data) + else: + data = graph_map_schema.transform_node(data) + data_list.append(data) + transform_count += 1 + writer.write_raw_data(data_list, parallel_writer=parallel_writer) + print("transformed {} record...".format(transform_count)) + except StopIteration: + if data_list: + writer.write_raw_data(data_list, parallel_writer=parallel_writer) + print("transformed {} record...".format(transform_count)) + break + + +def read_args(): + """ + read args + """ + parser = argparse.ArgumentParser(description='Mind record writer') + parser.add_argument('--mindrecord_script', type=str, default="template", + help='path where script is saved') + + parser.add_argument('--mindrecord_file', type=str, default="/tmp/mindrecord/xyz", + help='written file name prefix') + + parser.add_argument('--mindrecord_partitions', type=int, default=1, + help='number of written files') + + parser.add_argument('--mindrecord_header_size_by_bit', type=int, default=24, + help='mindrecord file header size') + + parser.add_argument('--mindrecord_page_size_by_bit', type=int, default=25, + help='mindrecord file page size') + + parser.add_argument('--mindrecord_workers', type=int, default=8, + help='number of parallel workers') + + parser.add_argument('--num_node_tasks', type=int, default=1, + help='number of node tasks') + + parser.add_argument('--num_edge_tasks', type=int, default=1, + help='number of node tasks') + + parser.add_argument('--graph_api_args', type=str, default="/tmp/nodes.csv:/tmp/edges.csv", + help='nodes and edges data file, csv format with header.') + + ret_args = parser.parse_args() + + return ret_args + + +def init_writer(mr_schema): + """ + init writer + """ + print("Init writer ...") + mr_writer = FileWriter(args.mindrecord_file, args.mindrecord_partitions) + + # set the header size + if args.mindrecord_header_size_by_bit != 24: + header_size = 1 << args.mindrecord_header_size_by_bit + mr_writer.set_header_size(header_size) + + # set the page size + if args.mindrecord_page_size_by_bit != 25: + page_size = 1 << args.mindrecord_page_size_by_bit + mr_writer.set_page_size(page_size) + + # create the schema + mr_writer.add_schema(mr_schema, "mindrecord_graph_schema") + + # open file and set header + mr_writer.open_and_set_header() + + return mr_writer + + +def run_parallel_workers(num_tasks): + """ + run parallel workers + """ + # set number of workers + num_workers = args.mindrecord_workers + + task_list = list(range(num_tasks)) + + if num_workers > num_tasks: + num_workers = num_tasks + + if os.name == 'nt': + for window_task_id in task_list: + exec_task(window_task_id, False) + elif num_tasks > 1: + with Pool(num_workers) as p: + p.map(exec_task, task_list) + else: + exec_task(0, False) + + +if __name__ == "__main__": + args = read_args() + print(args) + + start_time = time.time() + + # pass mr_api arguments + os.environ['graph_api_args'] = args.graph_api_args + + try: + mr_api = import_module(args.mindrecord_script + '.mr_api') + except ModuleNotFoundError: + raise RuntimeError("Unknown module path: {}".format(args.mindrecord_script + '.mr_api')) + + # init graph schema + graph_map_schema = GraphMapSchema() + + num_features, feature_data_types, feature_shapes = mr_api.node_profile + graph_map_schema.set_node_feature_profile(num_features, feature_data_types, feature_shapes) + + num_features, feature_data_types, feature_shapes = mr_api.edge_profile + graph_map_schema.set_edge_feature_profile(num_features, feature_data_types, feature_shapes) + + graph_schema = graph_map_schema.get_schema + + # init writer + writer = init_writer(graph_schema) + + # write nodes data + mindrecord_dict_data = mr_api.yield_nodes + run_parallel_workers(args.num_node_tasks) + + # write edges data + mindrecord_dict_data = mr_api.yield_edges + run_parallel_workers(args.num_edge_tasks) + + # writer wrap up + ret = writer.commit() + + end_time = time.time() + print("--------------------------------------------") + print("END. Total time: {}".format(end_time - start_time)) + print("--------------------------------------------") diff --git a/nlp/language_model/bert/MindSpore/README.md b/nlp/language_model/bert/MindSpore/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6b5e32c6cadb65b359d097d0b7ae6e3dbda7369d --- /dev/null +++ b/nlp/language_model/bert/MindSpore/README.md @@ -0,0 +1,48 @@ +# BERT +## Model description +The BERT network was proposed by Google in 2018. The network has made a breakthrough in the field of NLP. The network uses pre-training to achieve a large network structure without modifying, and only by adding an output layer to achieve multiple text-based tasks in fine-tuning. The backbone code of BERT adopts the Encoder structure of Transformer. The attention mechanism is introduced to enable the output layer to capture high-latitude global semantic information. The pre-training uses denoising and self-encoding tasks, namely MLM(Masked Language Model) and NSP(Next Sentence Prediction). No need to label data, pre-training can be performed on massive text data, and only a small amount of data to fine-tuning downstream tasks to obtain good results. The pre-training plus fune-tuning mode created by BERT is widely adopted by subsequent NLP networks. + +[Paper](https://arxiv.org/abs/1810.04805): Jacob Devlin, Ming-Wei Chang, Kenton Lee, Kristina Toutanova. [BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding]((https://arxiv.org/abs/1810.04805)). arXiv preprint arXiv:1810.04805. + +[Paper](https://arxiv.org/abs/1909.00204): Junqiu Wei, Xiaozhe Ren, Xiaoguang Li, Wenyong Huang, Yi Liao, Yasheng Wang, Jiashu Lin, Xin Jiang, Xiao Chen, Qun Liu. [NEZHA: Neural Contextualized Representation for Chinese Language Understanding](https://arxiv.org/abs/1909.00204). arXiv preprint arXiv:1909.00204. +## Step 1: Installing +``` +pip3 install -r requirements.txt +``` +## Step 2: Prepare Datasets +# 1. Download training dataset(.tf_record), eval dataset(.json), vocab.txt and checkpoint:bert_large_ascend_v130_enwiki_official_nlp_bs768_loss1.1.ckpt +``` +cd scripts +mkdir -p squad +``` +Please [BERT](https://github.com/google-research/bert#pre-training-with-bert) download vocab.txt here + +- Create fine-tune dataset + - Download dataset for fine-tuning and evaluation such as Chinese Named Entity Recognition[CLUENER](https://github.com/CLUEbenchmark/CLUENER2020), Chinese sentences classification[TNEWS](https://github.com/CLUEbenchmark/CLUE), Chinese Named Entity Recognition[ChineseNER](https://github.com/zjy-ucas/ChineseNER), English question and answering[SQuAD v1.1 train dataset](https://rajpurkar.github.io/SQuAD-explorer/dataset/train-v1.1.json), [SQuAD v1.1 eval dataset](https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v1.1.json), package of English sentences classification[GLUE](https://gluebenchmark.com/tasks). + - We haven't provide the scripts to create tfrecord yet, while converting dataset files from JSON format to TFRECORD format, please refer to run_classifier.py or run_squad.py file in [BERT](https://github.com/google-research/bert) repository or the CLUE official repository [CLUE](https://github.com/CLUEbenchmark/CLUE/blob/master/baselines/models/bert/run_classifier.py) and [CLUENER](https://github.com/CLUEbenchmark/CLUENER2020/tree/master/tf_version) + +# [Pretrained models](#contents) + +We have provided several kinds of pretrained checkpoint. + +- [Bert-base-zh](https://download.mindspore.cn/model_zoo/r1.3/bert_base_ascend_v130_zhwiki_official_nlp_bs256_acc91.72_recall95.06_F1score93.36/), trained on zh-wiki datasets with 128 length. +- [Bert-large-zh](https://download.mindspore.cn/model_zoo/r1.3/bert_large_ascend_v130_zhwiki_official_nlp_bs3072_loss0.8/), trained on zh-wiki datasets with 128 length. +- [Bert-large-en](https://download.mindspore.cn/model_zoo/r1.3/bert_large_ascend_v130_enwiki_official_nlp_bs768_loss1.1/), tarined on en-wiki datasets with 512 length. + +## Step 3: Training +``` +bash scripts/run_squad_gpu_distribute.sh 8 +``` +### [Evaluation result] +## Results on BI-V100 + +| GPUs | per step time | exact_match | F1 | +|------|-------------- |---------------|------| +| 1*8 | 1.898s | 71.9678 |81.422| +### 性能数据:NV +## Results on NV-V100s + +| GPUs | per step time | exact_match | F1 | +|------|-------------- |---------------|------| +| 1*8 | 1.877s | 71.9678 |81.422| + diff --git a/nlp/language_model/bert/MindSpore/ascend310_infer/CMakeLists.txt b/nlp/language_model/bert/MindSpore/ascend310_infer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ee3c85447340e0449ff2b70ed24f60a17e07b2b6 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/ascend310_infer/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.14.1) +project(Ascend310Infer) +add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -std=c++17 -Werror -Wall -fPIE -Wl,--allow-shlib-undefined") +set(PROJECT_SRC_ROOT ${CMAKE_CURRENT_LIST_DIR}/) +option(MINDSPORE_PATH "mindspore install path" "") +include_directories(${MINDSPORE_PATH}) +include_directories(${MINDSPORE_PATH}/include) +include_directories(${PROJECT_SRC_ROOT}) +find_library(MS_LIB libmindspore.so ${MINDSPORE_PATH}/lib) +file(GLOB_RECURSE MD_LIB ${MINDSPORE_PATH}/_c_dataengine*) + +add_executable(main src/main.cc src/utils.cc) +target_link_libraries(main ${MS_LIB} ${MD_LIB} gflags) diff --git a/nlp/language_model/bert/MindSpore/ascend310_infer/build.sh b/nlp/language_model/bert/MindSpore/ascend310_infer/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..713d7f657ddfa5f75b069351c55f8447f77c72d0 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/ascend310_infer/build.sh @@ -0,0 +1,29 @@ +#!/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. +# ============================================================================ +if [ -d out ]; then + rm -rf out +fi + +mkdir out +cd out || exit + +if [ -f "Makefile" ]; then + make clean +fi + +cmake .. \ + -DMINDSPORE_PATH="`pip show mindspore-ascend | grep Location | awk '{print $2"/mindspore"}' | xargs realpath`" +make diff --git a/nlp/language_model/bert/MindSpore/ascend310_infer/inc/utils.h b/nlp/language_model/bert/MindSpore/ascend310_infer/inc/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..efebe03a8c1179f5a1f9d5f7ee07e0352a9937c6 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/ascend310_infer/inc/utils.h @@ -0,0 +1,32 @@ +/** + * 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 MINDSPORE_INFERENCE_UTILS_H_ +#define MINDSPORE_INFERENCE_UTILS_H_ + +#include +#include +#include +#include +#include +#include "include/api/types.h" + +std::vector GetAllFiles(std::string_view dirName); +DIR *OpenDir(std::string_view dirName); +std::string RealPath(std::string_view path); +mindspore::MSTensor ReadFileToTensor(const std::string &file); +int WriteResult(const std::string& imageFile, const std::vector &outputs); +#endif diff --git a/nlp/language_model/bert/MindSpore/ascend310_infer/src/main.cc b/nlp/language_model/bert/MindSpore/ascend310_infer/src/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..595379e0b860cdc9a3f165b44430b6ed1fc25e1d --- /dev/null +++ b/nlp/language_model/bert/MindSpore/ascend310_infer/src/main.cc @@ -0,0 +1,152 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/api/model.h" +#include "include/api/context.h" +#include "include/api/types.h" +#include "include/api/serialization.h" +#include "include/dataset/execute.h" +#include "include/dataset/vision.h" +#include "inc/utils.h" + +using mindspore::Context; +using mindspore::Serialization; +using mindspore::Model; +using mindspore::Status; +using mindspore::MSTensor; +using mindspore::dataset::Execute; +using mindspore::ModelType; +using mindspore::GraphCell; +using mindspore::kSuccess; + +DEFINE_string(mindir_path, "", "mindir path"); +DEFINE_string(input0_path, ".", "input0 path"); +DEFINE_string(input1_path, ".", "input1 path"); +DEFINE_string(input2_path, ".", "input2 path"); +DEFINE_string(input3_path, ".", "input3 path"); +DEFINE_bool(use_crf, true, "use crf or not"); +DEFINE_int32(device_id, 0, "device id"); + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (RealPath(FLAGS_mindir_path).empty()) { + std::cout << "Invalid mindir" << std::endl; + return 1; + } + + auto context = std::make_shared(); + auto ascend310 = std::make_shared(); + ascend310->SetDeviceID(FLAGS_device_id); + context->MutableDeviceInfo().push_back(ascend310); + mindspore::Graph graph; + Serialization::Load(FLAGS_mindir_path, ModelType::kMindIR, &graph); + + Model model; + Status ret = model.Build(GraphCell(graph), context); + if (ret != kSuccess) { + std::cout << "ERROR: Build failed." << std::endl; + return 1; + } + + std::vector model_inputs = model.GetInputs(); + if (model_inputs.empty()) { + std::cout << "Invalid model, inputs is empty." << std::endl; + return 1; + } + + auto input0_files = GetAllFiles(FLAGS_input0_path); + auto input1_files = GetAllFiles(FLAGS_input1_path); + auto input2_files = GetAllFiles(FLAGS_input2_path); + auto input3_files = GetAllFiles(FLAGS_input3_path); + + if (input0_files.empty() || input1_files.empty() || input2_files.empty() || input3_files.empty()) { + std::cout << "ERROR: input data empty." << std::endl; + return 1; + } + + std::map costTime_map; + size_t size = input0_files.size(); + + for (size_t i = 0; i < size; ++i) { + struct timeval start = {0}; + struct timeval end = {0}; + double startTimeMs; + double endTimeMs; + std::vector inputs; + std::vector outputs; + std::cout << "Start predict input files:" << input0_files[i] << std::endl; + + auto input0 = ReadFileToTensor(input0_files[i]); + auto input1 = ReadFileToTensor(input1_files[i]); + auto input2 = ReadFileToTensor(input2_files[i]); + inputs.emplace_back(model_inputs[0].Name(), model_inputs[0].DataType(), model_inputs[0].Shape(), + input0.Data().get(), input0.DataSize()); + inputs.emplace_back(model_inputs[1].Name(), model_inputs[1].DataType(), model_inputs[1].Shape(), + input1.Data().get(), input1.DataSize()); + inputs.emplace_back(model_inputs[2].Name(), model_inputs[2].DataType(), model_inputs[2].Shape(), + input2.Data().get(), input2.DataSize()); + if (FLAGS_use_crf) { + auto input3 = ReadFileToTensor(input3_files[i]); + inputs.emplace_back(model_inputs[3].Name(), model_inputs[3].DataType(), model_inputs[3].Shape(), + input3.Data().get(), input3.DataSize()); + } + + gettimeofday(&start, nullptr); + ret = model.Predict(inputs, &outputs); + gettimeofday(&end, nullptr); + if (ret != kSuccess) { + std::cout << "Predict " << input0_files[i] << " failed." << std::endl; + return 1; + } + startTimeMs = (1.0 * start.tv_sec * 1000000 + start.tv_usec) / 1000; + endTimeMs = (1.0 * end.tv_sec * 1000000 + end.tv_usec) / 1000; + costTime_map.insert(std::pair(startTimeMs, endTimeMs)); + int rst = WriteResult(input0_files[i], outputs); + if (rst != 0) { + std::cout << "write result failed." << std::endl; + return rst; + } + } + double average = 0.0; + int inferCount = 0; + + for (auto iter = costTime_map.begin(); iter != costTime_map.end(); iter++) { + double diff = 0.0; + diff = iter->second - iter->first; + average += diff; + inferCount++; + } + average = average / inferCount; + std::stringstream timeCost; + timeCost << "NN inference cost average time: "<< average << " ms of infer_count " << inferCount << std::endl; + std::cout << "NN inference cost average time: "<< average << "ms of infer_count " << inferCount << std::endl; + std::string fileName = "./time_Result" + std::string("/test_perform_static.txt"); + std::ofstream fileStream(fileName.c_str(), std::ios::trunc); + fileStream << timeCost.str(); + fileStream.close(); + costTime_map.clear(); + return 0; +} diff --git a/nlp/language_model/bert/MindSpore/ascend310_infer/src/utils.cc b/nlp/language_model/bert/MindSpore/ascend310_infer/src/utils.cc new file mode 100644 index 0000000000000000000000000000000000000000..bc56f76149d87aa13a6534ce4ab1f8ea4eb45308 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/ascend310_infer/src/utils.cc @@ -0,0 +1,142 @@ +/** + * 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 +#include +#include +#include "inc/utils.h" + +using mindspore::MSTensor; +using mindspore::DataType; + +std::vector GetAllFiles(std::string_view dirName) { + struct dirent *filename; + DIR *dir = OpenDir(dirName); + if (dir == nullptr) { + return {}; + } + std::vector res; + while ((filename = readdir(dir)) != nullptr) { + std::string dName = std::string(filename->d_name); + if (dName == "." || dName == ".." || filename->d_type != DT_REG) { + continue; + } + res.emplace_back(std::string(dirName) + "/" + filename->d_name); + } + std::sort(res.begin(), res.end()); + for (auto &f : res) { + std::cout << "image file: " << f << std::endl; + } + return res; +} + +int WriteResult(const std::string& imageFile, const std::vector &outputs) { + std::string homePath = "./result_Files"; + const int INVALID_POINTER = -1; + const int ERROR = -2; + for (size_t i = 0; i < outputs.size(); ++i) { + size_t outputSize; + std::shared_ptr netOutput; + netOutput = outputs[i].Data(); + outputSize = outputs[i].DataSize(); + int pos = imageFile.rfind('/'); + std::string fileName(imageFile, pos + 1); + fileName.replace(fileName.find('.'), fileName.size() - fileName.find('.'), '_' + std::to_string(i) + ".bin"); + std::string outFileName = homePath + "/" + fileName; + FILE * outputFile = fopen(outFileName.c_str(), "wb"); + if (outputFile == nullptr) { + std::cout << "open result file " << outFileName << " failed" << std::endl; + return INVALID_POINTER; + } + size_t size = fwrite(netOutput.get(), sizeof(char), outputSize, outputFile); + if (size != outputSize) { + fclose(outputFile); + outputFile = nullptr; + std::cout << "write result file " << outFileName << " failed, write size[" << size << + "] is smaller than output size[" << outputSize << "], maybe the disk is full." << std::endl; + return ERROR; + } + fclose(outputFile); + outputFile = nullptr; + } + return 0; +} + +mindspore::MSTensor ReadFileToTensor(const std::string &file) { + if (file.empty()) { + std::cout << "Pointer file is nullptr" << std::endl; + return mindspore::MSTensor(); + } + + std::ifstream ifs(file); + if (!ifs.good()) { + std::cout << "File: " << file << " is not exist" << std::endl; + return mindspore::MSTensor(); + } + + if (!ifs.is_open()) { + std::cout << "File: " << file << "open failed" << std::endl; + return mindspore::MSTensor(); + } + + ifs.seekg(0, std::ios::end); + size_t size = ifs.tellg(); + mindspore::MSTensor buffer(file, mindspore::DataType::kNumberTypeUInt8, {static_cast(size)}, nullptr, size); + + ifs.seekg(0, std::ios::beg); + ifs.read(reinterpret_cast(buffer.MutableData()), size); + ifs.close(); + + return buffer; +} + + +DIR *OpenDir(std::string_view dirName) { + if (dirName.empty()) { + std::cout << " dirName is null ! " << std::endl; + return nullptr; + } + std::string realPath = RealPath(dirName); + struct stat s; + lstat(realPath.c_str(), &s); + if (!S_ISDIR(s.st_mode)) { + std::cout << "dirName is not a valid directory !" << std::endl; + return nullptr; + } + DIR *dir; + dir = opendir(realPath.c_str()); + if (dir == nullptr) { + std::cout << "Can not open dir " << dirName << std::endl; + return nullptr; + } + std::cout << "Successfully opened the dir " << dirName << std::endl; + return dir; +} + +std::string RealPath(std::string_view path) { + char realPathMem[PATH_MAX] = {0}; + char *realPathRet = nullptr; + realPathRet = realpath(path.data(), realPathMem); + + if (realPathRet == nullptr) { + std::cout << "File: " << path << " is not exist."; + return ""; + } + + std::string realPath(realPathMem); + std::cout << path << " realpath is: " << realPath << std::endl; + return realPath; +} diff --git a/nlp/language_model/bert/MindSpore/export.py b/nlp/language_model/bert/MindSpore/export.py new file mode 100644 index 0000000000000000000000000000000000000000..942ffeb2e5e3c4aaa97abf10a9dd42f56f269d9b --- /dev/null +++ b/nlp/language_model/bert/MindSpore/export.py @@ -0,0 +1,91 @@ +# Copyright 2020-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 checkpoint file into models""" +import os +import shutil +import numpy as np + +import mindspore.common.dtype as mstype +from mindspore import Tensor, context, load_checkpoint, export + +from src.finetune_eval_model import BertCLSModel, BertSquadModel, BertNERModel +from src.bert_for_finetune import BertNER +from src.utils import convert_labels_to_index +from src.model_utils.config import config as args, bert_net_cfg +from src.model_utils.moxing_adapter import moxing_wrapper +from src.model_utils.device_adapter import get_device_id + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + args.device_id = get_device_id() + _file_dir = os.path.dirname(os.path.abspath(__file__)) + args.export_ckpt_file = os.path.join(_file_dir, args.export_ckpt_file) + args.label_file_path = os.path.join(args.data_path, args.label_file_path) + args.export_file_name = os.path.join(_file_dir, args.export_file_name) + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_export(): + '''export function''' + context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target) + if args.device_target == "Ascend": + context.set_context(device_id=args.device_id) + + if args.description == "run_ner": + label_list = [] + with open(args.label_file_path) as f: + for label in f: + label_list.append(label.strip()) + + tag_to_index = convert_labels_to_index(label_list) + + if args.use_crf.lower() == "true": + max_val = max(tag_to_index.values()) + tag_to_index[""] = max_val + 1 + tag_to_index[""] = max_val + 2 + number_labels = len(tag_to_index) + net = BertNER(bert_net_cfg, args.export_batch_size, False, num_labels=number_labels, + use_crf=True, tag_to_index=tag_to_index) + else: + number_labels = len(tag_to_index) + net = BertNERModel(bert_net_cfg, False, number_labels, use_crf=(args.use_crf.lower() == "true")) + elif args.description == "run_classifier": + net = BertCLSModel(bert_net_cfg, False, num_labels=args.num_class) + elif args.description == "run_squad": + net = BertSquadModel(bert_net_cfg, False) + else: + raise ValueError("unsupported downstream task") + + load_checkpoint(args.export_ckpt_file, net=net) + net.set_train(False) + + input_ids = Tensor(np.zeros([args.export_batch_size, bert_net_cfg.seq_length]), mstype.int32) + input_mask = Tensor(np.zeros([args.export_batch_size, bert_net_cfg.seq_length]), mstype.int32) + token_type_id = Tensor(np.zeros([args.export_batch_size, bert_net_cfg.seq_length]), mstype.int32) + label_ids = Tensor(np.zeros([args.export_batch_size, bert_net_cfg.seq_length]), mstype.int32) + + if args.description == "run_ner" and args.use_crf.lower() == "true": + input_data = [input_ids, input_mask, token_type_id, label_ids] + else: + input_data = [input_ids, input_mask, token_type_id] + export(net, *input_data, file_name=args.export_file_name, file_format=args.file_format) + if args.enable_modelarts: + air_file = f"{args.export_file_name}.{args.file_format.lower()}" + shutil.move(air_file, args.output_path) + + +if __name__ == "__main__": + run_export() diff --git a/nlp/language_model/bert/MindSpore/infer/Dockerfile b/nlp/language_model/bert/MindSpore/infer/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d754732fc68084db5ec7d9a34aab3e3f50a20668 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/Dockerfile @@ -0,0 +1,34 @@ +# 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. +# ============================================================================ +ARG FROM_IMAGE_NAME +FROM ${FROM_IMAGE_NAME} + +ARG SDK_PKG + +RUN ln -s /usr/local/python3.7.5/bin/python3.7 /usr/bin/python + +RUN apt-get update && \ + apt-get install libglib2.0-dev -y || \ + rm -rf /var/lib/dpkg/info && \ + mkdir /var/lib/dpkg/info && \ + apt-get install libglib2.0-dev -y && \ + pip install pytest-runner==5.3.0 + +# pip install sdk_run +COPY $SDK_PKG . +RUN ls -hrlt +RUN chmod +x ${SDK_PKG} && \ + ./${SDK_PKG} --install-path=/home/run --install && \ + bash -c "source ~/.bashrc" \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/infer/convert/convert.sh b/nlp/language_model/bert/MindSpore/infer/convert/convert.sh new file mode 100644 index 0000000000000000000000000000000000000000..9d2afbfe71305b727d0f84daba292a919da30b69 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/convert/convert.sh @@ -0,0 +1,26 @@ +#!/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. +# ============================================================================ + +air_path=$1 +om_path=$2 + +echo "Input AIR file path: ${air_path}" +echo "Output OM file path: ${om_path}" + +atc --framework=1 --model="${air_path}" \ + --output="${om_path}" \ + --soc_version=Ascend310 \ + --op_select_implmode="high_precision" \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/infer/data/config/bert_base.pipeline b/nlp/language_model/bert/MindSpore/infer/data/config/bert_base.pipeline new file mode 100644 index 0000000000000000000000000000000000000000..e190d95565404152d2b95b34791e24b77fc371ee --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/data/config/bert_base.pipeline @@ -0,0 +1,46 @@ +{ + "im_bertbase": { + "stream_config": { + "deviceId": "0" + }, + "appsrc0": { + "props": { + "blocksize": "409600" + }, + "factory": "appsrc", + "next": "mxpi_tensorinfer0:0" + }, + "appsrc1": { + "props": { + "blocksize": "409600" + }, + "factory": "appsrc", + "next": "mxpi_tensorinfer0:1" + }, + "appsrc2": { + "props": { + "blocksize": "409600" + }, + "factory": "appsrc", + "next": "mxpi_tensorinfer0:2" + }, + "mxpi_tensorinfer0": { + "props": { + "dataSource": "appsrc0,appsrc1,appsrc2", + "modelPath": "../data/model/cluner.om" + }, + "factory": "mxpi_tensorinfer", + "next": "mxpi_dataserialize0" + }, + "mxpi_dataserialize0": { + "props": { + "outputDataKeys": "mxpi_tensorinfer0" + }, + "factory": "mxpi_dataserialize", + "next": "appsink0" + }, + "appsink0": { + "factory": "appsink" + } + } +} \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/infer/data/config/infer_label.txt b/nlp/language_model/bert/MindSpore/infer/data/config/infer_label.txt new file mode 100644 index 0000000000000000000000000000000000000000..3570af4bcecebb25ab6c217613a21e91f3165801 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/data/config/infer_label.txt @@ -0,0 +1,10 @@ +address +book +company +game +government +movie +name +organization +position +scene \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/infer/mxbase/CMakeLists.txt b/nlp/language_model/bert/MindSpore/infer/mxbase/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..071b5b8b548a774f7efe4f7b5c23f5725e30c3dd --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/mxbase/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.10.0) +project(bert) + +set(TARGET bert) + +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(DEFINED ENV{MXSDK_OPENSOURCE_DIR}) + set(OPENSOURCE_DIR $ENV{MXSDK_OPENSOURCE_DIR}) +else() + set(OPENSOURCE_DIR ${MXBASE_ROOT_DIR}/opensource) +endif() + +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/BertNerBase.cpp) +target_link_libraries(${TARGET} glog cpprest mxbase opencv_world stdc++fs) + +install(TARGETS ${TARGET} RUNTIME DESTINATION ${PROJECT_SOURCE_DIR}/) diff --git a/nlp/language_model/bert/MindSpore/infer/mxbase/build.sh b/nlp/language_model/bert/MindSpore/infer/mxbase/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..e23d258b6d1f5763224c8c57af4f0102d63cceff --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/mxbase/build.sh @@ -0,0 +1,55 @@ +#!/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. + +path_cur=$(dirname $0) + +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_bert() +{ + cd $path_cur + rm -rf build + mkdir -p build + cd build + cmake .. + make + ret=$? + if [ ${ret} -ne 0 ]; then + echo "Failed to build bert." + exit ${ret} + fi + make install +} + +check_env +build_bert \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/infer/mxbase/src/BertNerBase.cpp b/nlp/language_model/bert/MindSpore/infer/mxbase/src/BertNerBase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a1452e20084cfdc62b5cbece7666323a8f1f2d0 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/mxbase/src/BertNerBase.cpp @@ -0,0 +1,345 @@ +/** + * 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 "BertNerBase.h" +#include +#include +#include +#include +#include "MxBase/DeviceManager/DeviceManager.h" +#include "MxBase/Log/Log.h" + +const uint32_t EACH_LABEL_LENGTH = 4; +const uint32_t MAX_LENGTH = 128; +const uint32_t CLASS_NUM = 41; + +APP_ERROR BertNerBase::LoadLabels(const std::string &labelPath, std::vector *labelMap) { + std::ifstream infile; + // open label file + infile.open(labelPath, std::ios_base::in); + std::string s; + // check label file validity + if (infile.fail()) { + LogError << "Failed to open label file: " << labelPath << "."; + return APP_ERR_COMM_OPEN_FAIL; + } + labelMap->clear(); + // construct label vector + while (std::getline(infile, s)) { + if (s.size() == 0 || s[0] == '#') { + continue; + } + size_t eraseIndex = s.find_last_not_of("\r\n\t"); + if (eraseIndex != std::string::npos) { + s.erase(eraseIndex + 1, s.size() - eraseIndex); + } + labelMap->push_back(s); + } + infile.close(); + return APP_ERR_OK; +} + +APP_ERROR BertNerBase::Init(const InitParam &initParam) { + deviceId_ = initParam.deviceId; + 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; + } + dvppWrapper_ = std::make_shared(); + ret = dvppWrapper_->Init(); + if (ret != APP_ERR_OK) { + LogError << "DvppWrapper init failed, ret=" << ret << "."; + return ret; + } + model_ = std::make_shared(); + ret = model_->Init(initParam.modelPath, modelDesc_); + if (ret != APP_ERR_OK) { + LogError << "ModelInferenceProcessor init failed, ret=" << ret << "."; + return ret; + } + classNum_ = initParam.classNum; + // load labels from file + ret = LoadLabels(initParam.labelPath, &labelMap_); + if (ret != APP_ERR_OK) { + LogError << "Failed to load labels, ret=" << ret << "."; + return ret; + } + return APP_ERR_OK; +} + +APP_ERROR BertNerBase::DeInit() { + dvppWrapper_->DeInit(); + model_->DeInit(); + MxBase::DeviceManager::GetInstance()->DestroyDevices(); + return APP_ERR_OK; +} + +APP_ERROR BertNerBase::ReadTensorFromFile(const std::string &file, uint32_t *data, uint32_t size) { + if (data == NULL || size < MAX_LENGTH) { + LogError << "input data is invalid."; + return APP_ERR_COMM_INVALID_POINTER; + } + std::ifstream infile; + // open label file + infile.open(file, std::ios_base::in | std::ios_base::binary); + // check label file validity + if (infile.fail()) { + LogError << "Failed to open label file: " << file << "."; + return APP_ERR_COMM_OPEN_FAIL; + } + infile.read(reinterpret_cast(data), sizeof(uint32_t) * MAX_LENGTH); + infile.close(); + return APP_ERR_OK; +} + +APP_ERROR BertNerBase::ReadInputTensor(const std::string &fileName, uint32_t index, + std::vector *inputs) { + uint32_t data[MAX_LENGTH] = {0}; + APP_ERROR ret = ReadTensorFromFile(fileName, data, MAX_LENGTH); + if (ret != APP_ERR_OK) { + LogError << "ReadTensorFromFile failed."; + return ret; + } + + const uint32_t dataSize = modelDesc_.inputTensors[index].tensorSize; + MxBase::MemoryData memoryDataDst(dataSize, MxBase::MemoryData::MEMORY_DEVICE, deviceId_); + MxBase::MemoryData memoryDataSrc(reinterpret_cast(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; + } + + std::vector shape = {1, MAX_LENGTH}; + inputs->push_back(MxBase::TensorBase(memoryDataDst, false, shape, MxBase::TENSOR_DTYPE_UINT32)); + return APP_ERR_OK; +} + +APP_ERROR BertNerBase::Inference(const std::vector &inputs, + std::vector *outputs) { + auto dtypes = model_->GetOutputDataType(); + for (size_t i = 0; i < modelDesc_.outputTensors.size(); ++i) { + std::vector shape = {}; + for (size_t j = 0; j < modelDesc_.outputTensors[i].tensorDims.size(); ++j) { + shape.push_back((uint32_t)modelDesc_.outputTensors[i].tensorDims[j]); + } + MxBase::TensorBase tensor(shape, dtypes[i], MxBase::MemoryData::MemoryType::MEMORY_DEVICE, 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 = model_->ModelInference(inputs, *outputs, dynamicInfo); + auto endTime = std::chrono::high_resolution_clock::now(); + double costMs = std::chrono::duration(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 BertNerBase::PostProcess(std::vector *outputs, std::vector *argmax) { + MxBase::TensorBase &tensor = outputs->at(0); + APP_ERROR ret = tensor.ToHost(); + if (ret != APP_ERR_OK) { + LogError << GetError(ret) << "Tensor deploy to host failed."; + return ret; + } + // check tensor is available + auto outputShape = tensor.GetShape(); + uint32_t length = outputShape[0]; + uint32_t classNum = outputShape[1]; + LogInfo << "output shape is: " << outputShape[0] << " "<< outputShape[1] << std::endl; + + void* data = tensor.GetBuffer(); + for (uint32_t i = 0; i < length; i++) { + std::vector result = {}; + for (uint32_t j = 0; j < classNum; j++) { + float value = *(reinterpret_cast(data) + i * classNum + j); + result.push_back(value); + } + // argmax and get the class id + std::vector::iterator maxElement = std::max_element(std::begin(result), std::end(result)); + uint32_t argmaxIndex = maxElement - std::begin(result); + argmax->push_back(argmaxIndex); + } + + return APP_ERR_OK; +} + +APP_ERROR BertNerBase::CountPredictResult(const std::string &labelFile, const std::vector &argmax) { + uint32_t data[MAX_LENGTH] = {0}; + APP_ERROR ret = ReadTensorFromFile(labelFile, data, MAX_LENGTH); + if (ret != APP_ERR_OK) { + LogError << "ReadTensorFromFile failed."; + return ret; + } + uint32_t target[CLASS_NUM][MAX_LENGTH] = {0}; + uint32_t pred[CLASS_NUM][MAX_LENGTH] = {0}; + for (uint32_t i = 0; i < MAX_LENGTH; i++) { + if (data[i] > 0) { + target[data[i]][i] = 1; + } + if (argmax[i] > 0) { + pred[argmax[i]][i] = 1; + } + } + for (uint32_t i = 0; i < CLASS_NUM; i++) { + for (uint32_t j = 0; j < MAX_LENGTH; j++) { + // count True Positive and False Positive + if (pred[i][j] == 1) { + if (target[i][j] == 1) { + g_TP += 1; + } else { + g_FP += 1; + } + } + // count False Negative + if (target[i][j] == 1 && pred[i][j] != 1) { + g_FN += 1; + } + } + } + LogInfo << "TP: " << g_TP << ", FP: " << g_FP << ", FN: " << g_FN; + return APP_ERR_OK; +} + +void BertNerBase::GetClunerLabel(const std::vector &argmax, std::multimap> *clunerMap) { + bool findCluner = false; + uint32_t start = 0; + std::string clunerName; + for (uint32_t i = 0; i < argmax.size(); i++) { + if (argmax[i] > 0) { + if (!findCluner) { + start = i; + clunerName = labelMap_[(argmax[i] - 1) / EACH_LABEL_LENGTH]; + findCluner = true; + } else { + if (labelMap_[(argmax[i] - 1) / EACH_LABEL_LENGTH] != clunerName) { + std::vector position = {start - 1, i - 2}; + clunerMap->insert(std::pair>(clunerName, position)); + start = i; + clunerName = labelMap_[(argmax[i] - 1) / EACH_LABEL_LENGTH]; + } + } + } else { + if (findCluner) { + std::vector position = {start - 1, i - 2}; + clunerMap->insert(std::pair>(clunerName, position)); + findCluner = false; + } + } + } +} + +APP_ERROR BertNerBase::WriteResult(const std::string &fileName, const std::vector &argmax) { + std::string resultPathName = "result"; + // create result directory when it does not exit + if (access(resultPathName.c_str(), 0) != 0) { + int ret = mkdir(resultPathName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR); + if (ret != 0) { + LogError << "Failed to create result directory: " << resultPathName << ", ret = " << ret; + return APP_ERR_COMM_OPEN_FAIL; + } + } + // create result file under result directory + resultPathName = resultPathName + "/result.txt"; + std::ofstream tfile(resultPathName, std::ofstream::app); + if (tfile.fail()) { + LogError << "Failed to open result file: " << resultPathName; + return APP_ERR_COMM_OPEN_FAIL; + } + // write inference result into file + LogInfo << "=============================================================="; + LogInfo << "infer result of " << fileName << " is: "; + tfile << "file name is: " << fileName << std::endl; + std::multimap> clunerMap; + GetClunerLabel(argmax, &clunerMap); + for (auto &item : clunerMap) { + LogInfo << item.first << ": " << item.second[0] << ", " << item.second[1]; + tfile << item.first << ": " << item.second[0] << ", " << item.second[1] << std::endl; + } + LogInfo << "=============================================================="; + tfile.close(); + return APP_ERR_OK; +} + +APP_ERROR BertNerBase::Process(const std::string &inferPath, const std::string &fileName, bool eval) { + std::vector inputs = {}; + std::string inputIdsFile = inferPath + "00_data/" + fileName; + APP_ERROR ret = ReadInputTensor(inputIdsFile, INPUT_IDS, &inputs); + if (ret != APP_ERR_OK) { + LogError << "Read input ids failed, ret=" << ret << "."; + return ret; + } + std::string inputMaskFile = inferPath + "01_data/" + fileName; + ret = ReadInputTensor(inputMaskFile, INPUT_MASK, &inputs); + if (ret != APP_ERR_OK) { + LogError << "Read input mask file failed, ret=" << ret << "."; + return ret; + } + std::string tokenTypeIdFile = inferPath + "02_data/" + fileName; + ret = ReadInputTensor(tokenTypeIdFile, TOKEN_TYPE, &inputs); + if (ret != APP_ERR_OK) { + LogError << "Read token typeId file failed, ret=" << ret << "."; + return ret; + } + + std::vector outputs = {}; + ret = Inference(inputs, &outputs); + if (ret != APP_ERR_OK) { + LogError << "Inference failed, ret=" << ret << "."; + return ret; + } + + std::vector argmax; + ret = PostProcess(&outputs, &argmax); + if (ret != APP_ERR_OK) { + LogError << "PostProcess failed, ret=" << ret << "."; + return ret; + } + + ret = WriteResult(fileName, argmax); + if (ret != APP_ERR_OK) { + LogError << "save result failed, ret=" << ret << "."; + return ret; + } + + if (eval) { + std::string labelFile = inferPath + "03_data/" + fileName; + ret = CountPredictResult(labelFile, argmax); + if (ret != APP_ERR_OK) { + LogError << "CalcF1Score read label failed, ret=" << ret << "."; + return ret; + } + } + + return APP_ERR_OK; +} diff --git a/nlp/language_model/bert/MindSpore/infer/mxbase/src/BertNerBase.h b/nlp/language_model/bert/MindSpore/infer/mxbase/src/BertNerBase.h new file mode 100644 index 0000000000000000000000000000000000000000..8d481d6931854f0a3981e8cd812478a6ae80ab1f --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/mxbase/src/BertNerBase.h @@ -0,0 +1,72 @@ +/** + * 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_BERTBASE_H +#define MXBASE_BERTBASE_H + +#include +#include +#include +#include +#include +#include +#include "MxBase/DvppWrapper/DvppWrapper.h" +#include "MxBase/ModelInfer/ModelInferenceProcessor.h" +#include "MxBase/Tensor/TensorContext/TensorContext.h" + +extern std::vector g_inferCost; +extern uint32_t g_TP; +extern uint32_t g_FP; +extern uint32_t g_FN; + +struct InitParam { + uint32_t deviceId; + std::string labelPath; + std::string modelPath; + uint32_t classNum; +}; + +enum DataIndex { + INPUT_IDS = 0, + INPUT_MASK = 1, + TOKEN_TYPE = 2, +}; + +class BertNerBase { + public: + APP_ERROR Init(const InitParam &initParam); + APP_ERROR DeInit(); + APP_ERROR Inference(const std::vector &inputs, std::vector *outputs); + APP_ERROR Process(const std::string &inferPath, const std::string &fileName, bool eval); + APP_ERROR PostProcess(std::vector *outputs, std::vector *argmax); + protected: + APP_ERROR ReadTensorFromFile(const std::string &file, uint32_t *data, uint32_t size); + APP_ERROR ReadInputTensor(const std::string &fileName, uint32_t index, std::vector *inputs); + APP_ERROR LoadLabels(const std::string &labelPath, std::vector *labelMap); + APP_ERROR ReadInputTensor(const std::string &fileName, const std::vector &argmax); + APP_ERROR WriteResult(const std::string &fileName, const std::vector &argmax); + APP_ERROR CountPredictResult(const std::string &labelFile, const std::vector &argmax); + void GetClunerLabel(const std::vector &argmax, + std::multimap> *clunerMap); + private: + std::shared_ptr dvppWrapper_; + std::shared_ptr model_; + MxBase::ModelDesc modelDesc_ = {}; + std::vector labelMap_ = {}; + uint32_t deviceId_ = 0; + uint32_t classNum_ = 0; +}; +#endif diff --git a/nlp/language_model/bert/MindSpore/infer/mxbase/src/main.cpp b/nlp/language_model/bert/MindSpore/infer/mxbase/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dd2b4d833efdbd812cf2cd0511d910a48f4da777 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/mxbase/src/main.cpp @@ -0,0 +1,109 @@ +/** + * 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 +#include +#include +#include +#include +#include "BertNerBase.h" +#include "MxBase/Log/Log.h" + +std::vector g_inferCost; +uint32_t g_TP = 0; +uint32_t g_FP = 0; +uint32_t g_FN = 0; + +void InitBertParam(InitParam* initParam) { + initParam->deviceId = 0; + initParam->labelPath = "../data/config/infer_label.txt"; + initParam->modelPath = "../data/model/cluner.om"; + initParam->classNum = 41; +} + +APP_ERROR ReadFilesFromPath(const std::string &path, std::vector *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) { + // d_type == 8 is file + if (ptr->d_type == 8) { + files->push_back(ptr->d_name); + } + } + closedir(dir); + // sort ascending order + sort(files->begin(), files->end()); + return APP_ERR_OK; +} + +int main(int argc, char* argv[]) { + if (argc <= 1) { + LogWarn << "Please input image path, such as './bert /input/data 0'."; + return APP_ERR_OK; + } + + InitParam initParam; + InitBertParam(&initParam); + auto bertBase = std::make_shared(); + APP_ERROR ret = bertBase->Init(initParam); + if (ret != APP_ERR_OK) { + LogError << "Bertbase init failed, ret=" << ret << "."; + return ret; + } + + std::string inferPath = argv[1]; + std::vector files; + ret = ReadFilesFromPath(inferPath + "00_data", &files); + if (ret != APP_ERR_OK) { + LogError << "Read files from path failed, ret=" << ret << "."; + return ret; + } + // do eval and calc the f1 score + bool eval = atoi(argv[2]); + for (uint32_t i = 0; i < files.size(); i++) { + LogInfo << "read file name: " << files[i]; + ret = bertBase->Process(inferPath, files[i], eval); + if (ret != APP_ERR_OK) { + LogError << "Bertbase process failed, ret=" << ret << "."; + bertBase->DeInit(); + return ret; + } + } + + if (eval) { + LogInfo << "=============================================================="; + float precision = g_TP * 1.0 / (g_TP + g_FP); + LogInfo << "Precision: " << precision; + float recall = g_TP * 1.0 / (g_TP + g_FN); + LogInfo << "recall: " << recall; + LogInfo << "F1 Score: " << 2 * precision * recall / (precision + recall); + LogInfo << "=============================================================="; + } + bertBase->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."; + return APP_ERR_OK; +} diff --git a/nlp/language_model/bert/MindSpore/infer/sdk/main.py b/nlp/language_model/bert/MindSpore/infer/sdk/main.py new file mode 100644 index 0000000000000000000000000000000000000000..ce5130a8a44c03c42b4cf5377020076bbee8001c --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/sdk/main.py @@ -0,0 +1,291 @@ +# 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. +# ============================================================================ + +""" +sample script of CLUE infer using SDK run in docker +""" + +import argparse +import glob +import os +import time + +import MxpiDataType_pb2 as MxpiDataType +import numpy as np +from StreamManagerApi import StreamManagerApi, MxDataInput, InProtobufVector, \ + MxProtobufIn, StringVector + +TP = 0 +FP = 0 +FN = 0 + + +def parse_args(): + """set and check parameters.""" + parser = argparse.ArgumentParser(description="bert process") + parser.add_argument("--pipeline", type=str, default="", help="SDK infer pipeline") + parser.add_argument("--data_dir", type=str, default="", + help="Dataset contain input_ids, input_mask, segment_ids, label_ids") + parser.add_argument("--label_file", type=str, default="", help="label ids to name") + parser.add_argument("--output_file", type=str, default="", help="save result to file") + parser.add_argument("--f1_method", type=str, default="BF1", help="calc F1 use the number label,(BF1, MF1)") + parser.add_argument("--do_eval", type=bool, default=False, help="eval the accuracy of model ") + args_opt = parser.parse_args() + return args_opt + + +def send_source_data(appsrc_id, filename, 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 = np.fromfile(filename, dtype=np.int32) + tensor = np.expand_dims(tensor, 0) + tensor_package_list = MxpiDataType.MxpiTensorPackageList() + tensor_package = tensor_package_list.tensorPackageVec.add() + array_bytes = tensor.tobytes() + data_input = MxDataInput() + data_input.data = array_bytes + 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 = data_input.data + 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 + + +def send_appsrc_data(args, file_name, stream_name, stream_manager): + """ + send three stream to infer model, include input ids, input mask and token type_id. + + Returns: + bool: send data success or not + """ + input_ids = os.path.realpath(os.path.join(args.data_dir, "00_data", file_name)) + if not send_source_data(0, input_ids, stream_name, stream_manager): + return False + input_mask = os.path.realpath(os.path.join(args.data_dir, "01_data", file_name)) + if not send_source_data(1, input_mask, stream_name, stream_manager): + return False + token_type_id = os.path.realpath(os.path.join(args.data_dir, "02_data", file_name)) + if not send_source_data(2, token_type_id, stream_name, stream_manager): + return False + return True + + +def read_label_file(label_file): + """ + Args: + label_file: + "address" + "book" + ... + Returns: + label list + """ + label_list = [line.strip() for line in open(label_file).readlines()] + return label_list + + +def process_infer_to_cluner(args, logit_id, each_label_length=4): + """ + find label and position from the logit_id tensor. + + Args: + args: param of config. + logit_id: shape is [128], example: [0..32.34..0]. + each_label_length: each label have 4 prefix, ["S_", "B_", "M_", "E_"]. + + Returns: + dict of visualization result, as 'position': [9, 10] + """ + label_list = read_label_file(os.path.realpath(args.label_file)) + find_cluner = False + result_list = [] + for i, value in enumerate(logit_id): + if value > 0: + if not find_cluner: + start = i + cluner_name = label_list[(value - 1) // each_label_length] + find_cluner = True + else: + if label_list[(value - 1) // each_label_length] != cluner_name: + item = {} + item[cluner_name] = [start - 1, i - 2] + result_list.append(item) + start = i + cluner_name = label_list[(value - 1) // each_label_length] + else: + if find_cluner: + item = {} + item[cluner_name] = [start - 1, i - 2] + result_list.append(item) + find_cluner = False + + return result_list + + +def count_pred_result(args, file_name, logit_id, class_num=41, max_seq_length=128): + """ + support two method to calc f1 sore, if dataset has two class, suggest using BF1, + else more than two class, suggest using MF1. + Args: + args: param of config. + file_name: label file name. + logit_id: output tensor of infer. + class_num: cluner data default is 41. + max_seq_length: sentence input length default is 128. + + global: + TP: pred == target + FP: in pred but not in target + FN: in target but not in pred + """ + label_file = os.path.realpath(os.path.join(args.data_dir, "03_data", file_name)) + label_ids = np.fromfile(label_file, np.int32) + label_ids.reshape(max_seq_length, -1) + global TP, FP, FN + if args.f1_method == "BF1": + pos_eva = np.isin(logit_id, [i for i in range(1, class_num)]) + pos_label = np.isin(label_ids, [i for i in range(1, class_num)]) + TP += np.sum(pos_eva & pos_label) + FP += np.sum(pos_eva & (~pos_label)) + FN += np.sum((~pos_eva) & pos_label) + else: + target = np.zeros((len(label_ids), class_num), dtype=np.int32) + pred = np.zeros((len(logit_id), class_num), dtype=np.int32) + for i, label in enumerate(label_ids): + if label > 0: + target[i][label] = 1 + for i, label in enumerate(logit_id): + if label > 0: + pred[i][label] = 1 + target = target.reshape(class_num, -1) + pred = pred.reshape(class_num, -1) + for i in range(0, class_num): + for j in range(0, max_seq_length): + if pred[i][j] == 1: + if target[i][j] == 1: + TP += 1 + else: + FP += 1 + if target[i][j] == 1 and pred[i][j] != 1: + FN += 1 + + +def post_process(args, file_name, infer_result, max_seq_length=128): + """ + process the result of infer tensor to Visualization results. + Args: + args: param of config. + file_name: label file name. + infer_result: get logit from infer result + max_seq_length: sentence input length default is 128. + """ + # print the infer result + print("==============================================================") + result = MxpiDataType.MxpiTensorPackageList() + result.ParseFromString(infer_result[0].messageBuf) + res = np.frombuffer(result.tensorPackageVec[0].tensorVec[0].dataStr, dtype='&2 -e "\033[1;31m[WARN ][MxStream] $1\033[1;37m" ; } + +#export MX_SDK_HOME=/home/work/mxVision +export LD_LIBRARY_PATH=${MX_SDK_HOME}/lib:${MX_SDK_HOME}/opensource/lib:${MX_SDK_HOME}/opensource/lib64:/usr/local/Ascend/ascend-toolkit/latest/acllib/lib64:${LD_LIBRARY_PATH} +export GST_PLUGIN_SCANNER=${MX_SDK_HOME}/opensource/libexec/gstreamer-1.0/gst-plugin-scanner +export GST_PLUGIN_PATH=${MX_SDK_HOME}/opensource/lib/gstreamer-1.0:${MX_SDK_HOME}/lib/plugins + +#to set PYTHONPATH, import the StreamManagerApi.py +export PYTHONPATH=$PYTHONPATH:${MX_SDK_HOME}/python + +python3.7 main.py --pipeline=../data/config/bert_base.pipeline --data_dir=../data/input --label_file=../data/config/infer_label.txt --output_file=./output.txt --do_eval=True --f1_method=MF1 +exit 0 diff --git a/nlp/language_model/bert/MindSpore/infer/utils/data_processor_seq.py b/nlp/language_model/bert/MindSpore/infer/utils/data_processor_seq.py new file mode 100644 index 0000000000000000000000000000000000000000..b8235e2e146f2d6a12af95b2a329bf0c5814bc9c --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer/utils/data_processor_seq.py @@ -0,0 +1,215 @@ +# 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. +# ============================================================================ + +""" +process the cluener json +""" +import argparse +import collections +import json +import os + +import numpy as np +import tokenization + + +def parse_args(): + """set parameters.""" + parser = argparse.ArgumentParser(description="cluner data preprocess") + parser.add_argument("--infer_json", type=str, default="valid.json", help="the format of infer file is json.") + parser.add_argument("--label_file", type=str, default="label.txt", help="the label of infer json.") + parser.add_argument("--vocab_file", type=str, default="vocab.txt", help="the vocab file of chinese dataset.") + parser.add_argument("--max_seq_len", type=int, default=128, help="sentence length, default is 128.") + parser.add_argument("--output_path", type=str, default="./data", help="the path of convert dataset.") + + args_opt = parser.parse_args() + return args_opt + + +def convert_labels_to_index(label_file): + """ + Convert label name to label_list which indices for NER task. + Args: + label_file: parameters content label path + + Returns: + label2id: list id of label and each label extend four + """ + with open(label_file) as f: + label_list = f.readlines() + + label2id = collections.OrderedDict() + label2id["O"] = 0 + prefix = ["S_", "B_", "M_", "E_"] + index = 0 + for label in label_list: + for pre in prefix: + index += 1 + sub_label = pre + label.split('\n')[0] + label2id[sub_label] = index + return label2id + + +def process_one_example(tokenizer, label2id, text, label, max_seq_len=128): + """ + convert the text and label to tensor, result: + text: [101 3946 3419 4638 4413 7339 5303 754 ...] + mask: [1 1 1 1 1 1 1 1 ...] + segment_ids: [0 0 0 0 0 0 0 0 0 0 0 0 ...] + label: [0 26 28 0 0 0 0 ...] + """ + text_list = list(text) + label_list = list(label) + tokens = [] + labels = [] + for i, word in enumerate(text_list): + token = tokenizer.tokenize(word) + tokens.extend(token) + label_1 = label_list[i] + for m in range(len(token)): + if m == 0: + labels.append(label_1) + else: + print("some unknown token...") + labels.append(labels[0]) + if len(tokens) >= max_seq_len - 1: + tokens = tokens[0:(max_seq_len - 2)] + labels = labels[0:(max_seq_len - 2)] + ntokens = [] + segment_ids = [] + label_ids = [] + # set the begin symbol of sentence + ntokens.append("[CLS]") + segment_ids.append(0) + label_ids.append(0) + for i, token in enumerate(tokens): + ntokens.append(token) + segment_ids.append(0) + label_ids.append(label2id[labels[i]]) + # set the end symbol of sentence + ntokens.append("[SEP]") + segment_ids.append(0) + label_ids.append(0) + input_ids = tokenizer.convert_tokens_to_ids(ntokens) + input_mask = [1] * len(input_ids) + while len(input_ids) < max_seq_len: + input_ids.append(0) + input_mask.append(0) + segment_ids.append(0) + label_ids.append(0) + ntokens.append("**NULL**") + + feature = (input_ids, input_mask, segment_ids, label_ids) + return feature + + +def get_all_path(output_path): + """ + Args: + output_path: save path of convert dataset + Returns: + the path of ids, mask, token, label + """ + ids_path = os.path.join(output_path, "00_data") + mask_path = os.path.join(output_path, "01_data") + token_path = os.path.join(output_path, "02_data") + label_path = os.path.join(output_path, "03_data") + for path in [ids_path, mask_path, token_path, label_path]: + os.makedirs(path, 0o755, exist_ok=True) + + return ids_path, mask_path, token_path, label_path + + +def get_label_from_json(line_json): + """ + Args: + line_json: the line content from json file + Returns: + labels: convert label to label tensor + """ + text_len = len(line_json["text"]) + labels = ["O"] * text_len + for k, v in line_json["label"].items(): + for _, vv in v.items(): + for span in vv: + s = span[0] + e = span[1] + 1 + if e - s == 1: + labels[s] = "S_" + k + else: + labels[s] = "B_" + k + for i in range(s + 1, e - 1): + labels[i] = "M_" + k + labels[e - 1] = "E_" + k + return labels + + +def prepare_cluener_data(tokenizer, max_seq_len, label2id, path, out_path): + """ + Args: + tokenizer: token class convert word to id according vocab file + max_seq_len: the length of sentence + label2id: label list of word to id + path: dataset path + out_path: output path of convert result + """ + output_ids, output_mask, output_token, output_label = get_all_path(out_path) + example_count = 0 + for line in open(path): + if not line.strip(): + continue + line_json = json.loads(line.strip()) + labels = get_label_from_json(line_json) + feature = process_one_example(tokenizer, label2id, list(line_json["text"]), labels, + max_seq_len=max_seq_len) + file_name = "cluener_bs" + "_" + str(example_count) + ".bin" + ids_file_path = os.path.join(output_ids, file_name) + np.array(feature[0], dtype=np.int32).tofile(ids_file_path) + + mask_file_path = os.path.join(output_mask, file_name) + np.array(feature[1], dtype=np.int32).tofile(mask_file_path) + + token_file_path = os.path.join(output_token, file_name) + np.array(feature[2], dtype=np.int32).tofile(token_file_path) + + label_file_path = os.path.join(output_label, file_name) + np.array(feature[3], dtype=np.int32).tofile(label_file_path) + print("*** Example ***") + print(line_json["text"]) + print(line_json["label"]) + print("input_ids: %s" % " ".join([str(x) for x in feature[0]])) + print("input_mask: %s" % " ".join([str(x) for x in feature[1]])) + print("segment_ids: %s" % " ".join([str(x) for x in feature[2]])) + print("label: %s " % " ".join([str(x) for x in feature[3]])) + example_count += 1 + if example_count % 3000 == 0: + print(example_count) + print("total example:", example_count) + + +def run(): + """ + convert infer json to bin, each sentence is one file bin + """ + args = parse_args() + tokenizer = tokenization.FullTokenizer(vocab_file=args.vocab_file) + label2id = convert_labels_to_index(args.label_file) + + prepare_cluener_data(tokenizer, args.max_seq_len, label2id, path=args.infer_json, + out_path=args.output_path) + + +if __name__ == "__main__": + run() diff --git a/nlp/language_model/bert/MindSpore/infer_squad/convert/convert.sh b/nlp/language_model/bert/MindSpore/infer_squad/convert/convert.sh new file mode 100644 index 0000000000000000000000000000000000000000..9d2afbfe71305b727d0f84daba292a919da30b69 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/convert/convert.sh @@ -0,0 +1,26 @@ +#!/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. +# ============================================================================ + +air_path=$1 +om_path=$2 + +echo "Input AIR file path: ${air_path}" +echo "Output OM file path: ${om_path}" + +atc --framework=1 --model="${air_path}" \ + --output="${om_path}" \ + --soc_version=Ascend310 \ + --op_select_implmode="high_precision" \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/infer_squad/data/config/bert_base.pipeline b/nlp/language_model/bert/MindSpore/infer_squad/data/config/bert_base.pipeline new file mode 100644 index 0000000000000000000000000000000000000000..a5e45e4f45306d19ec96c7717e4bd30fcb50ea74 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/data/config/bert_base.pipeline @@ -0,0 +1,46 @@ +{ + "im_bertbase": { + "stream_config": { + "deviceId": "0" + }, + "appsrc0": { + "props": { + "blocksize": "409600" + }, + "factory": "appsrc", + "next": "mxpi_tensorinfer0:0" + }, + "appsrc1": { + "props": { + "blocksize": "409600" + }, + "factory": "appsrc", + "next": "mxpi_tensorinfer0:1" + }, + "appsrc2": { + "props": { + "blocksize": "409600" + }, + "factory": "appsrc", + "next": "mxpi_tensorinfer0:2" + }, + "mxpi_tensorinfer0": { + "props": { + "dataSource": "appsrc0,appsrc1,appsrc2", + "modelPath": "../data/model/bert_base.om" + }, + "factory": "mxpi_tensorinfer", + "next": "mxpi_dataserialize0" + }, + "mxpi_dataserialize0": { + "props": { + "outputDataKeys": "mxpi_tensorinfer0" + }, + "factory": "mxpi_dataserialize", + "next": "appsink0" + }, + "appsink0": { + "factory": "appsink" + } + } +} \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/infer_squad/docker_start_infer.sh b/nlp/language_model/bert/MindSpore/infer_squad/docker_start_infer.sh new file mode 100644 index 0000000000000000000000000000000000000000..ecd2ee12fcb0174a573920d475d7ded0e42851e9 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/docker_start_infer.sh @@ -0,0 +1,37 @@ +#!/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 +model_dir=$2 + +if [ -z "${docker_image}" ]; then + echo "please input docker_image" + exit 1 +fi + +if [ ! -d "${model_dir}" ]; then + echo "please input model_dir" + exit 1 +fi + +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 ${model_dir}:${model_dir} \ + ${docker_image} \ + /bin/bash \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/infer_squad/mxbase/CMakeLists.txt b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..08f24049c24a718733f5a91d98619a0de855c4a7 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.10.0) +project(bert) + +set(TARGET bert) + +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(DEFINED ENV{MXSDK_OPENSOURCE_DIR}) + set(OPENSOURCE_DIR $ENV{MXSDK_OPENSOURCE_DIR}) +else() + set(OPENSOURCE_DIR ${MXBASE_ROOT_DIR}/opensource) +endif() + +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/BertBase.cpp) +target_link_libraries(${TARGET} glog cpprest mxbase opencv_world stdc++fs) + +install(TARGETS ${TARGET} RUNTIME DESTINATION ${PROJECT_SOURCE_DIR}/) diff --git a/nlp/language_model/bert/MindSpore/infer_squad/mxbase/build.sh b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..e23d258b6d1f5763224c8c57af4f0102d63cceff --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/build.sh @@ -0,0 +1,55 @@ +#!/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. + +path_cur=$(dirname $0) + +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_bert() +{ + cd $path_cur + rm -rf build + mkdir -p build + cd build + cmake .. + make + ret=$? + if [ ${ret} -ne 0 ]; then + echo "Failed to build bert." + exit ${ret} + fi + make install +} + +check_env +build_bert \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/infer_squad/mxbase/postprocess.py b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/postprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..e69c20366490e5980f22ba31993b30f5860b74e3 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/postprocess.py @@ -0,0 +1,585 @@ +# 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. +# ============================================================================ + +'''postprocess''' +import argparse +import collections +import glob +import json +import math +import os +import pickle +import re +import string +import sys +import unicodedata + +import six +import numpy as np + + +def parse_args(): + """set and check parameters.""" + parser = argparse.ArgumentParser(description="bert process") + parser.add_argument("--data_dir", type=str, default="", + help="Dataset contain input_ids, input_mask, segment_ids, label_ids") + parser.add_argument("--eval_json_path", type=str, default="", help="label ids to name") + args_opt = parser.parse_args() + return args_opt + + +def f1_score(prediction, ground_truth): + """calculate f1 score""" + prediction_tokens = normalize_answer(prediction).split() + ground_truth_tokens = normalize_answer(ground_truth).split() + common = collections.Counter(prediction_tokens) & collections.Counter(ground_truth_tokens) + num_same = sum(common.values()) + if num_same == 0: + return 0 + precision = 1.0 * num_same / len(prediction_tokens) + recall = 1.0 * num_same / len(ground_truth_tokens) + f1 = (2 * precision * recall) / (precision + recall) + return f1 + + +def post_process(dataset_file, all_predictions, output_metrics="output.json"): + """ + process the result of infer tensor to Visualization results. + Args: + args: param of config. + file_name: label file name. + infer_result: get logit from infer result + max_seq_length: sentence input length default is 128. + """ + # print the infer result + with open(dataset_file) as ds: + print('==========') + dataset_json = json.load(ds) + dataset = dataset_json['data'] + print(dataset) + print('success') + re_json = evaluate(dataset, all_predictions) + print(json.dumps(re_json)) + with open(output_metrics, 'w') as wr: + wr.write(json.dumps(re_json)) + + +def normalize_answer(s): + """Lower text and remove punctuation, articles and extra whitespace.""" + def remove_articles(text): + return re.sub(r'\b(a|an|the)\b', ' ', text) + + def white_space_fix(text): + return ' '.join(text.split()) + + def remove_punc(text): + exclude = set(string.punctuation) + return ''.join(ch for ch in text if ch not in exclude) + + def lower(text): + return text.lower() + + return white_space_fix(remove_articles(remove_punc(lower(s)))) + + +def exact_match_score(prediction, ground_truth): + return normalize_answer(prediction) == normalize_answer(ground_truth) + + +def metric_max_over_ground_truths(metric_fn, prediction, ground_truths): + scores_for_ground_truths = [] + for ground_truth in ground_truths: + score = metric_fn(prediction, ground_truth) + scores_for_ground_truths.append(score) + return max(scores_for_ground_truths) + + +def evaluate(dataset, predictions): + """do evaluation""" + f1 = exact_match = total = 0 + for article in dataset: + for paragraph in article['paragraphs']: + for qa in paragraph['qas']: + total += 1 + if qa['id'] not in predictions: + message = 'Unanswered question ' + qa['id'] + \ + ' will receive score 0.' + print(message, file=sys.stderr) + continue + ground_truths = list(map(lambda x: x['text'], qa['answers'])) + if not ground_truths: + continue + prediction = predictions[qa['id']] + exact_match += metric_max_over_ground_truths( + exact_match_score, prediction, ground_truths) + f1 += metric_max_over_ground_truths( + f1_score, prediction, ground_truths) + + exact_match = 100.0 * exact_match / total + f1 = 100.0 * f1 / total + print(exact_match) + print(f1) + return {'exact_match': exact_match, 'f1': f1} + + +def get_infer_logits(args, file_name): + """ + get the result of model output. + Args: + infer_result: get logit from infer result + max_seq_length: sentence input length default is 384. + """ + infer_logits_path = os.path.realpath(os.path.join(args.data_dir, "10_data", file_name)) + data_0 = [] + data_1 = [] + with open(infer_logits_path, "r") as f: + for line in f: + data_0.append(float(line.strip('\n'))) + + for i in range(384): + data_1.append([data_0[i], data_0[384 + i]]) + res = np.array(data_1) + #print("output tensor is: ", res.shape) + start_logits = [float(x) for x in res[:, 0].flat] + end_logits = [float(x) for x in res[:, 1].flat] + + return start_logits, end_logits + + +def _get_best_indexes(logits, n_best_size): + """Get the n-best logits from a list.""" + index_and_score = sorted(enumerate(logits), key=lambda x: x[1], reverse=True) + + best_indexes = [] + for (i, score) in enumerate(index_and_score): + if i >= n_best_size: + break + best_indexes.append(score[0]) + return best_indexes + + +def get_prelim_predictions(args, file_name, start_logits, end_logits, n_best_size, max_answer_length): + """ + process the logits of infer tensor to Visualization results based on n_best_size and max_answer_length + Args: + args: param of config. + file_name: used feature file name. + start_logits: get start logit from infer result + end_logits: get end logit from infer result + n_best_size: best of n answer (start point and end point). + max_answer_length: maximum answer length + """ + feature_tokens_file = os.path.realpath(os.path.join(args.data_dir, "04_data", file_name)) + feature_token_to_orig_map_file = os.path.realpath(os.path.join(args.data_dir, "05_data", file_name)) + feature_token_is_max_context_file = os.path.realpath(os.path.join(args.data_dir, "06_data", file_name)) + example_index_file = os.path.realpath(os.path.join(args.data_dir, "09_data", file_name)) + tokens = pickle.load(open(feature_tokens_file, 'rb+')) + token_to_orig_map = pickle.load(open(feature_token_to_orig_map_file, 'rb+')) + token_is_max_context = pickle.load(open(feature_token_is_max_context_file, 'rb+')) + example_index = np.fromfile(example_index_file, np.int32)[0] + _PrelimPrediction = collections.namedtuple( + "PrelimPrediction", ["start_index", "end_index", "start_logit", "end_logit"]) + prelim_predictions = [] + # keep track of the minimum score of null start+end of position 0 + start_indexes = _get_best_indexes(start_logits, n_best_size) + end_indexes = _get_best_indexes(end_logits, n_best_size) + # if we could have irrelevant answers, get the min score of irrelevant + for start_index in start_indexes: + for end_index in end_indexes: + # We could hypothetically create invalid predictions, e.g., predict + # that the start of the span is in the question. We throw out all + # invalid predictions. + if start_index >= len(tokens): + continue + if end_index >= len(tokens): + continue + if start_index not in token_to_orig_map: + continue + if end_index not in token_to_orig_map: + continue + if not token_is_max_context.get(start_index, False): + continue + if end_index < start_index: + continue + length = end_index - start_index + 1 + if length > max_answer_length: + continue + prelim_predictions.append( + _PrelimPrediction( + start_index=start_index, + end_index=end_index, + start_logit=start_logits[start_index], + end_logit=end_logits[end_index])) + + prelim_predictions = sorted( + prelim_predictions, + key=lambda x: (x.start_logit + x.end_logit), + reverse=True) + return prelim_predictions, (tokens, token_to_orig_map, token_is_max_context, example_index) + + +def get_final_text(pred_text, orig_text, do_lower_case): + """Project the tokenized prediction back to the original text.""" + def _strip_spaces(text): + ns_chars = [] + ns_to_s_map = collections.OrderedDict() + for (i, c) in enumerate(text): + if c == " ": + continue + ns_to_s_map[len(ns_chars)] = i + ns_chars.append(c) + ns_text = "".join(ns_chars) + return (ns_text, ns_to_s_map) + + tokenizer = BasicTokenizer(do_lower_case=do_lower_case) + tok_text = " ".join(tokenizer.tokenize(orig_text)) + + start_position = tok_text.find(pred_text) + if start_position == -1: + return orig_text + end_position = start_position + len(pred_text) - 1 + + (orig_ns_text, orig_ns_to_s_map) = _strip_spaces(orig_text) + (tok_ns_text, tok_ns_to_s_map) = _strip_spaces(tok_text) + + if len(orig_ns_text) != len(tok_ns_text): + return orig_text + + tok_s_to_ns_map = {} + for (i, tok_index) in six.iteritems(tok_ns_to_s_map): + tok_s_to_ns_map[tok_index] = i + + orig_start_position = None + if start_position in tok_s_to_ns_map: + ns_start_position = tok_s_to_ns_map[start_position] + if ns_start_position in orig_ns_to_s_map: + orig_start_position = orig_ns_to_s_map[ns_start_position] + + if orig_start_position is None: + return orig_text + + orig_end_position = None + if end_position in tok_s_to_ns_map: + ns_end_position = tok_s_to_ns_map[end_position] + if ns_end_position in orig_ns_to_s_map: + orig_end_position = orig_ns_to_s_map[ns_end_position] + + if orig_end_position is None: + return orig_text + + output_text = orig_text[orig_start_position:(orig_end_position + 1)] + return output_text + + +def get_nbest(args, prelim_predictions, features, n_best_size, do_lower_case): + """get nbest predictions""" + _NbestPrediction = collections.namedtuple("NbestPrediction", ["text", "start_logit", "end_logit"]) + seen_predictions = {} + nbest = [] + (tokens, token_to_orig_map, token_is_max_context, example_index) = features + token_is_max_context = token_is_max_context + doc_tokens_file = os.path.realpath(os.path.join(args.data_dir, "07_data", 'squad_bs_'+str(example_index)+'.bin')) + qas_id_file = os.path.realpath(os.path.join(args.data_dir, "08_data", 'squad_bs_'+str(example_index)+'.bin')) + doc_tokens = pickle.load(open(doc_tokens_file, 'rb+')) + qas_id = pickle.load(open(qas_id_file, 'rb+')) + for pred in prelim_predictions: + if len(nbest) >= n_best_size: + break + if pred.start_index > 0: # this is a non-null prediction + tok_tokens = tokens[pred.start_index:(pred.end_index + 1)] + orig_doc_start = token_to_orig_map[pred.start_index] + orig_doc_end = token_to_orig_map[pred.end_index] + orig_tokens = doc_tokens[orig_doc_start:(orig_doc_end + 1)] + tok_text = " ".join(tok_tokens) + + # De-tokenize WordPieces that have been split off. + tok_text = tok_text.replace(" ##", "") + tok_text = tok_text.replace("##", "") + + # Clean whitespace + tok_text = tok_text.strip() + tok_text = " ".join(tok_text.split()) + orig_text = " ".join(orig_tokens) + final_text = get_final_text(tok_text, orig_text, do_lower_case) + if final_text in seen_predictions: + continue + + seen_predictions[final_text] = True + else: + final_text = "" + seen_predictions[final_text] = True + + nbest.append( + _NbestPrediction( + text=final_text, + start_logit=pred.start_logit, + end_logit=pred.end_logit)) + + # In very rare edge cases we could have no valid predictions. So we + # just create a nonce prediction in this case to avoid failure. + if not nbest: + nbest.append(_NbestPrediction(text="empty", start_logit=0.0, end_logit=0.0)) + + assert len(nbest) >= 1 + return nbest, qas_id + + +def _compute_softmax(scores): + """Compute softmax probability over raw logits.""" + if not scores: + return [] + + max_score = None + for score in scores: + if max_score is None or score > max_score: + max_score = score + + exp_scores = [] + total_sum = 0.0 + for score in scores: + x = math.exp(score - max_score) + exp_scores.append(x) + total_sum += x + + probs = [] + for score in exp_scores: + probs.append(score / total_sum) + return probs + + +def get_one_prediction(nbest): + '''get one prediction''' + total_scores = [] + best_non_null_entry = None + for entry in nbest: + total_scores.append(entry.start_logit + entry.end_logit) + if not best_non_null_entry: + if entry.text: + best_non_null_entry = entry + + probs = _compute_softmax(total_scores) + + nbest_json = [] + for (i, entry) in enumerate(nbest): + output = collections.OrderedDict() + output["text"] = entry.text + output["probability"] = probs[i] + output["start_logit"] = entry.start_logit + output["end_logit"] = entry.end_logit + nbest_json.append(output) + + assert len(nbest_json) >= 1 + return nbest_json + + +def whitespace_tokenize(text): + """Runs basic whitespace cleaning and splitting on a piece of text.""" + text = text.strip() + if not text: + return [] + tokens = text.split() + return tokens + + +def _is_punctuation(char): + """Checks whether `chars` is a punctuation character.""" + cp = ord(char) + # We treat all non-letter/number ASCII as punctuation. + # Characters such as "^", "$", and "`" are not in the Unicode + # Punctuation class but we treat them as punctuation anyways, for + # consistency. + if ((33 <= cp <= 47) or (58 <= cp <= 64) or + (91 <= cp <= 96) or (123 <= cp <= 126)): + return True + cat = unicodedata.category(char) + if cat.startswith("P"): + return True + return False + + +def _is_control(char): + """Checks whether `chars` is a control character.""" + # These are technically control characters but we count them as whitespace + # characters. + control_char = ["\t", "\n", "\r"] + if char in control_char: + return False + cat = unicodedata.category(char) + if cat in ("Cc", "Cf"): + return True + return False + + +def _is_whitespace(char): + """Checks whether `chars` is a whitespace character.""" + # \t, \n, and \r are technically control characters but we treat them + # as whitespace since they are generally considered as such. + whitespace_char = [" ", "\t", "\n", "\r"] + if char in whitespace_char: + return True + cat = unicodedata.category(char) + if cat == "Zs": + return True + return False + + +class BasicTokenizer(): + """ + Basic tokenizer + """ + def __init__(self, do_lower_case=True): + self.do_lower_case = do_lower_case + + def tokenize(self, text): + """ + Do basic tokenization. + Args: + text: text in unicode. + + Returns: + a list of tokens split from text + """ + text = self._clean_text(text) + text = self._tokenize_chinese_chars(text) + + orig_tokens = whitespace_tokenize(text) + split_tokens = [] + for token in orig_tokens: + if self.do_lower_case: + token = token.lower() + token = self._run_strip_accents(token) + aaa = self._run_split_on_punc(token) + split_tokens.extend(aaa) + + output_tokens = whitespace_tokenize(" ".join(split_tokens)) + return output_tokens + + def _run_strip_accents(self, text): + """Strips accents from a piece of text.""" + text = unicodedata.normalize("NFD", text) + output = [] + for char in text: + cat = unicodedata.category(char) + if cat == "Mn": + continue + output.append(char) + return "".join(output) + + def _run_split_on_punc(self, text): + """Splits punctuation on a piece of text.""" + i = 0 + start_new_word = True + output = [] + for char in text: + if _is_punctuation(char): + output.append([char]) + start_new_word = True + else: + if start_new_word: + output.append([]) + start_new_word = False + output[-1].append(char) + i += 1 + return ["".join(x) for x in output] + + def _clean_text(self, text): + """Performs invalid character removal and whitespace cleanup on text.""" + output = [] + for char in text: + cp = ord(char) + if cp == 0 or cp == 0xfffd or _is_control(char): + continue + if _is_whitespace(char): + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _tokenize_chinese_chars(self, text): + """Adds whitespace around any CJK character.""" + output = [] + for char in text: + cp = ord(char) + if self._is_chinese_char(cp): + output.append(" ") + output.append(char) + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _is_chinese_char(self, cp): + """Checks whether CP is the codepoint of a CJK character.""" + # This defines a "chinese character" as anything in the CJK Unicode block: + # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) + # + # Note that the CJK Unicode block is NOT all Japanese and Korean characters, + # despite its name. The modern Korean Hangul alphabet is a different block, + # as is Japanese Hiragana and Katakana. Those alphabets are used to write + # space-separated words, so they are not treated specially and handled + # like the all of the other languages. + if ((0x4E00 <= cp <= 0x9FFF) or + (0x3400 <= cp <= 0x4DBF) or + (0x20000 <= cp <= 0x2A6DF) or + (0x2A700 <= cp <= 0x2B73F) or + (0x2B740 <= cp <= 0x2B81F) or + (0x2B820 <= cp <= 0x2CEAF) or + (0xF900 <= cp <= 0xFAFF) or + (0x2F800 <= cp <= 0x2FA1F)): + return True + + return False + + +def run(): + """ + read pipeline and do infer + """ + args = parse_args() + # input_ids file list, every file content a tensor[1,128] + file_list = glob.glob(os.path.join(os.path.realpath(args.data_dir), "10_data", "*.txt")) + cwq_lists = [] + for i in range(len(file_list)): + b = os.path.split(file_list[i]) + cwq_lists.append(b) + + def take_second(elem): + return elem[1] + + cwq_lists.sort(key=take_second) + yms_lists = [] + for i in range(len(cwq_lists)): + c = cwq_lists[i][0] + '/' + cwq_lists[i][1] + yms_lists.append(c) + file_list = yms_lists + all_predictions = collections.OrderedDict() + for input_ids in file_list: + file_name = input_ids.split('/')[-1].split('.')[0] + '.bin' + start_logits, end_logits = get_infer_logits(args, input_ids.split('/')[-1]) + prelim_predictions, features = get_prelim_predictions(args, file_name, start_logits=start_logits, + end_logits=end_logits, n_best_size=20, + max_answer_length=30) + nbest, qas_id = get_nbest(args, prelim_predictions, features, n_best_size=20, do_lower_case=True) + nbest_json = get_one_prediction(nbest) + all_predictions[qas_id] = nbest_json[0]["text"] + file = open('infer_result.txt', 'w') + file.write(str(all_predictions)) + file.close() + print('done') + post_process(args.eval_json_path, all_predictions, output_metrics="output.json") + + +if __name__ == '__main__': + run() diff --git a/nlp/language_model/bert/MindSpore/infer_squad/mxbase/run.sh b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..ececd305cfc4f7b403a4b27b4ef66fe35e04b8f1 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/run.sh @@ -0,0 +1,32 @@ +#!/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. + +set -e + +# Simple log helper functions +info() { echo -e "\033[1;34m[INFO ][MxStream] $1\033[1;37m" ; } +warn() { echo >&2 -e "\033[1;31m[WARN ][MxStream] $1\033[1;37m" ; } + +export LD_LIBRARY_PATH=${MX_SDK_HOME}/lib:${MX_SDK_HOME}/opensource/lib:${MX_SDK_HOME}/opensource/lib64:/usr/local/Ascend/ascend-toolkit/latest/acllib/lib64:${LD_LIBRARY_PATH} +export GST_PLUGIN_SCANNER=${MX_SDK_HOME}/opensource/libexec/gstreamer-1.0/gst-plugin-scanner +export GST_PLUGIN_PATH=${MX_SDK_HOME}/opensource/lib/gstreamer-1.0:${MX_SDK_HOME}/lib/plugins + +#to set PYTHONPATH, import the StreamManagerApi.py +export PYTHONPATH=$PYTHONPATH:${MX_SDK_HOME}/python + +./bert ../data/input/ 0 +python3.7 postprocess.py --data_dir=../data/input --eval_json_path=../data/dev.json +exit 0 diff --git a/nlp/language_model/bert/MindSpore/infer_squad/mxbase/src/BertBase.cpp b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/src/BertBase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0429a0100dc1af58f7c629a78301a92fe129e0df --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/src/BertBase.cpp @@ -0,0 +1,304 @@ +/** + * 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 "BertBase.h" +#include +#include +#include +#include +#include "MxBase/DeviceManager/DeviceManager.h" +#include "MxBase/Log/Log.h" + +const uint32_t EACH_LABEL_LENGTH = 4; +const uint32_t MAX_LENGTH = 384; +const uint32_t CLASS_NUM = 41; + + +APP_ERROR BertBase::Init(const InitParam &initParam) { + deviceId_ = initParam.deviceId; + 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; + } + dvppWrapper_ = std::make_shared(); + ret = dvppWrapper_->Init(); + if (ret != APP_ERR_OK) { + LogError << "DvppWrapper init failed, ret=" << ret << "."; + return ret; + } + model_ = std::make_shared(); + ret = model_->Init(initParam.modelPath, modelDesc_); + if (ret != APP_ERR_OK) { + LogError << "ModelInferenceProcessor init failed, ret=" << ret << "."; + return ret; + } + + return APP_ERR_OK; +} + +APP_ERROR BertBase::DeInit() { + dvppWrapper_->DeInit(); + model_->DeInit(); + MxBase::DeviceManager::GetInstance()->DestroyDevices(); + return APP_ERR_OK; +} + +APP_ERROR BertBase::ReadTensorFromFile(const std::string &file, uint32_t *data, uint32_t size) { + if (data == NULL || size < MAX_LENGTH) { + LogError << "input data is invalid."; + return APP_ERR_COMM_INVALID_POINTER; + } + std::ifstream infile; + // open label file + infile.open(file, std::ios_base::in | std::ios_base::binary); + // check label file validity + if (infile.fail()) { + LogError << "Failed to open label file: " << file << "."; + return APP_ERR_COMM_OPEN_FAIL; + } + infile.read(reinterpret_cast(data), sizeof(uint32_t) * MAX_LENGTH); + infile.close(); + return APP_ERR_OK; +} + +APP_ERROR BertBase::ReadInputTensor(const std::string &fileName, uint32_t index, + std::vector *inputs) { + uint32_t data[MAX_LENGTH] = {0}; + APP_ERROR ret = ReadTensorFromFile(fileName, data, MAX_LENGTH); + if (ret != APP_ERR_OK) { + LogError << "ReadTensorFromFile failed."; + return ret; + } + + const uint32_t dataSize = modelDesc_.inputTensors[index].tensorSize; + MxBase::MemoryData memoryDataDst(dataSize, MxBase::MemoryData::MEMORY_DEVICE, deviceId_); + MxBase::MemoryData memoryDataSrc(reinterpret_cast(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; + } + + std::vector shape = {1, MAX_LENGTH}; + inputs->push_back(MxBase::TensorBase(memoryDataDst, false, shape, MxBase::TENSOR_DTYPE_UINT32)); + return APP_ERR_OK; +} + +APP_ERROR BertBase::Inference(const std::vector &inputs, + std::vector *outputs) { + auto dtypes = model_->GetOutputDataType(); + for (size_t i = 0; i < modelDesc_.outputTensors.size(); ++i) { + std::vector shape = {}; + for (size_t j = 0; j < modelDesc_.outputTensors[i].tensorDims.size(); ++j) { + shape.push_back((uint32_t)modelDesc_.outputTensors[i].tensorDims[j]); + } + MxBase::TensorBase tensor(shape, dtypes[i], MxBase::MemoryData::MemoryType::MEMORY_DEVICE, 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 = model_->ModelInference(inputs, *outputs, dynamicInfo); + auto endTime = std::chrono::high_resolution_clock::now(); + double costMs = std::chrono::duration(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 BertBase::PostProcess(std::vector *outputs, std::vector *argmax, \ + const std::string &fileName) { + MxBase::TensorBase &tensor = outputs->at(0); + APP_ERROR ret = tensor.ToHost(); + if (ret != APP_ERR_OK) { + LogError << GetError(ret) << "Tensor deploy to host failed."; + return ret; + } + // check tensor is available + auto outputShape = tensor.GetShape(); + uint32_t length = outputShape[1]; + uint32_t classNum = outputShape[2]; + LogInfo << "output shape is: " << outputShape[1] << " " << outputShape[2] << std::endl; + + void* data = tensor.GetBuffer(); + std::vector result_start = {}; + std::vector result_end = {}; + for (uint32_t i = 0; i < length; i++) { + for (uint32_t j = 0; j < classNum; j++) { + if (j == 0) { + float value_start = *(reinterpret_cast(data) + i * classNum + j); + result_start.push_back(value_start); + } else if (j == 1) { + float value_end = *(reinterpret_cast(data) + i * classNum + j); + result_end.push_back(value_end); + } + } + } + int result_start_size = result_start.size(); + + std::string resultPathName = "../data/input/10_data"; + // create result directory when it does not exit + if (access(resultPathName.c_str(), 0) != 0) { + ret = mkdir(resultPathName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR); + if (ret != 0) { + LogError << "Failed to create result directory: " << resultPathName << ", ret = " << ret; + return APP_ERR_COMM_OPEN_FAIL; + } + } + // create result file under result directory + std::string nobin_fileName = fileName.substr(0, fileName.length() - 4); + resultPathName = resultPathName + "/" + nobin_fileName + ".txt"; + std::ofstream tfile(resultPathName, std::ofstream::app); + if (tfile.fail()) { + LogError << "Failed to open result file: " << resultPathName; + return APP_ERR_COMM_OPEN_FAIL; + } + // write inference result into file + LogInfo << "=============================================================="; + for (int t = 0; t < result_start_size; t++) { + tfile << result_start[t] << std::endl; + } + for (int j = 0; j < result_start_size; j++) { + tfile << result_end[j] << std::endl; + } + + LogInfo << "=============================================================="; + tfile.close(); + + return APP_ERR_OK; +} + +APP_ERROR BertBase::CountPredictResult(const std::string &labelFile, const std::vector &argmax) { + uint32_t data[MAX_LENGTH] = {0}; + APP_ERROR ret = ReadTensorFromFile(labelFile, data, MAX_LENGTH); + if (ret != APP_ERR_OK) { + LogError << "ReadTensorFromFile failed."; + return ret; + } + uint32_t target[CLASS_NUM][MAX_LENGTH] = {0}; + uint32_t pred[CLASS_NUM][MAX_LENGTH] = {0}; + for (uint32_t i = 0; i < MAX_LENGTH; i++) { + if (data[i] > 0) { + target[data[i]][i] = 1; + } + if (argmax[i] > 0) { + pred[argmax[i]][i] = 1; + } + } + for (uint32_t i = 0; i < CLASS_NUM; i++) { + for (uint32_t j = 0; j < MAX_LENGTH; j++) { + // count True Positive and False Positive + if (pred[i][j] == 1) { + if (target[i][j] == 1) { + g_TP += 1; + } else { + g_FP += 1; + } + } + // count False Negative + if (target[i][j] == 1 && pred[i][j] != 1) { + g_FN += 1; + } + } + } + LogInfo << "TP: " << g_TP << ", FP: " << g_FP << ", FN: " << g_FN; + return APP_ERR_OK; +} + +APP_ERROR BertBase::WriteResult(const std::string &fileName, const std::vector &argmax) { + std::string resultPathName = "result"; + // create result directory when it does not exit + if (access(resultPathName.c_str(), 0) != 0) { + int ret = mkdir(resultPathName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR); + if (ret != 0) { + LogError << "Failed to create result directory: " << resultPathName << ", ret = " << ret; + return APP_ERR_COMM_OPEN_FAIL; + } + } + // create result file under result directory + resultPathName = resultPathName + "/result.txt"; + std::ofstream tfile(resultPathName, std::ofstream::app); + if (tfile.fail()) { + LogError << "Failed to open result file: " << resultPathName; + return APP_ERR_COMM_OPEN_FAIL; + } + // write inference result into file + LogInfo << "=============================================================="; + LogInfo << "infer result of " << fileName << " is: "; + tfile << "file name is: " << fileName << std::endl; + LogInfo << "=============================================================="; + tfile.close(); + return APP_ERR_OK; +} + +APP_ERROR BertBase::Process(const std::string &inferPath, const std::string &fileName, bool eval) { + std::vector inputs = {}; + std::string inputIdsFile = inferPath + "00_data/" + fileName; + APP_ERROR ret = ReadInputTensor(inputIdsFile, INPUT_IDS, &inputs); + if (ret != APP_ERR_OK) { + LogError << "Read input ids failed, ret=" << ret << "."; + return ret; + } + std::string inputMaskFile = inferPath + "01_data/" + fileName; + ret = ReadInputTensor(inputMaskFile, INPUT_MASK, &inputs); + if (ret != APP_ERR_OK) { + LogError << "Read input mask file failed, ret=" << ret << "."; + return ret; + } + std::string tokenTypeIdFile = inferPath + "02_data/" + fileName; + ret = ReadInputTensor(tokenTypeIdFile, TOKEN_TYPE, &inputs); + if (ret != APP_ERR_OK) { + LogError << "Read token typeId file failed, ret=" << ret << "."; + return ret; + } + + std::vector outputs = {}; + ret = Inference(inputs, &outputs); + if (ret != APP_ERR_OK) { + LogError << "Inference failed, ret=" << ret << "."; + return ret; + } + + std::vector argmax; + ret = PostProcess(&outputs, &argmax, fileName); + if (ret != APP_ERR_OK) { + LogError << "PostProcess failed, ret=" << ret << "."; + return ret; + } + if (eval) { + std::string labelFile = inferPath + "03_data/" + fileName; + ret = CountPredictResult(labelFile, argmax); + if (ret != APP_ERR_OK) { + LogError << "CalcF1Score read label failed, ret=" << ret << "."; + return ret; + } + } + return APP_ERR_OK; +} diff --git a/nlp/language_model/bert/MindSpore/infer_squad/mxbase/src/BertBase.h b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/src/BertBase.h new file mode 100644 index 0000000000000000000000000000000000000000..c75e7e384e5f43e7039814c03338be038a52baeb --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/src/BertBase.h @@ -0,0 +1,67 @@ +/** + * 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_BERTBASE_H +#define MXBASE_BERTBASE_H + +#include +#include +#include +#include +#include +#include +#include "MxBase/DvppWrapper/DvppWrapper.h" +#include "MxBase/ModelInfer/ModelInferenceProcessor.h" +#include "MxBase/Tensor/TensorContext/TensorContext.h" + +extern std::vector g_inferCost; +extern uint32_t g_TP; +extern uint32_t g_FP; +extern uint32_t g_FN; + +struct InitParam { + uint32_t deviceId; + std::string modelPath; +}; + +enum DataIndex { + INPUT_IDS = 0, + INPUT_MASK = 1, + TOKEN_TYPE = 2, +}; + +class BertBase { + public: + APP_ERROR Init(const InitParam &initParam); + APP_ERROR DeInit(); + APP_ERROR Inference(const std::vector &inputs, std::vector *outputs); + APP_ERROR Process(const std::string &inferPath, const std::string &fileName, bool eval); + APP_ERROR PostProcess(std::vector *outputs, \ + std::vector *argmax, const std::string &fileName); + protected: + APP_ERROR ReadTensorFromFile(const std::string &file, uint32_t *data, uint32_t size); + APP_ERROR ReadInputTensor(const std::string &fileName, uint32_t index, std::vector *inputs); + APP_ERROR LoadLabels(const std::string &labelPath, std::vector *labelMap); + APP_ERROR ReadInputTensor(const std::string &fileName, const std::vector &argmax); + APP_ERROR WriteResult(const std::string &fileName, const std::vector &argmax); + APP_ERROR CountPredictResult(const std::string &labelFile, const std::vector &argmax); + private: + std::shared_ptr dvppWrapper_; + std::shared_ptr model_; + MxBase::ModelDesc modelDesc_ = {}; + uint32_t deviceId_ = 0; +}; +#endif diff --git a/nlp/language_model/bert/MindSpore/infer_squad/mxbase/src/main.cpp b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e2e84dfc9976e6f697434cadc89e57c96721d4ee --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/mxbase/src/main.cpp @@ -0,0 +1,107 @@ +/** + * 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 +#include +#include +#include +#include +#include "BertBase.h" +#include "MxBase/Log/Log.h" + +std::vector g_inferCost; +uint32_t g_TP = 0; +uint32_t g_FP = 0; +uint32_t g_FN = 0; + +void InitBertParam(InitParam* initParam) { + initParam->deviceId = 0; + initParam->modelPath = "../data/model/bertbase.om"; +} + +APP_ERROR ReadFilesFromPath(const std::string &path, std::vector *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) { + // d_type == 8 is file + if (ptr->d_type == 8) { + files->push_back(ptr->d_name); + } + } + closedir(dir); + // sort ascending order + sort(files->begin(), files->end()); + return APP_ERR_OK; +} + +int main(int argc, char* argv[]) { + if (argc <= 1) { + LogWarn << "Please input image path, such as './bert /input/data 0'."; + return APP_ERR_OK; + } + + InitParam initParam; + InitBertParam(&initParam); + auto bertBase = std::make_shared(); + APP_ERROR ret = bertBase->Init(initParam); + if (ret != APP_ERR_OK) { + LogError << "Bertbase init failed, ret=" << ret << "."; + return ret; + } + + std::string inferPath = argv[1]; + std::vector files; + ret = ReadFilesFromPath(inferPath + "00_data", &files); + if (ret != APP_ERR_OK) { + LogError << "Read files from path failed, ret=" << ret << "."; + return ret; + } + // do eval and calc the f1 score + bool eval = atoi(argv[2]); + for (uint32_t i = 0; i < files.size(); i++) { + LogInfo << "read file name: " << files[i]; + ret = bertBase->Process(inferPath, files[i], eval); + if (ret != APP_ERR_OK) { + LogError << "Bertbase process failed, ret=" << ret << "."; + bertBase->DeInit(); + return ret; + } + } + + if (eval) { + LogInfo << "=============================================================="; + float precision = g_TP * 1.0 / (g_TP + g_FP); + LogInfo << "Precision: " << precision; + float recall = g_TP * 1.0 / (g_TP + g_FN); + LogInfo << "recall: " << recall; + LogInfo << "F1 Score: " << 2 * precision * recall / (precision + recall); + LogInfo << "=============================================================="; + } + bertBase->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."; + return APP_ERR_OK; +} diff --git a/nlp/language_model/bert/MindSpore/infer_squad/sdk/main.py b/nlp/language_model/bert/MindSpore/infer_squad/sdk/main.py new file mode 100644 index 0000000000000000000000000000000000000000..5f4acc4f6858c2323f9a8c89039c0d6a0a353964 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/sdk/main.py @@ -0,0 +1,684 @@ +# 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. +# ============================================================================ + +""" +sample script of CLUE infer using SDK run in docker +""" +import string +import re +import json +import sys +import math +import pickle +import unicodedata +import collections + +import argparse +import glob +import os +import time +import six + +import MxpiDataType_pb2 as MxpiDataType +import numpy as np +from StreamManagerApi import StreamManagerApi, MxDataInput, InProtobufVector, \ + MxProtobufIn, StringVector + +TP = 0 +FP = 0 +FN = 0 + + +def parse_args(): + """set and check parameters.""" + parser = argparse.ArgumentParser(description="bert process") + parser.add_argument("--pipeline", type=str, default="", help="SDK infer pipeline") + parser.add_argument("--data_dir", type=str, default="", + help="Dataset contain input_ids, input_mask, segment_ids, label_ids") + parser.add_argument("--eval_json_path", type=str, default="", help="label ids to name") + parser.add_argument("--output_file", type=str, default="", help="save result to file") + parser.add_argument("--f1_method", type=str, default="BF1", help="calc F1 use the number label,(BF1, MF1)") + parser.add_argument("--do_eval", type=bool, default=False, help="eval the accuracy of model ") + args_opt = parser.parse_args() + return args_opt + + +def send_source_data(appsrc_id, filename, 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 = np.fromfile(filename, dtype=np.int32) + tensor = np.expand_dims(tensor, 0) + tensor_package_list = MxpiDataType.MxpiTensorPackageList() + tensor_package = tensor_package_list.tensorPackageVec.add() + array_bytes = tensor.tobytes() + data_input = MxDataInput() + data_input.data = array_bytes + 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 = data_input.data + 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 + + +def send_appsrc_data(args, file_name, stream_name, stream_manager): + """ + send three stream to infer model, include input ids, input mask and token type_id. + + Returns: + bool: send data success or not + """ + input_ids = os.path.realpath(os.path.join(args.data_dir, "00_data", file_name)) + if not send_source_data(0, input_ids, stream_name, stream_manager): + return False + input_mask = os.path.realpath(os.path.join(args.data_dir, "01_data", file_name)) + if not send_source_data(1, input_mask, stream_name, stream_manager): + return False + token_type_id = os.path.realpath(os.path.join(args.data_dir, "02_data", file_name)) + if not send_source_data(2, token_type_id, stream_name, stream_manager): + return False + return True + + +def f1_score(prediction, ground_truth): + """calculate f1 score""" + prediction_tokens = normalize_answer(prediction).split() + ground_truth_tokens = normalize_answer(ground_truth).split() + common = collections.Counter(prediction_tokens) & collections.Counter(ground_truth_tokens) + num_same = sum(common.values()) + if num_same == 0: + return 0 + precision = 1.0 * num_same / len(prediction_tokens) + recall = 1.0 * num_same / len(ground_truth_tokens) + f1 = (2 * precision * recall) / (precision + recall) + return f1 + + +def post_process(dataset_file, all_predictions, output_metrics="output.json"): + """ + process the result of infer tensor to Visualization results. + Args: + args: param of config. + file_name: label file name. + infer_result: get logit from infer result + max_seq_length: sentence input length default is 128. + """ + # print the infer result + with open(dataset_file) as ds: + print('==========') + dataset_json = json.load(ds) + dataset = dataset_json['data'] + print(dataset) + print('success') + re_json = evaluate(dataset, all_predictions) + print(json.dumps(re_json)) + with open(output_metrics, 'w') as wr: + wr.write(json.dumps(re_json)) + + +def normalize_answer(s): + """Lower text and remove punctuation, articles and extra whitespace.""" + def remove_articles(text): + return re.sub(r'\b(a|an|the)\b', ' ', text) + + def white_space_fix(text): + return ' '.join(text.split()) + + def remove_punc(text): + exclude = set(string.punctuation) + return ''.join(ch for ch in text if ch not in exclude) + + def lower(text): + return text.lower() + + return white_space_fix(remove_articles(remove_punc(lower(s)))) + + +def exact_match_score(prediction, ground_truth): + return normalize_answer(prediction) == normalize_answer(ground_truth) + + +def metric_max_over_ground_truths(metric_fn, prediction, ground_truths): + scores_for_ground_truths = [] + for ground_truth in ground_truths: + score = metric_fn(prediction, ground_truth) + scores_for_ground_truths.append(score) + return max(scores_for_ground_truths) + + +def evaluate(dataset, predictions): + """do evaluation""" + f1 = exact_match = total = 0 + for article in dataset: + for paragraph in article['paragraphs']: + for qa in paragraph['qas']: + total += 1 + if qa['id'] not in predictions: + message = 'Unanswered question ' + qa['id'] + \ + ' will receive score 0.' + print(message, file=sys.stderr) + continue + ground_truths = list(map(lambda x: x['text'], qa['answers'])) + if not ground_truths: + continue + prediction = predictions[qa['id']] + exact_match += metric_max_over_ground_truths( + exact_match_score, prediction, ground_truths) + f1 += metric_max_over_ground_truths( + f1_score, prediction, ground_truths) + + exact_match = 100.0 * exact_match / total + f1 = 100.0 * f1 / total + print(exact_match) + print(f1) + return {'exact_match': exact_match, 'f1': f1} + + +def get_infer_logits(infer_result, max_seq_length=384): + """ + get the result of model output. + Args: + infer_result: get logit from infer result + max_seq_length: sentence input length default is 384. + """ + result = MxpiDataType.MxpiTensorPackageList() + result.ParseFromString(infer_result[0].messageBuf) + res = np.frombuffer(result.tensorPackageVec[0].tensorVec[0].dataStr, dtype='= n_best_size: + break + best_indexes.append(score[0]) + return best_indexes + + +def get_prelim_predictions(args, file_name, start_logits, end_logits, n_best_size, max_answer_length): + """ + process the logits of infer tensor to Visualization results based on n_best_size and max_answer_length + Args: + args: param of config. + file_name: used feature file name. + start_logits: get start logit from infer result + end_logits: get end logit from infer result + n_best_size: best of n answer (start point and end point). + max_answer_length: maximum answer length + """ + feature_tokens_file = os.path.realpath(os.path.join(args.data_dir, "04_data", file_name)) + feature_token_to_orig_map_file = os.path.realpath(os.path.join(args.data_dir, "05_data", file_name)) + feature_token_is_max_context_file = os.path.realpath(os.path.join(args.data_dir, "06_data", file_name)) + example_index_file = os.path.realpath(os.path.join(args.data_dir, "09_data", file_name)) + tokens = pickle.load(open(feature_tokens_file, 'rb+')) + token_to_orig_map = pickle.load(open(feature_token_to_orig_map_file, 'rb+')) + token_is_max_context = pickle.load(open(feature_token_is_max_context_file, 'rb+')) + example_index = np.fromfile(example_index_file, np.int32)[0] + _PrelimPrediction = collections.namedtuple( + "PrelimPrediction", ["start_index", "end_index", "start_logit", "end_logit"]) + prelim_predictions = [] + # keep track of the minimum score of null start+end of position 0 + start_indexes = _get_best_indexes(start_logits, n_best_size) + end_indexes = _get_best_indexes(end_logits, n_best_size) + # if we could have irrelevant answers, get the min score of irrelevant + for start_index in start_indexes: + for end_index in end_indexes: + # We could hypothetically create invalid predictions, e.g., predict + # that the start of the span is in the question. We throw out all + # invalid predictions. + if start_index >= len(tokens): + continue + if end_index >= len(tokens): + continue + if start_index not in token_to_orig_map: + continue + if end_index not in token_to_orig_map: + continue + if not token_is_max_context.get(start_index, False): + continue + if end_index < start_index: + continue + length = end_index - start_index + 1 + if length > max_answer_length: + continue + prelim_predictions.append( + _PrelimPrediction( + start_index=start_index, + end_index=end_index, + start_logit=start_logits[start_index], + end_logit=end_logits[end_index])) + + prelim_predictions = sorted( + prelim_predictions, + key=lambda x: (x.start_logit + x.end_logit), + reverse=True) + return prelim_predictions, (tokens, token_to_orig_map, token_is_max_context, example_index) + + +def get_final_text(pred_text, orig_text, do_lower_case): + """Project the tokenized prediction back to the original text.""" + def _strip_spaces(text): + ns_chars = [] + ns_to_s_map = collections.OrderedDict() + for (i, c) in enumerate(text): + if c == " ": + continue + ns_to_s_map[len(ns_chars)] = i + ns_chars.append(c) + ns_text = "".join(ns_chars) + return (ns_text, ns_to_s_map) + + tokenizer = BasicTokenizer(do_lower_case=do_lower_case) + tok_text = " ".join(tokenizer.tokenize(orig_text)) + + start_position = tok_text.find(pred_text) + if start_position == -1: + return orig_text + end_position = start_position + len(pred_text) - 1 + + (orig_ns_text, orig_ns_to_s_map) = _strip_spaces(orig_text) + (tok_ns_text, tok_ns_to_s_map) = _strip_spaces(tok_text) + + if len(orig_ns_text) != len(tok_ns_text): + return orig_text + + tok_s_to_ns_map = {} + for (i, tok_index) in six.iteritems(tok_ns_to_s_map): + tok_s_to_ns_map[tok_index] = i + + orig_start_position = None + if start_position in tok_s_to_ns_map: + ns_start_position = tok_s_to_ns_map[start_position] + if ns_start_position in orig_ns_to_s_map: + orig_start_position = orig_ns_to_s_map[ns_start_position] + + if orig_start_position is None: + return orig_text + + orig_end_position = None + if end_position in tok_s_to_ns_map: + ns_end_position = tok_s_to_ns_map[end_position] + if ns_end_position in orig_ns_to_s_map: + orig_end_position = orig_ns_to_s_map[ns_end_position] + + if orig_end_position is None: + return orig_text + + output_text = orig_text[orig_start_position:(orig_end_position + 1)] + return output_text + + +def get_nbest(args, prelim_predictions, features, n_best_size, do_lower_case): + """get nbest predictions""" + _NbestPrediction = collections.namedtuple("NbestPrediction", ["text", "start_logit", "end_logit"]) + seen_predictions = {} + nbest = [] + (tokens, token_to_orig_map, token_is_max_context, example_index) = features + token_is_max_context = token_is_max_context + doc_tokens_file = os.path.realpath(os.path.join(args.data_dir, "07_data", 'squad_bs_'+str(example_index)+'.bin')) + qas_id_file = os.path.realpath(os.path.join(args.data_dir, "08_data", 'squad_bs_'+str(example_index)+'.bin')) + doc_tokens = pickle.load(open(doc_tokens_file, 'rb+')) + qas_id = pickle.load(open(qas_id_file, 'rb+')) + for pred in prelim_predictions: + if len(nbest) >= n_best_size: + break + if pred.start_index > 0: # this is a non-null prediction + tok_tokens = tokens[pred.start_index:(pred.end_index + 1)] + orig_doc_start = token_to_orig_map[pred.start_index] + orig_doc_end = token_to_orig_map[pred.end_index] + orig_tokens = doc_tokens[orig_doc_start:(orig_doc_end + 1)] + tok_text = " ".join(tok_tokens) + + # De-tokenize WordPieces that have been split off. + tok_text = tok_text.replace(" ##", "") + tok_text = tok_text.replace("##", "") + + # Clean whitespace + tok_text = tok_text.strip() + tok_text = " ".join(tok_text.split()) + orig_text = " ".join(orig_tokens) + final_text = get_final_text(tok_text, orig_text, do_lower_case) + if final_text in seen_predictions: + continue + + seen_predictions[final_text] = True + else: + final_text = "" + seen_predictions[final_text] = True + + nbest.append( + _NbestPrediction( + text=final_text, + start_logit=pred.start_logit, + end_logit=pred.end_logit)) + + # In very rare edge cases we could have no valid predictions. So we + # just create a nonce prediction in this case to avoid failure. + if not nbest: + nbest.append(_NbestPrediction(text="empty", start_logit=0.0, end_logit=0.0)) + + assert len(nbest) >= 1 + return nbest, qas_id + + +def _compute_softmax(scores): + """Compute softmax probability over raw logits.""" + if not scores: + return [] + + max_score = None + for score in scores: + if max_score is None or score > max_score: + max_score = score + + exp_scores = [] + total_sum = 0.0 + for score in scores: + x = math.exp(score - max_score) + exp_scores.append(x) + total_sum += x + + probs = [] + for score in exp_scores: + probs.append(score / total_sum) + return probs + + +def get_one_prediction(nbest): + '''get one prediction''' + total_scores = [] + best_non_null_entry = None + for entry in nbest: + total_scores.append(entry.start_logit + entry.end_logit) + if not best_non_null_entry: + if entry.text: + best_non_null_entry = entry + + probs = _compute_softmax(total_scores) + + nbest_json = [] + for (i, entry) in enumerate(nbest): + output = collections.OrderedDict() + output["text"] = entry.text + output["probability"] = probs[i] + output["start_logit"] = entry.start_logit + output["end_logit"] = entry.end_logit + nbest_json.append(output) + + assert len(nbest_json) >= 1 + return nbest_json + + +def whitespace_tokenize(text): + """Runs basic whitespace cleaning and splitting on a piece of text.""" + text = text.strip() + if not text: + return [] + tokens = text.split() + return tokens + + +def _is_punctuation(char): + """Checks whether `chars` is a punctuation character.""" + cp = ord(char) + # We treat all non-letter/number ASCII as punctuation. + # Characters such as "^", "$", and "`" are not in the Unicode + # Punctuation class but we treat them as punctuation anyways, for + # consistency. + if ((33 <= cp <= 47) or (58 <= cp <= 64) or + (91 <= cp <= 96) or (123 <= cp <= 126)): + return True + cat = unicodedata.category(char) + if cat.startswith("P"): + return True + return False + + +def _is_control(char): + """Checks whether `chars` is a control character.""" + # These are technically control characters but we count them as whitespace + # characters. + control_char = ["\t", "\n", "\r"] + if char in control_char: + return False + cat = unicodedata.category(char) + if cat in ("Cc", "Cf"): + return True + return False + + +def _is_whitespace(char): + """Checks whether `chars` is a whitespace character.""" + # \t, \n, and \r are technically control characters but we treat them + # as whitespace since they are generally considered as such. + whitespace_char = [" ", "\t", "\n", "\r"] + if char in whitespace_char: + return True + cat = unicodedata.category(char) + if cat == "Zs": + return True + return False + + +class BasicTokenizer(): + """ + Basic tokenizer + """ + def __init__(self, do_lower_case=True): + self.do_lower_case = do_lower_case + + def tokenize(self, text): + """ + Do basic tokenization. + Args: + text: text in unicode. + + Returns: + a list of tokens split from text + """ + text = self._clean_text(text) + text = self._tokenize_chinese_chars(text) + + orig_tokens = whitespace_tokenize(text) + split_tokens = [] + for token in orig_tokens: + if self.do_lower_case: + token = token.lower() + token = self._run_strip_accents(token) + aaa = self._run_split_on_punc(token) + split_tokens.extend(aaa) + + output_tokens = whitespace_tokenize(" ".join(split_tokens)) + return output_tokens + + def _run_strip_accents(self, text): + """Strips accents from a piece of text.""" + text = unicodedata.normalize("NFD", text) + output = [] + for char in text: + cat = unicodedata.category(char) + if cat == "Mn": + continue + output.append(char) + return "".join(output) + + def _run_split_on_punc(self, text): + """Splits punctuation on a piece of text.""" + i = 0 + start_new_word = True + output = [] + for char in text: + if _is_punctuation(char): + output.append([char]) + start_new_word = True + else: + if start_new_word: + output.append([]) + start_new_word = False + output[-1].append(char) + i += 1 + return ["".join(x) for x in output] + + def _clean_text(self, text): + """Performs invalid character removal and whitespace cleanup on text.""" + output = [] + for char in text: + cp = ord(char) + if cp == 0 or cp == 0xfffd or _is_control(char): + continue + if _is_whitespace(char): + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _tokenize_chinese_chars(self, text): + """Adds whitespace around any CJK character.""" + output = [] + for char in text: + cp = ord(char) + if self._is_chinese_char(cp): + output.append(" ") + output.append(char) + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _is_chinese_char(self, cp): + """Checks whether CP is the codepoint of a CJK character.""" + # This defines a "chinese character" as anything in the CJK Unicode block: + # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) + # + # Note that the CJK Unicode block is NOT all Japanese and Korean characters, + # despite its name. The modern Korean Hangul alphabet is a different block, + # as is Japanese Hiragana and Katakana. Those alphabets are used to write + # space-separated words, so they are not treated specially and handled + # like the all of the other languages. + if ((0x4E00 <= cp <= 0x9FFF) or + (0x3400 <= cp <= 0x4DBF) or + (0x20000 <= cp <= 0x2A6DF) or + (0x2A700 <= cp <= 0x2B73F) or + (0x2B740 <= cp <= 0x2B81F) or + (0x2B820 <= cp <= 0x2CEAF) or + (0xF900 <= cp <= 0xFAFF) or + (0x2F800 <= cp <= 0x2FA1F)): + return True + + return False + + +def takeSecond(elem): + return elem[1] + + +def run(): + """ + 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'im_bertbase' + infer_total_time = 0 + # input_ids file list, every file content a tensor[1,128] + file_list = glob.glob(os.path.join(os.path.realpath(args.data_dir), "00_data", "*.bin")) + cwq_lists = [] + for i in range(len(file_list)): + b = os.path.split(file_list[i]) + cwq_lists.append(b) + + cwq_lists.sort(key=takeSecond) + yms_lists = [] + for i in range(len(cwq_lists)): + c = cwq_lists[i][0]+'/'+cwq_lists[i][1] + yms_lists.append(c) + file_list = yms_lists + all_predictions = collections.OrderedDict() + for input_ids in file_list: + file_name = input_ids.split('/')[-1] + if not send_appsrc_data(args, file_name, stream_name, stream_manager_api): + return + # Obtain the inference result by specifying streamName and uniqueId. + key_vec = StringVector() + key_vec.push_back(b'mxpi_tensorinfer0') + 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 + start_logits, end_logits = get_infer_logits(infer_result) + prelim_predictions, features = get_prelim_predictions(args, file_name, start_logits=start_logits, + end_logits=end_logits, n_best_size=20, + max_answer_length=30) + nbest, qas_id = get_nbest(args, prelim_predictions, features, n_best_size=20, do_lower_case=True) + nbest_json = get_one_prediction(nbest) + all_predictions[qas_id] = nbest_json[0]["text"] + file = open('infer_result.txt', 'w') + file.write(str(all_predictions)) + file.close() + print(all_predictions) + print('done') + post_process(args.eval_json_path, all_predictions, output_metrics="output.json") + + +if __name__ == '__main__': + run() diff --git a/nlp/language_model/bert/MindSpore/infer_squad/sdk/run.sh b/nlp/language_model/bert/MindSpore/infer_squad/sdk/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..98640696397e6cc66c1f764f719ce9042a16a78d --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/sdk/run.sh @@ -0,0 +1,32 @@ +#!/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. + +set -e + +# Simple log helper functions +info() { echo -e "\033[1;34m[INFO ][MxStream] $1\033[1;37m" ; } +warn() { echo >&2 -e "\033[1;31m[WARN ][MxStream] $1\033[1;37m" ; } + +#export MX_SDK_HOME=/home/work/mxVision +export LD_LIBRARY_PATH=${MX_SDK_HOME}/lib:${MX_SDK_HOME}/opensource/lib:${MX_SDK_HOME}/opensource/lib64:/usr/local/Ascend/ascend-toolkit/latest/acllib/lib64:${LD_LIBRARY_PATH} +export GST_PLUGIN_SCANNER=${MX_SDK_HOME}/opensource/libexec/gstreamer-1.0/gst-plugin-scanner +export GST_PLUGIN_PATH=${MX_SDK_HOME}/opensource/lib/gstreamer-1.0:${MX_SDK_HOME}/lib/plugins + +#to set PYTHONPATH, import the StreamManagerApi.py +export PYTHONPATH=$PYTHONPATH:${MX_SDK_HOME}/python + +python3.7 main.py --pipeline=../data/config/bert_base.pipeline --data_dir=../data/input --eval_json_path=../data/dev.json --output_file=./output.txt --do_eval=True --f1_method=MF1 +exit 0 diff --git a/nlp/language_model/bert/MindSpore/infer_squad/utils/create_squad_data.py b/nlp/language_model/bert/MindSpore/infer_squad/utils/create_squad_data.py new file mode 100644 index 0000000000000000000000000000000000000000..2eb441668e478ecbd2740ae0d40071c04f4b4d21 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/utils/create_squad_data.py @@ -0,0 +1,309 @@ +# 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. +# ============================================================================ + +"""create squad data""" + +import collections +import json +import tokenization + + +class SquadExample(): + """extract column contents from raw data""" + def __init__(self, + qas_id, + question_text, + doc_tokens, + orig_answer_text=None, + start_position=None, + end_position=None, + is_impossible=False): + self.qas_id = qas_id + self.question_text = question_text + self.doc_tokens = doc_tokens + self.orig_answer_text = orig_answer_text + self.start_position = start_position + self.end_position = end_position + self.is_impossible = is_impossible + + +class InputFeatures(): + """A single set of features of data.""" + def __init__(self, + unique_id, + example_index, + doc_span_index, + tokens, + token_to_orig_map, + token_is_max_context, + input_ids, + input_mask, + segment_ids, + start_position=None, + end_position=None, + is_impossible=None): + self.unique_id = unique_id + self.example_index = example_index + self.doc_span_index = doc_span_index + self.tokens = tokens + self.token_to_orig_map = token_to_orig_map + self.token_is_max_context = token_is_max_context + self.input_ids = input_ids + self.input_mask = input_mask + self.segment_ids = segment_ids + self.start_position = start_position + self.end_position = end_position + self.is_impossible = is_impossible + + +def read_squad_examples(input_file, is_training, version_2_with_negative=False): + """Return list of SquadExample from input_data or input_file (SQuAD json file)""" + with open(input_file, "r") as reader: + input_data = json.load(reader)["data"] + + def is_whitespace(c): + if c == " " or c == "\t" or c == "\r" or c == "\n" or ord(c) == 0x202F: + return True + return False + + def token_offset(text): + doc_tokens = [] + char_to_word_offset = [] + prev_is_whitespace = True + for c in paragraph_text: + if is_whitespace(c): + prev_is_whitespace = True + else: + if prev_is_whitespace: + doc_tokens.append(c) + else: + doc_tokens[-1] += c + prev_is_whitespace = False + char_to_word_offset.append(len(doc_tokens) - 1) + return (doc_tokens, char_to_word_offset) + + + def process_one_example(qa, is_training, version_2_with_negative, doc_tokens, char_to_word_offset): + qas_id = qa["id"] + question_text = qa["question"] + start_position = -1 + end_position = -1 + orig_answer_text = "" + is_impossible = False + if is_training: + if version_2_with_negative: + is_impossible = qa["is_impossible"] + if (len(qa["answers"]) != 1) and (not is_impossible): + raise ValueError("For training, each question should have exactly 1 answer.") + if not is_impossible: + answer = qa["answers"][0] + orig_answer_text = answer["text"] + answer_offset = answer["answer_start"] + answer_length = len(orig_answer_text) + start_position = char_to_word_offset[answer_offset] + end_position = char_to_word_offset[answer_offset + answer_length - 1] + actual_text = " ".join(doc_tokens[start_position:(end_position + 1)]) + cleaned_answer_text = " ".join(tokenization.whitespace_tokenize(orig_answer_text)) + if actual_text.find(cleaned_answer_text) == -1: + return None + example = SquadExample( + qas_id=qas_id, + question_text=question_text, + doc_tokens=doc_tokens, + orig_answer_text=orig_answer_text, + start_position=start_position, + end_position=end_position, + is_impossible=is_impossible) + return example + + + examples = [] + for entry in input_data: + for paragraph in entry["paragraphs"]: + paragraph_text = paragraph["context"] + doc_tokens, char_to_word_offset = token_offset(paragraph_text) + for qa in paragraph["qas"]: + one_example = process_one_example(qa, is_training, version_2_with_negative, + doc_tokens, char_to_word_offset) + if one_example is not None: + examples.append(one_example) + return examples + +def _check_is_max_context(doc_spans, cur_span_index, position): + """Check if this is the 'max context' doc span for the token.""" + best_score = None + best_span_index = None + for (span_index, doc_span) in enumerate(doc_spans): + end = doc_span.start + doc_span.length - 1 + if position < doc_span.start: + continue + if position > end: + continue + num_left_context = position - doc_span.start + num_right_context = end - position + score = min(num_left_context, num_right_context) + 0.01 * doc_span.length + if best_score is None or score > best_score: + best_score = score + best_span_index = span_index + return cur_span_index == best_span_index + +def _improve_answer_span(doc_tokens, input_start, input_end, tokenizer, + orig_answer_text): + """Returns tokenized answer spans that better match the annotated answer.""" + tok_answer_text = " ".join(tokenizer.tokenize(orig_answer_text)) + + for new_start in range(input_start, input_end + 1): + for new_end in range(input_end, new_start - 1, -1): + text_span = " ".join(doc_tokens[new_start:(new_end + 1)]) + if text_span == tok_answer_text: + return (new_start, new_end) + + return (input_start, input_end) + + +def convert_examples_to_features(examples, tokenizer, max_seq_length, doc_stride, + max_query_length, is_training, output_fn, vocab_file): + """Loads a data file into a list of `InputBatch`s.""" + unique_id = 1000000000 + output = [] + for (example_index, example) in enumerate(examples): + query_tokens = tokenizer.tokenize(example.question_text) + + if len(query_tokens) > max_query_length: + query_tokens = query_tokens[0:max_query_length] + + tok_to_orig_index = [] + orig_to_tok_index = [] + all_doc_tokens = [] + for (i, token) in enumerate(example.doc_tokens): + orig_to_tok_index.append(len(all_doc_tokens)) + sub_tokens = tokenizer.tokenize(token) + for sub_token in sub_tokens: + tok_to_orig_index.append(i) + all_doc_tokens.append(sub_token) + + tok_start_position = None + tok_end_position = None + if is_training and example.is_impossible: + tok_start_position = -1 + tok_end_position = -1 + if is_training and not example.is_impossible: + tok_start_position = orig_to_tok_index[example.start_position] + if example.end_position < len(example.doc_tokens) - 1: + tok_end_position = orig_to_tok_index[example.end_position + 1] - 1 + else: + tok_end_position = len(all_doc_tokens) - 1 + (tok_start_position, tok_end_position) = _improve_answer_span( + all_doc_tokens, tok_start_position, tok_end_position, tokenizer, + example.orig_answer_text) + + # The -3 accounts for [CLS], [SEP] and [SEP] + max_tokens_for_doc = max_seq_length - len(query_tokens) - 3 + + # We can have documents that are longer than the maximum sequence length. + # To deal with this we do a sliding window approach, where we take chunks + # of the up to our max length with a stride of `doc_stride`. + _DocSpan = collections.namedtuple("DocSpan", ["start", "length"]) + doc_spans = [] + start_offset = 0 + while start_offset < len(all_doc_tokens): + length = len(all_doc_tokens) - start_offset + if length > max_tokens_for_doc: + length = max_tokens_for_doc + doc_spans.append(_DocSpan(start=start_offset, length=length)) + if start_offset + length == len(all_doc_tokens): + break + start_offset += min(length, doc_stride) + + for (doc_span_index, doc_span) in enumerate(doc_spans): + tokens = [] + token_to_orig_map = {} + token_is_max_context = {} + segment_ids = [] + tokens.append("[CLS]") + segment_ids.append(0) + for token in query_tokens: + tokens.append(token) + segment_ids.append(0) + tokens.append("[SEP]") + segment_ids.append(0) + + for i in range(doc_span.length): + split_token_index = doc_span.start + i + token_to_orig_map[len(tokens)] = tok_to_orig_index[split_token_index] + + is_max_context = _check_is_max_context(doc_spans, doc_span_index, split_token_index) + token_is_max_context[len(tokens)] = is_max_context + tokens.append(all_doc_tokens[split_token_index]) + segment_ids.append(1) + tokens.append("[SEP]") + segment_ids.append(1) + + input_ids = tokenization.convert_tokens_to_ids(vocab_file, tokens) + + # The mask has 1 for real tokens and 0 for padding tokens. Only real + # tokens are attended to. + input_mask = [1] * len(input_ids) + + # Zero-pad up to the sequence length. + while len(input_ids) < max_seq_length: + input_ids.append(0) + input_mask.append(0) + segment_ids.append(0) + + assert len(input_ids) == max_seq_length + assert len(input_mask) == max_seq_length + assert len(segment_ids) == max_seq_length + + start_position = None + end_position = None + if is_training and not example.is_impossible: + # For training, if our document chunk does not contain an annotation + # we throw it out, since there is nothing to predict. + doc_start = doc_span.start + doc_end = doc_span.start + doc_span.length - 1 + out_of_span = False + if not (tok_start_position >= doc_start and tok_end_position <= doc_end): + out_of_span = True + if out_of_span: + start_position = 0 + end_position = 0 + else: + doc_offset = len(query_tokens) + 2 + start_position = tok_start_position - doc_start + doc_offset + end_position = tok_end_position - doc_start + doc_offset + + if is_training and example.is_impossible: + start_position = 0 + end_position = 0 + + feature = InputFeatures( + unique_id=unique_id, + example_index=example_index, + doc_span_index=doc_span_index, + tokens=tokens, + token_to_orig_map=token_to_orig_map, + token_is_max_context=token_is_max_context, + input_ids=input_ids, + input_mask=input_mask, + segment_ids=segment_ids, + start_position=start_position, + end_position=end_position, + is_impossible=example.is_impossible) + + # Run callback + output.append(feature) + unique_id += 1 + return output diff --git a/nlp/language_model/bert/MindSpore/infer_squad/utils/data_precess_squad.py b/nlp/language_model/bert/MindSpore/infer_squad/utils/data_precess_squad.py new file mode 100644 index 0000000000000000000000000000000000000000..7eb3aa9501ae042c97ddf05529d3c4fba102c617 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/utils/data_precess_squad.py @@ -0,0 +1,120 @@ +# 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. +# ============================================================================ + +'''squad data precess''' +import argparse +import os +import pickle +from create_squad_data import read_squad_examples, convert_examples_to_features +import numpy as np +import tokenization + +def parse_args(): + """set parameters.""" + parser = argparse.ArgumentParser(description="bert preprocess") + parser.add_argument("--vocab_path", type=str, default="../data/config/vocab.txt") + parser.add_argument("--dev_path", type=str, default="../data/dev.json") + parser.add_argument("--max_seq_len", type=int, default=128, help="sentence length, default is 128.") + parser.add_argument("--output_path", type=str, default="../data/input", help="the path of convert dataset.") + + args = parser.parse_args() + return args + + +def get_all_path(output_path): + """ + Args: + output_path: save path of convert dataset + Returns: + the path of ids, mask, token, label + """ + ids_path = os.path.join(output_path, "00_data")#input_ids + mask_path = os.path.join(output_path, "01_data")#input_mask + token_path = os.path.join(output_path, "02_data")#segment_ids + label_path = os.path.join(output_path, "03_data")#unique_id + tokens_path = os.path.join(output_path, "04_data")#04_tokens + token_to_orig_map_path = os.path.join(output_path, "05_data")#token_to_orig_map + token_is_max_context_path = os.path.join(output_path, "06_data")#token_is_max_context + doc_tokens_path = os.path.join(output_path, "07_data")#doc_tokens + qas_id_path = os.path.join(output_path, "08_data")#qas_id + example_index_path = os.path.join(output_path, "09_data")#example_index + + for path in [ids_path, mask_path, token_path, label_path, tokens_path, token_to_orig_map_path, + token_is_max_context_path, doc_tokens_path, qas_id_path, example_index_path]: + os.makedirs(path, 0o755, exist_ok=True) + + return ids_path, mask_path, token_path, label_path, tokens_path, token_to_orig_map_path, \ + token_is_max_context_path, doc_tokens_path, qas_id_path, example_index_path + + +def run(): + '''main function''' + args = parse_args() + input_ids, input_mask, segment_ids, unique_id, tokens, token_to_orig_map, token_is_max_context, doc_tokens, \ + qas_id, example_index = get_all_path(args.output_path) + tokenizer = tokenization.FullTokenizer(vocab_file=args.vocab_path, do_lower_case=True) + eval_examples = read_squad_examples(args.dev_path, False) + eval_features = convert_examples_to_features( + examples=eval_examples, + tokenizer=tokenizer, + max_seq_length=384, + doc_stride=128, + max_query_length=64, + is_training=False, + output_fn=None, + vocab_file=args.vocab_path) + + for i in range(len(eval_examples)): + file_name = "squad_bs" + "_" + str(i) + ".bin" + qas_id_path = os.path.join(qas_id, file_name) + with open(qas_id_path, "wb") as f: + pickle.dump(eval_examples[i].qas_id, f) + + doc_tokens_path = os.path.join(doc_tokens, file_name) + with open(doc_tokens_path, "wb") as f: + pickle.dump(eval_examples[i].doc_tokens, f) + + for i in range(len(eval_features)): + file_name = "squad_bs" + "_" + str(i) + ".bin" + ids_file_path = os.path.join(input_ids, file_name) + np.array(eval_features[i].input_ids, dtype=np.int32).tofile(ids_file_path) + + input_mask_path = os.path.join(input_mask, file_name) + np.array(eval_features[i].input_mask, dtype=np.int32).tofile(input_mask_path) + + segment_ids_path = os.path.join(segment_ids, file_name) + np.array(eval_features[i].segment_ids, dtype=np.int32).tofile(segment_ids_path) + + unique_id_path = os.path.join(unique_id, file_name) + np.array(eval_features[i].unique_id, dtype=np.int32).tofile(unique_id_path) + + tokens_path = os.path.join(tokens, file_name) + with open(tokens_path, "wb") as f: + pickle.dump(eval_features[i].tokens, f) + + token_to_orig_map_path = os.path.join(token_to_orig_map, file_name) + with open(token_to_orig_map_path, "wb") as f: + pickle.dump(eval_features[i].token_to_orig_map, f) + + token_is_max_context_path = os.path.join(token_is_max_context, file_name) + with open(token_is_max_context_path, "wb") as f: + pickle.dump(eval_features[i].token_is_max_context, f) + + example_index_path = os.path.join(example_index, file_name) + np.array(eval_features[i].example_index, dtype=np.int32).tofile(example_index_path) + + +if __name__ == "__main__": + run() diff --git a/nlp/language_model/bert/MindSpore/infer_squad/utils/tokenization.py b/nlp/language_model/bert/MindSpore/infer_squad/utils/tokenization.py new file mode 100644 index 0000000000000000000000000000000000000000..9a101507c63ea5751c35af2886ccea01e14faa49 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/infer_squad/utils/tokenization.py @@ -0,0 +1,329 @@ +# 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. +# ============================================================================ + +""" +Tokenization. +""" + +import unicodedata +import collections + +def convert_to_unicode(text): + """ + Convert text into unicode type. + Args: + text: input str. + + Returns: + input str in unicode. + """ + ret = text + if isinstance(text, str): + ret = text + elif isinstance(text, bytes): + ret = text.decode("utf-8", "ignore") + else: + raise ValueError("Unsupported string type: %s" % (type(text))) + return ret + + +def vocab_to_dict_key_token(vocab_file): + """Loads a vocab file into a dict, key is token.""" + vocab = collections.OrderedDict() + index = 0 + with open(vocab_file, "rb") as reader: + while True: + token = convert_to_unicode(reader.readline()) + if not token: + break + token = token.strip() + vocab[token] = index + index += 1 + return vocab + + +def vocab_to_dict_key_id(vocab_file): + """Loads a vocab file into a dict, key is id.""" + vocab = collections.OrderedDict() + index = 0 + with open(vocab_file, "r") as reader: + while True: + token = convert_to_unicode(reader.readline()) + if not token: + break + token = token.strip() + vocab[index] = token + index += 1 + return vocab + + +def whitespace_tokenize(text): + """Runs basic whitespace cleaning and splitting on a piece of text.""" + text = text.strip() + if not text: + return [] + tokens = text.split() + return tokens + + +def convert_tokens_to_ids(vocab_file, tokens): + """ + Convert tokens to ids. + Args: + vocab_file: path to vocab.txt. + tokens: list of tokens. + + Returns: + list of ids. + """ + vocab_dict = vocab_to_dict_key_token(vocab_file) + output = [] + for token in tokens: + output.append(vocab_dict[token]) + return output + + +def convert_ids_to_tokens(vocab_file, ids): + """ + Convert ids to tokens. + Args: + vocab_file: path to vocab.txt. + ids: list of ids. + + Returns: + list of tokens. + """ + vocab_dict = vocab_to_dict_key_id(vocab_file) + output = [] + for _id in ids: + output.append(vocab_dict[_id]) + return output + + +class FullTokenizer(): + """ + Full tokenizer + """ + def __init__(self, vocab_file, do_lower_case=True): + self.vocab_dict = vocab_to_dict_key_token(vocab_file) + self.do_lower_case = do_lower_case + self.basic_tokenize = BasicTokenizer(do_lower_case) + self.wordpiece_tokenize = WordpieceTokenizer(self.vocab_dict) + + def tokenize(self, text): + """ + Do full tokenization. + Args: + text: str of text. + + Returns: + list of tokens. + """ + tokens_ret = [] + text = convert_to_unicode(text) + for tokens in self.basic_tokenize.tokenize(text): + wordpiece_tokens = self.wordpiece_tokenize.tokenize(tokens) + tokens_ret.extend(wordpiece_tokens) + return tokens_ret + + +class BasicTokenizer(): + """ + Basic tokenizer + """ + def __init__(self, do_lower_case=True): + self.do_lower_case = do_lower_case + + def tokenize(self, text): + """ + Do basic tokenization. + Args: + text: text in unicode. + + Returns: + a list of tokens split from text + """ + text = self._clean_text(text) + text = self._tokenize_chinese_chars(text) + + orig_tokens = whitespace_tokenize(text) + split_tokens = [] + for token in orig_tokens: + if self.do_lower_case: + token = token.lower() + token = self._run_strip_accents(token) + aaa = self._run_split_on_punc(token) + split_tokens.extend(aaa) + + output_tokens = whitespace_tokenize(" ".join(split_tokens)) + return output_tokens + + def _run_strip_accents(self, text): + """Strips accents from a piece of text.""" + text = unicodedata.normalize("NFD", text) + output = [] + for char in text: + cat = unicodedata.category(char) + if cat == "Mn": + continue + output.append(char) + return "".join(output) + + def _run_split_on_punc(self, text): + """Splits punctuation on a piece of text.""" + i = 0 + start_new_word = True + output = [] + for char in text: + if _is_punctuation(char): + output.append([char]) + start_new_word = True + else: + if start_new_word: + output.append([]) + start_new_word = False + output[-1].append(char) + i += 1 + return ["".join(x) for x in output] + + def _clean_text(self, text): + """Performs invalid character removal and whitespace cleanup on text.""" + output = [] + for char in text: + cp = ord(char) + if cp == 0 or cp == 0xfffd or _is_control(char): + continue + if _is_whitespace(char): + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _tokenize_chinese_chars(self, text): + """Adds whitespace around any CJK character.""" + output = [] + for char in text: + cp = ord(char) + if self._is_chinese_char(cp): + output.append(" ") + output.append(char) + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _is_chinese_char(self, cp): + """Checks whether CP is the codepoint of a CJK character.""" + # This defines a "chinese character" as anything in the CJK Unicode block: + # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) + # + # Note that the CJK Unicode block is NOT all Japanese and Korean characters, + # despite its name. The modern Korean Hangul alphabet is a different block, + # as is Japanese Hiragana and Katakana. Those alphabets are used to write + # space-separated words, so they are not treated specially and handled + # like the all of the other languages. + if ((0x4E00 <= cp <= 0x9FFF) or + (0x3400 <= cp <= 0x4DBF) or + (0x20000 <= cp <= 0x2A6DF) or + (0x2A700 <= cp <= 0x2B73F) or + (0x2B740 <= cp <= 0x2B81F) or + (0x2B820 <= cp <= 0x2CEAF) or + (0xF900 <= cp <= 0xFAFF) or + (0x2F800 <= cp <= 0x2FA1F)): + return True + + return False + + +class WordpieceTokenizer(): + """ + Wordpiece tokenizer + """ + def __init__(self, vocab): + self.vocab_dict = vocab + + def tokenize(self, tokens): + """ + Do word-piece tokenization + Args: + tokens: a word. + + Returns: + a list of tokens that can be found in vocab dict. + """ + output_tokens = [] + tokens = convert_to_unicode(tokens) + for token in whitespace_tokenize(tokens): + chars = list(token) + len_chars = len(chars) + start = 0 + end = len_chars + while start < len_chars: + while start < end: + substr = "".join(token[start:end]) + if start != 0: + substr = "##" + substr + if substr in self.vocab_dict: + output_tokens.append(substr) + start = end + end = len_chars + else: + end = end - 1 + if start == end and start != len_chars: + output_tokens.append("[UNK]") + break + return output_tokens + + +def _is_whitespace(char): + """Checks whether `chars` is a whitespace character.""" + # \t, \n, and \r are technically control characters but we treat them + # as whitespace since they are generally considered as such. + whitespace_char = [" ", "\t", "\n", "\r"] + if char in whitespace_char: + return True + cat = unicodedata.category(char) + if cat == "Zs": + return True + return False + + +def _is_control(char): + """Checks whether `chars` is a control character.""" + # These are technically control characters but we count them as whitespace + # characters. + control_char = ["\t", "\n", "\r"] + if char in control_char: + return False + cat = unicodedata.category(char) + if cat in ("Cc", "Cf"): + return True + return False + + +def _is_punctuation(char): + """Checks whether `chars` is a punctuation character.""" + cp = ord(char) + # We treat all non-letter/number ASCII as punctuation. + # Characters such as "^", "$", and "`" are not in the Unicode + # Punctuation class but we treat them as punctuation anyways, for + # consistency. + if ((33 <= cp <= 47) or (58 <= cp <= 64) or + (91 <= cp <= 96) or (123 <= cp <= 126)): + return True + cat = unicodedata.category(char) + if cat.startswith("P"): + return True + return False diff --git a/nlp/language_model/bert/MindSpore/mindspore_hub_conf.py b/nlp/language_model/bert/MindSpore/mindspore_hub_conf.py new file mode 100644 index 0000000000000000000000000000000000000000..d4f7eb88208c1384ce4000254efd48555fa56229 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/mindspore_hub_conf.py @@ -0,0 +1,69 @@ +# Copyright 2020 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. +# ============================================================================ +''' +Bert hub interface for bert base and bert nezha +''' +from src.bert_model import BertModel +from src.bert_model import BertConfig +import mindspore.common.dtype as mstype +bert_net_cfg_base = BertConfig( + seq_length=128, + vocab_size=21128, + hidden_size=768, + num_hidden_layers=12, + num_attention_heads=12, + intermediate_size=3072, + hidden_act="gelu", + hidden_dropout_prob=0.1, + attention_probs_dropout_prob=0.1, + max_position_embeddings=512, + type_vocab_size=2, + initializer_range=0.02, + use_relative_positions=False, + dtype=mstype.float32, + compute_type=mstype.float16 +) +bert_net_cfg_nezha = BertConfig( + seq_length=128, + vocab_size=21128, + hidden_size=1024, + num_hidden_layers=24, + num_attention_heads=16, + intermediate_size=4096, + hidden_act="gelu", + hidden_dropout_prob=0.1, + attention_probs_dropout_prob=0.1, + max_position_embeddings=512, + type_vocab_size=2, + initializer_range=0.02, + use_relative_positions=True, + dtype=mstype.float32, + compute_type=mstype.float16 +) +def create_network(name, *args, **kwargs): + ''' + Create bert network for base and nezha. + ''' + if name == 'bert_base': + if "seq_length" in kwargs: + bert_net_cfg_base.seq_length = kwargs["seq_length"] + is_training = kwargs.get("is_training", False) + return BertModel(bert_net_cfg_base, is_training, *args) + if name == 'bert_nezha': + if "seq_length" in kwargs: + bert_net_cfg_nezha.seq_length = kwargs["seq_length"] + is_training = kwargs.get("is_training", False) + return BertModel(bert_net_cfg_nezha, is_training, *args) + raise NotImplementedError(f"{name} is not implemented in the repo") diff --git a/nlp/language_model/bert/MindSpore/modelarts/pretrain_config_modelart.yaml b/nlp/language_model/bert/MindSpore/modelarts/pretrain_config_modelart.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bc79a4a4795b9c87a5df2268ea50a3798ffc3656 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/modelarts/pretrain_config_modelart.yaml @@ -0,0 +1,200 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path" +device_target: "Ascend" +enable_profiling: False + +# ============================================================================== +description: 'run_pretrain' +distribute: 'false' +epoch_size: 40 +device_id: 0 +device_num: 1 +enable_save_ckpt: 'true' +enable_lossscale: 'true' +do_shuffle: 'true' +enable_data_sink: 'true' +data_sink_steps: 1 +accumulation_steps: 1 +allreduce_post_accumulation: 'true' +save_checkpoint_path: '' +load_checkpoint_path: '' +save_checkpoint_steps: 10000 +train_steps: -1 +save_checkpoint_num: 1 +data_dir: '' +schema_dir: '' + +# ============================================================================== +# pretrain related +batch_size: 32 +# Available: [base, nezha, large, large_boost] +bert_network: 'base' +loss_scale_value: 65536 +scale_factor: 2 +scale_window: 1000 +optimizer: 'Lamb' +enable_global_norm: False +# pretrain_eval related +train_with_eval: 'false' +eval_data_dir: "" +schema_file: "" +eval_ckpt: "" +eval_samples: 300000 +# bucket list, default: [] +bucket_list: [] +# optimizer related +AdamWeightDecay: + learning_rate: 0.00003 # 3e-5 + end_learning_rate: 0.0 + power: 5.0 + weight_decay: 0.00001 # 1e-5 + decay_filter: ['layernorm', 'bias'] + eps: 0.000001 # 1e-6 + warmup_steps: 10000 + +Lamb: + learning_rate: 0.0003 # 3e-4 + end_learning_rate: 0.0 + power: 2.0 + warmup_steps: 10000 + weight_decay: 0.01 + decay_filter: ['layernorm', 'bias'] + eps: 0.00000001 # 1e-8, + beta1: 0.99 + beta2: 0.999 + +Momentum: + learning_rate: 0.00002 # 2e-5 + momentum: 0.9 + +Thor: + lr_max: 0.006464 + lr_min: 0.000001 # 1e-6 + lr_power: 2.0 + lr_total_steps: 30000 + damping_max: 0.007035 + damping_min: 0.000001 # 1e-6 + damping_power: 4.0 + damping_total_steps: 30000 + momentum: 0.9 + weight_decay: 0.00001 # 1e-5 + loss_scale: 1024.0 + frequency: 100 +# ============================================================================== +# base +base_batch_size: 256 +base_net_cfg: + seq_length: 128 + vocab_size: 30522 + hidden_size: 768 + num_hidden_layers: 12 + num_attention_heads: 12 + intermediate_size: 3072 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 +# nezha +nezha_batch_size: 96 +nezha_net_cfg: + seq_length: 128 + vocab_size: 21128 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: True + dtype: mstype.float32 + compute_type: mstype.float16 +# large +large_batch_size: 24 +large_net_cfg: + seq_length: 512 + vocab_size: 30522 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 +# Accelerated large network which is only supported in Ascend yet. +large_boost_batch_size: 24 +large_boost_net_cfg: + seq_length: 512 + vocab_size: 30522 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "fast_gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 + + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Url for modelarts" +train_url: "Url for modelarts" +data_path: "The location of the input data." +output_path: "The location of the output file." +device_target: "Running platform, choose from Ascend or CPU, and default is Ascend." +enable_profiling: 'Whether enable profiling while training, default: False' + +distribute: "Run distribute, default is 'false'." +epoch_size: "Epoch size, default is 1." +enable_save_ckpt: "Enable save checkpoint, default is true." +enable_lossscale: "Use lossscale or not, default is not." +do_shuffle: "Enable shuffle for dataset, default is true." +enable_data_sink: "Enable data sink, default is true." +data_sink_steps: "Sink steps for each epoch, default is 1." +accumulation_steps: "Accumulating gradients N times before weight update, default is 1." +allreduce_post_accumulation: "Whether to allreduce after accumulation of N steps or after each step, default is true." +save_checkpoint_path: "Save checkpoint path" +load_checkpoint_path: "Load checkpoint file path" +save_checkpoint_steps: "Save checkpoint steps, default is 1000" +train_steps: "Training Steps, default is -1, meaning run all steps according to epoch number." +save_checkpoint_num: "Save checkpoint numbers, default is 1." +data_dir: "Data path, it is better to use absolute path" +schema_dir: "Schema path, it is better to use absolute path" +--- +# chocies +device_target: ['Ascend', 'GPU'] +distribute: ["true", "false"] +enable_save_ckpt: ["true", "false"] +enable_lossscale: ["true", "false"] +do_shuffle: ["true", "false"] +enable_data_sink: ["true", "false"] +allreduce_post_accumulation: ["true", "false"] diff --git a/nlp/language_model/bert/MindSpore/modelarts/train_start.py b/nlp/language_model/bert/MindSpore/modelarts/train_start.py new file mode 100644 index 0000000000000000000000000000000000000000..88eb9a85410fe983d0ccc02e9f260e9ecf34553a --- /dev/null +++ b/nlp/language_model/bert/MindSpore/modelarts/train_start.py @@ -0,0 +1,274 @@ +# 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. +# ============================================================================ + +''' +Bert finetune and evaluation script. +''' +import os +import collections +import shutil +import mindspore.common.dtype as mstype +from mindspore import log as logger +from mindspore import Tensor, context, load_checkpoint, export +from mindspore.nn.wrap.loss_scale import DynamicLossScaleUpdateCell +from mindspore.nn.optim import AdamWeightDecay, Lamb, Momentum +from mindspore.train.model import Model +from mindspore.train.callback import CheckpointConfig, ModelCheckpoint, TimeMonitor +from mindspore.train.serialization import load_param_into_net + +from src.bert_for_finetune import BertSquadCell, BertSquad +from src.dataset import create_squad_dataset +from src.utils import make_directory, LossCallBack, LoadNewestCkpt, BertLearningRate, convert_labels_to_index +from src.model_utils.config import config as args_opt, optimizer_cfg, bert_net_cfg +from src.model_utils.moxing_adapter import moxing_wrapper +from src.model_utils.device_adapter import get_device_id +from src.finetune_eval_model import BertCLSModel, BertSquadModel, BertNERModel +from src.bert_for_finetune import BertNER + +import numpy as np + + +_cur_dir = os.getcwd() + + +def do_train(dataset=None, network=None, load_checkpoint_path="", save_checkpoint_path="", epoch_num=1): + """ do train """ + if load_checkpoint_path == "": + raise ValueError("Pretrain model missed, finetune task must load pretrain model!") + steps_per_epoch = dataset.get_dataset_size() + # optimizer + if optimizer_cfg.optimizer == 'AdamWeightDecay': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.AdamWeightDecay.learning_rate, + end_learning_rate=optimizer_cfg.AdamWeightDecay.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.AdamWeightDecay.power) + params = network.trainable_params() + decay_params = list(filter(optimizer_cfg.AdamWeightDecay.decay_filter, params)) + other_params = list(filter(lambda x: not optimizer_cfg.AdamWeightDecay.decay_filter(x), params)) + group_params = [{'params': decay_params, 'weight_decay': optimizer_cfg.AdamWeightDecay.weight_decay}, + {'params': other_params, 'weight_decay': 0.0}] + + optimizer = AdamWeightDecay(group_params, lr_schedule, eps=optimizer_cfg.AdamWeightDecay.eps) + elif optimizer_cfg.optimizer == 'Lamb': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.Lamb.learning_rate, + end_learning_rate=optimizer_cfg.Lamb.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.Lamb.power) + optimizer = Lamb(network.trainable_params(), learning_rate=lr_schedule) + elif optimizer_cfg.optimizer == 'Momentum': + optimizer = Momentum(network.trainable_params(), learning_rate=optimizer_cfg.Momentum.learning_rate, + momentum=optimizer_cfg.Momentum.momentum) + else: + raise Exception("Optimizer not supported. support: [AdamWeightDecay, Lamb, Momentum]") + + # load checkpoint into network + ckpt_config = CheckpointConfig(save_checkpoint_steps=steps_per_epoch, keep_checkpoint_max=1) + ckpoint_cb = ModelCheckpoint(prefix="squad", + directory=None if save_checkpoint_path == "" else save_checkpoint_path, + config=ckpt_config) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(network, param_dict) + + update_cell = DynamicLossScaleUpdateCell(loss_scale_value=2 ** 32, scale_factor=2, scale_window=1000) + netwithgrads = BertSquadCell(network, optimizer=optimizer, scale_update_cell=update_cell) + model = Model(netwithgrads) + callbacks = [TimeMonitor(dataset.get_dataset_size()), LossCallBack(dataset.get_dataset_size()), ckpoint_cb] + model.train(epoch_num, dataset, callbacks=callbacks) + + +def do_eval(dataset=None, load_checkpoint_path="", eval_batch_size=1): + """ do eval """ + if load_checkpoint_path == "": + raise ValueError("Finetune model missed, evaluation task must load finetune model!") + net = BertSquad(bert_net_cfg, False, 2) + net.set_train(False) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(net, param_dict) + model = Model(net) + output = [] + RawResult = collections.namedtuple("RawResult", ["unique_id", "start_logits", "end_logits"]) + columns_list = ["input_ids", "input_mask", "segment_ids", "unique_ids"] + for data in dataset.create_dict_iterator(num_epochs=1): + input_data = [] + for i in columns_list: + input_data.append(data[i]) + input_ids, input_mask, segment_ids, unique_ids = input_data + start_positions = Tensor([1], mstype.float32) + end_positions = Tensor([1], mstype.float32) + is_impossible = Tensor([1], mstype.float32) + logits = model.predict(input_ids, input_mask, segment_ids, start_positions, + end_positions, unique_ids, is_impossible) + ids = logits[0].asnumpy() + start = logits[1].asnumpy() + end = logits[2].asnumpy() + + for i in range(eval_batch_size): + unique_id = int(ids[i]) + start_logits = [float(x) for x in start[i].flat] + end_logits = [float(x) for x in end[i].flat] + output.append(RawResult( + unique_id=unique_id, + start_logits=start_logits, + end_logits=end_logits)) + return output + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + args_opt.device_id = get_device_id() + _file_dir = os.path.dirname(os.path.abspath(__file__)) + args_opt.load_pretrain_checkpoint_path = os.path.join(_file_dir, args_opt.load_pretrain_checkpoint_path) + args_opt.load_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.load_finetune_checkpoint_path) + args_opt.save_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.save_finetune_checkpoint_path) + args_opt.vocab_file_path = os.path.join(args_opt.data_path, args_opt.vocab_file_path) + if args_opt.schema_file_path: + args_opt.schema_file_path = os.path.join(args_opt.data_path, args_opt.schema_file_path) + args_opt.train_data_file_path = os.path.join(args_opt.data_path, args_opt.train_data_file_path) + args_opt.eval_json_path = os.path.join(args_opt.data_path, args_opt.eval_json_path) + +def _get_last_ckpt(ckpt_dir): + ckpt_files = [ckpt_file for ckpt_file in os.listdir(ckpt_dir) + if ckpt_file.endswith('.ckpt')] + if not ckpt_files: + print("No ckpt file found.") + return None + + return os.path.join(ckpt_dir, sorted(ckpt_files)[-1]) + + +def run_export(ckpt_dir): + '''export function''' + ckpt_file = _get_last_ckpt(ckpt_dir) + context.set_context(mode=context.GRAPH_MODE, device_target=args_opt.device_target) + if args_opt.device_target == "Ascend": + context.set_context(device_id=args_opt.device_id) + + if args_opt.description == "run_ner": + label_list = [] + with open(args_opt.label_file_path) as f: + for label in f: + label_list.append(label.strip()) + + tag_to_index = convert_labels_to_index(label_list) + + if args_opt.use_crf.lower() == "true": + max_val = max(tag_to_index.values()) + tag_to_index[""] = max_val + 1 + tag_to_index[""] = max_val + 2 + number_labels = len(tag_to_index) + net = BertNER(bert_net_cfg, args_opt.export_batch_size, False, num_labels=number_labels, + use_crf=True, tag_to_index=tag_to_index) + else: + number_labels = len(tag_to_index) + net = BertNERModel(bert_net_cfg, False, number_labels, use_crf=(args_opt.use_crf.lower() == "true")) + elif args_opt.description == "run_classifier": + net = BertCLSModel(bert_net_cfg, False, num_labels=args_opt.num_class) + elif args_opt.description == "run_squad": + net = BertSquadModel(bert_net_cfg, False) + else: + raise ValueError("unsupported downstream task") + + load_checkpoint(ckpt_file, net=net) + net.set_train(False) + + input_ids = Tensor(np.zeros([args_opt.export_batch_size, bert_net_cfg.seq_length]), mstype.int32) + input_mask = Tensor(np.zeros([args_opt.export_batch_size, bert_net_cfg.seq_length]), mstype.int32) + token_type_id = Tensor(np.zeros([args_opt.export_batch_size, bert_net_cfg.seq_length]), mstype.int32) + label_ids = Tensor(np.zeros([args_opt.export_batch_size, bert_net_cfg.seq_length]), mstype.int32) + + if args_opt.description == "run_ner" and args_opt.use_crf.lower() == "true": + input_data = [input_ids, input_mask, token_type_id, label_ids] + else: + input_data = [input_ids, input_mask, token_type_id] + export(net, *input_data, file_name=args_opt.export_file_name, file_format=args_opt.file_format) + if args_opt.enable_modelarts: + air_file = f"{args_opt.export_file_name}.{args_opt.file_format.lower()}" + shutil.move(air_file, args_opt.output_path) + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_squad(): + """run squad task""" + if args_opt.do_train.lower() == "false" and args_opt.do_eval.lower() == "false": + raise ValueError("At least one of 'do_train' or 'do_eval' must be true") + if args_opt.do_train.lower() == "true" and args_opt.train_data_file_path == "": + raise ValueError("'train_data_file_path' must be set when do finetune task") + if args_opt.do_eval.lower() == "true": + if args_opt.vocab_file_path == "": + raise ValueError("'vocab_file_path' must be set when do evaluation task") + if args_opt.eval_json_path == "": + raise ValueError("'tokenization_file_path' must be set when do evaluation task") + epoch_num = args_opt.epoch_num + load_pretrain_checkpoint_path = args_opt.load_pretrain_checkpoint_path + save_finetune_checkpoint_path = args_opt.save_finetune_checkpoint_path + load_finetune_checkpoint_path = args_opt.load_finetune_checkpoint_path + target = args_opt.device_target + if target == "Ascend": + context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", device_id=args_opt.device_id) + elif target == "GPU": + context.set_context(mode=context.GRAPH_MODE, device_target="GPU") + context.set_context(enable_graph_kernel=True) + if bert_net_cfg.compute_type != mstype.float32: + logger.warning('GPU only support fp32 temporarily, run with fp32.') + bert_net_cfg.compute_type = mstype.float32 + else: + raise Exception("Target error, GPU or Ascend is supported.") + + netwithloss = BertSquad(bert_net_cfg, True, 2, dropout_prob=0.1) + + if args_opt.do_train.lower() == "true": + ds = create_squad_dataset(batch_size=args_opt.train_batch_size, + data_file_path=args_opt.train_data_file_path, + schema_file_path=args_opt.schema_file_path, + do_shuffle=(args_opt.train_data_shuffle.lower() == "true")) + do_train(ds, netwithloss, load_pretrain_checkpoint_path, save_finetune_checkpoint_path, epoch_num) + if args_opt.do_eval.lower() == "true": + if save_finetune_checkpoint_path == "": + load_finetune_checkpoint_dir = _cur_dir + else: + load_finetune_checkpoint_dir = make_directory(save_finetune_checkpoint_path) + load_finetune_checkpoint_path = LoadNewestCkpt(load_finetune_checkpoint_dir, "squad") + + if args_opt.do_eval.lower() == "true": + from src import tokenization + from src.create_squad_data import read_squad_examples, convert_examples_to_features + from src.squad_get_predictions import write_predictions + from src.squad_postprocess import SQuad_postprocess + tokenizer = tokenization.FullTokenizer(vocab_file=args_opt.vocab_file_path, do_lower_case=True) + eval_examples = read_squad_examples(args_opt.eval_json_path, False) + eval_features = convert_examples_to_features( + examples=eval_examples, + tokenizer=tokenizer, + max_seq_length=bert_net_cfg.seq_length, + doc_stride=128, + max_query_length=64, + is_training=False, + output_fn=None, + vocab_file=args_opt.vocab_file_path) + ds = create_squad_dataset(batch_size=args_opt.eval_batch_size, + data_file_path=eval_features, + schema_file_path=args_opt.schema_file_path, is_training=False, + do_shuffle=(args_opt.eval_data_shuffle.lower() == "true")) + outputs = do_eval(ds, load_finetune_checkpoint_path, args_opt.eval_batch_size) + all_predictions = write_predictions(eval_examples, eval_features, outputs, 20, 30, True) + SQuad_postprocess(args_opt.eval_json_path, all_predictions, output_metrics="output.json") + run_export(save_finetune_checkpoint_path) + + +if __name__ == "__main__": + run_squad() + \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/postprocess.py b/nlp/language_model/bert/MindSpore/postprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..7dc84f80af9715681242805dc6e9304c6ea83bb2 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/postprocess.py @@ -0,0 +1,100 @@ +# 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. +# ============================================================================ + +''' +postprocess script. +''' + +import os +import argparse +import numpy as np +from mindspore import Tensor +from src.assessment_method import Accuracy, F1, MCC, Spearman_Correlation + + +def eval_result_print(assessment_method_="accuracy", callback_=None): + """print eval result""" + if assessment_method_ == "accuracy": + print("acc_num {} , total_num {}, accuracy {:.6f}".format(callback_.acc_num, callback_.total_num, + callback_.acc_num / callback_.total_num)) + elif assessment_method_ == "bf1": + print("Precision {:.6f} ".format(callback_.TP / (callback_.TP + callback_.FP))) + print("Recall {:.6f} ".format(callback_.TP / (callback_.TP + callback_.FN))) + print("F1 {:.6f} ".format(2 * callback_.TP / (2 * callback_.TP + callback_.FP + callback_.FN))) + elif assessment_method_ == "mf1": + print("F1 {:.6f} ".format(callback_.eval()[0])) + elif assessment_method_ == "mcc": + print("MCC {:.6f} ".format(callback_.cal())) + elif assessment_method_ == "spearman_correlation": + print("Spearman Correlation is {:.6f} ".format(callback_.cal()[0])) + else: + raise ValueError("Assessment method not supported, support: [accuracy, f1, mcc, spearman_correlation]") + + +parser = argparse.ArgumentParser(description="postprocess") +parser.add_argument("--seq_length", type=int, default=128, help="seq_length, default is 128. You can get this value " + "through the relevant'*.yaml' filer") +parser.add_argument("--batch_size", type=int, default=1, help="Eval batch size, default is 1") +parser.add_argument("--label_dir", type=str, default="", help="label data dir") +parser.add_argument("--assessment_method", type=str, default="BF1", choices=["BF1", "clue_benchmark", "MF1"], + help="assessment_method include: [BF1, clue_benchmark, MF1], default is BF1") +parser.add_argument("--result_dir", type=str, default="./result_Files", help="infer result Files") +parser.add_argument("--use_crf", type=str, default="false", choices=["true", "false"], + help="Use crf, default is false") + +args, _ = parser.parse_known_args() + +if __name__ == "__main__": + num_class = 41 + assessment_method = args.assessment_method.lower() + use_crf = args.use_crf + + if assessment_method == "accuracy": + callback = Accuracy() + elif assessment_method == "bf1": + callback = F1((use_crf.lower() == "true"), num_class) + elif assessment_method == "mf1": + callback = F1((use_crf.lower() == "true"), num_labels=num_class, mode="MultiLabel") + elif assessment_method == "mcc": + callback = MCC() + elif assessment_method == "spearman_correlation": + callback = Spearman_Correlation() + else: + raise ValueError("Assessment method not supported, support: [accuracy, f1, mcc, spearman_correlation]") + + file_name = os.listdir(args.label_dir) + for f in file_name: + if use_crf.lower() == "true": + logits = () + for j in range(args.seq_length): + f_name = f.split('.')[0] + '_' + str(j) + '.bin' + data_tmp = np.fromfile(os.path.join(args.result_dir, f_name), np.int32) + data_tmp = data_tmp.reshape(args.batch_size, num_class + 2) + logits += ((Tensor(data_tmp),),) + f_name = f.split('.')[0] + '_' + str(args.seq_length) + '.bin' + data_tmp = np.fromfile(os.path.join(args.result_dir, f_name), np.int32).tolist() + data_tmp = Tensor(data_tmp) + logits = (logits, data_tmp) + else: + f_name = os.path.join(args.result_dir, f.split('.')[0] + '_0.bin') + logits = np.fromfile(f_name, np.float32).reshape(args.seq_length * args.batch_size, num_class) + logits = Tensor(logits) + label_ids = np.fromfile(os.path.join(args.label_dir, f), np.int32) + label_ids = Tensor(label_ids.reshape(args.batch_size, args.seq_length)) + callback.update(logits, label_ids) + + print("==============================================================") + eval_result_print(assessment_method, callback) + print("==============================================================") diff --git a/nlp/language_model/bert/MindSpore/preprocess.py b/nlp/language_model/bert/MindSpore/preprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..d1e427a4c0a59616f92fcdc55ef7dd57d7ff53f2 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/preprocess.py @@ -0,0 +1,97 @@ +# 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. +# ============================================================================ + +''' +Bert preprocess script. +''' + +import os +import argparse +from src.dataset import create_ner_dataset + + +def parse_args(): + """set and check parameters.""" + parser = argparse.ArgumentParser(description="bert preprocess") + parser.add_argument("--assessment_method", type=str, default="BF1", choices=["BF1", "clue_benchmark", "MF1"], + help="assessment_method include: [BF1, clue_benchmark, MF1], default is BF1") + parser.add_argument("--do_eval", type=str, default="false", choices=["true", "false"], + help="Eable eval, default is false") + parser.add_argument("--use_crf", type=str, default="false", choices=["true", "false"], + help="Use crf, default is false") + parser.add_argument("--eval_data_shuffle", type=str, default="false", choices=["true", "false"], + help="Enable eval data shuffle, default is false") + parser.add_argument("--eval_batch_size", type=int, default=1, help="Eval batch size, default is 1") + parser.add_argument("--vocab_file_path", type=str, default="", help="Vocab file path, used in clue benchmark") + parser.add_argument("--label_file_path", type=str, default="", help="label file path, used in clue benchmark") + parser.add_argument("--eval_data_file_path", type=str, default="", + help="Data path, it is better to use absolute path") + parser.add_argument("--dataset_format", type=str, default="mindrecord", choices=["mindrecord", "tfrecord"], + help="Dataset format, support mindrecord or tfrecord") + parser.add_argument("--schema_file_path", type=str, default="", + help="Schema path, it is better to use absolute path") + parser.add_argument('--result_path', type=str, default='./preprocess_Result/', help='result path') + + args_opt = parser.parse_args() + + if args_opt.do_eval.lower() == "true" and args_opt.eval_data_file_path == "": + raise ValueError("'eval_data_file_path' must be set when do evaluation task") + if args_opt.assessment_method.lower() == "clue_benchmark" and args_opt.vocab_file_path == "": + raise ValueError("'vocab_file_path' must be set to do clue benchmark") + if args_opt.use_crf.lower() == "true" and args_opt.label_file_path == "": + raise ValueError("'label_file_path' must be set to use crf") + if args_opt.assessment_method.lower() == "clue_benchmark" and args_opt.label_file_path == "": + raise ValueError("'label_file_path' must be set to do clue benchmark") + if args_opt.assessment_method.lower() == "clue_benchmark": + args_opt.eval_batch_size = 1 + return args_opt + + +if __name__ == "__main__": + args = parse_args() + assessment_method = args.assessment_method.lower() + if args.do_eval.lower() == "true": + ds = create_ner_dataset(batch_size=args.eval_batch_size, + assessment_method=assessment_method, data_file_path=args.eval_data_file_path, + schema_file_path=args.schema_file_path, dataset_format=args.dataset_format, + do_shuffle=(args.eval_data_shuffle.lower() == "true"), drop_remainder=False) + ids_path = os.path.join(args.result_path, "00_data") + mask_path = os.path.join(args.result_path, "01_data") + token_path = os.path.join(args.result_path, "02_data") + label_path = os.path.join(args.result_path, "03_data") + os.makedirs(ids_path) + os.makedirs(mask_path) + os.makedirs(token_path) + os.makedirs(label_path) + + for idx, data in enumerate(ds.create_dict_iterator(output_numpy=True, num_epochs=1)): + input_ids = data["input_ids"] + input_mask = data["input_mask"] + token_type_id = data["segment_ids"] + label_ids = data["label_ids"] + + file_name = "cluener_bs" + str(args.eval_batch_size) + "_" + str(idx) + ".bin" + ids_file_path = os.path.join(ids_path, file_name) + input_ids.tofile(ids_file_path) + + mask_file_path = os.path.join(mask_path, file_name) + input_mask.tofile(mask_file_path) + + token_file_path = os.path.join(token_path, file_name) + token_type_id.tofile(token_file_path) + + label_file_path = os.path.join(label_path, file_name) + label_ids.tofile(label_file_path) + print("=" * 20, "export bin files finished", "=" * 20) diff --git a/nlp/language_model/bert/MindSpore/pretrain_config.yaml b/nlp/language_model/bert/MindSpore/pretrain_config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0be21d5c93e16b960e2210a18f89b691419d2fcb --- /dev/null +++ b/nlp/language_model/bert/MindSpore/pretrain_config.yaml @@ -0,0 +1,200 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path" +device_target: "Ascend" +enable_profiling: False + +# ============================================================================== +description: 'run_pretrain' +distribute: 'false' +epoch_size: 40 +device_id: 0 +device_num: 1 +enable_save_ckpt: 'true' +enable_lossscale: 'true' +do_shuffle: 'true' +enable_data_sink: 'true' +data_sink_steps: 1 +accumulation_steps: 1 +allreduce_post_accumulation: 'true' +save_checkpoint_path: '' +load_checkpoint_path: '' +save_checkpoint_steps: 10000 +train_steps: -1 +save_checkpoint_num: 1 +data_dir: '' +schema_dir: '' + +# ============================================================================== +# pretrain related +batch_size: 32 +# Available: [base, nezha, large, large_boost] +bert_network: 'base' +loss_scale_value: 65536 +scale_factor: 2 +scale_window: 1000 +optimizer: 'Lamb' +enable_global_norm: False +# pretrain_eval related +train_with_eval: 'false' +eval_data_dir: "" +schema_file: "" +eval_ckpt: "" +eval_samples: 300000 +# bucket list, default: [] +bucket_list: [] +# optimizer related +AdamWeightDecay: + learning_rate: 0.00003 # 3e-5 + end_learning_rate: 0.0 + power: 5.0 + weight_decay: 0.00001 # 1e-5 + decay_filter: ['layernorm', 'bias'] + eps: 0.000001 # 1e-6 + warmup_steps: 10000 + +Lamb: + learning_rate: 0.0003 # 3e-4 + end_learning_rate: 0.0 + power: 2.0 + warmup_steps: 10000 + weight_decay: 0.01 + decay_filter: ['layernorm', 'bias'] + eps: 0.00000001 # 1e-8, + beta1: 0.99 + beta2: 0.999 + +Momentum: + learning_rate: 0.00002 # 2e-5 + momentum: 0.9 + +Thor: + lr_max: 0.006464 + lr_min: 0.000001 # 1e-6 + lr_power: 2.0 + lr_total_steps: 30000 + damping_max: 0.007035 + damping_min: 0.000001 # 1e-6 + damping_power: 4.0 + damping_total_steps: 30000 + momentum: 0.9 + weight_decay: 0.00001 # 1e-5 + loss_scale: 1024.0 + frequency: 100 +# ============================================================================== +# base +base_batch_size: 256 +base_net_cfg: + seq_length: 128 + vocab_size: 21128 + hidden_size: 768 + num_hidden_layers: 12 + num_attention_heads: 12 + intermediate_size: 3072 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 +# nezha +nezha_batch_size: 96 +nezha_net_cfg: + seq_length: 128 + vocab_size: 21128 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: True + dtype: mstype.float32 + compute_type: mstype.float16 +# large +large_batch_size: 24 +large_net_cfg: + seq_length: 512 + vocab_size: 30522 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 +# Accelerated large network which is only supported in Ascend yet. +large_boost_batch_size: 24 +large_boost_net_cfg: + seq_length: 512 + vocab_size: 30522 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "fast_gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 + + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Url for modelarts" +train_url: "Url for modelarts" +data_path: "The location of the input data." +output_path: "The location of the output file." +device_target: "Running platform, choose from Ascend or CPU, and default is Ascend." +enable_profiling: 'Whether enable profiling while training, default: False' + +distribute: "Run distribute, default is 'false'." +epoch_size: "Epoch size, default is 1." +enable_save_ckpt: "Enable save checkpoint, default is true." +enable_lossscale: "Use lossscale or not, default is not." +do_shuffle: "Enable shuffle for dataset, default is true." +enable_data_sink: "Enable data sink, default is true." +data_sink_steps: "Sink steps for each epoch, default is 1." +accumulation_steps: "Accumulating gradients N times before weight update, default is 1." +allreduce_post_accumulation: "Whether to allreduce after accumulation of N steps or after each step, default is true." +save_checkpoint_path: "Save checkpoint path" +load_checkpoint_path: "Load checkpoint file path" +save_checkpoint_steps: "Save checkpoint steps, default is 1000" +train_steps: "Training Steps, default is -1, meaning run all steps according to epoch number." +save_checkpoint_num: "Save checkpoint numbers, default is 1." +data_dir: "Data path, it is better to use absolute path" +schema_dir: "Schema path, it is better to use absolute path" +--- +# chocies +device_target: ['Ascend', 'GPU'] +distribute: ["true", "false"] +enable_save_ckpt: ["true", "false"] +enable_lossscale: ["true", "false"] +do_shuffle: ["true", "false"] +enable_data_sink: ["true", "false"] +allreduce_post_accumulation: ["true", "false"] diff --git a/nlp/language_model/bert/MindSpore/pretrain_config_Ascend_Boost.yaml b/nlp/language_model/bert/MindSpore/pretrain_config_Ascend_Boost.yaml new file mode 100644 index 0000000000000000000000000000000000000000..783fc310378d6ece1cd840897d36a340cb87dfbf --- /dev/null +++ b/nlp/language_model/bert/MindSpore/pretrain_config_Ascend_Boost.yaml @@ -0,0 +1,200 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path" +device_target: "Ascend" +enable_profiling: False + +# ============================================================================== +description: 'run_pretrain' +distribute: 'false' +epoch_size: 40 +device_id: 0 +device_num: 1 +enable_save_ckpt: 'true' +enable_lossscale: 'true' +do_shuffle: 'true' +enable_data_sink: 'true' +data_sink_steps: 1 +accumulation_steps: 1 +allreduce_post_accumulation: 'true' +save_checkpoint_path: '' +load_checkpoint_path: '' +save_checkpoint_steps: 10000 +train_steps: 17000 +save_checkpoint_num: 1 +data_dir: '' +schema_dir: '' + +# ============================================================================== +# pretrain related +batch_size: 32 +# Available: [base, nezha, large, large_boost] +bert_network: 'large_boost' +loss_scale_value: 65536 +scale_factor: 2 +scale_window: 1000 +optimizer: 'Lamb' +enable_global_norm: False +# pretrain_eval related +train_with_eval: 'false' +eval_data_dir: "" +schema_file: "" +eval_ckpt: "" +eval_samples: 300000 +# bucket list, default: [] +bucket_list: [128, 256, 384, 512] +# optimizer related +AdamWeightDecay: + learning_rate: 0.00003 # 3e-5 + end_learning_rate: 0.0 + power: 5.0 + weight_decay: 0.00001 # 1e-5 + decay_filter: ['layernorm', 'bias'] + eps: 0.000001 # 1e-6 + warmup_steps: 10000 + +Lamb: + learning_rate: 0.00035 # 3.5e-4 + end_learning_rate: 1.0e-9 + power: 1.2 + warmup_steps: 0 + weight_decay: 0.0166629 + decay_filter: ['layernorm', 'bias'] + eps: 0.000001 # 1e-6, + beta1: 0.86 + beta2: 0.98 + +Momentum: + learning_rate: 0.00002 # 2e-5 + momentum: 0.9 + +Thor: + lr_max: 0.006464 + lr_min: 0.000001 # 1e-6 + lr_power: 2.0 + lr_total_steps: 30000 + damping_max: 0.007035 + damping_min: 0.000001 # 1e-6 + damping_power: 4.0 + damping_total_steps: 30000 + momentum: 0.9 + weight_decay: 0.00001 # 1e-5 + loss_scale: 1024.0 + frequency: 100 +# ============================================================================== +# base +base_batch_size: 256 +base_net_cfg: + seq_length: 128 + vocab_size: 21128 + hidden_size: 768 + num_hidden_layers: 12 + num_attention_heads: 12 + intermediate_size: 3072 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 +# nezha +nezha_batch_size: 96 +nezha_net_cfg: + seq_length: 128 + vocab_size: 21128 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: True + dtype: mstype.float32 + compute_type: mstype.float16 +# large +large_batch_size: 24 +large_net_cfg: + seq_length: 512 + vocab_size: 30522 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 +# Accelerated large network which is only supported in Ascend yet. +large_boost_batch_size: 24 +large_boost_net_cfg: + seq_length: 512 + vocab_size: 30522 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "fast_gelu" + hidden_dropout_prob: 0.0 + attention_probs_dropout_prob: 0.0 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 + + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Url for modelarts" +train_url: "Url for modelarts" +data_path: "The location of the input data." +output_path: "The location of the output file." +device_target: "Running platform, choose from Ascend or CPU, and default is Ascend." +enable_profiling: 'Whether enable profiling while training, default: False' + +distribute: "Run distribute, default is 'false'." +epoch_size: "Epoch size, default is 1." +enable_save_ckpt: "Enable save checkpoint, default is true." +enable_lossscale: "Use lossscale or not, default is not." +do_shuffle: "Enable shuffle for dataset, default is true." +enable_data_sink: "Enable data sink, default is true." +data_sink_steps: "Sink steps for each epoch, default is 1." +accumulation_steps: "Accumulating gradients N times before weight update, default is 1." +allreduce_post_accumulation: "Whether to allreduce after accumulation of N steps or after each step, default is true." +save_checkpoint_path: "Save checkpoint path" +load_checkpoint_path: "Load checkpoint file path" +save_checkpoint_steps: "Save checkpoint steps, default is 1000" +train_steps: "Training Steps, default is -1, meaning run all steps according to epoch number." +save_checkpoint_num: "Save checkpoint numbers, default is 1." +data_dir: "Data path, it is better to use absolute path" +schema_dir: "Schema path, it is better to use absolute path" +--- +# chocies +device_target: ['Ascend', 'GPU'] +distribute: ["true", "false"] +enable_save_ckpt: ["true", "false"] +enable_lossscale: ["true", "false"] +do_shuffle: ["true", "false"] +enable_data_sink: ["true", "false"] +allreduce_post_accumulation: ["true", "false"] diff --git a/nlp/language_model/bert/MindSpore/pretrain_config_Ascend_Thor.yaml b/nlp/language_model/bert/MindSpore/pretrain_config_Ascend_Thor.yaml new file mode 100644 index 0000000000000000000000000000000000000000..42e45637036369edc3ab82ab997571e728460edb --- /dev/null +++ b/nlp/language_model/bert/MindSpore/pretrain_config_Ascend_Thor.yaml @@ -0,0 +1,200 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path" +device_target: "Ascend" +enable_profiling: False + +# ============================================================================== +description: 'run_pretrain' +distribute: 'false' +epoch_size: 40 +device_id: 0 +device_num: 1 +enable_save_ckpt: 'true' +enable_lossscale: 'false' +do_shuffle: 'true' +enable_data_sink: 'true' +data_sink_steps: 100 +accumulation_steps: 1 +allreduce_post_accumulation: 'true' +save_checkpoint_path: '' +load_checkpoint_path: '' +save_checkpoint_steps: 500 +train_steps: 2500 +save_checkpoint_num: 5 +data_dir: '' +schema_dir: '' + +# ============================================================================== +# pretrain related +batch_size: 20 +# Available: [base, nezha, large, large_boost] +bert_network: 'large_boost' +loss_scale_value: 65536 +scale_factor: 2 +scale_window: 1000 +optimizer: 'Thor' +enable_global_norm: False +# pretrain_eval related +train_with_eval: 'false' +eval_data_dir: "" +schema_file: "" +eval_ckpt: "" +eval_samples: 300000 +# bucket list, default: [] +bucket_list: [] +# optimizer related +AdamWeightDecay: + learning_rate: 0.00003 # 3e-5 + end_learning_rate: 0.0 + power: 5.0 + weight_decay: 0.00001 # 1e-5 + decay_filter: ['layernorm', 'bias'] + eps: 0.000001 # 1e-6 + warmup_steps: 10000 + beta1: 0.9 + beta2: 0.99 + +Lamb: + learning_rate: 0.0003 # 3e-4 + end_learning_rate: 0.0 + power: 2.0 + warmup_steps: 10000 + weight_decay: 0.01 + decay_filter: ['layernorm', 'bias'] + eps: 0.00000001 # 1e-8, + +Momentum: + learning_rate: 0.00002 # 2e-5 + momentum: 0.9 + +Thor: + lr_max: 0.006464 + lr_min: 0.000001 # 1e-6 + lr_power: 2.0 + lr_total_steps: 30000 + damping_max: 0.007035 + damping_min: 0.000001 # 1e-6 + damping_power: 4.0 + damping_total_steps: 30000 + momentum: 0.9 + weight_decay: 0.00001 # 1e-5 + loss_scale: 1024.0 + frequency: 100 +# ============================================================================== +# base +base_batch_size: 256 +base_net_cfg: + seq_length: 128 + vocab_size: 21128 + hidden_size: 768 + num_hidden_layers: 12 + num_attention_heads: 12 + intermediate_size: 3072 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 +# nezha +nezha_batch_size: 96 +nezha_net_cfg: + seq_length: 128 + vocab_size: 21128 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: True + dtype: mstype.float32 + compute_type: mstype.float16 +# large +large_batch_size: 20 +large_net_cfg: + seq_length: 512 + vocab_size: 30522 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 +# Accelerated large network which is only supported in Ascend yet. +large_boost_batch_size: 20 +large_boost_net_cfg: + seq_length: 512 + vocab_size: 30522 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "fast_gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 + + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Url for modelarts" +train_url: "Url for modelarts" +data_path: "The location of the input data." +output_path: "The location of the output file." +device_target: "Running platform, choose from Ascend or CPU, and default is Ascend." +enable_profiling: 'Whether enable profiling while training, default: False' + +distribute: "Run distribute, default is 'false'." +epoch_size: "Epoch size, default is 1." +enable_save_ckpt: "Enable save checkpoint, default is true." +enable_lossscale: "Use lossscale or not, default is not." +do_shuffle: "Enable shuffle for dataset, default is true." +enable_data_sink: "Enable data sink, default is true." +data_sink_steps: "Sink steps for each epoch, default is 1." +accumulation_steps: "Accumulating gradients N times before weight update, default is 1." +allreduce_post_accumulation: "Whether to allreduce after accumulation of N steps or after each step, default is true." +save_checkpoint_path: "Save checkpoint path" +load_checkpoint_path: "Load checkpoint file path" +save_checkpoint_steps: "Save checkpoint steps, default is 1000" +train_steps: "Training Steps, default is -1, meaning run all steps according to epoch number." +save_checkpoint_num: "Save checkpoint numbers, default is 1." +data_dir: "Data path, it is better to use absolute path" +schema_dir: "Schema path, it is better to use absolute path" +--- +# chocies +device_target: ['Ascend', 'GPU'] +distribute: ["true", "false"] +enable_save_ckpt: ["true", "false"] +enable_lossscale: ["true", "false"] +do_shuffle: ["true", "false"] +enable_data_sink: ["true", "false"] +allreduce_post_accumulation: ["true", "false"] diff --git a/nlp/language_model/bert/MindSpore/pretrain_eval.py b/nlp/language_model/bert/MindSpore/pretrain_eval.py new file mode 100644 index 0000000000000000000000000000000000000000..edb10579c2e658dfc162a5de6f6640071c919607 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/pretrain_eval.py @@ -0,0 +1,62 @@ +# Copyright 2020 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. +# ============================================================================ + +""" +Bert evaluation script. +""" + +import os +from mindspore import context +from mindspore.train.model import Model +from mindspore.train.serialization import load_checkpoint, load_param_into_net +from src.utils import BertMetric +from src.model_utils.config import config as cfg, bert_net_cfg +from src.bert_for_pre_training import BertPretrainEval +from src.dataset import create_eval_dataset + + +def bert_predict(): + ''' + Predict function + ''' + devid = int(os.getenv('DEVICE_ID')) + context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", device_id=devid) + dataset = create_eval_dataset(cfg.batch_size, 1, data_dir=cfg.eval_data_dir) + net_for_pretraining = BertPretrainEval(bert_net_cfg) + net_for_pretraining.set_train(False) + param_dict = load_checkpoint(cfg.eval_ckpt) + load_param_into_net(net_for_pretraining, param_dict) + model = Model(net_for_pretraining) + return model, dataset, net_for_pretraining + + +def MLM_eval(): + ''' + Evaluate function + ''' + _, dataset, net_for_pretraining = bert_predict() + net = Model(net_for_pretraining, eval_network=net_for_pretraining, + metrics={'name': BertMetric(cfg.batch_size)}) + res = net.eval(dataset, dataset_sink_mode=False) + print("==============================================================") + for _, v in res.items(): + print("Accuracy is: ", v) + print("==============================================================") + + +if __name__ == "__main__": + DEVICE_ID = 0 + os.environ['DEVICE_ID'] = str(DEVICE_ID) + MLM_eval() diff --git a/nlp/language_model/bert/MindSpore/requirements.txt b/nlp/language_model/bert/MindSpore/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..29b7498939ad33332cb171b6f2997d199f95c513 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/requirements.txt @@ -0,0 +1,4 @@ +numpy +easydict +wheel +lxml diff --git a/nlp/language_model/bert/MindSpore/run_classifier.py b/nlp/language_model/bert/MindSpore/run_classifier.py new file mode 100644 index 0000000000000000000000000000000000000000..851d0274130ac413f5d45b295d6411af8fbb584b --- /dev/null +++ b/nlp/language_model/bert/MindSpore/run_classifier.py @@ -0,0 +1,206 @@ +# Copyright 2020 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. +# ============================================================================ + +''' +Bert finetune and evaluation script. +''' + +import os +import mindspore.common.dtype as mstype +from mindspore import context +from mindspore import log as logger +from mindspore.nn.wrap.loss_scale import DynamicLossScaleUpdateCell +from mindspore.nn.optim import AdamWeightDecay, Lamb, Momentum +from mindspore.train.model import Model +from mindspore.train.callback import CheckpointConfig, ModelCheckpoint, TimeMonitor +from mindspore.train.serialization import load_checkpoint, load_param_into_net + +from src.bert_for_finetune import BertFinetuneCell, BertCLS +from src.dataset import create_classification_dataset +from src.assessment_method import Accuracy, F1, MCC, Spearman_Correlation +from src.utils import make_directory, LossCallBack, LoadNewestCkpt, BertLearningRate +from src.model_utils.config import config as args_opt, optimizer_cfg, bert_net_cfg +from src.model_utils.moxing_adapter import moxing_wrapper +from src.model_utils.device_adapter import get_device_id + +_cur_dir = os.getcwd() + + +def do_train(dataset=None, network=None, load_checkpoint_path="", save_checkpoint_path="", epoch_num=1): + """ do train """ + if load_checkpoint_path == "": + raise ValueError("Pretrain model missed, finetune task must load pretrain model!") + steps_per_epoch = dataset.get_dataset_size() + # optimizer + if optimizer_cfg.optimizer == 'AdamWeightDecay': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.AdamWeightDecay.learning_rate, + end_learning_rate=optimizer_cfg.AdamWeightDecay.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.AdamWeightDecay.power) + params = network.trainable_params() + decay_params = list(filter(optimizer_cfg.AdamWeightDecay.decay_filter, params)) + other_params = list(filter(lambda x: not optimizer_cfg.AdamWeightDecay.decay_filter(x), params)) + group_params = [{'params': decay_params, 'weight_decay': optimizer_cfg.AdamWeightDecay.weight_decay}, + {'params': other_params, 'weight_decay': 0.0}] + + optimizer = AdamWeightDecay(group_params, lr_schedule, eps=optimizer_cfg.AdamWeightDecay.eps) + elif optimizer_cfg.optimizer == 'Lamb': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.Lamb.learning_rate, + end_learning_rate=optimizer_cfg.Lamb.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.Lamb.power) + optimizer = Lamb(network.trainable_params(), learning_rate=lr_schedule) + elif optimizer_cfg.optimizer == 'Momentum': + optimizer = Momentum(network.trainable_params(), learning_rate=optimizer_cfg.Momentum.learning_rate, + momentum=optimizer_cfg.Momentum.momentum) + else: + raise Exception("Optimizer not supported. support: [AdamWeightDecay, Lamb, Momentum]") + + # load checkpoint into network + ckpt_config = CheckpointConfig(save_checkpoint_steps=steps_per_epoch, keep_checkpoint_max=1) + ckpoint_cb = ModelCheckpoint(prefix="classifier", + directory=None if save_checkpoint_path == "" else save_checkpoint_path, + config=ckpt_config) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(network, param_dict) + + update_cell = DynamicLossScaleUpdateCell(loss_scale_value=2**32, scale_factor=2, scale_window=1000) + netwithgrads = BertFinetuneCell(network, optimizer=optimizer, scale_update_cell=update_cell) + model = Model(netwithgrads) + callbacks = [TimeMonitor(dataset.get_dataset_size()), LossCallBack(dataset.get_dataset_size()), ckpoint_cb] + model.train(epoch_num, dataset, callbacks=callbacks) + + +def eval_result_print(assessment_method="accuracy", callback=None): + """ print eval result """ + if assessment_method == "accuracy": + print("acc_num {} , total_num {}, accuracy {:.6f}".format(callback.acc_num, callback.total_num, + callback.acc_num / callback.total_num)) + elif assessment_method == "f1": + print("Precision {:.6f} ".format(callback.TP / (callback.TP + callback.FP))) + print("Recall {:.6f} ".format(callback.TP / (callback.TP + callback.FN))) + print("F1 {:.6f} ".format(2 * callback.TP / (2 * callback.TP + callback.FP + callback.FN))) + elif assessment_method == "mcc": + print("MCC {:.6f} ".format(callback.cal())) + elif assessment_method == "spearman_correlation": + print("Spearman Correlation is {:.6f} ".format(callback.cal()[0])) + else: + raise ValueError("Assessment method not supported, support: [accuracy, f1, mcc, spearman_correlation]") + + +def do_eval(dataset=None, network=None, num_class=2, assessment_method="accuracy", load_checkpoint_path=""): + """ do eval """ + if load_checkpoint_path == "": + raise ValueError("Finetune model missed, evaluation task must load finetune model!") + net_for_pretraining = network(bert_net_cfg, False, num_class) + net_for_pretraining.set_train(False) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(net_for_pretraining, param_dict) + model = Model(net_for_pretraining) + + if assessment_method == "accuracy": + callback = Accuracy() + elif assessment_method == "f1": + callback = F1(False, num_class) + elif assessment_method == "mcc": + callback = MCC() + elif assessment_method == "spearman_correlation": + callback = Spearman_Correlation() + else: + raise ValueError("Assessment method not supported, support: [accuracy, f1, mcc, spearman_correlation]") + + columns_list = ["input_ids", "input_mask", "segment_ids", "label_ids"] + for data in dataset.create_dict_iterator(num_epochs=1): + input_data = [] + for i in columns_list: + input_data.append(data[i]) + input_ids, input_mask, token_type_id, label_ids = input_data + logits = model.predict(input_ids, input_mask, token_type_id, label_ids) + callback.update(logits, label_ids) + print("==============================================================") + eval_result_print(assessment_method, callback) + print("==============================================================") + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + args_opt.device_id = get_device_id() + _file_dir = os.path.dirname(os.path.abspath(__file__)) + args_opt.load_pretrain_checkpoint_path = os.path.join(_file_dir, args_opt.load_pretrain_checkpoint_path) + args_opt.load_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.load_finetune_checkpoint_path) + args_opt.save_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.save_finetune_checkpoint_path) + if args_opt.schema_file_path: + args_opt.schema_file_path = os.path.join(args_opt.data_path, args_opt.schema_file_path) + args_opt.train_data_file_path = os.path.join(args_opt.data_path, args_opt.train_data_file_path) + args_opt.eval_data_file_path = os.path.join(args_opt.data_path, args_opt.eval_data_file_path) + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_classifier(): + """run classifier task""" + if args_opt.do_train.lower() == "false" and args_opt.do_eval.lower() == "false": + raise ValueError("At least one of 'do_train' or 'do_eval' must be true") + if args_opt.do_train.lower() == "true" and args_opt.train_data_file_path == "": + raise ValueError("'train_data_file_path' must be set when do finetune task") + if args_opt.do_eval.lower() == "true" and args_opt.eval_data_file_path == "": + raise ValueError("'eval_data_file_path' must be set when do evaluation task") + epoch_num = args_opt.epoch_num + assessment_method = args_opt.assessment_method.lower() + load_pretrain_checkpoint_path = args_opt.load_pretrain_checkpoint_path + save_finetune_checkpoint_path = args_opt.save_finetune_checkpoint_path + load_finetune_checkpoint_path = args_opt.load_finetune_checkpoint_path + target = args_opt.device_target + if target == "Ascend": + context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", device_id=args_opt.device_id) + elif target == "GPU": + context.set_context(mode=context.GRAPH_MODE, device_target="GPU") + context.set_context(enable_graph_kernel=True) + if bert_net_cfg.compute_type != mstype.float32: + logger.warning('GPU only support fp32 temporarily, run with fp32.') + bert_net_cfg.compute_type = mstype.float32 + else: + raise Exception("Target error, GPU or Ascend is supported.") + + netwithloss = BertCLS(bert_net_cfg, True, num_labels=args_opt.num_class, dropout_prob=0.1, + assessment_method=assessment_method) + + if args_opt.do_train.lower() == "true": + ds = create_classification_dataset(batch_size=args_opt.train_batch_size, + assessment_method=assessment_method, + data_file_path=args_opt.train_data_file_path, + schema_file_path=args_opt.schema_file_path, + do_shuffle=(args_opt.train_data_shuffle.lower() == "true")) + do_train(ds, netwithloss, load_pretrain_checkpoint_path, save_finetune_checkpoint_path, epoch_num) + + if args_opt.do_eval.lower() == "true": + if save_finetune_checkpoint_path == "": + load_finetune_checkpoint_dir = _cur_dir + else: + load_finetune_checkpoint_dir = make_directory(save_finetune_checkpoint_path) + load_finetune_checkpoint_path = LoadNewestCkpt(load_finetune_checkpoint_dir, "classifier") + + if args_opt.do_eval.lower() == "true": + ds = create_classification_dataset(batch_size=args_opt.eval_batch_size, + assessment_method=assessment_method, + data_file_path=args_opt.eval_data_file_path, + schema_file_path=args_opt.schema_file_path, + do_shuffle=(args_opt.eval_data_shuffle.lower() == "true")) + do_eval(ds, BertCLS, args_opt.num_class, assessment_method, load_finetune_checkpoint_path) + + +if __name__ == "__main__": + run_classifier() diff --git a/nlp/language_model/bert/MindSpore/run_eval_onnx.py b/nlp/language_model/bert/MindSpore/run_eval_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..d35f5ab669b374c8933a5059215f59f2341552cc --- /dev/null +++ b/nlp/language_model/bert/MindSpore/run_eval_onnx.py @@ -0,0 +1,106 @@ +# 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. +# ============================================================================ + +''' +Inference script of ONNX exported from the Bert classification model. +''' + +import os +from src.dataset import create_classification_dataset +from src.assessment_method import Accuracy, F1, MCC, Spearman_Correlation +from src.model_utils.config import config as args_opt +from mindspore import Tensor, dtype +import onnxruntime as rt + + +def eval_result_print(assessment_method="accuracy", callback=None): + """ print eval result """ + if assessment_method == "accuracy": + print("acc_num {} , total_num {}, accuracy {:.6f}".format(callback.acc_num, callback.total_num, + callback.acc_num / callback.total_num)) + elif assessment_method == "f1": + print("Precision {:.6f} ".format(callback.TP / (callback.TP + callback.FP))) + print("Recall {:.6f} ".format(callback.TP / (callback.TP + callback.FN))) + print("F1 {:.6f} ".format(2 * callback.TP / (2 * callback.TP + callback.FP + callback.FN))) + elif assessment_method == "mcc": + print("MCC {:.6f} ".format(callback.cal())) + elif assessment_method == "spearman_correlation": + print("Spearman Correlation is {:.6f} ".format(callback.cal()[0])) + else: + raise ValueError("Assessment method not supported, support: [accuracy, f1, mcc, spearman_correlation]") + + +def do_eval_onnx(dataset=None, num_class=15, assessment_method="accuracy"): + """ do eval for onnx model""" + if assessment_method == "accuracy": + callback = Accuracy() + elif assessment_method == "f1": + callback = F1(False, num_class) + elif assessment_method == "mcc": + callback = MCC() + elif assessment_method == "spearman_correlation": + callback = Spearman_Correlation() + else: + raise ValueError("Assessment method not supported, support: [accuracy, f1, mcc, spearman_correlation]") + + columns_list = ["input_ids", "input_mask", "segment_ids", "label_ids"] + onnx_file_name = args_opt.export_file_name + if not args_opt.export_file_name.endswith('.onnx'): + onnx_file_name = onnx_file_name + '.onnx' + if not os.path.isabs(onnx_file_name): + onnx_file_name = os.getcwd() + '/' + onnx_file_name + if not os.path.exists(onnx_file_name): + raise ValueError("ONNX file not exists, please check onnx file has been saved and whether the " + "export_file_name is correct.") + sess = rt.InferenceSession(onnx_file_name) + input_name_0 = sess.get_inputs()[0].name + input_name_1 = sess.get_inputs()[1].name + input_name_2 = sess.get_inputs()[2].name + output_name_0 = sess.get_outputs()[0].name + + for data in dataset.create_dict_iterator(num_epochs=1): + input_data = [] + for i in columns_list: + input_data.append(data[i]) + input_ids, input_mask, token_type_id, label_ids = input_data + + x0 = input_ids.asnumpy() + x1 = input_mask.asnumpy() + x2 = token_type_id.asnumpy() + + result = sess.run([output_name_0], {input_name_0: x0, input_name_1: x1, input_name_2: x2}) + logits = Tensor(result[0], dtype.float32) + callback.update(logits, label_ids) + + print("==============================================================") + eval_result_print(assessment_method, callback) + print("==============================================================") + + +def run_classifier_onnx(): + """run classifier task for onnx model""" + if args_opt.eval_data_file_path == "": + raise ValueError("'eval_data_file_path' must be set when do onnx evaluation task") + assessment_method = args_opt.assessment_method.lower() + ds = create_classification_dataset(batch_size=args_opt.eval_batch_size, + assessment_method=assessment_method, + data_file_path=args_opt.eval_data_file_path, + schema_file_path=args_opt.schema_file_path, + do_shuffle=(args_opt.eval_data_shuffle.lower() == "true")) + do_eval_onnx(ds, args_opt.num_class, assessment_method) + + +if __name__ == "__main__": + run_classifier_onnx() diff --git a/nlp/language_model/bert/MindSpore/run_ner.py b/nlp/language_model/bert/MindSpore/run_ner.py new file mode 100644 index 0000000000000000000000000000000000000000..97499f7382ad86e249958ef57776d045b483c559 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/run_ner.py @@ -0,0 +1,251 @@ +# Copyright 2020 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. +# ============================================================================ + +''' +Bert finetune and evaluation script. +''' + +import os +import time +import mindspore.common.dtype as mstype +from mindspore import context +from mindspore import log as logger +from mindspore.nn.wrap.loss_scale import DynamicLossScaleUpdateCell +from mindspore.nn.optim import AdamWeightDecay, Lamb, Momentum +from mindspore.train.model import Model +from mindspore.train.callback import CheckpointConfig, ModelCheckpoint, TimeMonitor +from mindspore.train.serialization import load_checkpoint, load_param_into_net + +from src.bert_for_finetune import BertFinetuneCell, BertNER +from src.dataset import create_ner_dataset +from src.utils import make_directory, LossCallBack, LoadNewestCkpt, BertLearningRate, convert_labels_to_index +from src.assessment_method import Accuracy, F1, MCC, Spearman_Correlation +from src.model_utils.config import config as args_opt, optimizer_cfg, bert_net_cfg +from src.model_utils.moxing_adapter import moxing_wrapper +from src.model_utils.device_adapter import get_device_id +_cur_dir = os.getcwd() + + +def do_train(dataset=None, network=None, load_checkpoint_path="", save_checkpoint_path="", epoch_num=1): + """ do train """ + if load_checkpoint_path == "": + raise ValueError("Pretrain model missed, finetune task must load pretrain model!") + steps_per_epoch = dataset.get_dataset_size() + # optimizer + if optimizer_cfg.optimizer == 'AdamWeightDecay': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.AdamWeightDecay.learning_rate, + end_learning_rate=optimizer_cfg.AdamWeightDecay.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.AdamWeightDecay.power) + params = network.trainable_params() + decay_params = list(filter(optimizer_cfg.AdamWeightDecay.decay_filter, params)) + other_params = list(filter(lambda x: not optimizer_cfg.AdamWeightDecay.decay_filter(x), params)) + group_params = [{'params': decay_params, 'weight_decay': optimizer_cfg.AdamWeightDecay.weight_decay}, + {'params': other_params, 'weight_decay': 0.0}] + optimizer = AdamWeightDecay(group_params, lr_schedule, eps=optimizer_cfg.AdamWeightDecay.eps) + elif optimizer_cfg.optimizer == 'Lamb': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.Lamb.learning_rate, + end_learning_rate=optimizer_cfg.Lamb.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.Lamb.power) + optimizer = Lamb(network.trainable_params(), learning_rate=lr_schedule) + elif optimizer_cfg.optimizer == 'Momentum': + optimizer = Momentum(network.trainable_params(), learning_rate=optimizer_cfg.Momentum.learning_rate, + momentum=optimizer_cfg.Momentum.momentum) + else: + raise Exception("Optimizer not supported. support: [AdamWeightDecay, Lamb, Momentum]") + + # load checkpoint into network + ckpt_config = CheckpointConfig(save_checkpoint_steps=steps_per_epoch, keep_checkpoint_max=1) + ckpoint_cb = ModelCheckpoint(prefix="ner", + directory=None if save_checkpoint_path == "" else save_checkpoint_path, + config=ckpt_config) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(network, param_dict) + + update_cell = DynamicLossScaleUpdateCell(loss_scale_value=2**32, scale_factor=2, scale_window=1000) + netwithgrads = BertFinetuneCell(network, optimizer=optimizer, scale_update_cell=update_cell) + model = Model(netwithgrads) + callbacks = [TimeMonitor(dataset.get_dataset_size()), LossCallBack(dataset.get_dataset_size()), ckpoint_cb] + train_begin = time.time() + model.train(epoch_num, dataset, callbacks=callbacks) + train_end = time.time() + print("latency: {:.6f} s".format(train_end - train_begin)) + + +def eval_result_print(assessment_method="accuracy", callback=None): + """print eval result""" + if assessment_method == "accuracy": + print("acc_num {} , total_num {}, accuracy {:.6f}".format(callback.acc_num, callback.total_num, + callback.acc_num / callback.total_num)) + elif assessment_method == "bf1": + print("Precision {:.6f} ".format(callback.TP / (callback.TP + callback.FP))) + print("Recall {:.6f} ".format(callback.TP / (callback.TP + callback.FN))) + print("F1 {:.6f} ".format(2 * callback.TP / (2 * callback.TP + callback.FP + callback.FN))) + elif assessment_method == "mf1": + print("F1 {:.6f} ".format(callback.eval()[0])) + elif assessment_method == "mcc": + print("MCC {:.6f} ".format(callback.cal())) + elif assessment_method == "spearman_correlation": + print("Spearman Correlation is {:.6f} ".format(callback.cal()[0])) + else: + raise ValueError("Assessment method not supported, support: [accuracy, f1, mcc, spearman_correlation]") + + +def do_eval(dataset=None, network=None, use_crf="", with_lstm="", num_class=41, assessment_method="accuracy", + data_file="", load_checkpoint_path="", vocab_file="", label_file="", tag_to_index=None, batch_size=1): + """ do eval """ + if load_checkpoint_path == "": + raise ValueError("Finetune model missed, evaluation task must load finetune model!") + net_for_pretraining = network(bert_net_cfg, batch_size, False, num_class, with_lstm=(with_lstm.lower() == "true"), + use_crf=(use_crf.lower() == "true"), tag_to_index=tag_to_index) + net_for_pretraining.set_train(False) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(net_for_pretraining, param_dict) + model = Model(net_for_pretraining) + + if assessment_method == "clue_benchmark": + from src.cluener_evaluation import submit + submit(model=model, path=data_file, vocab_file=vocab_file, use_crf=use_crf, + label_file=label_file, tag_to_index=tag_to_index) + else: + if assessment_method == "accuracy": + callback = Accuracy() + elif assessment_method == "bf1": + callback = F1((use_crf.lower() == "true"), num_class) + elif assessment_method == "mf1": + callback = F1((use_crf.lower() == "true"), num_labels=num_class, mode="MultiLabel") + elif assessment_method == "mcc": + callback = MCC() + elif assessment_method == "spearman_correlation": + callback = Spearman_Correlation() + else: + raise ValueError("Assessment method not supported, support: [accuracy, f1, mcc, spearman_correlation]") + + columns_list = ["input_ids", "input_mask", "segment_ids", "label_ids"] + for data in dataset.create_dict_iterator(num_epochs=1): + input_data = [] + for i in columns_list: + input_data.append(data[i]) + input_ids, input_mask, token_type_id, label_ids = input_data + logits = model.predict(input_ids, input_mask, token_type_id, label_ids) + callback.update(logits, label_ids) + print("==============================================================") + eval_result_print(assessment_method, callback) + print("==============================================================") + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + args_opt.device_id = get_device_id() + _file_dir = os.path.dirname(os.path.abspath(__file__)) + args_opt.load_pretrain_checkpoint_path = os.path.join(_file_dir, args_opt.load_pretrain_checkpoint_path) + args_opt.load_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.load_finetune_checkpoint_path) + args_opt.save_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.save_finetune_checkpoint_path) + if args_opt.schema_file_path: + args_opt.schema_file_path = os.path.join(args_opt.data_path, args_opt.schema_file_path) + args_opt.train_data_file_path = os.path.join(args_opt.data_path, args_opt.train_data_file_path) + args_opt.eval_data_file_path = os.path.join(args_opt.data_path, args_opt.eval_data_file_path) + args_opt.label_file_path = os.path.join(args_opt.data_path, args_opt.label_file_path) + + +def determine_params(): + """Determine whether the parameters are reasonable.""" + if args_opt.do_train.lower() == "false" and args_opt.do_eval.lower() == "false": + raise ValueError("At least one of 'do_train' or 'do_eval' must be true") + if args_opt.do_train.lower() == "true" and args_opt.train_data_file_path == "": + raise ValueError("'train_data_file_path' must be set when do finetune task") + if args_opt.do_eval.lower() == "true" and args_opt.eval_data_file_path == "": + raise ValueError("'eval_data_file_path' must be set when do evaluation task") + if args_opt.assessment_method.lower() == "clue_benchmark" and args_opt.vocab_file_path == "": + raise ValueError("'vocab_file_path' must be set to do clue benchmark") + if args_opt.use_crf.lower() == "true" and args_opt.label_file_path == "": + raise ValueError("'label_file_path' must be set to use crf") + if args_opt.assessment_method.lower() == "clue_benchmark" and args_opt.label_file_path == "": + raise ValueError("'label_file_path' must be set to do clue benchmark") + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_ner(): + """run ner task""" + determine_params() + if args_opt.assessment_method.lower() == "clue_benchmark": + args_opt.eval_batch_size = 1 + epoch_num = args_opt.epoch_num + assessment_method = args_opt.assessment_method.lower() + load_pretrain_checkpoint_path = args_opt.load_pretrain_checkpoint_path + save_finetune_checkpoint_path = args_opt.save_finetune_checkpoint_path + load_finetune_checkpoint_path = args_opt.load_finetune_checkpoint_path + target = args_opt.device_target + if target == "Ascend": + context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", device_id=args_opt.device_id) + elif target == "GPU": + context.set_context(mode=context.GRAPH_MODE, device_target="GPU") + context.set_context(enable_graph_kernel=True) + if bert_net_cfg.compute_type != mstype.float32: + logger.warning('GPU only support fp32 temporarily, run with fp32.') + bert_net_cfg.compute_type = mstype.float32 + else: + raise Exception("Target error, GPU or Ascend is supported.") + label_list = [] + with open(args_opt.label_file_path) as f: + for label in f: + label_list.append(label.strip()) + tag_to_index = convert_labels_to_index(label_list) + if args_opt.use_crf.lower() == "true": + max_val = max(tag_to_index.values()) + tag_to_index[""] = max_val + 1 + tag_to_index[""] = max_val + 2 + number_labels = len(tag_to_index) + else: + number_labels = len(tag_to_index) + if args_opt.do_train.lower() == "true": + netwithloss = BertNER(bert_net_cfg, args_opt.train_batch_size, True, num_labels=number_labels, + use_crf=(args_opt.use_crf.lower() == "true"), + with_lstm=(args_opt.with_lstm.lower() == "true"), + tag_to_index=tag_to_index, dropout_prob=0.1) + ds = create_ner_dataset(batch_size=args_opt.train_batch_size, + assessment_method=assessment_method, data_file_path=args_opt.train_data_file_path, + schema_file_path=args_opt.schema_file_path, dataset_format=args_opt.dataset_format, + do_shuffle=(args_opt.train_data_shuffle.lower() == "true")) + print("==============================================================") + print("processor_name: {}".format(args_opt.device_target)) + print("test_name: BERT Finetune Training") + print("model_name: {}".format("BERT+MLP+CRF" if args_opt.use_crf.lower() == "true" else "BERT + MLP")) + print("batch_size: {}".format(args_opt.train_batch_size)) + + do_train(ds, netwithloss, load_pretrain_checkpoint_path, save_finetune_checkpoint_path, epoch_num) + + if args_opt.do_eval.lower() == "true": + if save_finetune_checkpoint_path == "": + load_finetune_checkpoint_dir = _cur_dir + else: + load_finetune_checkpoint_dir = make_directory(save_finetune_checkpoint_path) + load_finetune_checkpoint_path = LoadNewestCkpt(load_finetune_checkpoint_dir, "ner") + + if args_opt.do_eval.lower() == "true": + ds = create_ner_dataset(batch_size=args_opt.eval_batch_size, + assessment_method=assessment_method, data_file_path=args_opt.eval_data_file_path, + schema_file_path=args_opt.schema_file_path, dataset_format=args_opt.dataset_format, + do_shuffle=(args_opt.eval_data_shuffle.lower() == "true"), drop_remainder=False) + do_eval(ds, BertNER, args_opt.use_crf, args_opt.with_lstm, number_labels, assessment_method, + args_opt.eval_data_file_path, load_finetune_checkpoint_path, args_opt.vocab_file_path, + args_opt.label_file_path, tag_to_index, args_opt.eval_batch_size) + + +if __name__ == "__main__": + run_ner() diff --git a/nlp/language_model/bert/MindSpore/run_pretrain.py b/nlp/language_model/bert/MindSpore/run_pretrain.py new file mode 100644 index 0000000000000000000000000000000000000000..1e65d2eba819fb65a5635c71d325c818821d3f23 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/run_pretrain.py @@ -0,0 +1,278 @@ +# Copyright 2020-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. +# ============================================================================ +""" +#################pre_train bert example on zh-wiki######################## +python run_pretrain.py +""" +import os +import mindspore.communication.management as D +from mindspore.communication.management import get_rank +import mindspore.common.dtype as mstype +from mindspore import context +from mindspore.train.model import Model +from mindspore.context import ParallelMode +from mindspore.nn.wrap.loss_scale import DynamicLossScaleUpdateCell +from mindspore.train.callback import ModelCheckpoint, CheckpointConfig, TimeMonitor +from mindspore.train.serialization import load_checkpoint, load_param_into_net +from mindspore.train.train_thor import ConvertModelUtils +from mindspore.nn.optim import Lamb, Momentum, AdamWeightDecay, thor +from mindspore import log as logger +from mindspore.common import set_seed +from src import BertNetworkWithLoss, BertNetworkMatchBucket, \ + BertTrainOneStepCell, \ + BertTrainOneStepWithLossScaleCell, \ + BertTrainAccumulationAllReduceEachWithLossScaleCell, \ + BertTrainAccumulationAllReducePostWithLossScaleCell, \ + BertTrainOneStepWithLossScaleCellForAdam, \ + BertPretrainEval, \ + AdamWeightDecayForBert, AdamWeightDecayOp +from src.dataset import create_bert_dataset, create_eval_dataset +from src.utils import LossCallBack, BertLearningRate, EvalCallBack, BertMetric +from src.model_utils.config import config as cfg, bert_net_cfg +from src.model_utils.moxing_adapter import moxing_wrapper +from src.model_utils.device_adapter import get_device_id, get_device_num +_current_dir = os.path.dirname(os.path.realpath(__file__)) +from mindspore.train.callback import SummaryCollector + +def _set_bert_all_reduce_split(): + """set bert all_reduce fusion split, support num_hidden_layers is 12 and 24.""" + device_target = context.get_context('device_target') + enable_graph_kernel = context.get_context('enable_graph_kernel') + device_num = context.get_auto_parallel_context('device_num') + if bert_net_cfg.num_hidden_layers == 12: + if bert_net_cfg.use_relative_positions: + context.set_auto_parallel_context(all_reduce_fusion_config=[29, 58, 87, 116, 145, 174, 203, 217]) + else: + context.set_auto_parallel_context(all_reduce_fusion_config=[28, 55, 82, 109, 136, 163, 190, 205]) + if device_target == 'GPU' and enable_graph_kernel and device_num == 8: + context.set_auto_parallel_context(all_reduce_fusion_config=[180, 205]) + elif device_target == 'GPU' and enable_graph_kernel and device_num == 16: + context.set_auto_parallel_context(all_reduce_fusion_config=[120, 205]) + elif bert_net_cfg.num_hidden_layers == 24: + if bert_net_cfg.use_relative_positions: + context.set_auto_parallel_context(all_reduce_fusion_config=[30, 90, 150, 210, 270, 330, 390, 421]) + else: + context.set_auto_parallel_context(all_reduce_fusion_config=[38, 93, 148, 203, 258, 313, 368, 397]) + if device_target == 'Ascend' and enable_graph_kernel and device_num == 8: + context.set_auto_parallel_context(all_reduce_fusion_config=[ + 0, 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 35, 40, 50, 70, 93, 148, 203, 258, 313, 368, 397]) + + +def _get_optimizer(args_opt, network): + """get bert optimizer, support Lamb, Momentum, AdamWeightDecay.""" + if cfg.optimizer == 'Lamb': + lr_schedule = BertLearningRate(learning_rate=cfg.Lamb.learning_rate, + end_learning_rate=cfg.Lamb.end_learning_rate, + warmup_steps=cfg.Lamb.warmup_steps, + decay_steps=args_opt.train_steps, + power=cfg.Lamb.power) + params = network.trainable_params() + decay_params = list(filter(cfg.Lamb.decay_filter, params)) + other_params = list(filter(lambda x: not cfg.Lamb.decay_filter(x), params)) + group_params = [{'params': decay_params, 'weight_decay': cfg.Lamb.weight_decay}, + {'params': other_params}, + {'order_params': params}] + optimizer = Lamb(group_params, learning_rate=lr_schedule, beta1=cfg.Lamb.beta1, beta2=cfg.Lamb.beta2, + eps=cfg.Lamb.eps) + elif cfg.optimizer == 'Momentum': + optimizer = Momentum(network.trainable_params(), learning_rate=cfg.Momentum.learning_rate, + momentum=cfg.Momentum.momentum) + elif cfg.optimizer == 'AdamWeightDecay': + lr_schedule = BertLearningRate(learning_rate=cfg.AdamWeightDecay.learning_rate, + end_learning_rate=cfg.AdamWeightDecay.end_learning_rate, + warmup_steps=cfg.AdamWeightDecay.warmup_steps, + decay_steps=args_opt.train_steps, + power=cfg.AdamWeightDecay.power) + params = network.trainable_params() + decay_params = list(filter(cfg.AdamWeightDecay.decay_filter, params)) + other_params = list(filter(lambda x: not cfg.AdamWeightDecay.decay_filter(x), params)) + group_params = [{'params': decay_params, 'weight_decay': cfg.AdamWeightDecay.weight_decay}, + {'params': other_params, 'weight_decay': 0.0}, + {'order_params': params}] + if args_opt.enable_lossscale == "true" and args_opt.device_target == 'GPU': + optimizer = AdamWeightDecayForBert(group_params, learning_rate=lr_schedule, eps=cfg.AdamWeightDecay.eps) + elif context.get_context("mode") == context.PYNATIVE_MODE and args_opt.device_target == 'GPU': + optimizer = AdamWeightDecayOp(group_params, learning_rate=lr_schedule, eps=cfg.AdamWeightDecay.eps) + else: + optimizer = AdamWeightDecay(group_params, learning_rate=lr_schedule, eps=cfg.AdamWeightDecay.eps) + elif cfg.optimizer == "Thor": + from src.utils import get_bert_thor_lr, get_bert_thor_damping + lr = get_bert_thor_lr(cfg.Thor.lr_max, cfg.Thor.lr_min, cfg.Thor.lr_power, cfg.Thor.lr_total_steps) + damping = get_bert_thor_damping(cfg.Thor.damping_max, cfg.Thor.damping_min, cfg.Thor.damping_power, + cfg.Thor.damping_total_steps) + split_indices = None + if bert_net_cfg.num_hidden_layers == 12 and not bert_net_cfg.use_relative_positions: + split_indices = [28, 55, 77] + elif bert_net_cfg.num_hidden_layers == 24 and not bert_net_cfg.use_relative_positions: + split_indices = [38, 93, 149] + optimizer = thor(network, lr, damping, cfg.Thor.momentum, + cfg.Thor.weight_decay, cfg.Thor.loss_scale, cfg.batch_size, + decay_filter=lambda x: 'layernorm' not in x.name.lower() and 'bias' not in x.name.lower(), + split_indices=split_indices, enable_clip_grad=True, frequency=cfg.Thor.frequency) + else: + raise ValueError("Don't support optimizer {}, only support [Lamb, Momentum, AdamWeightDecay, Thor]". + format(cfg.optimizer)) + return optimizer + + +def _set_graph_kernel_context(device_target): + """Add suitable graph kernel context for different configs.""" + if device_target == 'GPU': + if cfg.bert_network == 'base': + context.set_context(enable_graph_kernel=True, + graph_kernel_flags="--enable_stitch_fusion=true " + "--enable_parallel_fusion=true " + "--enable_cluster_ops=BatchMatMul") + else: + context.set_context(enable_graph_kernel=True) + else: + logger.warning('Graph kernel only supports GPU back-end now, run with graph kernel off.') + + +def _check_compute_type(args_opt): + if args_opt.device_target == 'GPU' and bert_net_cfg.compute_type != mstype.float32 and cfg.bert_network != 'base': + warning_message = 'Gpu only support fp32 temporarily, run with fp32.' + bert_net_cfg.compute_type = mstype.float32 + if args_opt.enable_lossscale == "true": + args_opt.enable_lossscale = "false" + warning_message = 'Gpu only support fp32 temporarily, run with fp32 and disable lossscale.' + logger.warning(warning_message) + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + cfg.device_id = get_device_id() + cfg.device_num = get_device_num() + cfg.data_dir = cfg.data_path + cfg.save_checkpoint_path = os.path.join(cfg.output_path, cfg.save_checkpoint_path) + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_pretrain(): + """pre-train bert_clue""" + context.set_context(mode=context.GRAPH_MODE, device_target=cfg.device_target, device_id=cfg.device_id) + context.set_context(reserve_class_name_in_scope=False) + _set_graph_kernel_context(cfg.device_target) + ckpt_save_dir = cfg.save_checkpoint_path + if cfg.distribute == "true": + if cfg.device_target == 'Ascend': + D.init() + device_num = cfg.device_num + rank = cfg.device_id % device_num + else: + D.init() + device_num = D.get_group_size() + rank = D.get_rank() + ckpt_save_dir = os.path.join(cfg.save_checkpoint_path, 'ckpt_' + str(get_rank())) + + context.reset_auto_parallel_context() + context.set_auto_parallel_context(parallel_mode=ParallelMode.DATA_PARALLEL, gradients_mean=True, + device_num=device_num) + _set_bert_all_reduce_split() + else: + rank = 0 + device_num = 1 + + _check_compute_type(cfg) + + if cfg.accumulation_steps > 1: + logger.info("accumulation steps: {}".format(cfg.accumulation_steps)) + logger.info("global batch size: {}".format(cfg.batch_size * cfg.accumulation_steps)) + if cfg.enable_data_sink == "true": + cfg.data_sink_steps *= cfg.accumulation_steps + logger.info("data sink steps: {}".format(cfg.data_sink_steps)) + if cfg.enable_save_ckpt == "true": + cfg.save_checkpoint_steps *= cfg.accumulation_steps + logger.info("save checkpoint steps: {}".format(cfg.save_checkpoint_steps)) + + ds = create_bert_dataset(device_num, rank, cfg.do_shuffle, cfg.data_dir, cfg.schema_dir, cfg.batch_size, + cfg.bucket_list) + net_with_loss = BertNetworkWithLoss(bert_net_cfg, True) + + new_repeat_count = cfg.epoch_size * ds.get_dataset_size() // cfg.data_sink_steps + if cfg.train_steps > 0: + train_steps = cfg.train_steps * cfg.accumulation_steps + new_repeat_count = min(new_repeat_count, train_steps // cfg.data_sink_steps) + else: + cfg.train_steps = cfg.epoch_size * ds.get_dataset_size() // cfg.accumulation_steps + logger.info("train steps: {}".format(cfg.train_steps)) + + optimizer = _get_optimizer(cfg, net_with_loss) + callback = [TimeMonitor(cfg.data_sink_steps), LossCallBack(ds.get_dataset_size())] + if cfg.enable_save_ckpt == "true" and cfg.device_id % min(8, device_num) == 0: + config_ck = CheckpointConfig(save_checkpoint_steps=cfg.save_checkpoint_steps, + keep_checkpoint_max=cfg.save_checkpoint_num) + ckpoint_cb = ModelCheckpoint(prefix='checkpoint_bert', + directory=None if ckpt_save_dir == "" else ckpt_save_dir, config=config_ck) + callback.append(ckpoint_cb) + + if cfg.load_checkpoint_path: + param_dict = load_checkpoint(cfg.load_checkpoint_path) + load_param_into_net(net_with_loss, param_dict) + + if cfg.enable_lossscale == "true": + update_cell = DynamicLossScaleUpdateCell(loss_scale_value=cfg.loss_scale_value, + scale_factor=cfg.scale_factor, + scale_window=cfg.scale_window) + accumulation_steps = cfg.accumulation_steps + enable_global_norm = cfg.enable_global_norm + if accumulation_steps <= 1: + if cfg.optimizer == 'AdamWeightDecay' and cfg.device_target == 'GPU': + net_with_grads = BertTrainOneStepWithLossScaleCellForAdam(net_with_loss, optimizer=optimizer, + scale_update_cell=update_cell) + else: + net_with_grads = BertTrainOneStepWithLossScaleCell(net_with_loss, optimizer=optimizer, + scale_update_cell=update_cell) + else: + allreduce_post = cfg.distribute == "false" or cfg.allreduce_post_accumulation == "true" + net_with_accumulation = (BertTrainAccumulationAllReducePostWithLossScaleCell if allreduce_post else + BertTrainAccumulationAllReduceEachWithLossScaleCell) + net_with_grads = net_with_accumulation(net_with_loss, optimizer=optimizer, + scale_update_cell=update_cell, + accumulation_steps=accumulation_steps, + enable_global_norm=enable_global_norm) + else: + net_with_grads = BertTrainOneStepCell(net_with_loss, optimizer=optimizer, enable_clip_grad=True) + if cfg.optimizer == "Thor": + net_with_grads = BertTrainOneStepCell(net_with_loss, optimizer=optimizer, sens=cfg.Thor.loss_scale, + enable_clip_grad=False) + + if cfg.bucket_list: + net_with_grads = BertNetworkMatchBucket(net_with_grads, bert_net_cfg.seq_length, cfg.bucket_list) + + model = Model(net_with_grads) + + if cfg.train_with_eval == 'true': + net_eval = BertPretrainEval(bert_net_cfg, network=net_with_loss.bert) + eval_ds = create_eval_dataset(cfg.batch_size, device_num, rank, cfg.eval_data_dir, cfg.schema_dir) + model = Model(net_with_grads, eval_network=net_eval, metrics={'bert_acc': BertMetric(cfg.batch_size)}) + eval_callback = EvalCallBack(model, eval_ds, device_num * cfg.batch_size, cfg.eval_samples) + callback.append(eval_callback) + + # Init a SummaryCollector callback instance, and use it in model.train or model.eval + specified = {"collect_metric": True, "histogram_regular": "^conv1.*|^conv2.*", "collect_graph": True, + "collect_dataset_graph": True} + summary_collector = SummaryCollector(summary_dir="./summary_dir/summary_bert", collect_specified_data=specified, + collect_freq=1, keep_default_action=False, collect_tensor_freq=200) + callback.append(summary_collector) + + model = ConvertModelUtils().convert_to_thor_model(model, network=net_with_grads, optimizer=optimizer) + model.train(new_repeat_count, ds, callbacks=callback, + dataset_sink_mode=(cfg.enable_data_sink == "true"), sink_size=cfg.data_sink_steps) + + +if __name__ == '__main__': + set_seed(0) + run_pretrain() diff --git a/nlp/language_model/bert/MindSpore/run_squad.py b/nlp/language_model/bert/MindSpore/run_squad.py new file mode 100644 index 0000000000000000000000000000000000000000..9d74364913a86c92c7e8a18b0a277b6c9da16f42 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/run_squad.py @@ -0,0 +1,219 @@ +# Copyright 2020 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. +# ============================================================================ + +''' +Bert finetune and evaluation script. +''' +import os +import collections +import mindspore.common.dtype as mstype +from mindspore import context +from mindspore import log as logger +from mindspore.nn.wrap.loss_scale import DynamicLossScaleUpdateCell +from mindspore.nn.optim import AdamWeightDecay, Lamb, Momentum +from mindspore.common.tensor import Tensor +from mindspore.train.model import Model +from mindspore.train.callback import CheckpointConfig, ModelCheckpoint, TimeMonitor +from mindspore.train.serialization import load_checkpoint, load_param_into_net + +from src.bert_for_finetune import BertSquadCell, BertSquad +from src.dataset import create_squad_dataset +from src.utils import make_directory, LossCallBack, LoadNewestCkpt, BertLearningRate +from src.model_utils.config import config as args_opt, optimizer_cfg, bert_net_cfg +from src.model_utils.moxing_adapter import moxing_wrapper +from src.model_utils.device_adapter import get_device_id +from mindspore.train.callback import SummaryCollector +from src.model_utils.device_adapter import get_rank_id + +_cur_dir = os.getcwd() + + +def do_train(dataset=None, network=None, load_checkpoint_path="", save_checkpoint_path="", epoch_num=1): + """ do train """ + if load_checkpoint_path == "": + raise ValueError("Pretrain model missed, finetune task must load pretrain model!") + steps_per_epoch = dataset.get_dataset_size() + # optimizer + if optimizer_cfg.optimizer == 'AdamWeightDecay': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.AdamWeightDecay.learning_rate, + end_learning_rate=optimizer_cfg.AdamWeightDecay.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.AdamWeightDecay.power) + params = network.trainable_params() + decay_params = list(filter(optimizer_cfg.AdamWeightDecay.decay_filter, params)) + other_params = list(filter(lambda x: not optimizer_cfg.AdamWeightDecay.decay_filter(x), params)) + group_params = [{'params': decay_params, 'weight_decay': optimizer_cfg.AdamWeightDecay.weight_decay}, + {'params': other_params, 'weight_decay': 0.0}] + + optimizer = AdamWeightDecay(group_params, lr_schedule, eps=optimizer_cfg.AdamWeightDecay.eps) + elif optimizer_cfg.optimizer == 'Lamb': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.Lamb.learning_rate, + end_learning_rate=optimizer_cfg.Lamb.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.Lamb.power) + optimizer = Lamb(network.trainable_params(), learning_rate=lr_schedule) + elif optimizer_cfg.optimizer == 'Momentum': + optimizer = Momentum(network.trainable_params(), learning_rate=optimizer_cfg.Momentum.learning_rate, + momentum=optimizer_cfg.Momentum.momentum) + else: + raise Exception("Optimizer not supported. support: [AdamWeightDecay, Lamb, Momentum]") + + # load checkpoint into network + ckpt_config = CheckpointConfig(save_checkpoint_steps=steps_per_epoch, keep_checkpoint_max=1) + ckpoint_cb = ModelCheckpoint(prefix="squad", + directory=None if save_checkpoint_path == "" else save_checkpoint_path, + config=ckpt_config) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(network, param_dict) + + update_cell = DynamicLossScaleUpdateCell(loss_scale_value=2 ** 32, scale_factor=2, scale_window=1000) + netwithgrads = BertSquadCell(network, optimizer=optimizer, scale_update_cell=update_cell) + model = Model(netwithgrads) + callbacks = [TimeMonitor(dataset.get_dataset_size()), LossCallBack(dataset.get_dataset_size()), ckpoint_cb] + # model.train(epoch_num, dataset, callbacks=callbacks, dataset_sink_mode=False) + data_sink_steps = 1 + new_train_steps = epoch_num * steps_per_epoch // data_sink_steps + # Init a SummaryCollector callback instance, and use it in model.train or model.eval + specified = {"collect_metric": True, "collect_graph": True, "collect_dataset_graph": True} + summary_collector = SummaryCollector(summary_dir="./summary_dir/summary_bert-finetune", collect_specified_data=specified, + collect_freq=1, keep_default_action=False, collect_tensor_freq=200) + callbacks.append(summary_collector) + model.train(new_train_steps, dataset, callbacks=callbacks, dataset_sink_mode=True, sink_size=data_sink_steps) + + +def do_eval(dataset=None, load_checkpoint_path="", eval_batch_size=1): + """ do eval """ + if load_checkpoint_path == "": + raise ValueError("Finetune model missed, evaluation task must load finetune model!") + net = BertSquad(bert_net_cfg, False, 2) + net.set_train(False) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(net, param_dict) + model = Model(net) + output = [] + RawResult = collections.namedtuple("RawResult", ["unique_id", "start_logits", "end_logits"]) + columns_list = ["input_ids", "input_mask", "segment_ids", "unique_ids"] + for data in dataset.create_dict_iterator(num_epochs=1): + input_data = [] + for i in columns_list: + input_data.append(data[i]) + input_ids, input_mask, segment_ids, unique_ids = input_data + start_positions = Tensor([1], mstype.float32) + end_positions = Tensor([1], mstype.float32) + is_impossible = Tensor([1], mstype.float32) + logits = model.predict(input_ids, input_mask, segment_ids, start_positions, + end_positions, unique_ids, is_impossible) + ids = logits[0].asnumpy() + start = logits[1].asnumpy() + end = logits[2].asnumpy() + + for i in range(eval_batch_size): + unique_id = int(ids[i]) + start_logits = [float(x) for x in start[i].flat] + end_logits = [float(x) for x in end[i].flat] + output.append(RawResult( + unique_id=unique_id, + start_logits=start_logits, + end_logits=end_logits)) + return output + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + args_opt.device_id = get_device_id() + _file_dir = os.path.dirname(os.path.abspath(__file__)) + args_opt.load_pretrain_checkpoint_path = os.path.join(_file_dir, args_opt.load_pretrain_checkpoint_path) + args_opt.load_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.load_finetune_checkpoint_path) + args_opt.save_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.save_finetune_checkpoint_path) + args_opt.vocab_file_path = os.path.join(args_opt.data_path, args_opt.vocab_file_path) + if args_opt.schema_file_path: + args_opt.schema_file_path = os.path.join(args_opt.data_path, args_opt.schema_file_path) + args_opt.train_data_file_path = os.path.join(args_opt.data_path, args_opt.train_data_file_path) + args_opt.eval_json_path = os.path.join(args_opt.data_path, args_opt.eval_json_path) + + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_squad(): + """run squad task""" + if args_opt.do_train.lower() == "false" and args_opt.do_eval.lower() == "false": + raise ValueError("At least one of 'do_train' or 'do_eval' must be true") + if args_opt.do_train.lower() == "true" and args_opt.train_data_file_path == "": + raise ValueError("'train_data_file_path' must be set when do finetune task") + if args_opt.do_eval.lower() == "true": + if args_opt.vocab_file_path == "": + raise ValueError("'vocab_file_path' must be set when do evaluation task") + if args_opt.eval_json_path == "": + raise ValueError("'tokenization_file_path' must be set when do evaluation task") + epoch_num = args_opt.epoch_num + load_pretrain_checkpoint_path = args_opt.load_pretrain_checkpoint_path + save_finetune_checkpoint_path = args_opt.save_finetune_checkpoint_path + "ckpt_" + str(get_rank_id()) + "/" + load_finetune_checkpoint_path = args_opt.load_finetune_checkpoint_path + "ckpt_" + str(get_rank_id()) + "/" + target = args_opt.device_target + if target == "Ascend": + context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", device_id=args_opt.device_id) + elif target == "GPU": + context.set_context(mode=context.GRAPH_MODE, device_target="GPU") + context.set_context(enable_graph_kernel=False) + if bert_net_cfg.compute_type != mstype.float32: + logger.warning('GPU only support fp32 temporarily, run with fp32.') + bert_net_cfg.compute_type = mstype.float32 + else: + raise Exception("Target error, GPU or Ascend is supported.") + + netwithloss = BertSquad(bert_net_cfg, True, 2, dropout_prob=0.1) + + if args_opt.do_train.lower() == "true": + ds = create_squad_dataset(batch_size=args_opt.train_batch_size, + data_file_path=args_opt.train_data_file_path, + schema_file_path=args_opt.schema_file_path, + do_shuffle=(args_opt.train_data_shuffle.lower() == "true")) + do_train(ds, netwithloss, load_pretrain_checkpoint_path, save_finetune_checkpoint_path, epoch_num) + if args_opt.do_eval.lower() == "true": + if save_finetune_checkpoint_path == "": + load_finetune_checkpoint_dir = _cur_dir + else: + load_finetune_checkpoint_dir = make_directory(save_finetune_checkpoint_path) + load_finetune_checkpoint_path = LoadNewestCkpt(load_finetune_checkpoint_dir, "squad") + + if args_opt.do_eval.lower() == "true": + from src import tokenization + from src.create_squad_data import read_squad_examples, convert_examples_to_features + from src.squad_get_predictions import write_predictions + from src.squad_postprocess import SQuad_postprocess + tokenizer = tokenization.FullTokenizer(vocab_file=args_opt.vocab_file_path, do_lower_case=True) + eval_examples = read_squad_examples(args_opt.eval_json_path, False) + eval_features = convert_examples_to_features( + examples=eval_examples, + tokenizer=tokenizer, + max_seq_length=bert_net_cfg.seq_length, + doc_stride=128, + max_query_length=64, + is_training=False, + output_fn=None, + vocab_file=args_opt.vocab_file_path) + ds = create_squad_dataset(batch_size=args_opt.eval_batch_size, + data_file_path=eval_features, + schema_file_path=args_opt.schema_file_path, is_training=False, + do_shuffle=(args_opt.eval_data_shuffle.lower() == "true")) + outputs = do_eval(ds, load_finetune_checkpoint_path, args_opt.eval_batch_size) + all_predictions = write_predictions(eval_examples, eval_features, outputs, 20, 30, True) + SQuad_postprocess(args_opt.eval_json_path, all_predictions, output_metrics="output.json") + + +if __name__ == "__main__": + run_squad() diff --git a/nlp/language_model/bert/MindSpore/run_squad_distribute.py b/nlp/language_model/bert/MindSpore/run_squad_distribute.py new file mode 100644 index 0000000000000000000000000000000000000000..2ccc544170ea8d222c0854f93cf23f24a010d59b --- /dev/null +++ b/nlp/language_model/bert/MindSpore/run_squad_distribute.py @@ -0,0 +1,228 @@ +# Copyright 2020 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. +# ============================================================================ + +''' +Bert finetune and evaluation script. +''' +import os +import collections +import mindspore.common.dtype as mstype +from mindspore import context +from mindspore import log as logger +from mindspore.nn.wrap.loss_scale import DynamicLossScaleUpdateCell +from mindspore.nn.optim import AdamWeightDecay, Lamb, Momentum +from mindspore.common.tensor import Tensor +from mindspore.train.model import Model +from mindspore.train.callback import CheckpointConfig, ModelCheckpoint, TimeMonitor +from mindspore.train.serialization import load_checkpoint, load_param_into_net + +from src.bert_for_finetune import BertSquadCell, BertSquad +from src.dataset import create_squad_dataset +from src.utils import make_directory, LossCallBack, LoadNewestCkpt, BertLearningRate +from src.model_utils.config import config as args_opt, optimizer_cfg, bert_net_cfg +from src.model_utils.moxing_adapter import moxing_wrapper +from src.model_utils.device_adapter import get_device_id +from mindspore.train.callback import SummaryCollector + +from mindspore.communication.management import init, get_rank, get_group_size +from mindspore.context import ParallelMode + +_cur_dir = os.getcwd() + + +def do_train(dataset=None, network=None, load_checkpoint_path="", save_checkpoint_path="", epoch_num=1): + """ do train """ + if load_checkpoint_path == "": + raise ValueError("Pretrain model missed, finetune task must load pretrain model!") + steps_per_epoch = dataset.get_dataset_size() + # optimizer + if optimizer_cfg.optimizer == 'AdamWeightDecay': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.AdamWeightDecay.learning_rate, + end_learning_rate=optimizer_cfg.AdamWeightDecay.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.AdamWeightDecay.power) + params = network.trainable_params() + decay_params = list(filter(optimizer_cfg.AdamWeightDecay.decay_filter, params)) + other_params = list(filter(lambda x: not optimizer_cfg.AdamWeightDecay.decay_filter(x), params)) + group_params = [{'params': decay_params, 'weight_decay': optimizer_cfg.AdamWeightDecay.weight_decay}, + {'params': other_params, 'weight_decay': 0.0}] + + optimizer = AdamWeightDecay(group_params, lr_schedule, eps=optimizer_cfg.AdamWeightDecay.eps) + elif optimizer_cfg.optimizer == 'Lamb': + lr_schedule = BertLearningRate(learning_rate=optimizer_cfg.Lamb.learning_rate, + end_learning_rate=optimizer_cfg.Lamb.end_learning_rate, + warmup_steps=int(steps_per_epoch * epoch_num * 0.1), + decay_steps=steps_per_epoch * epoch_num, + power=optimizer_cfg.Lamb.power) + optimizer = Lamb(network.trainable_params(), learning_rate=lr_schedule) + elif optimizer_cfg.optimizer == 'Momentum': + optimizer = Momentum(network.trainable_params(), learning_rate=optimizer_cfg.Momentum.learning_rate, + momentum=optimizer_cfg.Momentum.momentum) + else: + raise Exception("Optimizer not supported. support: [AdamWeightDecay, Lamb, Momentum]") + + # load checkpoint into network + ckpt_config = CheckpointConfig(save_checkpoint_steps=steps_per_epoch, keep_checkpoint_max=1) + ckpoint_cb = ModelCheckpoint(prefix="squad", + directory=None if save_checkpoint_path == "" else save_checkpoint_path, + config=ckpt_config) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(network, param_dict) + + update_cell = DynamicLossScaleUpdateCell(loss_scale_value=2 ** 32, scale_factor=2, scale_window=1000) + netwithgrads = BertSquadCell(network, optimizer=optimizer, scale_update_cell=update_cell) + model = Model(netwithgrads) + callbacks = [TimeMonitor(dataset.get_dataset_size()), LossCallBack(dataset.get_dataset_size()), ckpoint_cb] + # model.train(epoch_num, dataset, callbacks=callbacks, dataset_sink_mode=False) + data_sink_steps = 1 + new_train_steps = epoch_num * steps_per_epoch // data_sink_steps + # Init a SummaryCollector callback instance, and use it in model.train or model.eval + specified = {"collect_metric": True, "collect_graph": True, "collect_dataset_graph": True} + summary_collector = SummaryCollector(summary_dir="./summary_dir/summary_bert-finetune", collect_specified_data=specified, + collect_freq=1, keep_default_action=False, collect_tensor_freq=200) + callbacks.append(summary_collector) + model.train(new_train_steps, dataset, callbacks=callbacks, dataset_sink_mode=True, sink_size=data_sink_steps) + + +def do_eval(dataset=None, load_checkpoint_path="", eval_batch_size=1): + """ do eval """ + if load_checkpoint_path == "": + raise ValueError("Finetune model missed, evaluation task must load finetune model!") + net = BertSquad(bert_net_cfg, False, 2) + net.set_train(False) + param_dict = load_checkpoint(load_checkpoint_path) + load_param_into_net(net, param_dict) + model = Model(net) + output = [] + RawResult = collections.namedtuple("RawResult", ["unique_id", "start_logits", "end_logits"]) + columns_list = ["input_ids", "input_mask", "segment_ids", "unique_ids"] + for data in dataset.create_dict_iterator(num_epochs=1): + input_data = [] + for i in columns_list: + input_data.append(data[i]) + input_ids, input_mask, segment_ids, unique_ids = input_data + start_positions = Tensor([1], mstype.float32) + end_positions = Tensor([1], mstype.float32) + is_impossible = Tensor([1], mstype.float32) + logits = model.predict(input_ids, input_mask, segment_ids, start_positions, + end_positions, unique_ids, is_impossible) + ids = logits[0].asnumpy() + start = logits[1].asnumpy() + end = logits[2].asnumpy() + + for i in range(eval_batch_size): + unique_id = int(ids[i]) + start_logits = [float(x) for x in start[i].flat] + end_logits = [float(x) for x in end[i].flat] + output.append(RawResult( + unique_id=unique_id, + start_logits=start_logits, + end_logits=end_logits)) + return output + + +def modelarts_pre_process(): + '''modelarts pre process function.''' + args_opt.device_id = get_device_id() + _file_dir = os.path.dirname(os.path.abspath(__file__)) + args_opt.load_pretrain_checkpoint_path = os.path.join(_file_dir, args_opt.load_pretrain_checkpoint_path) + args_opt.load_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.load_finetune_checkpoint_path) + args_opt.save_finetune_checkpoint_path = os.path.join(args_opt.output_path, args_opt.save_finetune_checkpoint_path) + args_opt.vocab_file_path = os.path.join(args_opt.data_path, args_opt.vocab_file_path) + if args_opt.schema_file_path: + args_opt.schema_file_path = os.path.join(args_opt.data_path, args_opt.schema_file_path) + args_opt.train_data_file_path = os.path.join(args_opt.data_path, args_opt.train_data_file_path) + args_opt.eval_json_path = os.path.join(args_opt.data_path, args_opt.eval_json_path) + + +@moxing_wrapper(pre_process=modelarts_pre_process) +def run_squad(): + """run squad task""" + if args_opt.do_train.lower() == "false" and args_opt.do_eval.lower() == "false": + raise ValueError("At least one of 'do_train' or 'do_eval' must be true") + if args_opt.do_train.lower() == "true" and args_opt.train_data_file_path == "": + raise ValueError("'train_data_file_path' must be set when do finetune task") + if args_opt.do_eval.lower() == "true": + if args_opt.vocab_file_path == "": + raise ValueError("'vocab_file_path' must be set when do evaluation task") + if args_opt.eval_json_path == "": + raise ValueError("'tokenization_file_path' must be set when do evaluation task") + epoch_num = args_opt.epoch_num + load_pretrain_checkpoint_path = args_opt.load_pretrain_checkpoint_path + save_finetune_checkpoint_path = args_opt.save_finetune_checkpoint_path + load_finetune_checkpoint_path = args_opt.load_finetune_checkpoint_path + target = args_opt.device_target + if target == "Ascend": + context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") + elif target == "GPU": + context.set_context(mode=context.GRAPH_MODE, device_target="GPU") + context.set_context(enable_graph_kernel=False) + if bert_net_cfg.compute_type != mstype.float32: + logger.warning('GPU only support fp32 temporarily, run with fp32.') + bert_net_cfg.compute_type = mstype.float32 + else: + raise Exception("Target error, GPU or Ascend is supported.") + + init("nccl") + rank_id = get_rank() + device_num = get_group_size() + context.set_auto_parallel_context(device_num=device_num, gradients_mean=True, parallel_mode=ParallelMode.DATA_PARALLEL) + data_set = ds.ImageFolderDataset(dataset_path, num_parallel_workers=8, shuffle=True, num_shards=device_num, shard_id=rank_id) + + netwithloss = BertSquad(bert_net_cfg, True, 2, dropout_prob=0.1) + + if args_opt.do_train.lower() == "true": + ds = create_squad_dataset_distribute(batch_size=args_opt.train_batch_size, + data_file_path=args_opt.train_data_file_path, + schema_file_path=args_opt.schema_file_path, + do_shuffle=(args_opt.train_data_shuffle.lower() == "true"), + device_num=device_num, + rank=rank_id) + do_train(ds, netwithloss, load_pretrain_checkpoint_path, save_finetune_checkpoint_path, epoch_num) + if args_opt.do_eval.lower() == "true": + if save_finetune_checkpoint_path == "": + load_finetune_checkpoint_dir = _cur_dir + else: + load_finetune_checkpoint_dir = make_directory(save_finetune_checkpoint_path) + load_finetune_checkpoint_path = LoadNewestCkpt(load_finetune_checkpoint_dir, "squad") + + if args_opt.do_eval.lower() == "true": + from src import tokenization + from src.create_squad_data import read_squad_examples, convert_examples_to_features + from src.squad_get_predictions import write_predictions + from src.squad_postprocess import SQuad_postprocess + tokenizer = tokenization.FullTokenizer(vocab_file=args_opt.vocab_file_path, do_lower_case=True) + eval_examples = read_squad_examples(args_opt.eval_json_path, False) + eval_features = convert_examples_to_features( + examples=eval_examples, + tokenizer=tokenizer, + max_seq_length=bert_net_cfg.seq_length, + doc_stride=128, + max_query_length=64, + is_training=False, + output_fn=None, + vocab_file=args_opt.vocab_file_path) + ds = create_squad_dataset(batch_size=args_opt.eval_batch_size, + data_file_path=eval_features, + schema_file_path=args_opt.schema_file_path, is_training=False, + do_shuffle=(args_opt.eval_data_shuffle.lower() == "true")) + outputs = do_eval(ds, load_finetune_checkpoint_path, args_opt.eval_batch_size) + all_predictions = write_predictions(eval_examples, eval_features, outputs, 20, 30, True) + SQuad_postprocess(args_opt.eval_json_path, all_predictions, output_metrics="output.json") + + +if __name__ == "__main__": + run_squad() diff --git a/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/README.md b/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/README.md new file mode 100644 index 0000000000000000000000000000000000000000..926f82fbbc826f01371336ec27f082a08ae02cb5 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/README.md @@ -0,0 +1,51 @@ +# Run distribute pretrain + +## Description + +The number of Ascend accelerators can be automatically allocated based on the device_num set in hccl config file, You don not need to specify that. + +## How to use + +For example, if we want to generate the launch command of the distributed training of Bert model on Ascend accelerators, we can run the following command in `/bert/` dir: + +```python +python ./scripts/ascend_distributed_launcher/get_distribute_pretrain_cmd.py --run_script_dir ./run_pretrain.py --hyper_parameter_config_dir ./scripts/ascend_distributed_launcher/hyper_parameter_config.ini --data_dir /path/dataset/ --hccl_config_dir model_zoo/utils/hccl_tools/hccl_2p_56_x.x.x.x.json +``` + +output: + +```python +hccl_config_dir: model_zoo/utils/hccl_tools/hccl_2p_56_x.x.x.x.json +the number of logical core: 192 +avg_core_per_rank: 96 +rank_size: 2 + +start training for rank 0, device 5: +rank_id: 0 +device_id: 5 +core nums: 0-95 +epoch_size: 8 +data_dir: /data/small_512/ +schema_dir: +log file dir: ./LOG5/log.txt + +start training for rank 1, device 6: +rank_id: 1 +device_id: 6 +core nums: 96-191 +epoch_size: 8 +data_dir: /data/small_512/ +schema_dir: +log file dir: ./LOG6/log.txt +``` + +## Note + +1. Note that `hccl_2p_56_x.x.x.x.json` can use [hccl_tools.py](https://gitee.com/mindspore/models/tree/r1.8/utils/hccl_tools) to generate. + +2. For hyper parameter, please note that you should customize the scripts `hyper_parameter_config.ini`. Please note that these two hyper parameters are not allowed to be configured here: + - device_id + - device_num + - data_dir + +3. For Other Model, please note that you should customize the option `run_script` and Corresponding `hyper_parameter_config.ini`. diff --git a/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/__init__.py b/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/get_distribute_pretrain_cmd.py b/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/get_distribute_pretrain_cmd.py new file mode 100644 index 0000000000000000000000000000000000000000..db85549506aba5795572eb22ba7d2453eb7085e7 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/get_distribute_pretrain_cmd.py @@ -0,0 +1,202 @@ +# Copyright 2020 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. +# ============================================================================ +"""distribute pretrain script""" +import os +import json +import configparser +import multiprocessing +from argparse import ArgumentParser + + +def parse_args(): + """ + parse args . + + Args: + + Returns: + args. + + Examples: + >>> parse_args() + """ + parser = ArgumentParser(description="mindspore distributed training") + + parser.add_argument("--run_script_dir", type=str, default="", + help="Run script path, it is better to use absolute path") + parser.add_argument("--hyper_parameter_config_dir", type=str, default="", + help="Hyper Parameter config path, it is better to use absolute path") + parser.add_argument("--data_dir", type=str, default="", + help="Data path, it is better to use absolute path") + parser.add_argument("--hccl_config_dir", type=str, default="", + help="Hccl config path, it is better to use absolute path") + parser.add_argument("--cmd_file", type=str, default="distributed_cmd.sh", + help="Path of the generated cmd file.") + parser.add_argument("--hccl_time_out", type=int, default=120, + help="Seconds to determine the hccl time out," + "default: 120, which is the same as hccl default config") + parser.add_argument("--hccn_config_file", type=str, default="/etc/hccn.conf", + help="Path of the hccn.conf file.") + + args = parser.parse_args() + return args + + +def append_cmd(cmd, s): + cmd += s + cmd += "\n" + return cmd + + +def append_cmd_env(cmd, key, value): + return append_cmd(cmd, "export " + str(key) + "=" + str(value)) + + +def set_envs(cmd, logic_id, rank_id): + """ + Set environment variables. + """ + cmd = append_cmd_env(cmd, "DEVICE_ID", str(logic_id)) + cmd = append_cmd_env(cmd, "RANK_ID", str(rank_id)) + cmd = append_cmd_env(cmd, "DEPLOY_MODE", '0') + cmd = append_cmd_env(cmd, "GE_USE_STATIC_MEMORY", '1') + return cmd + + +def make_dirs(cmd, logic_id): + """ + Make directories and change path. + """ + cmd = append_cmd(cmd, "rm -rf LOG" + str(logic_id)) + cmd = append_cmd(cmd, "mkdir ./LOG" + str(logic_id)) + cmd = append_cmd(cmd, "cp *.py ./LOG" + str(logic_id)) + cmd = append_cmd(cmd, "mkdir -p ./LOG" + str(logic_id) + "/ms_log") + cmd = append_cmd(cmd, "env > ./LOG" + str(logic_id) + "/env.log") + cur_dir = os.getcwd() + cmd = append_cmd_env(cmd, "GLOG_log_dir", cur_dir + "/LOG" + str(logic_id) + "/ms_log") + cmd = append_cmd_env(cmd, "GLOG_logtostderr", "0") + cmd = append_cmd(cmd, "cd " + cur_dir + "/LOG" + str(logic_id)) + return cmd + + +def print_info(rank_id, device_id, logic_id, cmdopt, epoch_size, data_dir, cur_dir): + """ + Print some information about scripts. + """ + print("\nstart training for rank " + str(rank_id) + ", device " + str(device_id) + ":") + print("rank_id:", rank_id) + print("device_id:", device_id) + print("logic_id", logic_id) + print("core_nums:", cmdopt) + print("epoch_size:", epoch_size) + print("data_dir:", data_dir) + print("log_file_dir: " + cur_dir + "/LOG" + str(logic_id) + "/pretraining_log.txt") + +def distribute_pretrain(): + """ + distribute pretrain scripts. The number of Ascend accelerators can be automatically allocated + based on the device_num set in hccl config file, You don not need to specify that. + """ + cmd = "" + print("start", __file__) + args = parse_args() + + run_script = args.run_script_dir + data_dir = args.data_dir + cf = configparser.ConfigParser() + cf.read(args.hyper_parameter_config_dir) + cfg = dict(cf.items("config")) + + print("hccl_config_dir:", args.hccl_config_dir) + print("hccl_time_out:", args.hccl_time_out) + cmd = append_cmd_env(cmd, 'HCCL_CONNECT_TIMEOUT', args.hccl_time_out) + cmd = append_cmd_env(cmd, 'RANK_TABLE_FILE', args.hccl_config_dir) + + cores = multiprocessing.cpu_count() + print("the number of logical core:", cores) + + # get device_ips + device_ips = {} + physic_logic_ids = {} + with open(args.hccn_config_file, 'r') as fin: + for hccn_item in fin.readlines(): + if hccn_item.strip().startswith('address_'): + device_id, device_ip = hccn_item.split('=') + device_id = device_id.split('_')[1] + device_ips[device_id] = device_ip.strip() + + if not device_ips: + raise ValueError("There is no address in hccn.conf file.") + + for logic_id, device_id in enumerate(sorted(device_ips.keys())): + physic_logic_ids[device_id] = logic_id + + with open(args.hccl_config_dir, "r", encoding="utf-8") as fin: + hccl_config = json.loads(fin.read()) + rank_size = 0 + for server in hccl_config["server_list"]: + rank_size += len(server["device"]) + if server["device"][0]["device_ip"] in device_ips.values(): + this_server = server + + cmd = append_cmd_env(cmd, "RANK_SIZE", str(rank_size)) + print("total rank size:", rank_size) + print("this server rank size:", len(this_server["device"])) + avg_core_per_rank = int(int(cores) / len(this_server["device"])) + core_gap = avg_core_per_rank - 1 + print("avg_core_per_rank:", avg_core_per_rank) + + count = 0 + for instance in this_server["device"]: + # device_id is the physical id, we use logic id to specific the selected device. + # While running on a server with 8 pcs, the logic ids are equal to the device ids. + device_id = instance["device_id"] + rank_id = instance["rank_id"] + logic_id = physic_logic_ids[device_id] + start = count * int(avg_core_per_rank) + count += 1 + end = start + core_gap + cmdopt = str(start) + "-" + str(end) + cur_dir = os.getcwd() + + cmd = set_envs(cmd, logic_id, rank_id) + cmd = make_dirs(cmd, logic_id) + + print_info(rank_id=rank_id, device_id=device_id, logic_id=logic_id, cmdopt=cmdopt, cur_dir=cur_dir, + epoch_size=str(cfg['epoch_size']), data_dir=data_dir) + + run_cmd = 'taskset -c ' + cmdopt + ' nohup python ' + run_script + " " + opt = " ".join(["--" + key + "=" + str(cfg[key]) for key in cfg.keys()]) + if ('device_id' in opt) or ('device_num' in opt) or ('data_dir' in opt): + raise ValueError("hyper_parameter_config.ini can not setting 'device_id'," + " 'device_num' or 'data_dir'! ") + run_cmd += opt + run_cmd += " --data_dir=" + data_dir + run_cmd += ' --device_id=' + str(logic_id) + ' --device_num=' \ + + str(rank_size) + ' >./pretraining_log.txt 2>&1 &' + + cmd = append_cmd(cmd, run_cmd) + cmd = append_cmd(cmd, "cd -") + cmd = append_cmd(cmd, "echo \"run with" + + " rank_id=" + str(rank_id) + + " device_id=" + str(device_id) + + " logic_id=" + str(logic_id) + "\"") + cmd += "\n" + + with open(args.cmd_file, "w") as f: + f.write(cmd) + +if __name__ == "__main__": + distribute_pretrain() diff --git a/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/hyper_parameter_config.ini b/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/hyper_parameter_config.ini new file mode 100644 index 0000000000000000000000000000000000000000..4e90f7d31a4db152aff874ea14f2f489430e7fab --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/ascend_distributed_launcher/hyper_parameter_config.ini @@ -0,0 +1,12 @@ +[config] +distribute=true +epoch_size=40 +enable_save_ckpt=true +do_shuffle=true +enable_data_sink=true +data_sink_steps=100 +accumulation_steps=1 +allreduce_post_accumulation=true +save_checkpoint_path=./ +save_checkpoint_num=1 +config_path=../../pretrain_config.yaml diff --git a/nlp/language_model/bert/MindSpore/scripts/docker_start.sh b/nlp/language_model/bert/MindSpore/scripts/docker_start.sh new file mode 100644 index 0000000000000000000000000000000000000000..c2feb7233087db74ed4b20046b1efa431786776b --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/docker_start.sh @@ -0,0 +1,38 @@ +#!/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 +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 /etc:/etc \ + -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ + -v /usr/local/Ascend/add-ons/:/usr/local/Ascend/add-ons \ + -v ${data_dir}:${data_dir} \ + -v ${model_dir}:${model_dir} \ + -v /root/ascend/log:/root/ascend/log ${docker_image} /bin/bash diff --git a/nlp/language_model/bert/MindSpore/scripts/ms2tf/ms2tf_config.py b/nlp/language_model/bert/MindSpore/scripts/ms2tf/ms2tf_config.py new file mode 100644 index 0000000000000000000000000000000000000000..36f038e563c3015f3fec0555a24542e2b2fbc33f --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/ms2tf/ms2tf_config.py @@ -0,0 +1,787 @@ +# Copyright 2022 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. +# ============================================================================ +""" + # tf and ms bert large checkpoint param transfer table + # key: tf + # value: ms +""" +param_name_dict = { + 'bert/embeddings/word_embeddings': 'bert.bert.bert_embedding_lookup.embedding_table', + 'bert/embeddings/token_type_embeddings': 'bert.bert.bert_embedding_postprocessor.' + 'token_type_embedding.embedding_table', + 'bert/embeddings/position_embeddings': 'bert.bert.bert_embedding_postprocessor.' + 'full_position_embedding.embedding_table', + 'bert/embeddings/LayerNorm/gamma': 'bert.bert.bert_embedding_postprocessor.layernorm.gamma', + 'bert/embeddings/LayerNorm/beta': 'bert.bert.bert_embedding_postprocessor.layernorm.beta', + 'bert/encoder/layer_0/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.0.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_0/attention/self/query/bias': 'bert.bert.bert_encoder.layers.0.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_0/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.0.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_0/attention/self/key/bias': 'bert.bert.bert_encoder.layers.0.attention.attention' + '.key_layer.bias', + 'bert/encoder/layer_0/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.0.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_0/attention/self/value/bias': 'bert.bert.bert_encoder.layers.0.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_0/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.0.attention.output.dense' + '.weight', + 'bert/encoder/layer_0/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.0.attention.output.dense.bias', + 'bert/encoder/layer_0/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.0.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_0/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.0.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_0/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.0.intermediate.weight', + 'bert/encoder/layer_0/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.0.intermediate.bias', + 'bert/encoder/layer_0/output/dense/kernel': 'bert.bert.bert_encoder.layers.0.output.dense.weight', + 'bert/encoder/layer_0/output/dense/bias': 'bert.bert.bert_encoder.layers.0.output.dense.bias', + 'bert/encoder/layer_0/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.0.output.layernorm.gamma', + 'bert/encoder/layer_0/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.0.output.layernorm.beta', + 'bert/encoder/layer_1/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.1.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_1/attention/self/query/bias': 'bert.bert.bert_encoder.layers.1.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_1/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.1.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_1/attention/self/key/bias': 'bert.bert.bert_encoder.layers.1.attention' + '.attention.key_layer.bias', + 'bert/encoder/layer_1/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.1.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_1/attention/self/value/bias': 'bert.bert.bert_encoder.layers.1.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_1/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.1.attention.output.dense' + '.weight', + 'bert/encoder/layer_1/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.1.attention.output.dense.bias', + 'bert/encoder/layer_1/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.1.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_1/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.1.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_1/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.1.intermediate.weight', + 'bert/encoder/layer_1/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.1.intermediate.bias', + 'bert/encoder/layer_1/output/dense/kernel': 'bert.bert.bert_encoder.layers.1.output.dense.weight', + 'bert/encoder/layer_1/output/dense/bias': 'bert.bert.bert_encoder.layers.1.output.dense.bias', + 'bert/encoder/layer_1/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.1.output.layernorm.gamma', + 'bert/encoder/layer_1/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.1.output.layernorm.beta', + 'bert/encoder/layer_2/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.2.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_2/attention/self/query/bias': 'bert.bert.bert_encoder.layers.2.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_2/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.2.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_2/attention/self/key/bias': 'bert.bert.bert_encoder.layers.2.attention' + '.attention.key_layer.bias', + 'bert/encoder/layer_2/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.2.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_2/attention/self/value/bias': 'bert.bert.bert_encoder.layers.2.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_2/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.2.attention.output.dense' + '.weight', + 'bert/encoder/layer_2/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.2.attention.output.dense.bias', + 'bert/encoder/layer_2/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.2.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_2/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.2.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_2/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.2.intermediate.weight', + 'bert/encoder/layer_2/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.2.intermediate.bias', + 'bert/encoder/layer_2/output/dense/kernel': 'bert.bert.bert_encoder.layers.2.output.dense.weight', + 'bert/encoder/layer_2/output/dense/bias': 'bert.bert.bert_encoder.layers.2.output.dense.bias', + 'bert/encoder/layer_2/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.2.output.layernorm.gamma', + 'bert/encoder/layer_2/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.2.output.layernorm.beta', + 'bert/encoder/layer_3/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.3.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_3/attention/self/query/bias': 'bert.bert.bert_encoder.layers.3.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_3/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.3.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_3/attention/self/key/bias': 'bert.bert.bert_encoder.layers.3.attention' + '.attention.key_layer.bias', + 'bert/encoder/layer_3/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.3.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_3/attention/self/value/bias': 'bert.bert.bert_encoder.layers.3.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_3/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.3.attention.output.dense' + '.weight', + 'bert/encoder/layer_3/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.3.attention.output.dense.bias', + 'bert/encoder/layer_3/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.3.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_3/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.3.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_3/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.3.intermediate.weight', + 'bert/encoder/layer_3/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.3.intermediate.bias', + 'bert/encoder/layer_3/output/dense/kernel': 'bert.bert.bert_encoder.layers.3.output.dense.weight', + 'bert/encoder/layer_3/output/dense/bias': 'bert.bert.bert_encoder.layers.3.output.dense.bias', + 'bert/encoder/layer_3/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.3.output.layernorm.gamma', + 'bert/encoder/layer_3/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.3.output.layernorm.beta', + 'bert/encoder/layer_4/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.4.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_4/attention/self/query/bias': 'bert.bert.bert_encoder.layers.4.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_4/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.4.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_4/attention/self/key/bias': 'bert.bert.bert_encoder.layers.4' + '.attention.attention.key_layer.bias', + 'bert/encoder/layer_4/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.4.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_4/attention/self/value/bias': 'bert.bert.bert_encoder.layers.4.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_4/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.4.attention.output.dense' + '.weight', + 'bert/encoder/layer_4/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.4.attention.output.dense.bias', + 'bert/encoder/layer_4/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.4.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_4/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.4.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_4/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.4.intermediate.weight', + 'bert/encoder/layer_4/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.4.intermediate.bias', + 'bert/encoder/layer_4/output/dense/kernel': 'bert.bert.bert_encoder.layers.4.output.dense.weight', + 'bert/encoder/layer_4/output/dense/bias': 'bert.bert.bert_encoder.layers.4.output.dense.bias', + 'bert/encoder/layer_4/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.4.output.layernorm.gamma', + 'bert/encoder/layer_4/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.4.output.layernorm.beta', + 'bert/encoder/layer_5/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.5.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_5/attention/self/query/bias': 'bert.bert.bert_encoder.layers.5.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_5/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.5.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_5/attention/self/key/bias': 'bert.bert.bert_encoder.layers.5.attention' + '.attention.key_layer.bias', + 'bert/encoder/layer_5/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.5.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_5/attention/self/value/bias': 'bert.bert.bert_encoder.layers.5.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_5/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.5.attention.output.dense' + '.weight', + 'bert/encoder/layer_5/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.5.attention.output.dense.bias', + 'bert/encoder/layer_5/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.5.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_5/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.5.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_5/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.5.intermediate.weight', + 'bert/encoder/layer_5/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.5.intermediate.bias', + 'bert/encoder/layer_5/output/dense/kernel': 'bert.bert.bert_encoder.layers.5.output.dense.weight', + 'bert/encoder/layer_5/output/dense/bias': 'bert.bert.bert_encoder.layers.5.output.dense.bias', + 'bert/encoder/layer_5/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.5.output.layernorm.gamma', + 'bert/encoder/layer_5/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.5.output.layernorm.beta', + 'bert/encoder/layer_6/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.6.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_6/attention/self/query/bias': 'bert.bert.bert_encoder.layers.6.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_6/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.6.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_6/attention/self/key/bias': 'bert.bert.bert_encoder.layers.6.attention' + '.attention.key_layer.bias', + 'bert/encoder/layer_6/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.6.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_6/attention/self/value/bias': 'bert.bert.bert_encoder.layers.6.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_6/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.6.attention.output.dense' + '.weight', + 'bert/encoder/layer_6/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.6.attention.output.dense.bias', + 'bert/encoder/layer_6/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.6.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_6/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.6.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_6/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.6.intermediate.weight', + 'bert/encoder/layer_6/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.6.intermediate.bias', + 'bert/encoder/layer_6/output/dense/kernel': 'bert.bert.bert_encoder.layers.6.output.dense.weight', + 'bert/encoder/layer_6/output/dense/bias': 'bert.bert.bert_encoder.layers.6.output.dense.bias', + 'bert/encoder/layer_6/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.6.output.layernorm.gamma', + 'bert/encoder/layer_6/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.6.output.layernorm.beta', + 'bert/encoder/layer_7/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.7.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_7/attention/self/query/bias': 'bert.bert.bert_encoder.layers.7.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_7/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.7.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_7/attention/self/key/bias': 'bert.bert.bert_encoder.layers.7.attention' + '.attention.key_layer.bias', + 'bert/encoder/layer_7/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.7.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_7/attention/self/value/bias': 'bert.bert.bert_encoder.layers.7.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_7/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.7.attention.output.dense' + '.weight', + 'bert/encoder/layer_7/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.7.attention.output.dense.bias', + 'bert/encoder/layer_7/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.7.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_7/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.7.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_7/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.7.intermediate.weight', + 'bert/encoder/layer_7/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.7.intermediate.bias', + 'bert/encoder/layer_7/output/dense/kernel': 'bert.bert.bert_encoder.layers.7.output.dense.weight', + 'bert/encoder/layer_7/output/dense/bias': 'bert.bert.bert_encoder.layers.7.output.dense.bias', + 'bert/encoder/layer_7/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.7.output.layernorm.gamma', + 'bert/encoder/layer_7/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.7.output.layernorm.beta', + 'bert/encoder/layer_8/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.8.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_8/attention/self/query/bias': 'bert.bert.bert_encoder.layers.8.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_8/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.8.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_8/attention/self/key/bias': 'bert.bert.bert_encoder.layers.8.attention' + '.attention.key_layer.bias', + 'bert/encoder/layer_8/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.8.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_8/attention/self/value/bias': 'bert.bert.bert_encoder.layers.8.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_8/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.8.attention.output.dense' + '.weight', + 'bert/encoder/layer_8/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.8.attention.output.dense.bias', + 'bert/encoder/layer_8/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.8.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_8/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.8.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_8/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.8.intermediate.weight', + 'bert/encoder/layer_8/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.8.intermediate.bias', + 'bert/encoder/layer_8/output/dense/kernel': 'bert.bert.bert_encoder.layers.8.output.dense.weight', + 'bert/encoder/layer_8/output/dense/bias': 'bert.bert.bert_encoder.layers.8.output.dense.bias', + 'bert/encoder/layer_8/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.8.output.layernorm.gamma', + 'bert/encoder/layer_8/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.8.output.layernorm.beta', + 'bert/encoder/layer_9/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.9.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_9/attention/self/query/bias': 'bert.bert.bert_encoder.layers.9.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_9/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.9.attention.attention.key_layer' + '.weight', + 'bert/encoder/layer_9/attention/self/key/bias': 'bert.bert.bert_encoder.layers.9.attention' + '.attention.key_layer.bias', + 'bert/encoder/layer_9/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.9.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_9/attention/self/value/bias': 'bert.bert.bert_encoder.layers.9.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_9/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.9.attention.output.dense' + '.weight', + 'bert/encoder/layer_9/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.9.attention.output.dense.bias', + 'bert/encoder/layer_9/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.9.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_9/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.9.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_9/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.9.intermediate.weight', + 'bert/encoder/layer_9/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.9.intermediate.bias', + 'bert/encoder/layer_9/output/dense/kernel': 'bert.bert.bert_encoder.layers.9.output.dense.weight', + 'bert/encoder/layer_9/output/dense/bias': 'bert.bert.bert_encoder.layers.9.output.dense.bias', + 'bert/encoder/layer_9/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.9.output.layernorm.gamma', + 'bert/encoder/layer_9/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.9.output.layernorm.beta', + 'bert/encoder/layer_10/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.10.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_10/attention/self/query/bias': 'bert.bert.bert_encoder.layers.10.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_10/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.10.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_10/attention/self/key/bias': 'bert.bert.bert_encoder.layers.10.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_10/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.10.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_10/attention/self/value/bias': 'bert.bert.bert_encoder.layers.10.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_10/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.10.attention.output.dense' + '.weight', + 'bert/encoder/layer_10/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.10.attention.output.dense.bias', + 'bert/encoder/layer_10/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.10.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_10/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.10.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_10/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.10.intermediate.weight', + 'bert/encoder/layer_10/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.10.intermediate.bias', + 'bert/encoder/layer_10/output/dense/kernel': 'bert.bert.bert_encoder.layers.10.output.dense.weight', + 'bert/encoder/layer_10/output/dense/bias': 'bert.bert.bert_encoder.layers.10.output.dense.bias', + 'bert/encoder/layer_10/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.10.output.layernorm.gamma', + 'bert/encoder/layer_10/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.10.output.layernorm.beta', + 'bert/encoder/layer_11/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.11.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_11/attention/self/query/bias': 'bert.bert.bert_encoder.layers.11.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_11/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.11.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_11/attention/self/key/bias': 'bert.bert.bert_encoder.layers.11.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_11/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.11.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_11/attention/self/value/bias': 'bert.bert.bert_encoder.layers.11.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_11/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.11.attention.output.dense' + '.weight', + 'bert/encoder/layer_11/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.11.attention.output.dense.bias', + 'bert/encoder/layer_11/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.11.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_11/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.11.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_11/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.11.intermediate.weight', + 'bert/encoder/layer_11/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.11.intermediate.bias', + 'bert/encoder/layer_11/output/dense/kernel': 'bert.bert.bert_encoder.layers.11.output.dense.weight', + 'bert/encoder/layer_11/output/dense/bias': 'bert.bert.bert_encoder.layers.11.output.dense.bias', + 'bert/encoder/layer_11/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.11.output.layernorm.gamma', + 'bert/encoder/layer_11/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.11.output.layernorm.beta', + 'bert/encoder/layer_12/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.12.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_12/attention/self/query/bias': 'bert.bert.bert_encoder.layers.12.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_12/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.12.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_12/attention/self/key/bias': 'bert.bert.bert_encoder.layers.12.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_12/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.12.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_12/attention/self/value/bias': 'bert.bert.bert_encoder.layers.12.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_12/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.12.attention.output.dense' + '.weight', + 'bert/encoder/layer_12/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.12.attention.output.dense.bias', + 'bert/encoder/layer_12/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.12.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_12/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.12.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_12/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.12.intermediate.weight', + 'bert/encoder/layer_12/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.12.intermediate.bias', + 'bert/encoder/layer_12/output/dense/kernel': 'bert.bert.bert_encoder.layers.12.output.dense.weight', + 'bert/encoder/layer_12/output/dense/bias': 'bert.bert.bert_encoder.layers.12.output.dense.bias', + 'bert/encoder/layer_12/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.12.output.layernorm.gamma', + 'bert/encoder/layer_12/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.12.output.layernorm.beta', + 'bert/encoder/layer_13/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.13.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_13/attention/self/query/bias': 'bert.bert.bert_encoder.layers.13.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_13/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.13.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_13/attention/self/key/bias': 'bert.bert.bert_encoder.layers.13.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_13/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.13.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_13/attention/self/value/bias': 'bert.bert.bert_encoder.layers.13.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_13/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.13.attention.output.dense' + '.weight', + 'bert/encoder/layer_13/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.13.attention.output.dense.bias', + 'bert/encoder/layer_13/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.13.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_13/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.13.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_13/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.13.intermediate.weight', + 'bert/encoder/layer_13/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.13.intermediate.bias', + 'bert/encoder/layer_13/output/dense/kernel': 'bert.bert.bert_encoder.layers.13.output.dense.weight', + 'bert/encoder/layer_13/output/dense/bias': 'bert.bert.bert_encoder.layers.13.output.dense.bias', + 'bert/encoder/layer_13/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.13.output.layernorm.gamma', + 'bert/encoder/layer_13/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.13.output.layernorm.beta', + 'bert/encoder/layer_14/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.14.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_14/attention/self/query/bias': 'bert.bert.bert_encoder.layers.14.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_14/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.14.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_14/attention/self/key/bias': 'bert.bert.bert_encoder.layers.14.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_14/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.14.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_14/attention/self/value/bias': 'bert.bert.bert_encoder.layers.14.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_14/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.14.attention.output.dense' + '.weight', + 'bert/encoder/layer_14/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.14.attention.output.dense.bias', + 'bert/encoder/layer_14/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.14.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_14/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.14.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_14/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.14.intermediate.weight', + 'bert/encoder/layer_14/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.14.intermediate.bias', + 'bert/encoder/layer_14/output/dense/kernel': 'bert.bert.bert_encoder.layers.14.output.dense.weight', + 'bert/encoder/layer_14/output/dense/bias': 'bert.bert.bert_encoder.layers.14.output.dense.bias', + 'bert/encoder/layer_14/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.14.output.layernorm.gamma', + 'bert/encoder/layer_14/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.14.output.layernorm.beta', + 'bert/encoder/layer_15/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.15.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_15/attention/self/query/bias': 'bert.bert.bert_encoder.layers.15.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_15/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.15.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_15/attention/self/key/bias': 'bert.bert.bert_encoder.layers.15.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_15/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.15.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_15/attention/self/value/bias': 'bert.bert.bert_encoder.layers.15.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_15/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.15.attention.output.dense' + '.weight', + 'bert/encoder/layer_15/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.15.attention.output.dense.bias', + 'bert/encoder/layer_15/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.15.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_15/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.15.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_15/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.15.intermediate.weight', + 'bert/encoder/layer_15/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.15.intermediate.bias', + 'bert/encoder/layer_15/output/dense/kernel': 'bert.bert.bert_encoder.layers.15.output.dense.weight', + 'bert/encoder/layer_15/output/dense/bias': 'bert.bert.bert_encoder.layers.15.output.dense.bias', + 'bert/encoder/layer_15/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.15.output.layernorm.gamma', + 'bert/encoder/layer_15/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.15.output.layernorm.beta', + 'bert/encoder/layer_16/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.16.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_16/attention/self/query/bias': 'bert.bert.bert_encoder.layers.16.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_16/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.16.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_16/attention/self/key/bias': 'bert.bert.bert_encoder.layers.16.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_16/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.16.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_16/attention/self/value/bias': 'bert.bert.bert_encoder.layers.16.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_16/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.16.attention.output.dense' + '.weight', + 'bert/encoder/layer_16/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.16.attention.output.dense.bias', + 'bert/encoder/layer_16/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.16.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_16/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.16.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_16/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.16.intermediate.weight', + 'bert/encoder/layer_16/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.16.intermediate.bias', + 'bert/encoder/layer_16/output/dense/kernel': 'bert.bert.bert_encoder.layers.16.output.dense.weight', + 'bert/encoder/layer_16/output/dense/bias': 'bert.bert.bert_encoder.layers.16.output.dense.bias', + 'bert/encoder/layer_16/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.16.output.layernorm.gamma', + 'bert/encoder/layer_16/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.16.output.layernorm.beta', + 'bert/encoder/layer_17/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.17.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_17/attention/self/query/bias': 'bert.bert.bert_encoder.layers.17.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_17/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.17.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_17/attention/self/key/bias': 'bert.bert.bert_encoder.layers.17.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_17/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.17.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_17/attention/self/value/bias': 'bert.bert.bert_encoder.layers.17.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_17/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.17.attention.output.dense' + '.weight', + 'bert/encoder/layer_17/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.17.attention.output.dense.bias', + 'bert/encoder/layer_17/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.17.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_17/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.17.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_17/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.17.intermediate.weight', + 'bert/encoder/layer_17/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.17.intermediate.bias', + 'bert/encoder/layer_17/output/dense/kernel': 'bert.bert.bert_encoder.layers.17.output.dense.weight', + 'bert/encoder/layer_17/output/dense/bias': 'bert.bert.bert_encoder.layers.17.output.dense.bias', + 'bert/encoder/layer_17/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.17.output.layernorm.gamma', + 'bert/encoder/layer_17/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.17.output.layernorm.beta', + 'bert/encoder/layer_18/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.18.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_18/attention/self/query/bias': 'bert.bert.bert_encoder.layers.18.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_18/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.18.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_18/attention/self/key/bias': 'bert.bert.bert_encoder.layers.18.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_18/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.18.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_18/attention/self/value/bias': 'bert.bert.bert_encoder.layers.18.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_18/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.18.attention.output.dense' + '.weight', + 'bert/encoder/layer_18/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.18.attention.output.dense.bias', + 'bert/encoder/layer_18/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.18.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_18/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.18.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_18/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.18.intermediate.weight', + 'bert/encoder/layer_18/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.18.intermediate.bias', + 'bert/encoder/layer_18/output/dense/kernel': 'bert.bert.bert_encoder.layers.18.output.dense.weight', + 'bert/encoder/layer_18/output/dense/bias': 'bert.bert.bert_encoder.layers.18.output.dense.bias', + 'bert/encoder/layer_18/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.18.output.layernorm.gamma', + 'bert/encoder/layer_18/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.18.output.layernorm.beta', + 'bert/encoder/layer_19/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.19.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_19/attention/self/query/bias': 'bert.bert.bert_encoder.layers.19.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_19/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.19.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_19/attention/self/key/bias': 'bert.bert.bert_encoder.layers.19.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_19/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.19.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_19/attention/self/value/bias': 'bert.bert.bert_encoder.layers.19.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_19/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.19.attention.output.dense' + '.weight', + 'bert/encoder/layer_19/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.19.attention.output.dense.bias', + 'bert/encoder/layer_19/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.19.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_19/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.19.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_19/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.19.intermediate.weight', + 'bert/encoder/layer_19/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.19.intermediate.bias', + 'bert/encoder/layer_19/output/dense/kernel': 'bert.bert.bert_encoder.layers.19.output.dense.weight', + 'bert/encoder/layer_19/output/dense/bias': 'bert.bert.bert_encoder.layers.19.output.dense.bias', + 'bert/encoder/layer_19/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.19.output.layernorm.gamma', + 'bert/encoder/layer_19/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.19.output.layernorm.beta', + 'bert/encoder/layer_20/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.20.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_20/attention/self/query/bias': 'bert.bert.bert_encoder.layers.20.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_20/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.20.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_20/attention/self/key/bias': 'bert.bert.bert_encoder.layers.20.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_20/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.20.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_20/attention/self/value/bias': 'bert.bert.bert_encoder.layers.20.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_20/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.20.attention.output.dense' + '.weight', + 'bert/encoder/layer_20/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.20.attention.output.dense.bias', + 'bert/encoder/layer_20/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.20.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_20/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.20.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_20/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.20.intermediate.weight', + 'bert/encoder/layer_20/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.20.intermediate.bias', + 'bert/encoder/layer_20/output/dense/kernel': 'bert.bert.bert_encoder.layers.20.output.dense.weight', + 'bert/encoder/layer_20/output/dense/bias': 'bert.bert.bert_encoder.layers.20.output.dense.bias', + 'bert/encoder/layer_20/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.20.output.layernorm.gamma', + 'bert/encoder/layer_20/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.20.output.layernorm.beta', + 'bert/encoder/layer_21/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.21.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_21/attention/self/query/bias': 'bert.bert.bert_encoder.layers.21.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_21/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.21.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_21/attention/self/key/bias': 'bert.bert.bert_encoder.layers.21.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_21/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.21.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_21/attention/self/value/bias': 'bert.bert.bert_encoder.layers.21.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_21/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.21.attention.output.dense' + '.weight', + 'bert/encoder/layer_21/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.21.attention.output.dense.bias', + 'bert/encoder/layer_21/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.21.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_21/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.21.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_21/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.21.intermediate.weight', + 'bert/encoder/layer_21/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.21.intermediate.bias', + 'bert/encoder/layer_21/output/dense/kernel': 'bert.bert.bert_encoder.layers.21.output.dense.weight', + 'bert/encoder/layer_21/output/dense/bias': 'bert.bert.bert_encoder.layers.21.output.dense.bias', + 'bert/encoder/layer_21/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.21.output.layernorm.gamma', + 'bert/encoder/layer_21/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.21.output.layernorm.beta', + 'bert/encoder/layer_22/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.22.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_22/attention/self/query/bias': 'bert.bert.bert_encoder.layers.22.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_22/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.22.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_22/attention/self/key/bias': 'bert.bert.bert_encoder.layers.22.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_22/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.22.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_22/attention/self/value/bias': 'bert.bert.bert_encoder.layers.22.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_22/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.22.attention.output.dense' + '.weight', + 'bert/encoder/layer_22/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.22.attention.output.dense.bias', + 'bert/encoder/layer_22/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.22.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_22/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.22.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_22/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.22.intermediate.weight', + 'bert/encoder/layer_22/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.22.intermediate.bias', + 'bert/encoder/layer_22/output/dense/kernel': 'bert.bert.bert_encoder.layers.22.output.dense.weight', + 'bert/encoder/layer_22/output/dense/bias': 'bert.bert.bert_encoder.layers.22.output.dense.bias', + 'bert/encoder/layer_22/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.22.output.layernorm.gamma', + 'bert/encoder/layer_22/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.22.output.layernorm.beta', + 'bert/encoder/layer_23/attention/self/query/kernel': 'bert.bert.bert_encoder.layers.23.attention.attention' + '.query_layer.weight', + 'bert/encoder/layer_23/attention/self/query/bias': 'bert.bert.bert_encoder.layers.23.attention.attention' + '.query_layer.bias', + 'bert/encoder/layer_23/attention/self/key/kernel': 'bert.bert.bert_encoder.layers.23.attention.attention' + '.key_layer.weight', + 'bert/encoder/layer_23/attention/self/key/bias': 'bert.bert.bert_encoder.layers.23.attention.attention.key_layer' + '.bias', + 'bert/encoder/layer_23/attention/self/value/kernel': 'bert.bert.bert_encoder.layers.23.attention.attention' + '.value_layer.weight', + 'bert/encoder/layer_23/attention/self/value/bias': 'bert.bert.bert_encoder.layers.23.attention.attention' + '.value_layer.bias', + 'bert/encoder/layer_23/attention/output/dense/kernel': 'bert.bert.bert_encoder.layers.23.attention.output.dense' + '.weight', + 'bert/encoder/layer_23/attention/output/dense/bias': 'bert.bert.bert_encoder.layers.23.attention.output.dense.bias', + 'bert/encoder/layer_23/attention/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.23.attention.output' + '.layernorm.gamma', + 'bert/encoder/layer_23/attention/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.23.attention.output' + '.layernorm.beta', + 'bert/encoder/layer_23/intermediate/dense/kernel': 'bert.bert.bert_encoder.layers.23.intermediate.weight', + 'bert/encoder/layer_23/intermediate/dense/bias': 'bert.bert.bert_encoder.layers.23.intermediate.bias', + 'bert/encoder/layer_23/output/dense/kernel': 'bert.bert.bert_encoder.layers.23.output.dense.weight', + 'bert/encoder/layer_23/output/dense/bias': 'bert.bert.bert_encoder.layers.23.output.dense.bias', + 'bert/encoder/layer_23/output/LayerNorm/gamma': 'bert.bert.bert_encoder.layers.23.output.layernorm.gamma', + 'bert/encoder/layer_23/output/LayerNorm/beta': 'bert.bert.bert_encoder.layers.23.output.layernorm.beta', + 'bert/pooler/dense/kernel': 'bert.bert.dense.weight', + 'bert/pooler/dense/bias': 'bert.bert.dense.bias', + 'cls/predictions/output_bias': 'bert.cls1.output_bias', + 'cls/predictions/transform/dense/kernel': 'bert.cls1.dense.weight', + 'cls/predictions/transform/dense/bias': 'bert.cls1.dense.bias', + 'cls/predictions/transform/LayerNorm/gamma': 'bert.cls1.layernorm.gamma', + 'cls/predictions/transform/LayerNorm/beta': 'bert.cls1.layernorm.beta', + 'cls/seq_relationship/output_weights': 'bert.cls2.dense.weight', + 'cls/seq_relationship/output_bias': 'bert.cls2.dense.bias', +} + +# Weights need to be transposed while transfer +transpose_list = [ + 'bert.bert.bert_encoder.layers.0.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.0.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.0.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.0.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.0.intermediate.weight', + 'bert.bert.bert_encoder.layers.0.output.dense.weight', + 'bert.bert.bert_encoder.layers.1.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.1.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.1.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.1.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.1.intermediate.weight', + 'bert.bert.bert_encoder.layers.1.output.dense.weight', + 'bert.bert.bert_encoder.layers.2.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.2.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.2.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.2.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.2.intermediate.weight', + 'bert.bert.bert_encoder.layers.2.output.dense.weight', + 'bert.bert.bert_encoder.layers.3.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.3.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.3.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.3.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.3.intermediate.weight', + 'bert.bert.bert_encoder.layers.3.output.dense.weight', + 'bert.bert.bert_encoder.layers.4.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.4.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.4.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.4.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.4.intermediate.weight', + 'bert.bert.bert_encoder.layers.4.output.dense.weight', + 'bert.bert.bert_encoder.layers.5.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.5.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.5.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.5.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.5.intermediate.weight', + 'bert.bert.bert_encoder.layers.5.output.dense.weight', + 'bert.bert.bert_encoder.layers.6.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.6.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.6.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.6.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.6.intermediate.weight', + 'bert.bert.bert_encoder.layers.6.output.dense.weight', + 'bert.bert.bert_encoder.layers.7.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.7.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.7.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.7.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.7.intermediate.weight', + 'bert.bert.bert_encoder.layers.7.output.dense.weight', + 'bert.bert.bert_encoder.layers.8.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.8.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.8.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.8.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.8.intermediate.weight', + 'bert.bert.bert_encoder.layers.8.output.dense.weight', + 'bert.bert.bert_encoder.layers.9.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.9.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.9.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.9.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.9.intermediate.weight', + 'bert.bert.bert_encoder.layers.9.output.dense.weight', + 'bert.bert.bert_encoder.layers.10.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.10.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.10.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.10.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.10.intermediate.weight', + 'bert.bert.bert_encoder.layers.10.output.dense.weight', + 'bert.bert.bert_encoder.layers.11.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.11.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.11.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.11.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.11.intermediate.weight', + 'bert.bert.bert_encoder.layers.11.output.dense.weight', + 'bert.bert.bert_encoder.layers.12.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.12.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.12.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.12.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.12.intermediate.weight', + 'bert.bert.bert_encoder.layers.12.output.dense.weight', + 'bert.bert.bert_encoder.layers.13.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.13.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.13.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.13.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.13.intermediate.weight', + 'bert.bert.bert_encoder.layers.13.output.dense.weight', + 'bert.bert.bert_encoder.layers.14.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.14.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.14.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.14.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.14.intermediate.weight', + 'bert.bert.bert_encoder.layers.14.output.dense.weight', + 'bert.bert.bert_encoder.layers.15.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.15.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.15.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.15.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.15.intermediate.weight', + 'bert.bert.bert_encoder.layers.15.output.dense.weight', + 'bert.bert.bert_encoder.layers.16.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.16.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.16.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.16.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.16.intermediate.weight', + 'bert.bert.bert_encoder.layers.16.output.dense.weight', + 'bert.bert.bert_encoder.layers.17.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.17.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.17.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.17.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.17.intermediate.weight', + 'bert.bert.bert_encoder.layers.17.output.dense.weight', + 'bert.bert.bert_encoder.layers.18.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.18.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.18.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.18.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.18.intermediate.weight', + 'bert.bert.bert_encoder.layers.18.output.dense.weight', + 'bert.bert.bert_encoder.layers.19.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.19.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.19.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.19.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.19.intermediate.weight', + 'bert.bert.bert_encoder.layers.19.output.dense.weight', + 'bert.bert.bert_encoder.layers.20.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.20.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.20.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.20.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.20.intermediate.weight', + 'bert.bert.bert_encoder.layers.20.output.dense.weight', + 'bert.bert.bert_encoder.layers.21.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.21.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.21.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.21.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.21.intermediate.weight', + 'bert.bert.bert_encoder.layers.21.output.dense.weight', + 'bert.bert.bert_encoder.layers.22.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.22.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.22.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.22.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.22.intermediate.weight', + 'bert.bert.bert_encoder.layers.22.output.dense.weight', + 'bert.bert.bert_encoder.layers.23.attention.attention.query_layer.weight', + 'bert.bert.bert_encoder.layers.23.attention.attention.key_layer.weight', + 'bert.bert.bert_encoder.layers.23.attention.attention.value_layer.weight', + 'bert.bert.bert_encoder.layers.23.attention.output.dense.weight', + 'bert.bert.bert_encoder.layers.23.intermediate.weight', + 'bert.bert.bert_encoder.layers.23.output.dense.weight', + 'bert.bert.dense.weight', + 'bert.cls1.dense.weight', +] diff --git a/nlp/language_model/bert/MindSpore/scripts/ms2tf/ms_and_tf_checkpoint_transfer_tools.py b/nlp/language_model/bert/MindSpore/scripts/ms2tf/ms_and_tf_checkpoint_transfer_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..4b00b977919e2ab27a4da14b03a4c28e7ecc7a42 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/ms2tf/ms_and_tf_checkpoint_transfer_tools.py @@ -0,0 +1,142 @@ +# Copyright 2022 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. +# ============================================================================ +""" + mindspore and tensorflow checkpoint transfer tools + You only need a tf checkpoint to create a mindspore one while using 'tf2ms'. + But you need both two: an existed tf checkpoint and a mindspore one, while using 'ms2tf'. + example: + python ms_and_tf_checkpoint_transfer_tools.py \ + --tf_ckpt_path=./model.ckpt-28252 \ + --new_ckpt_path=./new_ckpt.ckpt \ + --tarnsfer_option=tf2ms +""" + +import argparse +import tensorflow as tf +from mindspore.common.tensor import Tensor +from mindspore.train.serialization import load_checkpoint, save_checkpoint +from ms2tf_config import param_name_dict as ms2tf_param_dict, transpose_list + + +def convert_ms_2_tf(tf_ckpt_path, ms_ckpt_path, new_ckpt_path): + """ + convert ms checkpoint to tf checkpoint + """ + # load MS checkpoint + ms_param_dict = load_checkpoint(ms_ckpt_path) + for name in ms_param_dict.keys(): + if isinstance(ms_param_dict[name].data, Tensor): + ms_param_dict[name] = ms_param_dict[name].data.asnumpy() + + convert_count = 0 + with tf.Session() as sess: + # convert ms shape to tf + print("start convert parameter ...") + new_var_list = [] + for var_name, shape in tf.contrib.framework.list_variables(tf_ckpt_path): + if var_name in ms2tf_param_dict: + ms_name = ms2tf_param_dict[var_name] + + new_tensor = tf.convert_to_tensor(ms_param_dict[ms_name]) + if ms_name in transpose_list: + new_tensor = tf.transpose(new_tensor, (1, 0)) + if new_tensor.shape != tuple(shape): + raise ValueError("shape is not matched after transpose!! {}, {}" + .format(str(new_tensor.shape), str(tuple(shape)))) + + if new_tensor.shape != tuple(shape): + raise ValueError("shape is not matched after transpose!! {}, {}" + .format(str(new_tensor.shape), str(tuple(shape)))) + var = tf.Variable(new_tensor, name=var_name) + convert_count = convert_count + 1 + else: + var = tf.Variable(tf.contrib.framework.load_variable(tf_ckpt_path, var_name), name=var_name) + new_var_list.append(var) + print('convert value num: ', convert_count, " of ", len(ms2tf_param_dict)) + + # saving tf checkpoint + print("start saving ...") + saver = tf.train.Saver(var_list=new_var_list) + sess.run(tf.global_variables_initializer()) + saver.save(sess, new_ckpt_path) + print("tf checkpoint was save in :", new_ckpt_path) + + return True + + +def convert_tf_2_ms(tf_ckpt_path, ms_ckpt_path, new_ckpt_path): + """ + convert tf checkpoint to ms checkpoint + """ + tf2ms_param_dict = dict(zip(ms2tf_param_dict.values(), ms2tf_param_dict.keys())) + + new_params_list = [] + flag_tf1 = tf.__version__[0] == '1' + session = tf.compat.v1.Session() + count = 0 + for ms_name in tf2ms_param_dict.keys(): + count += 1 + param_dict = {} + + tf_name = tf2ms_param_dict[ms_name] + data = tf.train.load_variable(tf_ckpt_path, tf_name) + + if ms_name in transpose_list: + data = tf.transpose(data, (1, 0)) + data = data.eval(session=session) if flag_tf1 else data.numpy() + + param_dict['name'] = ms_name + param_dict['data'] = Tensor(data) + + new_params_list.append(param_dict) + print("start saving checkpoint ...") + save_checkpoint(new_params_list, new_ckpt_path) + print("ms checkpoint was save in :", new_ckpt_path) + + return True + + +def main(): + """ + tf checkpoint transfer to ms or ms checkpoint transfer to tf + """ + parser = argparse.ArgumentParser(description='checkpoint transfer.') + parser.add_argument("--tf_ckpt_path", type=str, default='./tf-bert/bs64k_32k_ckpt_model.ckpt-28252', + help="TensorFlow checkpoint dir, default is: './tf-bert/bs64k_32k_ckpt_model.ckpt-28252'.") + parser.add_argument("--ms_ckpt_path", type=str, default='./ms-bert/large_en.ckpt', + help="MindSpore checkpoint dir, default is: './ms-bert/large_en.ckpt'.") + parser.add_argument("--new_ckpt_path", type=str, default='./new_ckpt/new_bert_large_en.ckpt', + help="New checkpoint dir, default is: './new_ckpt/new_bert_large_en.ckpt'.") + parser.add_argument("--transfer_option", type=str, default='ms2tf', choices=['ms2tf', 'tf2ms'], + help="option of transfer ms2tf or tf2ms, default is ms2tf.") + + args_opt = parser.parse_args() + + if args_opt.transfer_option == 'ms2tf': + print("start ms2tf option ...") + tf_ckpt_path = args_opt.tf_ckpt_path + ms_ckpt_path = args_opt.ms_ckpt_path + new_ckpt_path = args_opt.new_ckpt_path + convert_ms_2_tf(tf_ckpt_path, ms_ckpt_path, new_ckpt_path) + elif args_opt.transfer_option == 'tf2ms': + print("start tf2ms option ...") + tf_ckpt_path = args_opt.tf_ckpt_path + ms_ckpt_path = args_opt.ms_ckpt_path + new_ckpt_path = args_opt.new_ckpt_path + convert_tf_2_ms(tf_ckpt_path, ms_ckpt_path, new_ckpt_path) + + +if __name__ == "__main__": + main() diff --git a/nlp/language_model/bert/MindSpore/scripts/run_classifier.sh b/nlp/language_model/bert/MindSpore/scripts/run_classifier.sh new file mode 100644 index 0000000000000000000000000000000000000000..87b351346404eae8f7b04e4df1c8aaed3c247c28 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_classifier.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_classifier.sh DEVICE_ID" +echo "DEVICE_ID is optional, default value is zero" +echo "for example: bash scripts/run_classifier.sh DEVICE_ID 1" +echo "assessment_method include: [MCC, Spearman_correlation ,Accuracy]" +echo "==============================================================================================================" + +if [ -z $1 ] +then + export DEVICE_ID=0 +else + export DEVICE_ID=$1 +fi + + +mkdir -p ms_log +CUR_DIR=`pwd` +PROJECT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +export GLOG_log_dir=${CUR_DIR}/ms_log +export GLOG_logtostderr=0 +python ${PROJECT_DIR}/../run_classifier.py \ + --config_path="../../task_classifier_config.yaml" \ + --device_target="Ascend" \ + --do_train="true" \ + --do_eval="false" \ + --assessment_method="Accuracy" \ + --device_id=$DEVICE_ID \ + --epoch_num=3 \ + --num_class=2 \ + --train_data_shuffle="true" \ + --eval_data_shuffle="false" \ + --train_batch_size=32 \ + --eval_batch_size=1 \ + --save_finetune_checkpoint_path="" \ + --load_pretrain_checkpoint_path="" \ + --load_finetune_checkpoint_path="" \ + --train_data_file_path="" \ + --eval_data_file_path="" \ + --schema_file_path="" > classifier_log.txt 2>&1 & diff --git a/nlp/language_model/bert/MindSpore/scripts/run_classifier_gpu.sh b/nlp/language_model/bert/MindSpore/scripts/run_classifier_gpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..3a9fe335241a46cc872732aaabc978ef4fa2b783 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_classifier_gpu.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_classifier_gpu.sh DEVICE_ID" +echo "DEVICE_ID is optional, default value is zero" +echo "for example: bash scripts/run_classifier_gpu.sh DEVICE_ID 1" +echo "assessment_method include: [MCC, Spearman_correlation ,Accuracy]" +echo "==============================================================================================================" + +if [ -z $1 ] +then + export CUDA_VISIBLE_DEVICES=0 +else + export CUDA_VISIBLE_DEVICES="$1" +fi + + +mkdir -p ms_log +CUR_DIR=`pwd` +PROJECT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +export GLOG_log_dir=${CUR_DIR}/ms_log +export GLOG_logtostderr=0 +python ${PROJECT_DIR}/../run_classifier.py \ + --config_path="../../task_classifier_config.yaml" \ + --device_target="GPU" \ + --do_train="true" \ + --do_eval="false" \ + --assessment_method="Accuracy" \ + --epoch_num=3 \ + --num_class=2 \ + --train_data_shuffle="true" \ + --eval_data_shuffle="false" \ + --train_batch_size=32 \ + --eval_batch_size=1 \ + --save_finetune_checkpoint_path="" \ + --load_pretrain_checkpoint_path="" \ + --load_finetune_checkpoint_path="" \ + --train_data_file_path="" \ + --eval_data_file_path="" \ + --schema_file_path="" > classifier_log.txt 2>&1 & diff --git a/nlp/language_model/bert/MindSpore/scripts/run_distributed_pretrain_ascend.sh b/nlp/language_model/bert/MindSpore/scripts/run_distributed_pretrain_ascend.sh new file mode 100644 index 0000000000000000000000000000000000000000..07719312e3bec8f2a19d797b21a78c6d2d26e69e --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_distributed_pretrain_ascend.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_distributed_pretrain_ascend.sh DATA_DIR RANK_TABLE_FILE" +echo "for example: bash scripts/run_distributed_pretrain_ascend.sh /path/dataset /path/hccl.json" +echo "It is better to use absolute path." +echo "For hyper parameter, please note that you should customize the scripts: + '{CUR_DIR}/scripts/ascend_distributed_launcher/hyper_parameter_config.ini' " +echo "==============================================================================================================" +CUR_DIR=`pwd` +ulimit -s 302400 +python ${CUR_DIR}/scripts/ascend_distributed_launcher/get_distribute_pretrain_cmd.py \ + --run_script_dir=${CUR_DIR}/run_pretrain.py \ + --hyper_parameter_config_dir=${CUR_DIR}/scripts/ascend_distributed_launcher/hyper_parameter_config.ini \ + --data_dir=$1 \ + --hccl_config_dir=$2 \ + --hccl_time_out=600 \ + --hccn_config_file='/etc/hccn.conf' \ + --cmd_file=distributed_cmd.sh + +bash distributed_cmd.sh diff --git a/nlp/language_model/bert/MindSpore/scripts/run_distributed_pretrain_for_gpu.sh b/nlp/language_model/bert/MindSpore/scripts/run_distributed_pretrain_for_gpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..770dab31195c38fd8a1e77bc93c5cc643399b20e --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_distributed_pretrain_for_gpu.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_distributed_pretrain.sh DEVICE_NUM EPOCH_SIZE DATA_DIR SCHEMA_DIR" +echo "for example: bash scripts/run_distributed_pretrain.sh 8 40 /path/zh-wiki/ [/path/Schema.json](optional)" +echo "It is better to use absolute path." +echo "==============================================================================================================" + +RANK_SIZE=$1 +EPOCH_SIZE=$2 +DATA_DIR=$3 +SCHEMA_DIR=$4 + +mpirun --allow-run-as-root -n $RANK_SIZE --output-filename log_output --merge-stderr-to-stdout \ + python run_pretrain.py \ + --device_target="GPU" \ + --distribute="true" \ + --epoch_size=$EPOCH_SIZE \ + --enable_save_ckpt="true" \ + --enable_lossscale="true" \ + --do_shuffle="true" \ + --enable_data_sink="true" \ + --data_sink_steps=20 \ + --load_checkpoint_path="" \ + --save_checkpoint_steps=10000 \ + --save_checkpoint_num=1 \ + --data_dir=$DATA_DIR \ + --schema_dir=$SCHEMA_DIR > log.txt 2>&1 & + diff --git a/nlp/language_model/bert/MindSpore/scripts/run_infer_310.sh b/nlp/language_model/bert/MindSpore/scripts/run_infer_310.sh new file mode 100644 index 0000000000000000000000000000000000000000..60ad15b6fb914e34e946d51dcaf7125d62625c23 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_infer_310.sh @@ -0,0 +1,141 @@ +#!/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. +# ============================================================================ + +if [[ $# -lt 7 || $# -gt 8 ]]; then + echo "Usage: bash run_infer_310.sh [MINDIR_PATH] [LABEL_PATH] [DATA_FILE_PATH] [DATASET_FORMAT] [SCHEMA_PATH] [USE_CRF] [NEED_PREPROCESS] [DEVICE_ID] + USE_CRF is mandatory, and must choose from [true|false], it's case-insensitive + NEED_PREPROCESS means weather need preprocess or not, it's value is 'y' or 'n'. + DEVICE_ID is optional, it can be set by environment variable device_id, otherwise the value is zero" +exit 1 +fi + +get_real_path(){ + if [ -z "$1" ]; then + echo "" + elif [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} +model=$(get_real_path $1) +label_file_path=$(get_real_path $2) +eval_data_file_path=$(get_real_path $3) +dataset_format=$4 +schema_file_path=$(get_real_path $5) +net_type=${6,,} +if [ $net_type == 'true' ]; then + echo "downstream: CRF" +elif [ $net_type == 'false' ]; then + echo "downstream: NER" +else + echo "[USE_CRF]:true or false" + exit 1 +fi + +if [ "$7" == "y" ] || [ "$7" == "n" ];then + need_preprocess=$7 +else + echo "weather need preprocess or not, it's value must be in [y, n]" + exit 1 +fi + +device_id=0 +if [ $# == 8 ]; then + device_id=$8 +fi + +echo "mindir name: "$model +echo "label_file_path: "$label_file_path +echo "eval_data_file_path: "$eval_data_file_path +echo "dataset_format: "$dataset_format +echo "schema_file_path: "$schema_file_path +echo "need preprocess: "$need_preprocess +echo "device id: "$device_id + +export ASCEND_HOME=/usr/local/Ascend/ +if [ -d ${ASCEND_HOME}/ascend-toolkit ]; then + export PATH=$ASCEND_HOME/fwkacllib/bin:$ASCEND_HOME/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/ascend-toolkit/latest/atc/bin:$PATH + export LD_LIBRARY_PATH=$ASCEND_HOME/fwkacllib/lib64:/usr/local/lib:$ASCEND_HOME/ascend-toolkit/latest/atc/lib64:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export TBE_IMPL_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe + export PYTHONPATH=$ASCEND_HOME/fwkacllib/python/site-packages:${TBE_IMPL_PATH}:$ASCEND_HOME/ascend-toolkit/latest/fwkacllib/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/ascend-toolkit/latest/opp +else + export ASCEND_HOME=/usr/local/Ascend/latest/ + export PATH=$ASCEND_HOME/fwkacllib/bin:$ASCEND_HOME/fwkacllib/ccec_compiler/bin:$ASCEND_HOME/atc/ccec_compiler/bin:$ASCEND_HOME/atc/bin:$PATH + export LD_LIBRARY_PATH=$ASCEND_HOME/fwkacllib/lib64:/usr/local/lib:$ASCEND_HOME/atc/lib64:$ASCEND_HOME/acllib/lib64:$ASCEND_HOME/driver/lib64:$ASCEND_HOME/add-ons:$LD_LIBRARY_PATH + export PYTHONPATH=$ASCEND_HOME/fwkacllib/python/site-packages:$ASCEND_HOME/atc/python/site-packages:$PYTHONPATH + export ASCEND_OPP_PATH=$ASCEND_HOME/opp +fi + +function preprocess_data() +{ + if [ -d preprocess_Result ]; then + rm -rf ./preprocess_Result + fi + mkdir preprocess_Result + python ../preprocess.py --use_crf=$net_type --do_eval=true --label_file_path=$label_file_path --eval_data_file_path=$eval_data_file_path --dataset_format=$dataset_format --schema_file_path=$schema_file_path --result_path=./preprocess_Result/ +} + +function compile_app() +{ + cd ../ascend310_infer || exit + bash build.sh &> build.log +} + +function infer() +{ + cd - || exit + if [ -d result_Files ]; then + rm -rf ./result_Files + fi + if [ -d time_Result ]; then + rm -rf ./time_Result + fi + mkdir result_Files + mkdir time_Result + + ../ascend310_infer/out/main --mindir_path=$model --input0_path=./preprocess_Result/00_data --input1_path=./preprocess_Result/01_data --input2_path=./preprocess_Result/02_data --input3_path=./preprocess_Result/03_data --use_crf=$net_type --device_id=$device_id &> infer.log + +} + +function cal_acc() +{ + python ../postprocess.py --result_path=./result_Files --label_dir=./preprocess_Result/03_data --use_crf=$net_type &> acc.log +} + +if [ $need_preprocess == "y" ]; then + preprocess_data + if [ $? -ne 0 ]; then + echo "preprocess dataset failed" + exit 1 + fi +fi +compile_app +if [ $? -ne 0 ]; then + echo "compile app code failed" + exit 1 +fi +infer +if [ $? -ne 0 ]; then + echo " execute inference failed" + exit 1 +fi +cal_acc +if [ $? -ne 0 ]; then + echo "calculate accuracy failed" + exit 1 +fi \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/scripts/run_ner.sh b/nlp/language_model/bert/MindSpore/scripts/run_ner.sh new file mode 100644 index 0000000000000000000000000000000000000000..d472a5f96cea2f1b2dcea59624bb3c51a7a5c673 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_ner.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_ner.sh DEVICE_ID" +echo "DEVICE_ID is optional, default value is zero" +echo "for example: bash scripts/run_ner.sh 1" +echo "assessment_method include: [BF1, MF1, clue_benchmark]" +echo "==============================================================================================================" + +if [ -z $1 ] +then + export DEVICE_ID=0 +else + export DEVICE_ID=$1 +fi + +mkdir -p ms_log +CUR_DIR=`pwd` +PROJECT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +export GLOG_log_dir=${CUR_DIR}/ms_log +export GLOG_logtostderr=0 +python ${PROJECT_DIR}/../run_ner.py \ + --config_path="../../task_ner_config.yaml" \ + --device_target="Ascend" \ + --do_train="true" \ + --do_eval="false" \ + --assessment_method="BF1" \ + --use_crf="false" \ + --with_lstm="false" \ + --device_id=$DEVICE_ID \ + --epoch_num=5 \ + --train_data_shuffle="true" \ + --eval_data_shuffle="false" \ + --train_batch_size=32 \ + --eval_batch_size=1 \ + --vocab_file_path="" \ + --label_file_path="" \ + --save_finetune_checkpoint_path="" \ + --load_pretrain_checkpoint_path="" \ + --load_finetune_checkpoint_path="" \ + --train_data_file_path="" \ + --eval_data_file_path="" \ + --dataset_format="tfrecord" \ + --schema_file_path="" > ner_log.txt 2>&1 & diff --git a/nlp/language_model/bert/MindSpore/scripts/run_ner_gpu.sh b/nlp/language_model/bert/MindSpore/scripts/run_ner_gpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..7c6aaa6559937c6b8556d2d2bec515ff44b85fd7 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_ner_gpu.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_ner_gpu.sh DEVICE_ID" +echo "DEVICE_ID is optional, default value is zero" +echo "for example: bash scripts/run_ner_gpu.sh 1" +echo "assessment_method include: [BF1, MF1, clue_benchmark]" +echo "==============================================================================================================" + +if [ -z $1 ] +then + export CUDA_VISIBLE_DEVICES=0 +else + export CUDA_VISIBLE_DEVICES="$1" +fi + +mkdir -p ms_log +CUR_DIR=`pwd` +PROJECT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +export GLOG_log_dir=${CUR_DIR}/ms_log +export GLOG_logtostderr=0 +python ${PROJECT_DIR}/../run_ner.py \ + --config_path="../../task_ner_config.yaml" \ + --device_target="GPU" \ + --do_train="true" \ + --do_eval="false" \ + --assessment_method="BF1" \ + --use_crf="false" \ + --with_lstm="false" \ + --epoch_num=5 \ + --train_data_shuffle="true" \ + --eval_data_shuffle="false" \ + --train_batch_size=32 \ + --eval_batch_size=1 \ + --vocab_file_path="" \ + --label_file_path="" \ + --save_finetune_checkpoint_path="" \ + --load_pretrain_checkpoint_path="" \ + --load_finetune_checkpoint_path="" \ + --train_data_file_path="" \ + --eval_data_file_path="" \ + --dataset_format="tfrecord" \ + --schema_file_path="" > ner_log.txt 2>&1 & diff --git a/nlp/language_model/bert/MindSpore/scripts/run_squad.sh b/nlp/language_model/bert/MindSpore/scripts/run_squad.sh new file mode 100644 index 0000000000000000000000000000000000000000..1bf600564c6e4f21102ea8581b810f7f6670c1c0 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_squad.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_squad.sh DEVICE_ID" +echo "DEVICE_ID is optional, default value is zero" +echo "for example: bash scripts/run_squad.sh 1" +echo "assessment_method include: [Accuracy]" +echo "==============================================================================================================" + +if [ -z $1 ] +then + export DEVICE_ID=0 +else + export DEVICE_ID=$1 +fi + +mkdir -p ms_log +CUR_DIR=`pwd` +PROJECT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +export GLOG_log_dir=${CUR_DIR}/ms_log +export GLOG_logtostderr=0 +python ${PROJECT_DIR}/../run_squad.py \ + --config_path="../../task_squad_config.yaml" \ + --device_target="Ascend" \ + --do_train="true" \ + --do_eval="false" \ + --device_id=$DEVICE_ID \ + --epoch_num=3 \ + --num_class=2 \ + --train_data_shuffle="true" \ + --eval_data_shuffle="false" \ + --train_batch_size=32 \ + --eval_batch_size=1 \ + --vocab_file_path="" \ + --save_finetune_checkpoint_path="" \ + --load_pretrain_checkpoint_path="" \ + --load_finetune_checkpoint_path="" \ + --train_data_file_path="" \ + --eval_json_path="" \ + --schema_file_path="" > squad_log.txt 2>&1 & diff --git a/nlp/language_model/bert/MindSpore/scripts/run_squad_gpu.sh b/nlp/language_model/bert/MindSpore/scripts/run_squad_gpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..93490eb17532001726ac1a49f3535d0b732fbf76 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_squad_gpu.sh @@ -0,0 +1,66 @@ +#!/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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_squad_gpu.sh DEVICE_ID" +echo "DEVICE_ID is optional, default value is zero" +echo "for example: bash scripts/run_squad_gpu.sh 1" +echo "assessment_method include: [Accuracy]" +echo "==============================================================================================================" + +if [ -z $1 ] +then + export CUDA_VISIBLE_DEVICES=0 +else + export CUDA_VISIBLE_DEVICES="$1" +fi + +mkdir -p squad +# 1. Download training dataset(.tf_record), eval dataset(.json), vocab.txt and checkpoint +training_tf_record_file=squad/train.tf_record +if [[ ! -f "${training_tf_record_file}" ]]; then + cd squad + wget http://10.201.40.22/files/datasets/bert/squad_data_with_tf_record.tar + tar xvf squad_data_with_tf_record.tar + rm -rf squad_data_with_tf_record.tar + cd - +fi + +mkdir -p ms_log +CUR_DIR=`pwd` +PROJECT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +export GLOG_log_dir=${CUR_DIR}/ms_log +export GLOG_logtostderr=0 +python3 ${PROJECT_DIR}/../run_squad.py \ + --config_path="../../task_squad_config.yaml" \ + --device_target="GPU" \ + --do_train="true" \ + --do_eval="true" \ + --device_id=0 \ + --epoch_num=1 \ + --num_class=2 \ + --train_data_shuffle="true" \ + --eval_data_shuffle="false" \ + --train_batch_size=12 \ + --eval_batch_size=1 \ + --vocab_file_path="./squad/vocab.txt" \ + --save_finetune_checkpoint_path="" \ + --load_pretrain_checkpoint_path="./squad/bert_large_ascend_v130_enwiki_official_nlp_bs768_loss1.1.ckpt" \ + --load_finetune_checkpoint_path="" \ + --train_data_file_path="./squad/train.tf_record" \ + --eval_json_path="./squad/dev-v1.1.json" \ + --schema_file_path="" 2>&1 | tee squad.txt diff --git a/nlp/language_model/bert/MindSpore/scripts/run_squad_gpu_distribute.sh b/nlp/language_model/bert/MindSpore/scripts/run_squad_gpu_distribute.sh new file mode 100644 index 0000000000000000000000000000000000000000..a3feaf0b037d4cfba3b03fa46454acfead32ab3c --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_squad_gpu_distribute.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Copyright 2021 Huawei Technologies Co., Ltd +# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_squad_gpu.sh rank_size" +echo "for example: bash scripts/run_squad_gpu.sh 8" +echo "assessment_method include: [Accuracy]" +echo "==============================================================================================================" + + + +RANK_SIZE=$1 +mkdir -p ms_log +CUR_DIR=`pwd` +PROJECT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +export GLOG_log_dir=${CUR_DIR}/ms_log +export GLOG_logtostderr=0 +mpirun -n $RANK_SIZE --output-filename log_output --merge-stderr-to-stdout --allow-run-as-root \ +python3 ${PROJECT_DIR}/../run_squad.py \ + --config_path="../../task_squad_config.yaml" \ + --device_target="GPU" \ + --do_train="true" \ + --do_eval="true" \ + --epoch_num=1 \ + --num_class=2 \ + --train_data_shuffle="true" \ + --eval_data_shuffle="false" \ + --train_batch_size=12 \ + --eval_batch_size=1 \ + --vocab_file_path="./squad/vocab.txt" \ + --save_finetune_checkpoint_path="" \ + --load_pretrain_checkpoint_path="./squad/bert_large_ascend_v130_enwiki_official_nlp_bs768_loss1.1.ckpt" \ + --load_finetune_checkpoint_path="" \ + --train_data_file_path="./squad/train.tf_record" \ + --eval_json_path="./squad/dev-v1.1.json" \ + --schema_file_path="" 2>&1 | tee squad.txt diff --git a/nlp/language_model/bert/MindSpore/scripts/run_standalone_pretrain_ascend.sh b/nlp/language_model/bert/MindSpore/scripts/run_standalone_pretrain_ascend.sh new file mode 100644 index 0000000000000000000000000000000000000000..f81ed7bab7671f0769c3a2847dd4fd7a66eae118 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_standalone_pretrain_ascend.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_standalone_pretrain_ascend.sh DEVICE_ID EPOCH_SIZE DATA_DIR SCHEMA_DIR" +echo "for example: bash scripts/run_standalone_pretrain_ascend.sh 0 40 /path/zh-wiki/ [/path/Schema.json](optional)" +echo "==============================================================================================================" + +DEVICE_ID=$1 +EPOCH_SIZE=$2 +DATA_DIR=$3 +SCHEMA_DIR=$4 +ulimit -s 102400 + +mkdir -p ms_log +PROJECT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +CUR_DIR=`pwd` +export GLOG_log_dir=${CUR_DIR}/ms_log +export GLOG_logtostderr=0 +python ${PROJECT_DIR}/../run_pretrain.py \ + --distribute="false" \ + --epoch_size=$EPOCH_SIZE \ + --device_id=$DEVICE_ID \ + --enable_save_ckpt="true" \ + --enable_lossscale="true" \ + --do_shuffle="true" \ + --enable_data_sink="true" \ + --data_sink_steps=1 \ + --accumulation_steps=1 \ + --load_checkpoint_path="" \ + --save_checkpoint_steps=10000 \ + --save_checkpoint_num=1 \ + --data_dir=$DATA_DIR \ + --schema_dir=$SCHEMA_DIR > pretraining_log.txt 2>&1 & diff --git a/nlp/language_model/bert/MindSpore/scripts/run_standalone_pretrain_for_gpu.sh b/nlp/language_model/bert/MindSpore/scripts/run_standalone_pretrain_for_gpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..eb7cc6a589d99fb14d983edcee618013236717f1 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/scripts/run_standalone_pretrain_for_gpu.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Copyright 2020 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash scripts/run_standalone_pretrain.sh DEVICE_ID EPOCH_SIZE DATA_DIR SCHEMA_DIR" +echo "for example: bash scripts/run_standalone_pretrain.sh 0 40 /path/zh-wiki/ [/path/Schema.json](optional)" +echo "==============================================================================================================" + +DEVICE_ID=$1 +EPOCH_SIZE=$2 +DATA_DIR=$3 +SCHEMA_DIR=$4 + +export CUDA_VISIBLE_DEVICES=$DEVICE_ID + +mkdir -p ms_log +CUR_DIR=`pwd` +export GLOG_log_dir=${CUR_DIR}/ms_log +export GLOG_logtostderr=0 +python run_pretrain.py \ + --device_target="GPU" \ + --distribute="false" \ + --epoch_size=$EPOCH_SIZE \ + --enable_save_ckpt="true" \ + --enable_lossscale="true" \ + --do_shuffle="true" \ + --enable_data_sink="true" \ + --data_sink_steps=20 \ + --load_checkpoint_path="" \ + --save_checkpoint_path="" \ + --save_checkpoint_steps=10000 \ + --save_checkpoint_num=1 \ + --data_dir=$DATA_DIR \ + --schema_dir=$SCHEMA_DIR > log.txt 2>&1 & diff --git a/nlp/language_model/bert/MindSpore/src/CRF.py b/nlp/language_model/bert/MindSpore/src/CRF.py new file mode 100644 index 0000000000000000000000000000000000000000..1f0e67d7ba29940e40e5dc18e1f78d24d1dc37cf --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/CRF.py @@ -0,0 +1,177 @@ +# Copyright 2020 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. +# ============================================================================ + +''' +CRF script. +''' + +import numpy as np +import mindspore.nn as nn +from mindspore.ops import operations as P +from mindspore.common.tensor import Tensor +from mindspore.common.parameter import Parameter +import mindspore.common.dtype as mstype + +class CRF(nn.Cell): + ''' + Conditional Random Field + Args: + tag_to_index: The dict for tag to index mapping with extra "" and ""sign. + batch_size: Batch size, i.e., the length of the first dimension. + seq_length: Sequence length, i.e., the length of the second dimension. + is_training: Specifies whether to use training mode. + Returns: + Training mode: Tensor, total loss. + Evaluation mode: Tuple, the index for each step with the highest score; Tuple, the index for the last + step with the highest score. + ''' + def __init__(self, tag_to_index, batch_size=1, seq_length=128, is_training=True): + + super(CRF, self).__init__() + self.target_size = len(tag_to_index) + self.is_training = is_training + self.tag_to_index = tag_to_index + self.batch_size = batch_size + self.seq_length = seq_length + self.START_TAG = "" + self.STOP_TAG = "" + self.START_VALUE = Tensor(self.target_size-2, dtype=mstype.int32) + self.STOP_VALUE = Tensor(self.target_size-1, dtype=mstype.int32) + transitions = np.random.normal(size=(self.target_size, self.target_size)).astype(np.float32) + transitions[tag_to_index[self.START_TAG], :] = -10000 + transitions[:, tag_to_index[self.STOP_TAG]] = -10000 + self.transitions = Parameter(Tensor(transitions)) + self.cat = P.Concat(axis=-1) + self.argmax = P.ArgMaxWithValue(axis=-1) + self.log = P.Log() + self.exp = P.Exp() + self.sum = P.ReduceSum() + self.tile = P.Tile() + self.reduce_sum = P.ReduceSum(keep_dims=True) + self.reshape = P.Reshape() + self.expand = P.ExpandDims() + self.mean = P.ReduceMean() + init_alphas = np.ones(shape=(self.batch_size, self.target_size)) * -10000.0 + init_alphas[:, self.tag_to_index[self.START_TAG]] = 0. + self.init_alphas = Tensor(init_alphas, dtype=mstype.float32) + self.cast = P.Cast() + self.reduce_max = P.ReduceMax(keep_dims=True) + self.on_value = Tensor(1.0, dtype=mstype.float32) + self.off_value = Tensor(0.0, dtype=mstype.float32) + self.onehot = P.OneHot() + + def log_sum_exp(self, logits): + ''' + Compute the log_sum_exp score for Normalization factor. + ''' + max_score = self.reduce_max(logits, -1) #16 5 5 + score = self.log(self.reduce_sum(self.exp(logits - max_score), -1)) + score = max_score + score + return score + + def _realpath_score(self, features, label): + ''' + Compute the emission and transition score for the real path. + ''' + label = label * 1 + concat_A = self.tile(self.reshape(self.START_VALUE, (1,)), (self.batch_size,)) + concat_A = self.reshape(concat_A, (self.batch_size, 1)) + labels = self.cat((concat_A, label)) + onehot_label = self.onehot(label, self.target_size, self.on_value, self.off_value) + emits = features * onehot_label + labels = self.onehot(labels, self.target_size, self.on_value, self.off_value) + label1 = labels[:, 1:, :] + label2 = labels[:, :self.seq_length, :] + label1 = self.expand(label1, 3) + label2 = self.expand(label2, 2) + label_trans = label1 * label2 + transitions = self.expand(self.expand(self.transitions, 0), 0) + trans = transitions * label_trans + score = self.sum(emits, (1, 2)) + self.sum(trans, (1, 2, 3)) + stop_value_index = labels[:, (self.seq_length-1):self.seq_length, :] + stop_value = self.transitions[(self.target_size-1):self.target_size, :] + stop_score = stop_value * self.reshape(stop_value_index, (self.batch_size, self.target_size)) + score = score + self.sum(stop_score, 1) + score = self.reshape(score, (self.batch_size, -1)) + return score + + def _normalization_factor(self, features): + ''' + Compute the total score for all the paths. + ''' + forward_var = self.init_alphas + forward_var = self.expand(forward_var, 1) + for idx in range(self.seq_length): + feat = features[:, idx:(idx+1), :] + emit_score = self.reshape(feat, (self.batch_size, self.target_size, 1)) + next_tag_var = emit_score + self.transitions + forward_var + forward_var = self.log_sum_exp(next_tag_var) + forward_var = self.reshape(forward_var, (self.batch_size, 1, self.target_size)) + terminal_var = forward_var + self.reshape(self.transitions[(self.target_size-1):self.target_size, :], (1, -1)) + alpha = self.log_sum_exp(terminal_var) + alpha = self.reshape(alpha, (self.batch_size, -1)) + return alpha + + def _decoder(self, features): + ''' + Viterbi decode for evaluation. + ''' + backpointers = () + forward_var = self.init_alphas + for idx in range(self.seq_length): + feat = features[:, idx:(idx+1), :] + feat = self.reshape(feat, (self.batch_size, self.target_size)) + bptrs_t = () + + next_tag_var = self.expand(forward_var, 1) + self.transitions + best_tag_id, best_tag_value = self.argmax(next_tag_var) + bptrs_t += (best_tag_id,) + forward_var = best_tag_value + feat + + backpointers += (bptrs_t,) + terminal_var = forward_var + self.reshape(self.transitions[(self.target_size-1):self.target_size, :], (1, -1)) + best_tag_id, _ = self.argmax(terminal_var) + return backpointers, best_tag_id + + def construct(self, features, label): + if self.is_training: + forward_score = self._normalization_factor(features) + gold_score = self._realpath_score(features, label) + return_value = self.mean(forward_score - gold_score) + else: + path_list, tag = self._decoder(features) + return_value = path_list, tag + return return_value + +def postprocess(backpointers, best_tag_id): + ''' + Do postprocess + ''' + best_tag_id = best_tag_id.asnumpy() + batch_size = len(best_tag_id) + best_path = [] + for i in range(batch_size): + best_path.append([]) + best_local_id = best_tag_id[i] + best_path[-1].append(best_local_id) + for bptrs_t in reversed(backpointers): + bptrs_t = bptrs_t[0].asnumpy() + local_idx = bptrs_t[i] + best_local_id = local_idx[best_local_id] + best_path[-1].append(best_local_id) + # Pop off the start tag (we dont want to return that to the caller) + best_path[-1].pop() + best_path[-1].reverse() + return best_path diff --git a/nlp/language_model/bert/MindSpore/src/__init__.py b/nlp/language_model/bert/MindSpore/src/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..85eb83764f73425ac85278751f3e79ce743fc17c --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/__init__.py @@ -0,0 +1,39 @@ +# Copyright 2020 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. +# ============================================================================ +"""Bert Init.""" +from .bert_for_pre_training import BertNetworkWithLoss, BertPreTraining, \ + BertPretrainingLoss, GetMaskedLMOutput, GetNextSentenceOutput, \ + BertTrainOneStepCell, BertTrainOneStepWithLossScaleCell, \ + BertTrainAccumulationAllReduceEachWithLossScaleCell, \ + BertTrainAccumulationAllReducePostWithLossScaleCell, \ + BertTrainOneStepWithLossScaleCellForAdam, \ + BertNetworkMatchBucket, BertPretrainEval +from .bert_model import BertAttention, BertConfig, BertEncoderCell, BertModel, \ + BertOutput, BertSelfAttention, BertTransformer, EmbeddingLookup, \ + EmbeddingPostprocessor, RelaPosEmbeddingsGenerator, RelaPosMatrixGenerator, \ + SaturateCast, CreateAttentionMaskFromInputMask +from .adam import AdamWeightDecayForBert, AdamWeightDecayOp +__all__ = [ + "BertNetworkWithLoss", "BertPreTraining", "BertPretrainingLoss", + "GetMaskedLMOutput", "GetNextSentenceOutput", "BertTrainOneStepCell", + "BertTrainOneStepWithLossScaleCell", "BertTrainAccumulationAllReduceEachWithLossScaleCell", + "BertTrainAccumulationAllReducePostWithLossScaleCell", + "BertNetworkMatchBucket", "BertPretrainEval", + "BertAttention", "BertConfig", "BertEncoderCell", "BertModel", "BertOutput", + "BertSelfAttention", "BertTransformer", "EmbeddingLookup", + "EmbeddingPostprocessor", "RelaPosEmbeddingsGenerator", "AdamWeightDecayForBert", + "RelaPosMatrixGenerator", "SaturateCast", "CreateAttentionMaskFromInputMask", + "BertTrainOneStepWithLossScaleCellForAdam", "AdamWeightDecayOp" +] diff --git a/nlp/language_model/bert/MindSpore/src/adam.py b/nlp/language_model/bert/MindSpore/src/adam.py new file mode 100644 index 0000000000000000000000000000000000000000..eed12a99064e90b232d11c246ece465697c65c8a --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/adam.py @@ -0,0 +1,414 @@ +# 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. +# ============================================================================ +"""AdamWeightDecayForBert, a customized Adam for bert. Input: gradient, overflow flag.""" +import numpy as np + +from mindspore.common import dtype as mstype +from mindspore.ops import operations as P +from mindspore.ops import composite as C +from mindspore.ops import functional as F +from mindspore.common.tensor import Tensor +from mindspore._checkparam import Validator as validator +from mindspore._checkparam import Rel +from mindspore.nn.optim.optimizer import Optimizer + +_adam_opt = C.MultitypeFuncGraph("adam_opt") +_scaler_one = Tensor(1, mstype.int32) +_scaler_ten = Tensor(10, mstype.float32) + +@_adam_opt.register("Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", + "Tensor", "Bool", "Bool") +def _update_run_kernel(beta1, beta2, eps, lr, weight_decay, param, m, v, gradient, decay_flags, optim_filter): + """ + Update parameters by AdamWeightDecay op. + """ + if optim_filter: + adam = P.AdamWeightDecay() + if decay_flags: + next_param = adam(param, m, v, lr, beta1, beta2, eps, Tensor(weight_decay, mstype.float32), gradient) + else: + next_param = adam(param, m, v, lr, beta1, beta2, eps, Tensor(0.0, mstype.float32), gradient) + return next_param + return gradient + +@_adam_opt.register("Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", + "Tensor", "Bool", "Bool") +def _update_run_op(beta1, beta2, eps, lr, overflow, weight_decay, param, m, v, gradient, decay_flag, optim_filter): + """ + Update parameters. + + Args: + beta1 (Tensor): The exponential decay rate for the 1st moment estimations. Should be in range (0.0, 1.0). + beta2 (Tensor): The exponential decay rate for the 2nd moment estimations. Should be in range (0.0, 1.0). + eps (Tensor): Term added to the denominator to improve numerical stability. Should be greater than 0. + lr (Tensor): Learning rate. + overflow (Tensor): Whether overflow occurs. + weight_decay (Tensor): Weight decay. Should be equal to or greater than 0. + param (Tensor): Parameters. + m (Tensor): m value of parameters. + v (Tensor): v value of parameters. + gradient (Tensor): Gradient of parameters. + decay_flag (bool): Applies weight decay or not. + optim_filter (bool): Applies parameter update or not. + + Returns: + Tensor, the new value of v after updating. + """ + if optim_filter: + op_mul = P.Mul() + op_square = P.Square() + op_sqrt = P.Sqrt() + op_cast = P.Cast() + op_reshape = P.Reshape() + op_shape = P.Shape() + op_select = P.Select() + + param_fp32 = op_cast(param, mstype.float32) + m_fp32 = op_cast(m, mstype.float32) + v_fp32 = op_cast(v, mstype.float32) + gradient_fp32 = op_cast(gradient, mstype.float32) + + cond = op_cast(F.fill(mstype.int32, op_shape(m_fp32), 1) * op_reshape(overflow, (())), mstype.bool_) + next_m = op_mul(beta1, m_fp32) + op_select(cond, m_fp32,\ + op_mul(op_cast(F.tuple_to_array((1.0,)), mstype.float32) - beta1, gradient_fp32)) + + next_v = op_mul(beta2, v_fp32) + op_select(cond, v_fp32,\ + op_mul(op_cast(F.tuple_to_array((1.0,)), mstype.float32) - beta2, op_square(gradient_fp32))) + + update = next_m / (eps + op_sqrt(next_v)) + if decay_flag: + update = op_mul(weight_decay, param_fp32) + update + + update_with_lr = op_mul(lr, update) + zeros = F.fill(mstype.float32, op_shape(param_fp32), 0) + next_param = param_fp32 - op_select(cond, zeros, op_reshape(update_with_lr, op_shape(param_fp32))) + + next_param = F.depend(next_param, F.assign(param, op_cast(next_param, F.dtype(param)))) + next_param = F.depend(next_param, F.assign(m, op_cast(next_m, F.dtype(m)))) + next_param = F.depend(next_param, F.assign(v, op_cast(next_v, F.dtype(v)))) + + return op_cast(next_param, F.dtype(param)) + return gradient + + +@_adam_opt.register("Function", "Function", "Function", "Function", "Bool", "Bool", "Bool", "Tensor", "Tensor", + "Tensor", "Tensor", "Tensor", "Tensor", "RowTensor", "Tensor", "Tensor", "Tensor", "Bool", "Bool") +def _run_opt_with_sparse(opt, sparse_opt, push, pull, use_locking, use_nesterov, target, beta1_power, + beta2_power, beta1, beta2, eps, lr, gradient, param, m, v, ps_parameter, cache_enable): + """Apply sparse adam optimizer to the weight parameter when the gradient is sparse.""" + success = True + indices = gradient.indices + values = gradient.values + if ps_parameter and not cache_enable: + op_shape = P.Shape() + shapes = (op_shape(param), op_shape(m), op_shape(v), + op_shape(beta1_power), op_shape(beta2_power), op_shape(lr), op_shape(beta1), + op_shape(beta2), op_shape(eps), op_shape(values), op_shape(indices)) + success = F.depend(success, pull(push((beta1_power, beta2_power, lr, beta1, beta2, + eps, values, indices), shapes), param)) + return success + + if not target: + success = F.depend(success, sparse_opt(param, m, v, beta1_power, beta2_power, lr, beta1, beta2, + eps, values, indices)) + else: + op_mul = P.Mul() + op_square = P.Square() + op_sqrt = P.Sqrt() + scatter_add = P.ScatterAdd(use_locking) + + success = F.depend(success, F.assign(m, op_mul(beta1, m))) + success = F.depend(success, F.assign(v, op_mul(beta2, v))) + + grad_indices = gradient.indices + grad_value = gradient.values + + next_m = scatter_add(m, + grad_indices, + op_mul(F.tuple_to_array((1.0,)) - beta1, grad_value)) + + next_v = scatter_add(v, + grad_indices, + op_mul(F.tuple_to_array((1.0,)) - beta2, op_square(grad_value))) + + if use_nesterov: + m_temp = next_m * _scaler_ten + F.assign(m, op_mul(beta1, next_m)) + div_value = scatter_add(m, + op_mul(grad_indices, _scaler_one), + op_mul(F.tuple_to_array((1.0,)) - beta1, grad_value)) + param_update = div_value / (op_sqrt(next_v) + eps) + F.assign(m, m_temp / _scaler_ten) + else: + param_update = next_m / (op_sqrt(next_v) + eps) + + lr_t = lr * op_sqrt(1 - beta2_power) / (1 - beta1_power) + next_param = param - lr_t * param_update + + success = F.depend(success, F.assign(param, next_param)) + success = F.depend(success, F.assign(m, next_m)) + success = F.depend(success, F.assign(v, next_v)) + + return success + + +@_adam_opt.register("Function", "Function", "Function", "Function", "Bool", "Bool", "Bool", "Tensor", "Tensor", + "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Bool", "Bool") +def _run_opt_with_one_number(opt, sparse_opt, push, pull, use_locking, use_nesterov, target, + beta1_power, beta2_power, beta1, beta2, eps, lr, gradient, param, + moment1, moment2, ps_parameter, cache_enable): + """Apply adam optimizer to the weight parameter using Tensor.""" + success = True + if ps_parameter and not cache_enable: + op_shape = P.Shape() + success = F.depend(success, pull(push((beta1_power, beta2_power, lr, beta1, beta2, eps, gradient), + (op_shape(param), op_shape(moment1), op_shape(moment2))), param)) + else: + success = F.depend(success, opt(param, moment1, moment2, beta1_power, beta2_power, lr, beta1, beta2, + eps, gradient)) + return success + + +@_adam_opt.register("Function", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", "Tensor", + "Tensor", "Tensor") +def _run_off_load_opt(opt, beta1_power, beta2_power, beta1, beta2, eps, lr, gradient, param, moment1, moment2): + """Apply AdamOffload optimizer to the weight parameter using Tensor.""" + success = True + delat_param = opt(moment1, moment2, beta1_power, beta2_power, lr, beta1, beta2, eps, gradient) + success = F.depend(success, F.assign_add(param, delat_param)) + return success + + +def _check_param_value(beta1, beta2, eps, prim_name): + """Check the type of inputs.""" + validator.check_value_type("beta1", beta1, [float], prim_name) + validator.check_value_type("beta2", beta2, [float], prim_name) + validator.check_value_type("eps", eps, [float], prim_name) + validator.check_float_range(beta1, 0.0, 1.0, Rel.INC_NEITHER, "beta1", prim_name) + validator.check_float_range(beta2, 0.0, 1.0, Rel.INC_NEITHER, "beta2", prim_name) + validator.check_positive_float(eps, "eps", prim_name) + +class AdamWeightDecayForBert(Optimizer): + """ + Implements the Adam algorithm to fix the weight decay. + + Note: + When separating parameter groups, the weight decay in each group will be applied on the parameters if the + weight decay is positive. When not separating parameter groups, the `weight_decay` in the API will be applied + on the parameters without 'beta' or 'gamma' in their names if `weight_decay` is positive. + + To improve parameter groups performance, the customized order of parameters can be supported. + + Args: + params (Union[list[Parameter], list[dict]]): When the `params` is a list of `Parameter` which will be updated, + the element in `params` must be class `Parameter`. When the `params` is a list of `dict`, the "params", + "lr", "weight_decay" and "order_params" are the keys can be parsed. + + - params: Required. The value must be a list of `Parameter`. + + - lr: Optional. If "lr" is in the keys, the value of the corresponding learning rate will be used. + If not, the `learning_rate` in the API will be used. + + - weight_decay: Optional. If "weight_decay" is in the keys, the value of the corresponding weight decay + will be used. If not, the `weight_decay` in the API will be used. + + - order_params: Optional. If "order_params" is in the keys, the value must be the order of parameters and + the order will be followed in the optimizer. There are no other keys in the `dict` and the parameters + which in the 'order_params' must be in one of group parameters. + + learning_rate (Union[float, Tensor, Iterable, LearningRateSchedule]): A value or a graph for the learning rate. + When the learning_rate is an Iterable or a Tensor in a 1D dimension, use the dynamic learning rate, then + the i-th step will take the i-th value as the learning rate. When the learning_rate is LearningRateSchedule, + use dynamic learning rate, the i-th learning rate will be calculated during the process of training + according to the formula of LearningRateSchedule. When the learning_rate is a float or a Tensor in a zero + dimension, use fixed learning rate. Other cases are not supported. The float learning rate must be + equal to or greater than 0. If the type of `learning_rate` is int, it will be converted to float. + Default: 1e-3. + beta1 (float): The exponential decay rate for the 1st moment estimations. Default: 0.9. + Should be in range (0.0, 1.0). + beta2 (float): The exponential decay rate for the 2nd moment estimations. Default: 0.999. + Should be in range (0.0, 1.0). + eps (float): Term added to the denominator to improve numerical stability. Default: 1e-6. + Should be greater than 0. + weight_decay (float): Weight decay (L2 penalty). It must be equal to or greater than 0. Default: 0.0. + + Inputs: + - **gradients** (tuple[Tensor]) - The gradients of `params`, the shape is the same as `params`. + - **overflow** (tuple[Tensor]) - The overflow flag in dynamiclossscale. + + Outputs: + tuple[bool], all elements are True. + + Supported Platforms: + ``Ascend`` ``GPU`` + + Examples: + >>> net = Net() + >>> #1) All parameters use the same learning rate and weight decay + >>> optim = AdamWeightDecay(params=net.trainable_params()) + >>> + >>> #2) Use parameter groups and set different values + >>> conv_params = list(filter(lambda x: 'conv' in x.name, net.trainable_params())) + >>> no_conv_params = list(filter(lambda x: 'conv' not in x.name, net.trainable_params())) + >>> group_params = [{'params': conv_params, 'weight_decay': 0.01}, + ... {'params': no_conv_params, 'lr': 0.01}, + ... {'order_params': net.trainable_params()}] + >>> optim = AdamWeightDecay(group_params, learning_rate=0.1, weight_decay=0.0) + >>> # The conv_params's parameters will use default learning rate of 0.1 and weight decay of 0.01. + >>> # The no_conv_params's parameters will use learning rate of 0.01 and default weight decay of 0.0. + >>> # The final parameters order in which the optimizer will be followed is the value of 'order_params'. + >>> + >>> loss = nn.SoftmaxCrossEntropyWithLogits() + >>> model = Model(net, loss_fn=loss, optimizer=optim) + """ + def __init__(self, params, learning_rate=1e-3, beta1=0.9, beta2=0.999, eps=1e-6, weight_decay=0.0): + super(AdamWeightDecayForBert, self).__init__(learning_rate, params, weight_decay) + _check_param_value(beta1, beta2, eps, self.cls_name) + self.beta1 = Tensor(np.array([beta1]).astype(np.float32)) + self.beta2 = Tensor(np.array([beta2]).astype(np.float32)) + self.eps = Tensor(np.array([eps]).astype(np.float32)) + self.moments1 = self.parameters.clone(prefix="adam_m", init='zeros') + self.moments2 = self.parameters.clone(prefix="adam_v", init='zeros') + self.hyper_map = C.HyperMap() + self.op_select = P.Select() + self.op_cast = P.Cast() + self.op_reshape = P.Reshape() + self.op_shape = P.Shape() + + def construct(self, gradients, overflow): + """AdamWeightDecayForBert""" + lr = self.get_lr() + cond = self.op_cast(F.fill(mstype.int32, self.op_shape(self.beta1), 1) *\ + self.op_reshape(overflow, (())), mstype.bool_) + beta1 = self.op_select(cond, self.op_cast(F.tuple_to_array((1.0,)), mstype.float32), self.beta1) + beta2 = self.op_select(cond, self.op_cast(F.tuple_to_array((1.0,)), mstype.float32), self.beta2) + if self.is_group: + if self.is_group_lr: + optim_result = self.hyper_map(F.partial(_adam_opt, self.beta1, self.beta2, self.eps), + lr, self.weight_decay, self.parameters, self.moments1, self.moments2, + gradients, self.decay_flags, self.optim_filter) + else: + optim_result = self.hyper_map(F.partial(_adam_opt, beta1, beta2, self.eps, lr, overflow), + self.weight_decay, self.parameters, self.moments1, self.moments2, + gradients, self.decay_flags, self.optim_filter) + else: + optim_result = self.hyper_map(F.partial(_adam_opt, self.beta1, self.beta2, self.eps, lr, self.weight_decay), + self.parameters, self.moments1, self.moments2, + gradients, self.decay_flags, self.optim_filter) + if self.use_parallel: + self.broadcast_params(optim_result) + return optim_result + +class AdamWeightDecayOp(Optimizer): + """ + Implements the Adam algorithm to fix the weight decay. It is a complete operator, not a combination of other ops. + + Note: + When separating parameter groups, the weight decay in each group will be applied on the parameters if the + weight decay is positive. When not separating parameter groups, the `weight_decay` in the API will be applied + on the parameters without 'beta' or 'gamma' in their names if `weight_decay` is positive. + + To improve parameter groups performance, the customized order of parameters can be supported. + + Args: + params (Union[list[Parameter], list[dict]]): When the `params` is a list of `Parameter` which will be updated, + the element in `params` must be class `Parameter`. When the `params` is a list of `dict`, the "params", + "lr", "weight_decay" and "order_params" are the keys can be parsed. + + - params: Required. The value must be a list of `Parameter`. + + - lr: Optional. If "lr" is in the keys, the value of the corresponding learning rate will be used. + If not, the `learning_rate` in the API will be used. + + - weight_decay: Optional. If "weight_decay" is in the keys, the value of the corresponding weight decay + will be used. If not, the `weight_decay` in the API will be used. + + - order_params: Optional. If "order_params" is in the keys, the value must be the order of parameters and + the order will be followed in the optimizer. There are no other keys in the `dict` and the parameters + which in the 'order_params' must be in one of group parameters. + + learning_rate (Union[float, Tensor, Iterable, LearningRateSchedule]): A value or a graph for the learning rate. + When the learning_rate is an Iterable or a Tensor in a 1D dimension, use the dynamic learning rate, then + the i-th step will take the i-th value as the learning rate. When the learning_rate is LearningRateSchedule, + use dynamic learning rate, the i-th learning rate will be calculated during the process of training + according to the formula of LearningRateSchedule. When the learning_rate is a float or a Tensor in a zero + dimension, use fixed learning rate. Other cases are not supported. The float learning rate must be + equal to or greater than 0. If the type of `learning_rate` is int, it will be converted to float. + Default: 1e-3. + beta1 (float): The exponential decay rate for the 1st moment estimations. Default: 0.9. + Should be in range (0.0, 1.0). + beta2 (float): The exponential decay rate for the 2nd moment estimations. Default: 0.999. + Should be in range (0.0, 1.0). + eps (float): Term added to the denominator to improve numerical stability. Default: 1e-6. + Should be greater than 0. + weight_decay (float): Weight decay (L2 penalty). It must be equal to or greater than 0. Default: 0.0. + + Inputs: + - **gradients** (tuple[Tensor]) - The gradients of `params`, the shape is the same as `params`. + + Outputs: + tuple[bool], all elements are True. + + Supported Platforms: + ``GPU`` + + Examples: + >>> net = Net() + >>> #1) All parameters use the same learning rate and weight decay + >>> optim = AdamWeightDecayOp(params=net.trainable_params()) + >>> + >>> #2) Use parameter groups and set different values + >>> conv_params = list(filter(lambda x: 'conv' in x.name, net.trainable_params())) + >>> no_conv_params = list(filter(lambda x: 'conv' not in x.name, net.trainable_params())) + >>> group_params = [{'params': conv_params, 'weight_decay': 0.01}, + ... {'params': no_conv_params, 'lr': 0.01}, + ... {'order_params': net.trainable_params()}] + >>> optim = AdamWeightDecayOp(group_params, learning_rate=0.1, weight_decay=0.0) + >>> # The conv_params's parameters will use default learning rate of 0.1 and weight decay of 0.01. + >>> # The no_conv_params's parameters will use learning rate of 0.01 and default weight decay of 0.0. + >>> # The final parameters order in which the optimizer will be followed is the value of 'order_params'. + >>> + >>> loss = nn.SoftmaxCrossEntropyWithLogits() + >>> model = Model(net, loss_fn=loss, optimizer=optim) + """ + def __init__(self, params, learning_rate=1e-3, beta1=0.9, beta2=0.999, eps=1e-6, weight_decay=0.0): + super(AdamWeightDecayOp, self).__init__(learning_rate, params, weight_decay) + _check_param_value(beta1, beta2, eps, self.cls_name) + self.beta1 = Tensor(np.array([beta1]).astype(np.float32)) + self.beta2 = Tensor(np.array([beta2]).astype(np.float32)) + self.eps = Tensor(np.array([eps]).astype(np.float32)) + self.moments1 = self.parameters.clone(prefix="adam_m", init='zeros') + self.moments2 = self.parameters.clone(prefix="adam_v", init='zeros') + self.hyper_map = C.HyperMap() + + def construct(self, gradients): + """AdamWeightDecayOp""" + lr = self.get_lr() + if self.is_group: + if self.is_group_lr: + optim_result = self.hyper_map(F.partial(_adam_opt, self.beta1, self.beta2, self.eps), + lr, self.weight_decay, self.parameters, self.moments1, self.moments2, + gradients, self.decay_flags, self.optim_filter) + else: + optim_result = self.hyper_map(F.partial(_adam_opt, self.beta1, self.beta2, self.eps, lr), + self.weight_decay, self.parameters, self.moments1, self.moments2, + gradients, self.decay_flags, self.optim_filter) + else: + optim_result = self.hyper_map(F.partial(_adam_opt, self.beta1, self.beta2, self.eps, lr, self.weight_decay), + self.parameters, self.moments1, self.moments2, + gradients, self.decay_flags, self.optim_filter) + if self.use_parallel: + self.broadcast_params(optim_result) + return optim_result diff --git a/nlp/language_model/bert/MindSpore/src/assessment_method.py b/nlp/language_model/bert/MindSpore/src/assessment_method.py new file mode 100644 index 0000000000000000000000000000000000000000..bd840654a00d201d8baefba9626821b23dfc2ede --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/assessment_method.py @@ -0,0 +1,154 @@ +# Copyright 2020 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. +# ============================================================================ + +''' +Bert evaluation assessment method script. +''' +import math +import numpy as np +from mindspore.nn.metrics import ConfusionMatrixMetric +from .CRF import postprocess + +class Accuracy(): + ''' + calculate accuracy + ''' + def __init__(self): + self.acc_num = 0 + self.total_num = 0 + def update(self, logits, labels): + labels = labels.asnumpy() + labels = np.reshape(labels, -1) + logits = logits.asnumpy() + logit_id = np.argmax(logits, axis=-1) + self.acc_num += np.sum(labels == logit_id) + self.total_num += len(labels) + +class F1(): + ''' + calculate F1 score + ''' + def __init__(self, use_crf=False, num_labels=2, mode="Binary"): + self.TP = 0 + self.FP = 0 + self.FN = 0 + self.use_crf = use_crf + self.num_labels = num_labels + self.mode = mode + if self.mode.lower() not in ("binary", "multilabel"): + raise ValueError("Assessment mode not supported, support: [Binary, MultiLabel]") + if self.mode.lower() != "binary": + self.metric = ConfusionMatrixMetric(skip_channel=False, metric_name=("f1 score"), + calculation_method=False, decrease="mean") + + def update(self, logits, labels): + ''' + update F1 score + ''' + labels = labels.asnumpy() + labels = np.reshape(labels, -1) + if self.use_crf: + backpointers, best_tag_id = logits + best_path = postprocess(backpointers, best_tag_id) + logit_id = [] + for ele in best_path: + logit_id.extend(ele) + else: + logits = logits.asnumpy() + logit_id = np.argmax(logits, axis=-1) + logit_id = np.reshape(logit_id, -1) + + if self.mode.lower() == "binary": + pos_eva = np.isin(logit_id, [i for i in range(1, self.num_labels)]) + pos_label = np.isin(labels, [i for i in range(1, self.num_labels)]) + self.TP += np.sum(pos_eva&pos_label) + self.FP += np.sum(pos_eva&(~pos_label)) + self.FN += np.sum((~pos_eva)&pos_label) + else: + target = np.zeros((len(labels), self.num_labels), dtype=np.int) + pred = np.zeros((len(logit_id), self.num_labels), dtype=np.int) + for i, label in enumerate(labels): + target[i][label] = 1 + for i, label in enumerate(logit_id): + pred[i][label] = 1 + self.metric.update(pred, target) + + def eval(self): + return self.metric.eval() + + +class MCC(): + ''' + Calculate Matthews Correlation Coefficient + ''' + def __init__(self): + self.TP = 0 + self.FP = 0 + self.FN = 0 + self.TN = 0 + def update(self, logits, labels): + ''' + MCC update + ''' + labels = labels.asnumpy() + labels = np.reshape(labels, -1) + labels = labels.astype(np.bool) + logits = logits.asnumpy() + logit_id = np.argmax(logits, axis=-1) + logit_id = np.reshape(logit_id, -1) + logit_id = logit_id.astype(np.bool) + ornot = logit_id ^ labels + + self.TP += (~ornot & labels).sum() + self.FP += (ornot & ~labels).sum() + self.FN += (ornot & labels).sum() + self.TN += (~ornot & ~labels).sum() + + def cal(self): + mcc = (self.TP*self.TN - self.FP*self.FN)/math.sqrt((self.TP+self.FP)*(self.TP+self.FN) * + (self.TN+self.FP)*(self.TN+self.FN)) + return mcc + +class Spearman_Correlation(): + ''' + Calculate Spearman Correlation Coefficient + ''' + def __init__(self): + self.label = [] + self.logit = [] + + def update(self, logits, labels): + labels = labels.asnumpy() + labels = np.reshape(labels, -1) + logits = logits.asnumpy() + logits = np.reshape(logits, -1) + self.label.append(labels) + self.logit.append(logits) + + def cal(self): + ''' + Calculate Spearman Correlation + ''' + label = np.concatenate(self.label) + logit = np.concatenate(self.logit) + sort_label = label.argsort()[::-1] + sort_logit = logit.argsort()[::-1] + n = len(label) + d_acc = 0 + for i in range(n): + d = np.where(sort_label == i)[0] - np.where(sort_logit == i)[0] + d_acc += d**2 + ps = 1 - 6*d_acc/n/(n**2-1) + return ps diff --git a/nlp/language_model/bert/MindSpore/src/bert_for_finetune.py b/nlp/language_model/bert/MindSpore/src/bert_for_finetune.py new file mode 100644 index 0000000000000000000000000000000000000000..2c1734c66b1752c197070f6b65901bd030a20b5c --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/bert_for_finetune.py @@ -0,0 +1,363 @@ +# Copyright 2020 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. +# ============================================================================ + +''' +Bert for finetune script. +''' + +import mindspore.nn as nn +from mindspore.ops import operations as P +from mindspore.ops import functional as F +from mindspore.ops import composite as C +from mindspore.common.tensor import Tensor +from mindspore.common.parameter import Parameter +from mindspore.common import dtype as mstype +from mindspore.nn.wrap.grad_reducer import DistributedGradReducer +from mindspore.context import ParallelMode +from mindspore.communication.management import get_group_size +from mindspore import context +from .bert_for_pre_training import clip_grad +from .finetune_eval_model import BertCLSModel, BertNERModel, BertSquadModel +from .utils import CrossEntropyCalculation + +GRADIENT_CLIP_TYPE = 1 +GRADIENT_CLIP_VALUE = 1.0 +grad_scale = C.MultitypeFuncGraph("grad_scale") +reciprocal = P.Reciprocal() + + +@grad_scale.register("Tensor", "Tensor") +def tensor_grad_scale(scale, grad): + return grad * reciprocal(scale) + + +_grad_overflow = C.MultitypeFuncGraph("_grad_overflow") +grad_overflow = P.FloatStatus() + + +@_grad_overflow.register("Tensor") +def _tensor_grad_overflow(grad): + return grad_overflow(grad) + + +class BertFinetuneCell(nn.Cell): + """ + Especially defined for finetuning where only four inputs tensor are needed. + + Append an optimizer to the training network after that the construct + function can be called to create the backward graph. + + Different from the builtin loss_scale wrapper cell, we apply grad_clip before the optimization. + + Args: + network (Cell): The training network. Note that loss function should have been added. + optimizer (Optimizer): Optimizer for updating the weights. + scale_update_cell (Cell): Cell to do the loss scale. Default: None. + """ + + def __init__(self, network, optimizer, scale_update_cell=None): + + super(BertFinetuneCell, self).__init__(auto_prefix=False) + self.network = network + self.network.set_grad() + self.weights = optimizer.parameters + self.optimizer = optimizer + self.grad = C.GradOperation(get_by_list=True, + sens_param=True) + self.reducer_flag = False + self.allreduce = P.AllReduce() + self.parallel_mode = context.get_auto_parallel_context("parallel_mode") + if self.parallel_mode in [ParallelMode.DATA_PARALLEL, ParallelMode.HYBRID_PARALLEL]: + self.reducer_flag = True + self.grad_reducer = None + if self.reducer_flag: + mean = context.get_auto_parallel_context("gradients_mean") + degree = get_group_size() + self.grad_reducer = DistributedGradReducer(optimizer.parameters, mean, degree) + self.is_distributed = (self.parallel_mode != ParallelMode.STAND_ALONE) + self.cast = P.Cast() + self.gpu_target = False + if context.get_context("device_target") == "GPU": + self.gpu_target = True + self.float_status = P.FloatStatus() + self.addn = P.AddN() + self.reshape = P.Reshape() + else: + self.alloc_status = P.NPUAllocFloatStatus() + self.get_status = P.NPUGetFloatStatus() + self.clear_status = P.NPUClearFloatStatus() + self.reduce_sum = P.ReduceSum(keep_dims=False) + self.base = Tensor(1, mstype.float32) + self.less_equal = P.LessEqual() + self.hyper_map = C.HyperMap() + self.loss_scale = None + self.loss_scaling_manager = scale_update_cell + if scale_update_cell: + self.loss_scale = Parameter(Tensor(scale_update_cell.get_loss_scale(), dtype=mstype.float32)) + + def construct(self, + input_ids, + input_mask, + token_type_id, + label_ids, + sens=None): + """Bert Finetune""" + + weights = self.weights + init = False + loss = self.network(input_ids, + input_mask, + token_type_id, + label_ids) + if sens is None: + scaling_sens = self.loss_scale + else: + scaling_sens = sens + + if not self.gpu_target: + init = self.alloc_status() + init = F.depend(init, loss) + clear_status = self.clear_status(init) + scaling_sens = F.depend(scaling_sens, clear_status) + grads = self.grad(self.network, weights)(input_ids, + input_mask, + token_type_id, + label_ids, + self.cast(scaling_sens, + mstype.float32)) + grads = self.hyper_map(F.partial(grad_scale, scaling_sens), grads) + grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads) + if self.reducer_flag: + grads = self.grad_reducer(grads) + if not self.gpu_target: + init = F.depend(init, grads) + get_status = self.get_status(init) + init = F.depend(init, get_status) + flag_sum = self.reduce_sum(init, (0,)) + else: + flag_sum = self.hyper_map(F.partial(_grad_overflow), grads) + flag_sum = self.addn(flag_sum) + flag_sum = self.reshape(flag_sum, (())) + if self.is_distributed: + flag_reduce = self.allreduce(flag_sum) + cond = self.less_equal(self.base, flag_reduce) + else: + cond = self.less_equal(self.base, flag_sum) + overflow = cond + if sens is None: + overflow = self.loss_scaling_manager(self.loss_scale, cond) + if not overflow: + self.optimizer(grads) + return (loss, cond) + + +class BertSquadCell(nn.Cell): + """ + specifically defined for finetuning where only four inputs tensor are needed. + """ + + def __init__(self, network, optimizer, scale_update_cell=None): + super(BertSquadCell, self).__init__(auto_prefix=False) + self.network = network + self.network.set_grad() + self.weights = optimizer.parameters + self.optimizer = optimizer + self.grad = C.GradOperation(get_by_list=True, sens_param=True) + self.reducer_flag = False + self.allreduce = P.AllReduce() + self.parallel_mode = context.get_auto_parallel_context("parallel_mode") + if self.parallel_mode in [ParallelMode.DATA_PARALLEL, ParallelMode.HYBRID_PARALLEL]: + self.reducer_flag = True + self.grad_reducer = None + if self.reducer_flag: + mean = context.get_auto_parallel_context("gradients_mean") + degree = get_group_size() + self.grad_reducer = DistributedGradReducer(optimizer.parameters, mean, degree) + self.is_distributed = (self.parallel_mode != ParallelMode.STAND_ALONE) + self.cast = P.Cast() + self.gpu_target = False + if context.get_context("device_target") == "GPU": + self.gpu_target = True + self.float_status = P.FloatStatus() + self.addn = P.AddN() + self.reshape = P.Reshape() + else: + self.alloc_status = P.NPUAllocFloatStatus() + self.get_status = P.NPUGetFloatStatus() + self.clear_status = P.NPUClearFloatStatus() + self.reduce_sum = P.ReduceSum(keep_dims=False) + self.base = Tensor(1, mstype.float32) + self.less_equal = P.LessEqual() + self.hyper_map = C.HyperMap() + self.loss_scale = None + self.loss_scaling_manager = scale_update_cell + if scale_update_cell: + self.loss_scale = Parameter(Tensor(scale_update_cell.get_loss_scale(), dtype=mstype.float32)) + + def construct(self, + input_ids, + input_mask, + token_type_id, + start_position, + end_position, + unique_id, + is_impossible, + sens=None): + """BertSquad""" + weights = self.weights + init = False + loss = self.network(input_ids, + input_mask, + token_type_id, + start_position, + end_position, + unique_id, + is_impossible) + if sens is None: + scaling_sens = self.loss_scale + else: + scaling_sens = sens + if not self.gpu_target: + init = self.alloc_status() + init = F.depend(init, loss) + clear_status = self.clear_status(init) + scaling_sens = F.depend(scaling_sens, clear_status) + grads = self.grad(self.network, weights)(input_ids, + input_mask, + token_type_id, + start_position, + end_position, + unique_id, + is_impossible, + self.cast(scaling_sens, + mstype.float32)) + grads = self.hyper_map(F.partial(grad_scale, scaling_sens), grads) + grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads) + if self.reducer_flag: + grads = self.grad_reducer(grads) + if not self.gpu_target: + init = F.depend(init, grads) + get_status = self.get_status(init) + init = F.depend(init, get_status) + flag_sum = self.reduce_sum(init, (0,)) + else: + flag_sum = self.hyper_map(F.partial(_grad_overflow), grads) + flag_sum = self.addn(flag_sum) + flag_sum = self.reshape(flag_sum, (())) + if self.is_distributed: + flag_reduce = self.allreduce(flag_sum) + cond = self.less_equal(self.base, flag_reduce) + else: + cond = self.less_equal(self.base, flag_sum) + overflow = cond + if sens is None: + overflow = self.loss_scaling_manager(self.loss_scale, cond) + if not overflow: + self.optimizer(grads) + return (loss, cond) + + +class BertCLS(nn.Cell): + """ + Train interface for classification finetuning task. + """ + + def __init__(self, config, is_training, num_labels=2, dropout_prob=0.0, use_one_hot_embeddings=False, + assessment_method=""): + super(BertCLS, self).__init__() + self.bert = BertCLSModel(config, is_training, num_labels, dropout_prob, use_one_hot_embeddings, + assessment_method) + self.loss = CrossEntropyCalculation(is_training) + self.num_labels = num_labels + self.assessment_method = assessment_method + self.is_training = is_training + + def construct(self, input_ids, input_mask, token_type_id, label_ids): + logits = self.bert(input_ids, input_mask, token_type_id) + if self.assessment_method == "spearman_correlation": + if self.is_training: + loss = self.loss(logits, label_ids) + else: + loss = logits + else: + loss = self.loss(logits, label_ids, self.num_labels) + return loss + + +class BertNER(nn.Cell): + """ + Train interface for sequence labeling finetuning task. + """ + + def __init__(self, config, batch_size, is_training, num_labels=11, use_crf=False, with_lstm=False, + tag_to_index=None, dropout_prob=0.0, use_one_hot_embeddings=False): + super(BertNER, self).__init__() + self.bert = BertNERModel(config, is_training, num_labels, use_crf, with_lstm, dropout_prob, + use_one_hot_embeddings) + if use_crf: + if not tag_to_index: + raise Exception("The dict for tag-index mapping should be provided for CRF.") + from src.CRF import CRF + self.loss = CRF(tag_to_index, batch_size, config.seq_length, is_training) + else: + self.loss = CrossEntropyCalculation(is_training) + self.num_labels = num_labels + self.use_crf = use_crf + + def construct(self, input_ids, input_mask, token_type_id, label_ids): + logits = self.bert(input_ids, input_mask, token_type_id) + if self.use_crf: + loss = self.loss(logits, label_ids) + else: + loss = self.loss(logits, label_ids, self.num_labels) + return loss + + +class BertSquad(nn.Cell): + ''' + Train interface for SQuAD finetuning task. + ''' + + def __init__(self, config, is_training, num_labels=2, dropout_prob=0.0, use_one_hot_embeddings=False): + super(BertSquad, self).__init__() + self.bert = BertSquadModel(config, is_training, num_labels, dropout_prob, use_one_hot_embeddings) + self.loss = CrossEntropyCalculation(is_training) + self.num_labels = num_labels + self.seq_length = config.seq_length + self.is_training = is_training + self.total_num = Parameter(Tensor([0], mstype.float32)) + self.start_num = Parameter(Tensor([0], mstype.float32)) + self.end_num = Parameter(Tensor([0], mstype.float32)) + self.sum = P.ReduceSum() + self.equal = P.Equal() + self.argmax = P.ArgMaxWithValue(axis=1) + self.squeeze = P.Squeeze(axis=-1) + + def construct(self, input_ids, input_mask, token_type_id, start_position, end_position, unique_id, is_impossible): + """interface for SQuAD finetuning task""" + logits = self.bert(input_ids, input_mask, token_type_id) + if self.is_training: + unstacked_logits_0 = self.squeeze(logits[:, :, 0:1]) + unstacked_logits_1 = self.squeeze(logits[:, :, 1:2]) + start_loss = self.loss(unstacked_logits_0, start_position, self.seq_length) + end_loss = self.loss(unstacked_logits_1, end_position, self.seq_length) + total_loss = (start_loss + end_loss) / 2.0 + else: + start_logits = self.squeeze(logits[:, :, 0:1]) + start_logits = start_logits + 100 * input_mask + end_logits = self.squeeze(logits[:, :, 1:2]) + end_logits = end_logits + 100 * input_mask + total_loss = (unique_id, start_logits, end_logits) + return total_loss diff --git a/nlp/language_model/bert/MindSpore/src/bert_for_pre_training.py b/nlp/language_model/bert/MindSpore/src/bert_for_pre_training.py new file mode 100644 index 0000000000000000000000000000000000000000..1a8245b25675d1926eb02742d257d9cb81feb39a --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/bert_for_pre_training.py @@ -0,0 +1,926 @@ +# Copyright 2020-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. +# ============================================================================ +"""Bert for pretraining.""" +import numpy as np + +import mindspore.nn as nn +from mindspore.common.initializer import initializer, TruncatedNormal +from mindspore.ops import operations as P +from mindspore.ops import functional as F +from mindspore.ops import composite as C +from mindspore.common.tensor import Tensor +from mindspore.common.parameter import Parameter +from mindspore.common.api import ms_function +from mindspore.common import dtype as mstype +from mindspore.nn.wrap.grad_reducer import DistributedGradReducer +from mindspore.context import ParallelMode +from mindspore.communication.management import get_group_size +from mindspore import context +from .bert_model import BertModel + +GRADIENT_CLIP_TYPE = 1 +GRADIENT_CLIP_VALUE = 1.0 + +clip_grad = C.MultitypeFuncGraph("clip_grad") + + +@clip_grad.register("Number", "Number", "Tensor") +def _clip_grad(clip_type, clip_value, grad): + """ + Clip gradients. + + Inputs: + clip_type (int): The way to clip, 0 for 'value', 1 for 'norm'. + clip_value (float): Specifies how much to clip. + grad (tuple[Tensor]): Gradients. + + Outputs: + tuple[Tensor], clipped gradients. + """ + if clip_type not in (0, 1): + return grad + dt = F.dtype(grad) + if clip_type == 0: + new_grad = C.clip_by_value(grad, F.cast(F.tuple_to_array((-clip_value,)), dt), + F.cast(F.tuple_to_array((clip_value,)), dt)) + else: + new_grad = nn.ClipByNorm()(grad, F.cast(F.tuple_to_array((clip_value,)), dt)) + return new_grad + + +class GetMaskedLMOutput(nn.Cell): + """ + Get masked lm output. + + Args: + config (BertConfig): The config of BertModel. + + Returns: + Tensor, masked lm output. + """ + + def __init__(self, config): + super(GetMaskedLMOutput, self).__init__() + self.width = config.hidden_size + self.reshape = P.Reshape() + self.gather = P.Gather() + + weight_init = TruncatedNormal(config.initializer_range) + self.dense = nn.Dense(self.width, + config.hidden_size, + weight_init=weight_init, + activation=config.hidden_act).to_float(config.compute_type) + self.layernorm = nn.LayerNorm((config.hidden_size,)).to_float(config.compute_type) + self.output_bias = Parameter( + initializer( + 'zero', + config.vocab_size)) + self.matmul = P.MatMul(transpose_b=True) + self.log_softmax = nn.LogSoftmax(axis=-1) + self.shape_flat_offsets = (-1, 1) + self.last_idx = (-1,) + self.shape_flat_sequence_tensor = (-1, self.width) + self.cast = P.Cast() + self.compute_type = config.compute_type + self.dtype = config.dtype + + def construct(self, + input_tensor, + output_weights, + positions): + """Get output log_probs""" + input_shape = P.Shape()(input_tensor) + rng = F.tuple_to_array(F.make_range(input_shape[0])) + flat_offsets = self.reshape(rng * input_shape[1], self.shape_flat_offsets) + flat_position = self.reshape(positions + flat_offsets, self.last_idx) + flat_sequence_tensor = self.reshape(input_tensor, self.shape_flat_sequence_tensor) + input_tensor = self.gather(flat_sequence_tensor, flat_position, 0) + input_tensor = self.cast(input_tensor, self.compute_type) + output_weights = self.cast(output_weights, self.compute_type) + input_tensor = self.dense(input_tensor) + input_tensor = self.layernorm(input_tensor) + logits = self.matmul(input_tensor, output_weights) + logits = self.cast(logits, self.dtype) + logits = logits + self.output_bias + log_probs = self.log_softmax(logits) + return log_probs + + +class GetNextSentenceOutput(nn.Cell): + """ + Get next sentence output. + + Args: + config (BertConfig): The config of Bert. + + Returns: + Tensor, next sentence output. + """ + + def __init__(self, config): + super(GetNextSentenceOutput, self).__init__() + self.log_softmax = P.LogSoftmax() + weight_init = TruncatedNormal(config.initializer_range) + self.dense = nn.Dense(config.hidden_size, 2, + weight_init=weight_init, has_bias=True).to_float(config.compute_type) + self.dtype = config.dtype + self.cast = P.Cast() + + def construct(self, input_tensor): + logits = self.dense(input_tensor) + logits = self.cast(logits, self.dtype) + log_prob = self.log_softmax(logits) + return log_prob + + +class BertPreTraining(nn.Cell): + """ + Bert pretraining network. + + Args: + config (BertConfig): The config of BertModel. + is_training (bool): Specifies whether to use the training mode. + use_one_hot_embeddings (bool): Specifies whether to use one-hot for embeddings. + + Returns: + Tensor, prediction_scores, seq_relationship_score. + """ + + def __init__(self, config, is_training, use_one_hot_embeddings): + super(BertPreTraining, self).__init__() + self.bert = BertModel(config, is_training, use_one_hot_embeddings) + self.cls1 = GetMaskedLMOutput(config) + self.cls2 = GetNextSentenceOutput(config) + + def construct(self, input_ids, input_mask, token_type_id, + masked_lm_positions): + sequence_output, pooled_output, embedding_table = \ + self.bert(input_ids, token_type_id, input_mask) + prediction_scores = self.cls1(sequence_output, + embedding_table, + masked_lm_positions) + seq_relationship_score = self.cls2(pooled_output) + return prediction_scores, seq_relationship_score + + +class BertPretrainingLoss(nn.Cell): + """ + Provide bert pre-training loss. + + Args: + config (BertConfig): The config of BertModel. + + Returns: + Tensor, total loss. + """ + + def __init__(self, config): + super(BertPretrainingLoss, self).__init__() + self.vocab_size = config.vocab_size + self.onehot = P.OneHot() + self.on_value = Tensor(1.0, mstype.float32) + self.off_value = Tensor(0.0, mstype.float32) + self.reduce_sum = P.ReduceSum() + self.reduce_mean = P.ReduceMean() + self.reshape = P.Reshape() + self.last_idx = (-1,) + self.neg = P.Neg() + self.cast = P.Cast() + + def construct(self, prediction_scores, seq_relationship_score, masked_lm_ids, + masked_lm_weights, next_sentence_labels): + """Defines the computation performed.""" + label_ids = self.reshape(masked_lm_ids, self.last_idx) + label_weights = self.cast(self.reshape(masked_lm_weights, self.last_idx), mstype.float32) + one_hot_labels = self.onehot(label_ids, self.vocab_size, self.on_value, self.off_value) + + per_example_loss = self.neg(self.reduce_sum(prediction_scores * one_hot_labels, self.last_idx)) + numerator = self.reduce_sum(label_weights * per_example_loss, ()) + denominator = self.reduce_sum(label_weights, ()) + self.cast(F.tuple_to_array((1e-5,)), mstype.float32) + masked_lm_loss = numerator / denominator + + # next_sentence_loss + labels = self.reshape(next_sentence_labels, self.last_idx) + one_hot_labels = self.onehot(labels, 2, self.on_value, self.off_value) + per_example_loss = self.neg(self.reduce_sum( + one_hot_labels * seq_relationship_score, self.last_idx)) + next_sentence_loss = self.reduce_mean(per_example_loss, self.last_idx) + + # total_loss + total_loss = masked_lm_loss + next_sentence_loss + + return total_loss + + +class BertNetworkWithLoss(nn.Cell): + """ + Provide bert pre-training loss through network. + + Args: + config (BertConfig): The config of BertModel. + is_training (bool): Specifies whether to use the training mode. + use_one_hot_embeddings (bool): Specifies whether to use one-hot for embeddings. Default: False. + + Returns: + Tensor, the loss of the network. + """ + + def __init__(self, config, is_training, use_one_hot_embeddings=False): + super(BertNetworkWithLoss, self).__init__() + self.bert = BertPreTraining(config, is_training, use_one_hot_embeddings) + self.loss = BertPretrainingLoss(config) + self.cast = P.Cast() + + def construct(self, + input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights): + """Get pre-training loss""" + prediction_scores, seq_relationship_score = \ + self.bert(input_ids, input_mask, token_type_id, masked_lm_positions) + total_loss = self.loss(prediction_scores, seq_relationship_score, + masked_lm_ids, masked_lm_weights, next_sentence_labels) + return self.cast(total_loss, mstype.float32) + + +class BertTrainOneStepCell(nn.TrainOneStepCell): + """ + Encapsulation class of bert network training. + + Append an optimizer to the training network after that the construct + function can be called to create the backward graph. + + Args: + network (Cell): The training network. Note that loss function should have been added. + optimizer (Optimizer): Optimizer for updating the weights. + sens (Number): The adjust parameter. Default: 1.0. + enable_clip_grad (boolean): If True, clip gradients in BertTrainOneStepCell. Default: True. + """ + + def __init__(self, network, optimizer, sens=1.0, enable_clip_grad=True): + super(BertTrainOneStepCell, self).__init__(network, optimizer, sens) + self.cast = P.Cast() + self.hyper_map = C.HyperMap() + self.enable_clip_grad = enable_clip_grad + self.enable_tuple_broaden = True + + def set_sens(self, value): + self.sens = value + + @ms_function + def clip_grads(self, grads): + grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads) + return grads + + def construct(self, + input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights): + """Defines the computation performed.""" + weights = self.weights + + loss = self.network(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights) + grads = self.grad(self.network, weights)(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + self.cast(F.tuple_to_array((self.sens,)), + mstype.float32)) + if self.enable_clip_grad: + grads = self.clip_grads(grads) + grads = self.grad_reducer(grads) + self.optimizer(grads) + return loss + + +grad_scale = C.MultitypeFuncGraph("grad_scale") +reciprocal = P.Reciprocal() + + +@grad_scale.register("Tensor", "Tensor") +def tensor_grad_scale(scale, grad): + return grad * reciprocal(scale) + + +_grad_overflow = C.MultitypeFuncGraph("_grad_overflow") +grad_overflow = P.FloatStatus() + + +@_grad_overflow.register("Tensor") +def _tensor_grad_overflow(grad): + return grad_overflow(grad) + + +class BertTrainOneStepWithLossScaleCell(nn.TrainOneStepWithLossScaleCell): + """ + Encapsulation class of bert network training. + + Append an optimizer to the training network after that the construct + function can be called to create the backward graph. + + Args: + network (Cell): The training network. Note that loss function should have been added. + optimizer (Optimizer): Optimizer for updating the weights. + scale_update_cell (Cell): Cell to do the loss scale. Default: None. + """ + + def __init__(self, network, optimizer, scale_update_cell=None): + super(BertTrainOneStepWithLossScaleCell, self).__init__(network, optimizer, scale_update_cell) + self.cast = P.Cast() + self.degree = 1 + if self.reducer_flag: + self.degree = get_group_size() + self.grad_reducer = DistributedGradReducer(optimizer.parameters, False, self.degree) + + self.loss_scale = None + self.loss_scaling_manager = scale_update_cell + if scale_update_cell: + self.loss_scale = Parameter(Tensor(scale_update_cell.get_loss_scale(), dtype=mstype.float32)) + self.enable_tuple_broaden = True + + @ms_function + def clip_grads(self, grads): + grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads) + return grads + + def construct(self, + input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + sens=None): + """Defines the computation performed.""" + weights = self.weights + loss = self.network(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights) + if sens is None: + scaling_sens = self.loss_scale + else: + scaling_sens = sens + status, scaling_sens = self.start_overflow_check(loss, scaling_sens) + grads = self.grad(self.network, weights)(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + self.cast(scaling_sens, + mstype.float32)) + # apply grad reducer on grads + grads = self.grad_reducer(grads) + degree_sens = self.cast(scaling_sens * self.degree, mstype.float32) + grads = self.hyper_map(F.partial(grad_scale, degree_sens), grads) + grads = self.clip_grads(grads) + + cond = self.get_overflow_status(status, grads) + overflow = cond + if sens is None: + overflow = self.loss_scaling_manager(self.loss_scale, cond) + if not overflow: + self.optimizer(grads) + return (loss, cond, scaling_sens) + + +class BertTrainOneStepWithLossScaleCellForAdam(nn.TrainOneStepWithLossScaleCell): + """ + Encapsulation class of bert network training. + + Append an optimizer to the training network after that the construct + function can be called to create the backward graph. + Different from BertTrainOneStepWithLossScaleCell, the optimizer takes the overflow + condition as input. + + Args: + network (Cell): The training network. Note that loss function should have been added. + optimizer (Optimizer): Optimizer for updating the weights. + scale_update_cell (Cell): Cell to do the loss scale. Default: None. + """ + def __init__(self, network, optimizer, scale_update_cell=None): + super(BertTrainOneStepWithLossScaleCellForAdam, self).__init__(network, optimizer, scale_update_cell) + self.cast = P.Cast() + self.degree = 1 + if self.reducer_flag: + self.degree = get_group_size() + self.grad_reducer = DistributedGradReducer(optimizer.parameters, False, self.degree) + self.loss_scale = None + self.loss_scaling_manager = scale_update_cell + if scale_update_cell: + self.loss_scale = Parameter(Tensor(scale_update_cell.get_loss_scale(), dtype=mstype.float32)) + self.enable_tuple_broaden = True + + @ms_function + def clip_grads(self, grads): + grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads) + return grads + + def construct(self, + input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + sens=None): + """Defines the computation performed.""" + weights = self.weights + loss = self.network(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights) + if sens is None: + scaling_sens = self.loss_scale + else: + scaling_sens = sens + + status, scaling_sens = self.start_overflow_check(loss, scaling_sens) + grads = self.grad(self.network, weights)(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + self.cast(scaling_sens, + mstype.float32)) + # apply grad reducer on grads + grads = self.grad_reducer(grads) + grads = self.hyper_map(F.partial(grad_scale, scaling_sens * self.degree), grads) + grads = self.clip_grads(grads) + cond = self.get_overflow_status(status, grads) + overflow = cond + if self.loss_scaling_manager is not None: + overflow = self.loss_scaling_manager(scaling_sens, cond) + self.optimizer(grads, overflow) + return (loss, cond, scaling_sens) + +cast = P.Cast() +add_grads = C.MultitypeFuncGraph("add_grads") + + +@add_grads.register("Tensor", "Tensor") +def _add_grads(accu_grad, grad): + return accu_grad + cast(grad, mstype.float32) + +update_accu_grads = C.MultitypeFuncGraph("update_accu_grads") + +@update_accu_grads.register("Tensor", "Tensor") +def _update_accu_grads(accu_grad, grad): + succ = True + return F.depend(succ, F.assign(accu_grad, cast(grad, mstype.float32))) + +accumulate_accu_grads = C.MultitypeFuncGraph("accumulate_accu_grads") + +@accumulate_accu_grads.register("Tensor", "Tensor") +def _accumulate_accu_grads(accu_grad, grad): + succ = True + return F.depend(succ, F.assign_add(accu_grad, cast(grad, mstype.float32))) + + +zeroslike = P.ZerosLike() +reset_accu_grads = C.MultitypeFuncGraph("reset_accu_grads") + + +@reset_accu_grads.register("Tensor") +def _reset_accu_grads(accu_grad): + succ = True + return F.depend(succ, F.assign(accu_grad, zeroslike(accu_grad))) + + +class BertTrainAccumulationAllReducePostWithLossScaleCell(nn.Cell): + """ + Encapsulation class of bert network training. + + Append an optimizer to the training network after that the construct + function can be called to create the backward graph. + + To mimic higher batch size, gradients are accumulated N times before weight update. + + For distribution mode, allreduce will only be implemented in the weight updated step, + i.e. the sub-step after gradients accumulated N times. + + Args: + network (Cell): The training network. Note that loss function should have been added. + optimizer (Optimizer): Optimizer for updating the weights. + scale_update_cell (Cell): Cell to do the loss scale. Default: None. + accumulation_steps (int): Number of accumulation steps before gradient update. The global batch size = + batch_size * accumulation_steps. Default: 1. + """ + + def __init__(self, network, optimizer, scale_update_cell=None, accumulation_steps=1, enable_global_norm=False): + super(BertTrainAccumulationAllReducePostWithLossScaleCell, self).__init__(auto_prefix=False) + self.network = network + self.network.set_grad() + self.weights = optimizer.parameters + self.optimizer = optimizer + self.accumulation_steps = accumulation_steps + self.enable_global_norm = enable_global_norm + self.one = Tensor(np.array([1]).astype(np.int32)) + self.zero = Tensor(np.array([0]).astype(np.int32)) + self.local_step = Parameter(initializer(0, [1], mstype.int32)) + self.accu_grads = self.weights.clone(prefix="accu_grads", init='zeros') + self.accu_overflow = Parameter(initializer(0, [1], mstype.int32)) + self.accu_loss = Parameter(initializer(0, [1], mstype.float32)) + + self.grad = C.GradOperation(get_by_list=True, sens_param=True) + self.reducer_flag = False + self.parallel_mode = context.get_auto_parallel_context("parallel_mode") + if self.parallel_mode in [ParallelMode.DATA_PARALLEL, ParallelMode.HYBRID_PARALLEL]: + self.reducer_flag = True + self.grad_reducer = F.identity + self.degree = 1 + if self.reducer_flag: + self.degree = get_group_size() + self.grad_reducer = DistributedGradReducer(optimizer.parameters, False, self.degree) + self.is_distributed = (self.parallel_mode != ParallelMode.STAND_ALONE) + self.overflow_reducer = F.identity + if self.is_distributed: + self.overflow_reducer = P.AllReduce() + self.cast = P.Cast() + self.alloc_status = P.NPUAllocFloatStatus() + self.get_status = P.NPUGetFloatStatus() + self.clear_status = P.NPUClearFloatStatus() + self.reduce_sum = P.ReduceSum(keep_dims=False) + self.base = Tensor(1, mstype.float32) + self.less_equal = P.LessEqual() + self.logical_or = P.LogicalOr() + self.not_equal = P.NotEqual() + self.select = P.Select() + self.reshape = P.Reshape() + self.hyper_map = C.HyperMap() + self.loss_scale = None + self.loss_scaling_manager = scale_update_cell + if scale_update_cell: + self.loss_scale = Parameter(Tensor(scale_update_cell.get_loss_scale(), dtype=mstype.float32)) + + def construct(self, + input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + sens=None): + """Defines the computation performed.""" + weights = self.weights + loss = self.network(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights) + if sens is None: + scaling_sens = self.loss_scale + else: + scaling_sens = sens + # alloc status and clear should be right before gradoperation + init = self.alloc_status() + init = F.depend(init, loss) + clear_status = self.clear_status(init) + scaling_sens = F.depend(scaling_sens, clear_status) + # update accumulation parameters + is_accu_step = self.not_equal(self.local_step, self.accumulation_steps) + self.local_step = self.select(is_accu_step, self.local_step + self.one, self.one) + self.accu_loss = self.select(is_accu_step, self.accu_loss + loss, loss) + mean_loss = self.accu_loss / self.local_step + is_accu_step = self.not_equal(self.local_step, self.accumulation_steps) + + grads = self.grad(self.network, weights)(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + self.cast(scaling_sens, + mstype.float32)) + + accu_succ = self.hyper_map(accumulate_accu_grads, self.accu_grads, grads) + mean_loss = F.depend(mean_loss, accu_succ) + + init = F.depend(init, mean_loss) + get_status = self.get_status(init) + init = F.depend(init, get_status) + flag_sum = self.reduce_sum(init, (0,)) + overflow = self.less_equal(self.base, flag_sum) + overflow = self.logical_or(self.not_equal(self.accu_overflow, self.zero), overflow) + accu_overflow = self.select(overflow, self.one, self.zero) + self.accu_overflow = self.select(is_accu_step, accu_overflow, self.zero) + + if not is_accu_step: + # apply grad reducer on grads + grads = self.grad_reducer(self.accu_grads) + scaling = scaling_sens * self.degree * self.accumulation_steps + grads = self.hyper_map(F.partial(grad_scale, scaling), grads) + if self.enable_global_norm: + grads = C.clip_by_global_norm(grads, 1.0, None) + else: + grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads) + accu_overflow = F.depend(accu_overflow, grads) + accu_overflow = self.overflow_reducer(accu_overflow) + overflow = self.less_equal(self.base, accu_overflow) + accu_succ = self.hyper_map(reset_accu_grads, self.accu_grads) + overflow = F.depend(overflow, accu_succ) + overflow = self.reshape(overflow, (())) + if sens is None: + overflow = self.loss_scaling_manager(self.loss_scale, overflow) + if not overflow: + self.optimizer(grads) + + return (mean_loss, overflow, scaling_sens) + + +class BertTrainAccumulationAllReduceEachWithLossScaleCell(nn.Cell): + """ + Encapsulation class of bert network training. + + Append an optimizer to the training network after that the construct + function can be called to create the backward graph. + + To mimic higher batch size, gradients are accumulated N times before weight update. + + For distribution mode, allreduce will be implemented after each sub-step and the trailing time + will be overided by backend optimization pass. + + Args: + network (Cell): The training network. Note that loss function should have been added. + optimizer (Optimizer): Optimizer for updating the weights. + scale_update_cell (Cell): Cell to do the loss scale. Default: None. + accumulation_steps (int): Number of accumulation steps before gradient update. The global batch size = + batch_size * accumulation_steps. Default: 1. + """ + def __init__(self, network, optimizer, scale_update_cell=None, accumulation_steps=1, enable_global_norm=False): + super(BertTrainAccumulationAllReduceEachWithLossScaleCell, self).__init__(auto_prefix=False) + self.network = network + self.network.set_grad() + self.weights = optimizer.parameters + self.optimizer = optimizer + self.accumulation_steps = accumulation_steps + self.enable_global_norm = enable_global_norm + self.one = Tensor(np.array([1]).astype(np.int32)) + self.zero = Tensor(np.array([0]).astype(np.int32)) + self.local_step = Parameter(initializer(0, [1], mstype.int32)) + self.accu_grads = self.weights.clone(prefix="accu_grads", init='zeros') + self.accu_overflow = Parameter(initializer(0, [1], mstype.int32)) + self.accu_loss = Parameter(initializer(0, [1], mstype.float32)) + + self.grad = C.GradOperation(get_by_list=True, sens_param=True) + self.reducer_flag = False + self.parallel_mode = context.get_auto_parallel_context("parallel_mode") + if self.parallel_mode in [ParallelMode.DATA_PARALLEL, ParallelMode.HYBRID_PARALLEL]: + self.reducer_flag = True + self.grad_reducer = F.identity + self.degree = 1 + if self.reducer_flag: + self.degree = get_group_size() + self.grad_reducer = DistributedGradReducer(optimizer.parameters, False, self.degree) + self.is_distributed = (self.parallel_mode != ParallelMode.STAND_ALONE) + self.overflow_reducer = F.identity + if self.is_distributed: + self.overflow_reducer = P.AllReduce() + self.cast = P.Cast() + self.alloc_status = P.NPUAllocFloatStatus() + self.get_status = P.NPUGetFloatStatus() + self.clear_before_grad = P.NPUClearFloatStatus() + self.reduce_sum = P.ReduceSum(keep_dims=False) + self.base = Tensor(1, mstype.float32) + self.less_equal = P.LessEqual() + self.logical_or = P.LogicalOr() + self.not_equal = P.NotEqual() + self.select = P.Select() + self.reshape = P.Reshape() + self.hyper_map = C.HyperMap() + self.loss_scale = None + self.loss_scaling_manager = scale_update_cell + if scale_update_cell: + self.loss_scale = Parameter(Tensor(scale_update_cell.get_loss_scale(), dtype=mstype.float32)) + + @C.add_flags(has_effect=True) + def construct(self, + input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + sens=None): + """Defines the computation performed.""" + weights = self.weights + loss = self.network(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights) + if sens is None: + scaling_sens = self.loss_scale + else: + scaling_sens = sens + + # update accumulation parameters + is_accu_step = self.not_equal(self.local_step, self.accumulation_steps) + self.local_step = self.select(is_accu_step, self.local_step + self.one, self.one) + self.accu_loss = self.select(is_accu_step, self.accu_loss + loss, loss) + mean_loss = self.accu_loss / self.local_step + is_accu_step = self.not_equal(self.local_step, self.accumulation_steps) + + # alloc status and clear should be right before gradoperation + init = self.alloc_status() + self.clear_before_grad(init) + grads = self.grad(self.network, weights)(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + self.cast(scaling_sens, + mstype.float32)) + + + accu_grads = self.hyper_map(add_grads, self.accu_grads, grads) + scaling = scaling_sens * self.degree * self.accumulation_steps + grads = self.hyper_map(F.partial(grad_scale, scaling), accu_grads) + grads = self.grad_reducer(grads) + + self.get_status(init) + flag_sum = self.reduce_sum(init, (0,)) + flag_reduce = self.overflow_reducer(flag_sum) + overflow = self.less_equal(self.base, flag_reduce) + overflow = self.logical_or(self.not_equal(self.accu_overflow, self.zero), overflow) + accu_overflow = self.select(overflow, self.one, self.zero) + self.accu_overflow = self.select(is_accu_step, accu_overflow, self.zero) + overflow = self.reshape(overflow, (())) + + if is_accu_step: + succ = False + accu_succ = self.hyper_map(update_accu_grads, self.accu_grads, accu_grads) + succ = F.depend(succ, accu_succ) + else: + if sens is None: + overflow = self.loss_scaling_manager(self.loss_scale, overflow) + if overflow: + succ = False + else: + if self.enable_global_norm: + grads = C.clip_by_global_norm(grads, 1.0, None) + else: + grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads) + + succ = self.optimizer(grads) + + accu_succ = self.hyper_map(reset_accu_grads, self.accu_grads) + succ = F.depend(succ, accu_succ) + + ret = (mean_loss, overflow, scaling_sens) + return F.depend(ret, succ) + + +class BertNetworkMatchBucket(nn.Cell): + ''' + Bert execute according to different sentence lengths. + ''' + def __init__(self, network, seq_length, bucket_list=None): + super(BertNetworkMatchBucket, self).__init__() + self.network = network + if not bucket_list or not isinstance(bucket_list, list): + bucket_list = [seq_length] + self.bucket_list = [bucket for bucket in bucket_list if bucket <= seq_length] + + if network.reducer_flag: + reuse_attr = 'reuse_communication_node' + if not network.grad_reducer.split_fusion: + hccl_op = network.grad_reducer.allreduce + network.grad_reducer.allreduce = hccl_op.add_prim_attr(reuse_attr, getattr(hccl_op, 'fusion')) + else: + new_op_list = [] + for hccl_op in network.grad_reducer.op_list: + new_op = hccl_op.add_prim_attr(reuse_attr, getattr(hccl_op, 'fusion')) + new_op_list.append(new_op) + network.grad_reducer.op_list = new_op_list + + def construct(self, + input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights, + sentence_flag): + """Switch network according to sentence length.""" + for bucket in self.bucket_list: + if sentence_flag == bucket: + input_ids = input_ids[:, :bucket] + input_mask = input_mask[:, :bucket] + token_type_id = token_type_id[:, :bucket] + loss = self.network(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights) + return loss + + loss = self.network(input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights) + return loss + + +class BertPretrainEval(nn.Cell): + ''' + Evaluate MaskedLM prediction scores + ''' + def __init__(self, config, network=None): + super(BertPretrainEval, self).__init__(auto_prefix=False) + if network is None: + self.network = BertPreTraining(config, False, False) + else: + self.network = network + self.argmax = P.Argmax(axis=-1, output_type=mstype.int32) + self.equal = P.Equal() + self.sum = P.ReduceSum() + self.reshape = P.Reshape() + self.shape = P.Shape() + self.cast = P.Cast() + self.allreduce = P.AllReduce() + self.reduce_flag = False + parallel_mode = context.get_auto_parallel_context("parallel_mode") + if parallel_mode in [ParallelMode.DATA_PARALLEL, ParallelMode.HYBRID_PARALLEL]: + self.reduce_flag = True + + def construct(self, + input_ids, + input_mask, + token_type_id, + next_sentence_labels, + masked_lm_positions, + masked_lm_ids, + masked_lm_weights): + """Calculate prediction scores""" + bs, _ = self.shape(input_ids) + mlm, _ = self.network(input_ids, input_mask, token_type_id, masked_lm_positions) + index = self.argmax(mlm) + index = self.reshape(index, (bs, -1)) + eval_acc = self.equal(index, masked_lm_ids) + eval_acc = self.cast(eval_acc, mstype.float32) + real_acc = eval_acc * masked_lm_weights + acc = self.sum(real_acc) + total = self.sum(masked_lm_weights) + + if self.reduce_flag: + acc = self.allreduce(acc) + total = self.allreduce(total) + + return acc, total diff --git a/nlp/language_model/bert/MindSpore/src/bert_model.py b/nlp/language_model/bert/MindSpore/src/bert_model.py new file mode 100644 index 0000000000000000000000000000000000000000..ef6c219c7566a40d6959ab1c0b61d9a1124ff540 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/bert_model.py @@ -0,0 +1,846 @@ +# Copyright 2020 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. +# ============================================================================ +"""Bert model.""" + +import math +import copy +import numpy as np +import mindspore.common.dtype as mstype +import mindspore.nn as nn +import mindspore.ops.functional as F +from mindspore.common.initializer import TruncatedNormal, initializer +from mindspore.ops import operations as P +from mindspore.ops import composite as C +from mindspore.common.tensor import Tensor +from mindspore.common.parameter import Parameter + + +class BertConfig: + """ + Configuration for `BertModel`. + + Args: + seq_length (int): Length of input sequence. Default: 128. + vocab_size (int): The shape of each embedding vector. Default: 32000. + hidden_size (int): Size of the bert encoder layers. Default: 768. + num_hidden_layers (int): Number of hidden layers in the BertTransformer encoder + cell. Default: 12. + num_attention_heads (int): Number of attention heads in the BertTransformer + encoder cell. Default: 12. + intermediate_size (int): Size of intermediate layer in the BertTransformer + encoder cell. Default: 3072. + hidden_act (str): Activation function used in the BertTransformer encoder + cell. Default: "gelu". + hidden_dropout_prob (float): The dropout probability for BertOutput. Default: 0.1. + attention_probs_dropout_prob (float): The dropout probability for + BertAttention. Default: 0.1. + max_position_embeddings (int): Maximum length of sequences used in this + model. Default: 512. + type_vocab_size (int): Size of token type vocab. Default: 16. + initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02. + use_relative_positions (bool): Specifies whether to use relative positions. Default: False. + dtype (:class:`mindspore.dtype`): Data type of the input. Default: mstype.float32. + compute_type (:class:`mindspore.dtype`): Compute type in BertTransformer. Default: mstype.float32. + """ + def __init__(self, + seq_length=128, + vocab_size=32000, + hidden_size=768, + num_hidden_layers=12, + num_attention_heads=12, + intermediate_size=3072, + hidden_act="gelu", + hidden_dropout_prob=0.1, + attention_probs_dropout_prob=0.1, + max_position_embeddings=512, + type_vocab_size=16, + initializer_range=0.02, + use_relative_positions=False, + dtype=mstype.float32, + compute_type=mstype.float32): + self.seq_length = seq_length + self.vocab_size = vocab_size + self.hidden_size = hidden_size + self.num_hidden_layers = num_hidden_layers + self.num_attention_heads = num_attention_heads + self.hidden_act = hidden_act + self.intermediate_size = intermediate_size + self.hidden_dropout_prob = hidden_dropout_prob + self.attention_probs_dropout_prob = attention_probs_dropout_prob + self.max_position_embeddings = max_position_embeddings + self.type_vocab_size = type_vocab_size + self.initializer_range = initializer_range + self.use_relative_positions = use_relative_positions + self.dtype = dtype + self.compute_type = compute_type + + +class EmbeddingLookup(nn.Cell): + """ + A embeddings lookup table with a fixed dictionary and size. + + Args: + vocab_size (int): Size of the dictionary of embeddings. + embedding_size (int): The size of each embedding vector. + embedding_shape (list): [batch_size, seq_length, embedding_size], the shape of + each embedding vector. + use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False. + initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02. + """ + def __init__(self, + vocab_size, + embedding_size, + embedding_shape, + use_one_hot_embeddings=False, + initializer_range=0.02): + super(EmbeddingLookup, self).__init__() + self.vocab_size = vocab_size + self.use_one_hot_embeddings = use_one_hot_embeddings + self.embedding_table = Parameter(initializer + (TruncatedNormal(initializer_range), + [vocab_size, embedding_size])) + self.expand = P.ExpandDims() + self.shape_flat = (-1,) + self.gather = P.Gather() + self.one_hot = P.OneHot() + self.on_value = Tensor(1.0, mstype.float32) + self.off_value = Tensor(0.0, mstype.float32) + self.array_mul = P.MatMul() + self.reshape = P.Reshape() + self.shape = tuple(embedding_shape) + + def construct(self, input_ids): + """Get output and embeddings lookup table""" + extended_ids = self.expand(input_ids, -1) + flat_ids = self.reshape(extended_ids, self.shape_flat) + if self.use_one_hot_embeddings: + one_hot_ids = self.one_hot(flat_ids, self.vocab_size, self.on_value, self.off_value) + output_for_reshape = self.array_mul( + one_hot_ids, self.embedding_table) + else: + output_for_reshape = self.gather(self.embedding_table, flat_ids, 0) + output = self.reshape(output_for_reshape, self.shape) + return output, self.embedding_table + + +class EmbeddingPostprocessor(nn.Cell): + """ + Postprocessors apply positional and token type embeddings to word embeddings. + + Args: + embedding_size (int): The size of each embedding vector. + embedding_shape (list): [batch_size, seq_length, embedding_size], the shape of + each embedding vector. + use_token_type (bool): Specifies whether to use token type embeddings. Default: False. + token_type_vocab_size (int): Size of token type vocab. Default: 16. + use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False. + initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02. + max_position_embeddings (int): Maximum length of sequences used in this + model. Default: 512. + dropout_prob (float): The dropout probability. Default: 0.1. + """ + def __init__(self, + embedding_size, + embedding_shape, + use_relative_positions=False, + use_token_type=False, + token_type_vocab_size=16, + use_one_hot_embeddings=False, + initializer_range=0.02, + max_position_embeddings=512, + dropout_prob=0.1): + super(EmbeddingPostprocessor, self).__init__() + self.use_token_type = use_token_type + self.token_type_vocab_size = token_type_vocab_size + self.use_one_hot_embeddings = use_one_hot_embeddings + self.max_position_embeddings = max_position_embeddings + self.token_type_embedding = nn.Embedding( + vocab_size=token_type_vocab_size, + embedding_size=embedding_size, + use_one_hot=use_one_hot_embeddings) + self.shape_flat = (-1,) + self.one_hot = P.OneHot() + self.on_value = Tensor(1.0, mstype.float32) + self.off_value = Tensor(0.1, mstype.float32) + self.array_mul = P.MatMul() + self.reshape = P.Reshape() + self.shape = tuple(embedding_shape) + self.dropout = nn.Dropout(1 - dropout_prob) + self.gather = P.Gather() + self.use_relative_positions = use_relative_positions + self.slice = P.StridedSlice() + _, seq, _ = self.shape + self.full_position_embedding = nn.Embedding( + vocab_size=max_position_embeddings, + embedding_size=embedding_size, + use_one_hot=False) + self.layernorm = nn.LayerNorm((embedding_size,)) + self.position_ids = Tensor(np.arange(seq).reshape(-1, seq).astype(np.int32)) + self.add = P.Add() + + def construct(self, token_type_ids, word_embeddings): + """Postprocessors apply positional and token type embeddings to word embeddings.""" + output = word_embeddings + if self.use_token_type: + token_type_embeddings = self.token_type_embedding(token_type_ids) + output = self.add(output, token_type_embeddings) + if not self.use_relative_positions: + shape = F.shape(output) + position_ids = self.position_ids[:, :shape[1]] + position_embeddings = self.full_position_embedding(position_ids) + output = self.add(output, position_embeddings) + output = self.layernorm(output) + output = self.dropout(output) + return output + + +class BertOutput(nn.Cell): + """ + Apply a linear computation to hidden status and a residual computation to input. + + Args: + in_channels (int): Input channels. + out_channels (int): Output channels. + initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02. + dropout_prob (float): The dropout probability. Default: 0.1. + compute_type (:class:`mindspore.dtype`): Compute type in BertTransformer. Default: mstype.float32. + """ + def __init__(self, + in_channels, + out_channels, + initializer_range=0.02, + dropout_prob=0.1, + compute_type=mstype.float32): + super(BertOutput, self).__init__() + self.dense = nn.Dense(in_channels, out_channels, + weight_init=TruncatedNormal(initializer_range)).to_float(compute_type) + self.dropout = nn.Dropout(1 - dropout_prob) + self.dropout_prob = dropout_prob + self.add = P.Add() + self.layernorm = nn.LayerNorm((out_channels,)).to_float(compute_type) + self.cast = P.Cast() + + def construct(self, hidden_status, input_tensor): + output = self.dense(hidden_status) + output = self.dropout(output) + output = self.add(input_tensor, output) + output = self.layernorm(output) + return output + + +class RelaPosMatrixGenerator(nn.Cell): + """ + Generates matrix of relative positions between inputs. + + Args: + length (int): Length of one dim for the matrix to be generated. + max_relative_position (int): Max value of relative position. + """ + def __init__(self, max_relative_position): + super(RelaPosMatrixGenerator, self).__init__() + self._max_relative_position = max_relative_position + self._min_relative_position = -max_relative_position + + self.tile = P.Tile() + self.range_mat = P.Reshape() + self.sub = P.Sub() + self.expanddims = P.ExpandDims() + self.cast = P.Cast() + + def construct(self, length): + """Generates matrix of relative positions between inputs.""" + range_vec_row_out = self.cast(F.tuple_to_array(F.make_range(length)), mstype.int32) + range_vec_col_out = self.range_mat(range_vec_row_out, (length, -1)) + tile_row_out = self.tile(range_vec_row_out, (length,)) + tile_col_out = self.tile(range_vec_col_out, (1, length)) + range_mat_out = self.range_mat(tile_row_out, (length, length)) + transpose_out = self.range_mat(tile_col_out, (length, length)) + distance_mat = self.sub(range_mat_out, transpose_out) + + distance_mat_clipped = C.clip_by_value(distance_mat, + self._min_relative_position, + self._max_relative_position) + + # Shift values to be >=0. Each integer still uniquely identifies a + # relative position difference. + final_mat = distance_mat_clipped + self._max_relative_position + return final_mat + + +class RelaPosEmbeddingsGenerator(nn.Cell): + """ + Generates tensor of size [length, length, depth]. + + Args: + length (int): Length of one dim for the matrix to be generated. + depth (int): Size of each attention head. + max_relative_position (int): Maxmum value of relative position. + initializer_range (float): Initialization value of TruncatedNormal. + use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False. + """ + def __init__(self, + depth, + max_relative_position, + initializer_range, + use_one_hot_embeddings=False): + super(RelaPosEmbeddingsGenerator, self).__init__() + self.depth = depth + self.vocab_size = max_relative_position * 2 + 1 + self.use_one_hot_embeddings = use_one_hot_embeddings + + self.embeddings_table = Parameter( + initializer(TruncatedNormal(initializer_range), + [self.vocab_size, self.depth])) + + self.relative_positions_matrix = RelaPosMatrixGenerator(max_relative_position=max_relative_position) + self.reshape = P.Reshape() + self.one_hot = nn.OneHot(depth=self.vocab_size) + self.shape = P.Shape() + self.gather = P.Gather() # index_select + self.matmul = P.BatchMatMul() + + def construct(self, length): + """Generate embedding for each relative position of dimension depth.""" + relative_positions_matrix_out = self.relative_positions_matrix(length) + + if self.use_one_hot_embeddings: + flat_relative_positions_matrix = self.reshape(relative_positions_matrix_out, (-1,)) + one_hot_relative_positions_matrix = self.one_hot( + flat_relative_positions_matrix) + embeddings = self.matmul(one_hot_relative_positions_matrix, self.embeddings_table) + my_shape = self.shape(relative_positions_matrix_out) + (self.depth,) + embeddings = self.reshape(embeddings, my_shape) + else: + embeddings = self.gather(self.embeddings_table, + relative_positions_matrix_out, 0) + return embeddings + + +class SaturateCast(nn.Cell): + """ + Performs a safe saturating cast. This operation applies proper clamping before casting to prevent + the danger that the value will overflow or underflow. + + Args: + src_type (:class:`mindspore.dtype`): The type of the elements of the input tensor. Default: mstype.float32. + dst_type (:class:`mindspore.dtype`): The type of the elements of the output tensor. Default: mstype.float32. + """ + def __init__(self, src_type=mstype.float32, dst_type=mstype.float32): + super(SaturateCast, self).__init__() + np_type = mstype.dtype_to_nptype(dst_type) + + self.tensor_min_type = float(np.finfo(np_type).min) + self.tensor_max_type = float(np.finfo(np_type).max) + + self.min_op = P.Minimum() + self.max_op = P.Maximum() + self.cast = P.Cast() + self.dst_type = dst_type + + def construct(self, x): + out = self.max_op(x, self.tensor_min_type) + out = self.min_op(out, self.tensor_max_type) + return self.cast(out, self.dst_type) + + +class BertAttention(nn.Cell): + """ + Apply multi-headed attention from "from_tensor" to "to_tensor". + + Args: + from_tensor_width (int): Size of last dim of from_tensor. + to_tensor_width (int): Size of last dim of to_tensor. + num_attention_heads (int): Number of attention heads. Default: 1. + size_per_head (int): Size of each attention head. Default: 512. + query_act (str): Activation function for the query transform. Default: None. + key_act (str): Activation function for the key transform. Default: None. + value_act (str): Activation function for the value transform. Default: None. + has_attention_mask (bool): Specifies whether to use attention mask. Default: False. + attention_probs_dropout_prob (float): The dropout probability for + BertAttention. Default: 0.0. + use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False. + initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02. + use_relative_positions (bool): Specifies whether to use relative positions. Default: False. + compute_type (:class:`mindspore.dtype`): Compute type in BertAttention. Default: mstype.float32. + """ + def __init__(self, + from_tensor_width, + to_tensor_width, + num_attention_heads=1, + size_per_head=512, + query_act=None, + key_act=None, + value_act=None, + has_attention_mask=False, + attention_probs_dropout_prob=0.0, + use_one_hot_embeddings=False, + initializer_range=0.02, + use_relative_positions=False, + compute_type=mstype.float32): + + super(BertAttention, self).__init__() + self.num_attention_heads = num_attention_heads + self.size_per_head = size_per_head + self.has_attention_mask = has_attention_mask + self.use_relative_positions = use_relative_positions + + self.scores_mul = 1.0 / math.sqrt(float(self.size_per_head)) + self.reshape = P.Reshape() + self.shape_from_2d = (-1, from_tensor_width) + self.shape_to_2d = (-1, to_tensor_width) + weight = TruncatedNormal(initializer_range) + units = num_attention_heads * size_per_head + self.query_layer = nn.Dense(from_tensor_width, + units, + activation=query_act, + weight_init=weight).to_float(compute_type) + self.key_layer = nn.Dense(to_tensor_width, + units, + activation=key_act, + weight_init=weight).to_float(compute_type) + self.value_layer = nn.Dense(to_tensor_width, + units, + activation=value_act, + weight_init=weight).to_float(compute_type) + + self.matmul_trans_b = P.BatchMatMul(transpose_b=True) + self.multiply = P.Mul() + self.transpose = P.Transpose() + self.trans_shape = (0, 2, 1, 3) + self.trans_shape_relative = (2, 0, 1, 3) + self.trans_shape_position = (1, 2, 0, 3) + self.multiply_data = -10000.0 + self.matmul = P.BatchMatMul() + + self.softmax = nn.Softmax() + self.dropout = nn.Dropout(1 - attention_probs_dropout_prob) + + if self.has_attention_mask: + self.expand_dims = P.ExpandDims() + self.sub = P.Sub() + self.add = P.Add() + self.cast = P.Cast() + self.get_dtype = P.DType() + + self.shape_return = (-1, num_attention_heads * size_per_head) + + self.cast_compute_type = SaturateCast(dst_type=compute_type) + if self.use_relative_positions: + self._generate_relative_positions_embeddings = \ + RelaPosEmbeddingsGenerator(depth=size_per_head, + max_relative_position=16, + initializer_range=initializer_range, + use_one_hot_embeddings=use_one_hot_embeddings) + + def construct(self, from_tensor, to_tensor, attention_mask): + """reshape 2d/3d input tensors to 2d""" + shape_from = F.shape(attention_mask)[2] + from_tensor = F.depend(from_tensor, shape_from) + from_tensor_2d = self.reshape(from_tensor, self.shape_from_2d) + to_tensor_2d = self.reshape(to_tensor, self.shape_to_2d) + query_out = self.query_layer(from_tensor_2d) + key_out = self.key_layer(to_tensor_2d) + value_out = self.value_layer(to_tensor_2d) + + query_layer = self.reshape(query_out, (-1, shape_from, self.num_attention_heads, self.size_per_head)) + query_layer = self.transpose(query_layer, self.trans_shape) + key_layer = self.reshape(key_out, (-1, shape_from, self.num_attention_heads, self.size_per_head)) + key_layer = self.transpose(key_layer, self.trans_shape) + + attention_scores = self.matmul_trans_b(query_layer, key_layer) + + # use_relative_position, supplementary logic + if self.use_relative_positions: + # relations_keys is [F|T, F|T, H] + relations_keys = self._generate_relative_positions_embeddings(shape_from) + relations_keys = self.cast_compute_type(relations_keys) + # query_layer_t is [F, B, N, H] + query_layer_t = self.transpose(query_layer, self.trans_shape_relative) + # query_layer_r is [F, B * N, H] + query_layer_r = self.reshape(query_layer_t, + (shape_from, + -1, + self.size_per_head)) + # key_position_scores is [F, B * N, F|T] + key_position_scores = self.matmul_trans_b(query_layer_r, + relations_keys) + # key_position_scores_r is [F, B, N, F|T] + key_position_scores_r = self.reshape(key_position_scores, + (shape_from, + -1, + self.num_attention_heads, + shape_from)) + # key_position_scores_r_t is [B, N, F, F|T] + key_position_scores_r_t = self.transpose(key_position_scores_r, + self.trans_shape_position) + attention_scores = attention_scores + key_position_scores_r_t + + attention_scores = self.multiply(self.scores_mul, attention_scores) + + if self.has_attention_mask: + attention_mask = self.expand_dims(attention_mask, 1) + multiply_out = self.sub(self.cast(F.tuple_to_array((1.0,)), self.get_dtype(attention_scores)), + self.cast(attention_mask, self.get_dtype(attention_scores))) + + adder = self.multiply(multiply_out, self.multiply_data) + attention_scores = self.add(adder, attention_scores) + + attention_probs = self.softmax(attention_scores) + attention_probs = self.dropout(attention_probs) + + value_layer = self.reshape(value_out, (-1, shape_from, self.num_attention_heads, self.size_per_head)) + value_layer = self.transpose(value_layer, self.trans_shape) + context_layer = self.matmul(attention_probs, value_layer) + + # use_relative_position, supplementary logic + if self.use_relative_positions: + # relations_values is [F|T, F|T, H] + relations_values = self._generate_relative_positions_embeddings(shape_from) + relations_values = self.cast_compute_type(relations_values) + # attention_probs_t is [F, B, N, T] + attention_probs_t = self.transpose(attention_probs, self.trans_shape_relative) + # attention_probs_r is [F, B * N, T] + attention_probs_r = self.reshape( + attention_probs_t, + (shape_from, + -1, + shape_from)) + # value_position_scores is [F, B * N, H] + value_position_scores = self.matmul(attention_probs_r, + relations_values) + # value_position_scores_r is [F, B, N, H] + value_position_scores_r = self.reshape(value_position_scores, + (shape_from, + -1, + self.num_attention_heads, + self.size_per_head)) + # value_position_scores_r_t is [B, N, F, H] + value_position_scores_r_t = self.transpose(value_position_scores_r, + self.trans_shape_position) + context_layer = context_layer + value_position_scores_r_t + + context_layer = self.transpose(context_layer, self.trans_shape) + context_layer = self.reshape(context_layer, self.shape_return) + + return context_layer + + +class BertSelfAttention(nn.Cell): + """ + Apply self-attention. + + Args: + hidden_size (int): Size of the bert encoder layers. + num_attention_heads (int): Number of attention heads. Default: 12. + attention_probs_dropout_prob (float): The dropout probability for + BertAttention. Default: 0.1. + use_one_hot_embeddings (bool): Specifies whether to use one_hot encoding form. Default: False. + initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02. + hidden_dropout_prob (float): The dropout probability for BertOutput. Default: 0.1. + use_relative_positions (bool): Specifies whether to use relative positions. Default: False. + compute_type (:class:`mindspore.dtype`): Compute type in BertSelfAttention. Default: mstype.float32. + """ + def __init__(self, + hidden_size, + num_attention_heads=12, + attention_probs_dropout_prob=0.1, + use_one_hot_embeddings=False, + initializer_range=0.02, + hidden_dropout_prob=0.1, + use_relative_positions=False, + compute_type=mstype.float32): + super(BertSelfAttention, self).__init__() + if hidden_size % num_attention_heads != 0: + raise ValueError("The hidden size (%d) is not a multiple of the number " + "of attention heads (%d)" % (hidden_size, num_attention_heads)) + + self.size_per_head = int(hidden_size / num_attention_heads) + + self.attention = BertAttention( + from_tensor_width=hidden_size, + to_tensor_width=hidden_size, + num_attention_heads=num_attention_heads, + size_per_head=self.size_per_head, + attention_probs_dropout_prob=attention_probs_dropout_prob, + use_one_hot_embeddings=use_one_hot_embeddings, + initializer_range=initializer_range, + use_relative_positions=use_relative_positions, + has_attention_mask=True, + compute_type=compute_type) + + self.output = BertOutput(in_channels=hidden_size, + out_channels=hidden_size, + initializer_range=initializer_range, + dropout_prob=hidden_dropout_prob, + compute_type=compute_type) + self.reshape = P.Reshape() + self.shape = (-1, hidden_size) + + def construct(self, input_tensor, attention_mask): + attention_output = self.attention(input_tensor, input_tensor, attention_mask) + output = self.output(attention_output, input_tensor) + return output + + +class BertEncoderCell(nn.Cell): + """ + Encoder cells used in BertTransformer. + + Args: + hidden_size (int): Size of the bert encoder layers. Default: 768. + num_attention_heads (int): Number of attention heads. Default: 12. + intermediate_size (int): Size of intermediate layer. Default: 3072. + attention_probs_dropout_prob (float): The dropout probability for + BertAttention. Default: 0.02. + use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False. + initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02. + hidden_dropout_prob (float): The dropout probability for BertOutput. Default: 0.1. + use_relative_positions (bool): Specifies whether to use relative positions. Default: False. + hidden_act (str): Activation function. Default: "gelu". + compute_type (:class:`mindspore.dtype`): Compute type in attention. Default: mstype.float32. + """ + def __init__(self, + hidden_size=768, + num_attention_heads=12, + intermediate_size=3072, + attention_probs_dropout_prob=0.02, + use_one_hot_embeddings=False, + initializer_range=0.02, + hidden_dropout_prob=0.1, + use_relative_positions=False, + hidden_act="gelu", + compute_type=mstype.float32): + super(BertEncoderCell, self).__init__() + self.attention = BertSelfAttention( + hidden_size=hidden_size, + num_attention_heads=num_attention_heads, + attention_probs_dropout_prob=attention_probs_dropout_prob, + use_one_hot_embeddings=use_one_hot_embeddings, + initializer_range=initializer_range, + hidden_dropout_prob=hidden_dropout_prob, + use_relative_positions=use_relative_positions, + compute_type=compute_type) + self.intermediate = nn.Dense(in_channels=hidden_size, + out_channels=intermediate_size, + activation=hidden_act, + weight_init=TruncatedNormal(initializer_range)).to_float(compute_type) + self.output = BertOutput(in_channels=intermediate_size, + out_channels=hidden_size, + initializer_range=initializer_range, + dropout_prob=hidden_dropout_prob, + compute_type=compute_type) + + def construct(self, hidden_states, attention_mask): + # self-attention + attention_output = self.attention(hidden_states, attention_mask) + # feed construct + intermediate_output = self.intermediate(attention_output) + # add and normalize + output = self.output(intermediate_output, attention_output) + return output + + +class BertTransformer(nn.Cell): + """ + Multi-layer bert transformer. + + Args: + hidden_size (int): Size of the encoder layers. + num_hidden_layers (int): Number of hidden layers in encoder cells. + num_attention_heads (int): Number of attention heads in encoder cells. Default: 12. + intermediate_size (int): Size of intermediate layer in encoder cells. Default: 3072. + attention_probs_dropout_prob (float): The dropout probability for + BertAttention. Default: 0.1. + use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False. + initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02. + hidden_dropout_prob (float): The dropout probability for BertOutput. Default: 0.1. + use_relative_positions (bool): Specifies whether to use relative positions. Default: False. + hidden_act (str): Activation function used in the encoder cells. Default: "gelu". + compute_type (:class:`mindspore.dtype`): Compute type in BertTransformer. Default: mstype.float32. + return_all_encoders (bool): Specifies whether to return all encoders. Default: False. + """ + def __init__(self, + hidden_size, + num_hidden_layers, + num_attention_heads=12, + intermediate_size=3072, + attention_probs_dropout_prob=0.1, + use_one_hot_embeddings=False, + initializer_range=0.02, + hidden_dropout_prob=0.1, + use_relative_positions=False, + hidden_act="gelu", + compute_type=mstype.float32, + return_all_encoders=False): + super(BertTransformer, self).__init__() + self.return_all_encoders = return_all_encoders + + layers = [] + for _ in range(num_hidden_layers): + layer = BertEncoderCell(hidden_size=hidden_size, + num_attention_heads=num_attention_heads, + intermediate_size=intermediate_size, + attention_probs_dropout_prob=attention_probs_dropout_prob, + use_one_hot_embeddings=use_one_hot_embeddings, + initializer_range=initializer_range, + hidden_dropout_prob=hidden_dropout_prob, + use_relative_positions=use_relative_positions, + hidden_act=hidden_act, + compute_type=compute_type) + layers.append(layer) + + self.layers = nn.CellList(layers) + + self.reshape = P.Reshape() + self.shape = (-1, hidden_size) + + def construct(self, input_tensor, attention_mask): + """Multi-layer bert transformer.""" + prev_output = self.reshape(input_tensor, self.shape) + + all_encoder_layers = () + for layer_module in self.layers: + layer_output = layer_module(prev_output, attention_mask) + prev_output = layer_output + + if self.return_all_encoders: + shape = F.shape(input_tensor) + layer_output = self.reshape(layer_output, shape) + all_encoder_layers = all_encoder_layers + (layer_output,) + + if not self.return_all_encoders: + shape = F.shape(input_tensor) + prev_output = self.reshape(prev_output, shape) + all_encoder_layers = all_encoder_layers + (prev_output,) + return all_encoder_layers + + +class CreateAttentionMaskFromInputMask(nn.Cell): + """ + Create attention mask according to input mask. + + Args: + config (Class): Configuration for BertModel. + """ + def __init__(self, config): + super(CreateAttentionMaskFromInputMask, self).__init__() + self.input_mask = None + + self.cast = P.Cast() + self.reshape = P.Reshape() + + def construct(self, input_mask): + seq_length = F.shape(input_mask)[1] + attention_mask = self.cast(self.reshape(input_mask, (-1, 1, seq_length)), mstype.float32) + return attention_mask + + +class BertModel(nn.Cell): + """ + Bidirectional Encoder Representations from Transformers. + + Args: + config (Class): Configuration for BertModel. + is_training (bool): True for training mode. False for eval mode. + use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False. + """ + def __init__(self, + config, + is_training, + use_one_hot_embeddings=False): + super(BertModel, self).__init__() + config = copy.deepcopy(config) + if not is_training: + config.hidden_dropout_prob = 0.0 + config.attention_probs_dropout_prob = 0.0 + + self.hidden_size = config.hidden_size + self.num_hidden_layers = config.num_hidden_layers + self.embedding_size = config.hidden_size + self.token_type_ids = None + + self.last_idx = self.num_hidden_layers - 1 + output_embedding_shape = [-1, config.seq_length, self.embedding_size] + + self.bert_embedding_lookup = nn.Embedding( + vocab_size=config.vocab_size, + embedding_size=self.embedding_size, + use_one_hot=use_one_hot_embeddings, + embedding_table=TruncatedNormal(config.initializer_range)) + + self.bert_embedding_postprocessor = EmbeddingPostprocessor( + embedding_size=self.embedding_size, + embedding_shape=output_embedding_shape, + use_relative_positions=config.use_relative_positions, + use_token_type=True, + token_type_vocab_size=config.type_vocab_size, + use_one_hot_embeddings=use_one_hot_embeddings, + initializer_range=0.02, + max_position_embeddings=config.max_position_embeddings, + dropout_prob=config.hidden_dropout_prob) + + self.bert_encoder = BertTransformer( + hidden_size=self.hidden_size, + num_attention_heads=config.num_attention_heads, + num_hidden_layers=self.num_hidden_layers, + intermediate_size=config.intermediate_size, + attention_probs_dropout_prob=config.attention_probs_dropout_prob, + use_one_hot_embeddings=use_one_hot_embeddings, + initializer_range=config.initializer_range, + hidden_dropout_prob=config.hidden_dropout_prob, + use_relative_positions=config.use_relative_positions, + hidden_act=config.hidden_act, + compute_type=config.compute_type, + return_all_encoders=True) + + self.cast = P.Cast() + self.dtype = config.dtype + self.cast_compute_type = SaturateCast(dst_type=config.compute_type) + self.slice = P.StridedSlice() + + self.squeeze_1 = P.Squeeze(axis=1) + self.dense = nn.Dense(self.hidden_size, self.hidden_size, + activation="tanh", + weight_init=TruncatedNormal(config.initializer_range)).to_float(config.compute_type) + self._create_attention_mask_from_input_mask = CreateAttentionMaskFromInputMask(config) + + def construct(self, input_ids, token_type_ids, input_mask): + """Bidirectional Encoder Representations from Transformers.""" + # embedding + embedding_tables = self.bert_embedding_lookup.embedding_table + word_embeddings = self.bert_embedding_lookup(input_ids) + embedding_output = self.bert_embedding_postprocessor(token_type_ids, + word_embeddings) + + # attention mask [batch_size, seq_length, seq_length] + attention_mask = self._create_attention_mask_from_input_mask(input_mask) + + # bert encoder + encoder_output = self.bert_encoder(self.cast_compute_type(embedding_output), + attention_mask) + + sequence_output = self.cast(encoder_output[self.last_idx], self.dtype) + + # pooler + batch_size = P.Shape()(input_ids)[0] + sequence_slice = self.slice(sequence_output, + (0, 0, 0), + (batch_size, 1, self.hidden_size), + (1, 1, 1)) + first_token = self.squeeze_1(sequence_slice) + pooled_output = self.dense(first_token) + pooled_output = self.cast(pooled_output, self.dtype) + + return sequence_output, pooled_output, embedding_tables diff --git a/nlp/language_model/bert/MindSpore/src/cluener_evaluation.py b/nlp/language_model/bert/MindSpore/src/cluener_evaluation.py new file mode 100644 index 0000000000000000000000000000000000000000..9534f61cb3c9db8639516b01ff26eebc5a4c7f9a --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/cluener_evaluation.py @@ -0,0 +1,76 @@ +# Copyright 2020 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. +# ============================================================================ + +'''bert clue evaluation''' + +import json +import numpy as np +import mindspore.common.dtype as mstype +from mindspore.common.tensor import Tensor +from src import tokenization +from src.sample_process import label_generation, process_one_example_p +from src.CRF import postprocess +from src.model_utils.config import bert_net_cfg +from src.score import get_result + +def process(model=None, text="", tokenizer_=None, use_crf="", tag_to_index=None, vocab=""): + """ + process text. + """ + data = [text] + features = [] + res = [] + ids = [] + for i in data: + feature = process_one_example_p(tokenizer_, vocab, i, max_seq_len=bert_net_cfg.seq_length) + features.append(feature) + input_ids, input_mask, token_type_id = feature + input_ids = Tensor(np.array(input_ids), mstype.int32) + input_mask = Tensor(np.array(input_mask), mstype.int32) + token_type_id = Tensor(np.array(token_type_id), mstype.int32) + if use_crf.lower() == "true": + backpointers, best_tag_id = model.predict(input_ids, input_mask, token_type_id, Tensor(1)) + best_path = postprocess(backpointers, best_tag_id) + logits = [] + for ele in best_path: + logits.extend(ele) + ids = logits + else: + logits = model.predict(input_ids, input_mask, token_type_id, Tensor(1)) + ids = logits.asnumpy() + ids = np.argmax(ids, axis=-1) + ids = list(ids) + res = label_generation(text=text, probs=ids, tag_to_index=tag_to_index) + return res + +def submit(model=None, path="", vocab_file="", use_crf="", label_file="", tag_to_index=None): + """ + submit task + """ + tokenizer_ = tokenization.FullTokenizer(vocab_file=vocab_file) + data = [] + for line in open(path): + if not line.strip(): + continue + oneline = json.loads(line.strip()) + res = process(model=model, text=oneline["text"], tokenizer_=tokenizer_, + use_crf=use_crf, tag_to_index=tag_to_index, vocab=vocab_file) + data.append(json.dumps({"label": res}, ensure_ascii=False)) + open("ner_predict.json", "w").write("\n".join(data)) + labels = [] + with open(label_file) as f: + for label in f: + labels.append(label.strip()) + get_result(labels, "ner_predict.json", path) diff --git a/nlp/language_model/bert/MindSpore/src/create_squad_data.py b/nlp/language_model/bert/MindSpore/src/create_squad_data.py new file mode 100644 index 0000000000000000000000000000000000000000..b2ca2aefba19775ee65e269f14317520c60f9467 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/create_squad_data.py @@ -0,0 +1,309 @@ +# Copyright 2020 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. +# ============================================================================ + +"""create squad data""" + +import collections +import json +from src import tokenization + + +class SquadExample(): + """extract column contents from raw data""" + def __init__(self, + qas_id, + question_text, + doc_tokens, + orig_answer_text=None, + start_position=None, + end_position=None, + is_impossible=False): + self.qas_id = qas_id + self.question_text = question_text + self.doc_tokens = doc_tokens + self.orig_answer_text = orig_answer_text + self.start_position = start_position + self.end_position = end_position + self.is_impossible = is_impossible + + +class InputFeatures(): + """A single set of features of data.""" + def __init__(self, + unique_id, + example_index, + doc_span_index, + tokens, + token_to_orig_map, + token_is_max_context, + input_ids, + input_mask, + segment_ids, + start_position=None, + end_position=None, + is_impossible=None): + self.unique_id = unique_id + self.example_index = example_index + self.doc_span_index = doc_span_index + self.tokens = tokens + self.token_to_orig_map = token_to_orig_map + self.token_is_max_context = token_is_max_context + self.input_ids = input_ids + self.input_mask = input_mask + self.segment_ids = segment_ids + self.start_position = start_position + self.end_position = end_position + self.is_impossible = is_impossible + + +def read_squad_examples(input_file, is_training, version_2_with_negative=False): + """Return list of SquadExample from input_data or input_file (SQuAD json file)""" + with open(input_file, "r") as reader: + input_data = json.load(reader)["data"] + + def is_whitespace(c): + if c == " " or c == "\t" or c == "\r" or c == "\n" or ord(c) == 0x202F: + return True + return False + + def token_offset(text): + doc_tokens = [] + char_to_word_offset = [] + prev_is_whitespace = True + for c in paragraph_text: + if is_whitespace(c): + prev_is_whitespace = True + else: + if prev_is_whitespace: + doc_tokens.append(c) + else: + doc_tokens[-1] += c + prev_is_whitespace = False + char_to_word_offset.append(len(doc_tokens) - 1) + return (doc_tokens, char_to_word_offset) + + + def process_one_example(qa, is_training, version_2_with_negative, doc_tokens, char_to_word_offset): + qas_id = qa["id"] + question_text = qa["question"] + start_position = -1 + end_position = -1 + orig_answer_text = "" + is_impossible = False + if is_training: + if version_2_with_negative: + is_impossible = qa["is_impossible"] + if (len(qa["answers"]) != 1) and (not is_impossible): + raise ValueError("For training, each question should have exactly 1 answer.") + if not is_impossible: + answer = qa["answers"][0] + orig_answer_text = answer["text"] + answer_offset = answer["answer_start"] + answer_length = len(orig_answer_text) + start_position = char_to_word_offset[answer_offset] + end_position = char_to_word_offset[answer_offset + answer_length - 1] + actual_text = " ".join(doc_tokens[start_position:(end_position + 1)]) + cleaned_answer_text = " ".join(tokenization.whitespace_tokenize(orig_answer_text)) + if actual_text.find(cleaned_answer_text) == -1: + return None + example = SquadExample( + qas_id=qas_id, + question_text=question_text, + doc_tokens=doc_tokens, + orig_answer_text=orig_answer_text, + start_position=start_position, + end_position=end_position, + is_impossible=is_impossible) + return example + + + examples = [] + for entry in input_data: + for paragraph in entry["paragraphs"]: + paragraph_text = paragraph["context"] + doc_tokens, char_to_word_offset = token_offset(paragraph_text) + for qa in paragraph["qas"]: + one_example = process_one_example(qa, is_training, version_2_with_negative, + doc_tokens, char_to_word_offset) + if one_example is not None: + examples.append(one_example) + return examples + +def _check_is_max_context(doc_spans, cur_span_index, position): + """Check if this is the 'max context' doc span for the token.""" + best_score = None + best_span_index = None + for (span_index, doc_span) in enumerate(doc_spans): + end = doc_span.start + doc_span.length - 1 + if position < doc_span.start: + continue + if position > end: + continue + num_left_context = position - doc_span.start + num_right_context = end - position + score = min(num_left_context, num_right_context) + 0.01 * doc_span.length + if best_score is None or score > best_score: + best_score = score + best_span_index = span_index + return cur_span_index == best_span_index + +def _improve_answer_span(doc_tokens, input_start, input_end, tokenizer, + orig_answer_text): + """Returns tokenized answer spans that better match the annotated answer.""" + tok_answer_text = " ".join(tokenizer.tokenize(orig_answer_text)) + + for new_start in range(input_start, input_end + 1): + for new_end in range(input_end, new_start - 1, -1): + text_span = " ".join(doc_tokens[new_start:(new_end + 1)]) + if text_span == tok_answer_text: + return (new_start, new_end) + + return (input_start, input_end) + + +def convert_examples_to_features(examples, tokenizer, max_seq_length, doc_stride, + max_query_length, is_training, output_fn, vocab_file): + """Loads a data file into a list of `InputBatch`s.""" + unique_id = 1000000000 + output = [] + for (example_index, example) in enumerate(examples): + query_tokens = tokenizer.tokenize(example.question_text) + + if len(query_tokens) > max_query_length: + query_tokens = query_tokens[0:max_query_length] + + tok_to_orig_index = [] + orig_to_tok_index = [] + all_doc_tokens = [] + for (i, token) in enumerate(example.doc_tokens): + orig_to_tok_index.append(len(all_doc_tokens)) + sub_tokens = tokenizer.tokenize(token) + for sub_token in sub_tokens: + tok_to_orig_index.append(i) + all_doc_tokens.append(sub_token) + + tok_start_position = None + tok_end_position = None + if is_training and example.is_impossible: + tok_start_position = -1 + tok_end_position = -1 + if is_training and not example.is_impossible: + tok_start_position = orig_to_tok_index[example.start_position] + if example.end_position < len(example.doc_tokens) - 1: + tok_end_position = orig_to_tok_index[example.end_position + 1] - 1 + else: + tok_end_position = len(all_doc_tokens) - 1 + (tok_start_position, tok_end_position) = _improve_answer_span( + all_doc_tokens, tok_start_position, tok_end_position, tokenizer, + example.orig_answer_text) + + # The -3 accounts for [CLS], [SEP] and [SEP] + max_tokens_for_doc = max_seq_length - len(query_tokens) - 3 + + # We can have documents that are longer than the maximum sequence length. + # To deal with this we do a sliding window approach, where we take chunks + # of the up to our max length with a stride of `doc_stride`. + _DocSpan = collections.namedtuple("DocSpan", ["start", "length"]) + doc_spans = [] + start_offset = 0 + while start_offset < len(all_doc_tokens): + length = len(all_doc_tokens) - start_offset + if length > max_tokens_for_doc: + length = max_tokens_for_doc + doc_spans.append(_DocSpan(start=start_offset, length=length)) + if start_offset + length == len(all_doc_tokens): + break + start_offset += min(length, doc_stride) + + for (doc_span_index, doc_span) in enumerate(doc_spans): + tokens = [] + token_to_orig_map = {} + token_is_max_context = {} + segment_ids = [] + tokens.append("[CLS]") + segment_ids.append(0) + for token in query_tokens: + tokens.append(token) + segment_ids.append(0) + tokens.append("[SEP]") + segment_ids.append(0) + + for i in range(doc_span.length): + split_token_index = doc_span.start + i + token_to_orig_map[len(tokens)] = tok_to_orig_index[split_token_index] + + is_max_context = _check_is_max_context(doc_spans, doc_span_index, split_token_index) + token_is_max_context[len(tokens)] = is_max_context + tokens.append(all_doc_tokens[split_token_index]) + segment_ids.append(1) + tokens.append("[SEP]") + segment_ids.append(1) + + input_ids = tokenization.convert_tokens_to_ids(vocab_file, tokens) + + # The mask has 1 for real tokens and 0 for padding tokens. Only real + # tokens are attended to. + input_mask = [1] * len(input_ids) + + # Zero-pad up to the sequence length. + while len(input_ids) < max_seq_length: + input_ids.append(0) + input_mask.append(0) + segment_ids.append(0) + + assert len(input_ids) == max_seq_length + assert len(input_mask) == max_seq_length + assert len(segment_ids) == max_seq_length + + start_position = None + end_position = None + if is_training and not example.is_impossible: + # For training, if our document chunk does not contain an annotation + # we throw it out, since there is nothing to predict. + doc_start = doc_span.start + doc_end = doc_span.start + doc_span.length - 1 + out_of_span = False + if not (tok_start_position >= doc_start and tok_end_position <= doc_end): + out_of_span = True + if out_of_span: + start_position = 0 + end_position = 0 + else: + doc_offset = len(query_tokens) + 2 + start_position = tok_start_position - doc_start + doc_offset + end_position = tok_end_position - doc_start + doc_offset + + if is_training and example.is_impossible: + start_position = 0 + end_position = 0 + + feature = InputFeatures( + unique_id=unique_id, + example_index=example_index, + doc_span_index=doc_span_index, + tokens=tokens, + token_to_orig_map=token_to_orig_map, + token_is_max_context=token_is_max_context, + input_ids=input_ids, + input_mask=input_mask, + segment_ids=segment_ids, + start_position=start_position, + end_position=end_position, + is_impossible=example.is_impossible) + + # Run callback + output.append(feature) + unique_id += 1 + return output diff --git a/nlp/language_model/bert/MindSpore/src/dataset.py b/nlp/language_model/bert/MindSpore/src/dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..d4845b5f266979e3de5d5ef81ae558afd9048db3 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/dataset.py @@ -0,0 +1,289 @@ +# Copyright 2020-2022 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. +# ============================================================================ +""" +Data operations, will be used in run_pretrain.py +""" +import os +import math +import numpy as np +import mindspore.common.dtype as mstype +import mindspore.dataset as ds +import mindspore.dataset.transforms as C +from mindspore import log as logger + + +class BucketDatasetGenerator: + """ + Provide data distribution of different gears for the bert network. + + Args: + dataset (Dataset): The training dataset. + batch_size (Int): The training batchsize. + bucket_list (List): List of different sentence lengths, such as [128, 256, 512]. Default: None. + """ + def __init__(self, dataset, batch_size, bucket_list=None): + self.dataset = dataset + self.batch_size = batch_size + self.bucket_list = bucket_list + bucket_size = len(bucket_list) + self.random_list = np.random.binomial(n=(bucket_size - 1), p=0.55, size=self.__len__()) + self.random_list = (self.random_list + 2) % bucket_size + self.random_list = [bucket_list[i] for i in self.random_list] + self._init_variables() + + def _init_variables(self): + self.data_bucket = {bucket: [] for bucket in self.bucket_list} + self.iter = 0 + self.remaining_data = [] + self.stage = 0 + + def __next__(self): + if self.stage != 0: + return self._process_remaining_data() + + for item in self.iterator: + for seq_length in self.bucket_list: + if np.sum(item[1]) <= seq_length: + self.data_bucket[seq_length].append(item) + break + for key in self.data_bucket.keys(): + data = self.data_bucket[key] + if len(data) >= self.batch_size and self.random_list[self.iter] == key: + self.data_bucket[key] = self.data_bucket[key][self.batch_size:] + self.iter += 1 + return self._package_data(data, key) + self.stage = 1 + for value in self.data_bucket.values(): + self.remaining_data += list(value) + return self._process_remaining_data() + + def _package_data(self, data, key): + """package a set of data.""" + arr = data[0] + for i in range(1, self.batch_size): + current_data = data[i] + for j in range(len(current_data)): + arr[j] = np.concatenate((arr[j], current_data[j])) + res = () + for label in arr: + newlabel = np.reshape(label, (self.batch_size, -1)) + res += (newlabel,) + res += (np.array(key, np.int32),) + return res + + def _process_remaining_data(self): + """process remaining data.""" + if self.batch_size > len(self.remaining_data) or self.iter >= self.__len__(): + self._init_variables() + raise StopIteration + remaining_data = self.remaining_data[:self.batch_size] + self.remaining_data = self.remaining_data[self.batch_size:] + self.iter += 1 + return self._package_data(remaining_data, self.bucket_list[-1]) + + def __iter__(self): + self._init_variables() + self.iterator = self.dataset.create_tuple_iterator(output_numpy=True) + return self + + def __len__(self): + return self.dataset.get_dataset_size() // self.batch_size + + +def create_bert_dataset(device_num=1, rank=0, do_shuffle="true", data_dir=None, schema_dir=None, batch_size=32, + bucket_list=None): + """create train dataset""" + # apply repeat operations + files = os.listdir(data_dir) + data_files = [] + for file_name in files: + if "tfrecord" in file_name: + data_files.append(os.path.join(data_dir, file_name)) + data_set = ds.TFRecordDataset(data_files, schema_dir if schema_dir != "" else None, + columns_list=["input_ids", "input_mask", "segment_ids", "next_sentence_labels", + "masked_lm_positions", "masked_lm_ids", "masked_lm_weights"], + shuffle=ds.Shuffle.FILES if do_shuffle == "true" else False, + num_shards=device_num, shard_id=rank, shard_equal_rows=True) + if bucket_list: + bucket_dataset = BucketDatasetGenerator(data_set, batch_size, bucket_list=bucket_list) + data_set = ds.GeneratorDataset(bucket_dataset, + column_names=["input_ids", "input_mask", "segment_ids", "next_sentence_labels", + "masked_lm_positions", "masked_lm_ids", "masked_lm_weights", + "sentence_flag"], + shuffle=False) + else: + data_set = data_set.batch(batch_size, drop_remainder=True) + ori_dataset_size = data_set.get_dataset_size() + print('origin dataset size: ', ori_dataset_size) + type_cast_op = C.TypeCast(mstype.int32) + data_set = data_set.map(operations=type_cast_op, input_columns="masked_lm_ids") + data_set = data_set.map(operations=type_cast_op, input_columns="masked_lm_positions") + data_set = data_set.map(operations=type_cast_op, input_columns="next_sentence_labels") + data_set = data_set.map(operations=type_cast_op, input_columns="segment_ids") + data_set = data_set.map(operations=type_cast_op, input_columns="input_mask") + data_set = data_set.map(operations=type_cast_op, input_columns="input_ids") + # apply batch operations + logger.info("data size: {}".format(data_set.get_dataset_size())) + logger.info("repeat count: {}".format(data_set.get_repeat_count())) + return data_set + + +def create_ner_dataset(batch_size=1, assessment_method="accuracy", data_file_path=None, + dataset_format="mindrecord", schema_file_path=None, do_shuffle=True, drop_remainder=True): + """create finetune or evaluation dataset""" + type_cast_op = C.TypeCast(mstype.int32) + if dataset_format == "mindrecord": + dataset = ds.MindDataset([data_file_path], + columns_list=["input_ids", "input_mask", "segment_ids", "label_ids"], + shuffle=do_shuffle) + else: + dataset = ds.TFRecordDataset([data_file_path], schema_file_path if schema_file_path != "" else None, + columns_list=["input_ids", "input_mask", "segment_ids", "label_ids"], + shuffle=do_shuffle) + if assessment_method == "Spearman_correlation": + type_cast_op_float = C.TypeCast(mstype.float32) + dataset = dataset.map(operations=type_cast_op_float, input_columns="label_ids") + else: + dataset = dataset.map(operations=type_cast_op, input_columns="label_ids") + dataset = dataset.map(operations=type_cast_op, input_columns="segment_ids") + dataset = dataset.map(operations=type_cast_op, input_columns="input_mask") + dataset = dataset.map(operations=type_cast_op, input_columns="input_ids") + # apply batch operations + dataset = dataset.batch(batch_size, drop_remainder=drop_remainder) + return dataset + + +def create_classification_dataset(batch_size=1, assessment_method="accuracy", + data_file_path=None, schema_file_path=None, do_shuffle=True): + """create finetune or evaluation dataset""" + type_cast_op = C.TypeCast(mstype.int32) + data_set = ds.TFRecordDataset([data_file_path], schema_file_path if schema_file_path != "" else None, + columns_list=["input_ids", "input_mask", "segment_ids", "label_ids"], + shuffle=do_shuffle) + if assessment_method == "Spearman_correlation": + type_cast_op_float = C.TypeCast(mstype.float32) + data_set = data_set.map(operations=type_cast_op_float, input_columns="label_ids") + else: + data_set = data_set.map(operations=type_cast_op, input_columns="label_ids") + data_set = data_set.map(operations=type_cast_op, input_columns="segment_ids") + data_set = data_set.map(operations=type_cast_op, input_columns="input_mask") + data_set = data_set.map(operations=type_cast_op, input_columns="input_ids") + # apply batch operations + data_set = data_set.batch(batch_size, drop_remainder=True) + return data_set + + +def generator_squad(data_features): + for feature in data_features: + yield (feature.input_ids, feature.input_mask, feature.segment_ids, feature.unique_id) + + +def create_squad_dataset(batch_size=1, data_file_path=None, schema_file_path=None, + is_training=True, do_shuffle=True): + """create finetune or evaluation dataset""" + type_cast_op = C.TypeCast(mstype.int32) + if is_training: + data_set = ds.TFRecordDataset([data_file_path], schema_file_path if schema_file_path != "" else None, + columns_list=["input_ids", "input_mask", "segment_ids", "start_positions", + "end_positions", "unique_ids", "is_impossible"], + shuffle=do_shuffle) + data_set = data_set.map(operations=type_cast_op, input_columns="start_positions") + data_set = data_set.map(operations=type_cast_op, input_columns="end_positions") + else: + data_set = ds.GeneratorDataset(generator_squad(data_file_path), shuffle=do_shuffle, + column_names=["input_ids", "input_mask", "segment_ids", "unique_ids"]) + data_set = data_set.map(operations=type_cast_op, input_columns="segment_ids") + data_set = data_set.map(operations=type_cast_op, input_columns="input_mask") + data_set = data_set.map(operations=type_cast_op, input_columns="input_ids") + data_set = data_set.map(operations=type_cast_op, input_columns="unique_ids") + # apply batch operations + data_set = data_set.batch(batch_size, drop_remainder=True) + return data_set + +def create_squad_dataset_distribute(batch_size=1, data_file_path=None, schema_file_path=None, + is_training=True, do_shuffle=True, device_num=1, rank=0): + """create finetune or evaluation dataset""" + type_cast_op = C.TypeCast(mstype.int32) + if is_training: + data_set = ds.TFRecordDataset([data_file_path], schema_file_path if schema_file_path != "" else None, + columns_list=["input_ids", "input_mask", "segment_ids", "start_positions", + "end_positions", "unique_ids", "is_impossible"], + num_shards=device_num, shard_id=rank ,shuffle=do_shuffle) + data_set = data_set.map(operations=type_cast_op, input_columns="start_positions") + data_set = data_set.map(operations=type_cast_op, input_columns="end_positions") + else: + data_set = ds.GeneratorDataset(generator_squad(data_file_path), shuffle=do_shuffle, + column_names=["input_ids", "input_mask", "segment_ids", "unique_ids"], + num_shards=device_num, shard_id=rank) + data_set = data_set.map(operations=type_cast_op, input_columns="segment_ids") + data_set = data_set.map(operations=type_cast_op, input_columns="input_mask") + data_set = data_set.map(operations=type_cast_op, input_columns="input_ids") + data_set = data_set.map(operations=type_cast_op, input_columns="unique_ids") + # apply batch operations + data_set = data_set.batch(batch_size, drop_remainder=True) + return data_set + +def create_eval_dataset(batchsize=32, device_num=1, rank=0, data_dir=None, schema_dir=None): + """create evaluation dataset""" + data_files = [] + if os.path.isdir(data_dir): + files = os.listdir(data_dir) + for file_name in files: + if "tfrecord" in file_name: + data_files.append(os.path.join(data_dir, file_name)) + else: + data_files.append(data_dir) + data_set = ds.TFRecordDataset(data_files, schema_dir if schema_dir != "" else None, + columns_list=["input_ids", "input_mask", "segment_ids", "next_sentence_labels", + "masked_lm_positions", "masked_lm_ids", "masked_lm_weights"], + shard_equal_rows=True) + ori_dataset_size = data_set.get_dataset_size() + print("origin eval size: ", ori_dataset_size) + dtypes = data_set.output_types() + shapes = data_set.output_shapes() + output_batches = math.ceil(ori_dataset_size / device_num / batchsize) + padded_num = output_batches * device_num * batchsize - ori_dataset_size + print("padded num: ", padded_num) + if padded_num > 0: + item = {"input_ids": np.zeros(shapes[0], dtypes[0]), + "input_mask": np.zeros(shapes[1], dtypes[1]), + "segment_ids": np.zeros(shapes[2], dtypes[2]), + "next_sentence_labels": np.zeros(shapes[3], dtypes[3]), + "masked_lm_positions": np.zeros(shapes[4], dtypes[4]), + "masked_lm_ids": np.zeros(shapes[5], dtypes[5]), + "masked_lm_weights": np.zeros(shapes[6], dtypes[6])} + padded_samples = [item for x in range(padded_num)] + padded_ds = ds.PaddedDataset(padded_samples) + eval_ds = data_set + padded_ds + sampler = ds.DistributedSampler(num_shards=device_num, shard_id=rank, shuffle=False) + eval_ds.use_sampler(sampler) + else: + eval_ds = ds.TFRecordDataset(data_files, schema_dir if schema_dir != "" else None, + columns_list=["input_ids", "input_mask", "segment_ids", "next_sentence_labels", + "masked_lm_positions", "masked_lm_ids", "masked_lm_weights"], + num_shards=device_num, shard_id=rank, shard_equal_rows=True) + + type_cast_op = C.TypeCast(mstype.int32) + eval_ds = eval_ds.map(input_columns="masked_lm_ids", operations=type_cast_op) + eval_ds = eval_ds.map(input_columns="masked_lm_positions", operations=type_cast_op) + eval_ds = eval_ds.map(input_columns="next_sentence_labels", operations=type_cast_op) + eval_ds = eval_ds.map(input_columns="segment_ids", operations=type_cast_op) + eval_ds = eval_ds.map(input_columns="input_mask", operations=type_cast_op) + eval_ds = eval_ds.map(input_columns="input_ids", operations=type_cast_op) + + eval_ds = eval_ds.batch(batchsize, drop_remainder=True) + print("eval data size: {}".format(eval_ds.get_dataset_size())) + print("eval repeat count: {}".format(eval_ds.get_repeat_count())) + return eval_ds diff --git a/nlp/language_model/bert/MindSpore/src/finetune_data_preprocess.py b/nlp/language_model/bert/MindSpore/src/finetune_data_preprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..3f9f682b56b2077fbb2ed8e749a22fb0578b6009 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/finetune_data_preprocess.py @@ -0,0 +1,252 @@ +# Copyright 2020-2022 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. +# ============================================================================ + +""" +sample script of processing CLUE classification dataset using mindspore.dataset.text for fine-tuning bert +""" + +import os +import argparse +import numpy as np +from lxml import etree + +import mindspore.common.dtype as mstype +import mindspore.dataset as ds +import mindspore.dataset.text as text +import mindspore.dataset.transforms as ops +from utils import convert_labels_to_index + + +def process_tnews_clue_dataset(data_dir, label_list, bert_vocab_path, data_usage='train', shuffle_dataset=False, + max_seq_len=128, batch_size=64, drop_remainder=True): + """Process TNEWS dataset""" + ### Loading TNEWS from CLUEDataset + assert data_usage in ['train', 'eval', 'test'] + if data_usage == 'train': + dataset = ds.CLUEDataset(os.path.join(data_dir, "train.json"), task='TNEWS', + usage=data_usage, shuffle=shuffle_dataset) + elif data_usage == 'eval': + dataset = ds.CLUEDataset(os.path.join(data_dir, "dev.json"), task='TNEWS', + usage=data_usage, shuffle=shuffle_dataset) + else: + dataset = ds.CLUEDataset(os.path.join(data_dir, "test.json"), task='TNEWS', + usage=data_usage, shuffle=shuffle_dataset) + ### Processing label + if data_usage == 'test': + dataset = dataset.map(operations=ops.Duplicate(), input_columns=["id"], output_columns=["id", "label_id"], + column_order=["id", "label_id", "sentence"]) + dataset = dataset.map(operations=ops.Fill(0), input_columns=["label_id"]) + else: + label_vocab = text.Vocab.from_list(label_list) + label_lookup = text.Lookup(label_vocab) + dataset = dataset.map(operations=label_lookup, input_columns="label_desc", output_columns="label_id") + ### Processing sentence + vocab = text.Vocab.from_file(bert_vocab_path) + tokenizer = text.BertTokenizer(vocab, lower_case=True) + lookup = text.Lookup(vocab, unknown_token='[UNK]') + dataset = dataset.map(operations=tokenizer, input_columns=["sentence"]) + dataset = dataset.map(operations=ops.Slice(slice(0, max_seq_len)), input_columns=["sentence"]) + dataset = dataset.map(operations=ops.Concatenate(prepend=np.array(["[CLS]"], dtype='S'), + append=np.array(["[SEP]"], dtype='S')), input_columns=["sentence"]) + dataset = dataset.map(operations=lookup, input_columns=["sentence"], output_columns=["text_ids"]) + dataset = dataset.map(operations=ops.PadEnd([max_seq_len], 0), input_columns=["text_ids"]) + dataset = dataset.map(operations=ops.Duplicate(), input_columns=["text_ids"], + output_columns=["text_ids", "mask_ids"], + column_order=["text_ids", "mask_ids", "label_id"]) + dataset = dataset.map(operations=ops.Mask(ops.Relational.NE, 0, mstype.int32), input_columns=["mask_ids"]) + dataset = dataset.map(operations=ops.Duplicate(), input_columns=["text_ids"], + output_columns=["text_ids", "segment_ids"], + column_order=["text_ids", "mask_ids", "segment_ids", "label_id"]) + dataset = dataset.map(operations=ops.Fill(0), input_columns=["segment_ids"]) + dataset = dataset.batch(batch_size, drop_remainder=drop_remainder) + return dataset + + +def process_cmnli_clue_dataset(data_dir, label_list, bert_vocab_path, data_usage='train', shuffle_dataset=False, + max_seq_len=128, batch_size=64, drop_remainder=True): + """Process CMNLI dataset""" + ### Loading CMNLI from CLUEDataset + assert data_usage in ['train', 'eval', 'test'] + if data_usage == 'train': + dataset = ds.CLUEDataset(os.path.join(data_dir, "train.json"), task='CMNLI', + usage=data_usage, shuffle=shuffle_dataset) + elif data_usage == 'eval': + dataset = ds.CLUEDataset(os.path.join(data_dir, "dev.json"), task='CMNLI', + usage=data_usage, shuffle=shuffle_dataset) + else: + dataset = ds.CLUEDataset(os.path.join(data_dir, "test.json"), task='CMNLI', + usage=data_usage, shuffle=shuffle_dataset) + ### Processing label + if data_usage == 'test': + dataset = dataset.map(operations=ops.Duplicate(), input_columns=["id"], output_columns=["id", "label_id"], + column_order=["id", "label_id", "sentence1", "sentence2"]) + dataset = dataset.map(operations=ops.Fill(0), input_columns=["label_id"]) + else: + label_vocab = text.Vocab.from_list(label_list) + label_lookup = text.Lookup(label_vocab) + dataset = dataset.map(operations=label_lookup, input_columns="label", output_columns="label_id") + ### Processing sentence pairs + vocab = text.Vocab.from_file(bert_vocab_path) + tokenizer = text.BertTokenizer(vocab, lower_case=True) + lookup = text.Lookup(vocab, unknown_token='[UNK]') + ### Tokenizing sentences and truncate sequence pair + dataset = dataset.map(operations=tokenizer, input_columns=["sentence1"]) + dataset = dataset.map(operations=tokenizer, input_columns=["sentence2"]) + dataset = dataset.map(operations=text.TruncateSequencePair(max_seq_len - 3), + input_columns=["sentence1", "sentence2"]) + ### Adding special tokens + dataset = dataset.map(operations=ops.Concatenate(prepend=np.array(["[CLS]"], dtype='S'), + append=np.array(["[SEP]"], dtype='S')), + input_columns=["sentence1"]) + dataset = dataset.map(operations=ops.Concatenate(append=np.array(["[SEP]"], dtype='S')), + input_columns=["sentence2"]) + ### Generating segment_ids + dataset = dataset.map(operations=ops.Duplicate(), input_columns=["sentence1"], + output_columns=["sentence1", "type_sentence1"], + column_order=["sentence1", "type_sentence1", "sentence2", "label_id"]) + dataset = dataset.map(operations=ops.Duplicate(), + input_columns=["sentence2"], output_columns=["sentence2", "type_sentence2"], + column_order=["sentence1", "type_sentence1", "sentence2", "type_sentence2", "label_id"]) + dataset = dataset.map(operations=[lookup, ops.Fill(0)], input_columns=["type_sentence1"]) + dataset = dataset.map(operations=[lookup, ops.Fill(1)], input_columns=["type_sentence2"]) + dataset = dataset.map(operations=ops.Concatenate(), + input_columns=["type_sentence1", "type_sentence2"], output_columns=["segment_ids"], + column_order=["sentence1", "sentence2", "segment_ids", "label_id"]) + dataset = dataset.map(operations=ops.PadEnd([max_seq_len], 0), input_columns=["segment_ids"]) + ### Generating text_ids + dataset = dataset.map(operations=ops.Concatenate(), + input_columns=["sentence1", "sentence2"], output_columns=["text_ids"], + column_order=["text_ids", "segment_ids", "label_id"]) + dataset = dataset.map(operations=lookup, input_columns=["text_ids"]) + dataset = dataset.map(operations=ops.PadEnd([max_seq_len], 0), input_columns=["text_ids"]) + ### Generating mask_ids + dataset = dataset.map(operations=ops.Duplicate(), input_columns=["text_ids"], + output_columns=["text_ids", "mask_ids"], + column_order=["text_ids", "mask_ids", "segment_ids", "label_id"]) + dataset = dataset.map(operations=ops.Mask(ops.Relational.NE, 0, mstype.int32), input_columns=["mask_ids"]) + dataset = dataset.batch(batch_size, drop_remainder=drop_remainder) + return dataset + + +def process_msra(data_file, class_filter=None, split_begin=None, split_end=None): + """ + Data pre-process for MSRA dataset + Args: + data_file (path): The original dataset file path. + class_filter (list of str): Only tags within the class_filter will be counted unless the list is None. + split_begin (float): Only data after split_begin part will be counted. Used for split dataset + into training and evaluation subsets if needed. + split_end (float): Only data before split_end part will be counted. Used for split dataset + into training and evaluation subsets if needed. + """ + tree = etree.parse(data_file) + root = tree.getroot() + print("original dataset length: ", len(root)) + dataset_size = len(root) + beg = 0 if split_begin is None or not 0 <= split_begin <= 1.0 else int(dataset_size * split_begin) + end = dataset_size if split_end is None or not 0 <= split_end <= 1.0 else int(dataset_size * split_end) + print("preporcessed dataset_size: ", end - beg) + for i in range(beg, end): + sentence = root[i] + tags = [] + content = "" + for phrases in sentence: + labeled_words = [word for word in phrases] + if labeled_words: + for words in phrases: + name = words.tag + label = words.get("TYPE") + words = words.text + if not words: + continue + content += words + if class_filter and name not in class_filter: + tags += ["O" for _ in words] + else: + length = len(words) + labels = ["S_"] if length == 1 else ["B_"] + ["M_" for i in range(length - 2)] + ["E_"] + tags += [ele + label for ele in labels] + else: + phrases = phrases.text + if phrases: + content += phrases + tags += ["O" for ele in phrases] + if len(content) != len(tags): + raise ValueError("Mismathc length of content: ", len(content), " and label: ", len(tags)) + yield (np.array("".join(content)), np.array(list(tags))) + + +def process_ner_msra_dataset(data_dir, label_list, bert_vocab_path, max_seq_len=128, class_filter=None, + split_begin=None, split_end=None): + """Process MSRA dataset""" + ### Loading MSRA from CLUEDataset + dataset = ds.GeneratorDataset(process_msra(data_dir, class_filter, split_begin, split_end), + column_names=['text', 'label']) + + ### Processing label + label_vocab = text.Vocab.from_list(label_list) + label_lookup = text.Lookup(label_vocab) + dataset = dataset.map(operations=label_lookup, input_columns="label", output_columns="label_ids") + dataset = dataset.map(operations=ops.Concatenate(prepend=np.array([0], dtype='i')), + input_columns=["label_ids"]) + dataset = dataset.map(operations=ops.Slice(slice(0, max_seq_len)), input_columns=["label_ids"]) + dataset = dataset.map(operations=ops.PadEnd([max_seq_len], 0), input_columns=["label_ids"]) + ### Processing sentence + vocab = text.Vocab.from_file(bert_vocab_path) + lookup = text.Lookup(vocab, unknown_token='[UNK]') + unicode_char_tokenizer = text.UnicodeCharTokenizer() + dataset = dataset.map(operations=unicode_char_tokenizer, input_columns=["text"], output_columns=["sentence"]) + dataset = dataset.map(operations=ops.Slice(slice(0, max_seq_len-2)), input_columns=["sentence"]) + dataset = dataset.map(operations=ops.Concatenate(prepend=np.array(["[CLS]"], dtype='S'), + append=np.array(["[SEP]"], dtype='S')), input_columns=["sentence"]) + dataset = dataset.map(operations=lookup, input_columns=["sentence"], output_columns=["input_ids"]) + dataset = dataset.map(operations=ops.PadEnd([max_seq_len], 0), input_columns=["input_ids"]) + dataset = dataset.map(operations=ops.Duplicate(), input_columns=["input_ids"], + output_columns=["input_ids", "input_mask"], + column_order=["input_ids", "input_mask", "label_ids"]) + dataset = dataset.map(operations=ops.Mask(ops.Relational.NE, 0, mstype.int32), input_columns=["input_mask"]) + dataset = dataset.map(operations=ops.Duplicate(), input_columns=["input_ids"], + output_columns=["input_ids", "segment_ids"], + column_order=["input_ids", "input_mask", "segment_ids", "label_ids"]) + dataset = dataset.map(operations=ops.Fill(0), input_columns=["segment_ids"]) + return dataset + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="create mindrecord") + parser.add_argument("--data_dir", type=str, default="", help="dataset path") + parser.add_argument("--vocab_file", type=str, default="", help="Vocab file path") + parser.add_argument("--save_path", type=str, default="./my.mindrecord", help="Path to save mindrecord") + parser.add_argument("--label2id", type=str, default="", + help="Label2id file path, please keep in mind that each label name should be consistent with" + "the type name labeled in the oroginal dataset file") + parser.add_argument("--max_seq_len", type=int, default=128, help="Sequence length") + parser.add_argument("--class_filter", nargs='*', help="Specified classes will be counted, if empty all in counted") + parser.add_argument("--split_begin", type=float, default=None, help="Specified subsets of data will be counted," + "if not None, the data will counted begin from split_begin") + parser.add_argument("--split_end", type=float, default=None, help="Specified subsets of data will be counted," + "if not None, the data before split_end will be counted ") + + args_opt = parser.parse_args() + if args_opt.label2id == "": + raise ValueError("label2id should not be empty") + labels_list = [] + with open(args_opt.label2id) as f: + for tag in f: + labels_list.append(tag.strip()) + tag_to_index = list(convert_labels_to_index(labels_list).keys()) + ds = process_ner_msra_dataset(args_opt.data_dir, tag_to_index, args_opt.vocab_file, args_opt.max_seq_len, + args_opt.class_filter, args_opt.split_begin, args_opt.split_end) + ds.save(args_opt.save_path) diff --git a/nlp/language_model/bert/MindSpore/src/finetune_eval_model.py b/nlp/language_model/bert/MindSpore/src/finetune_eval_model.py new file mode 100644 index 0000000000000000000000000000000000000000..1dd21ae760fd38102fc85a5c4cadb9b694ed56a5 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/finetune_eval_model.py @@ -0,0 +1,151 @@ +# Copyright 2020 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. +# ============================================================================ + +''' +Bert finetune and evaluation model script. +''' +import mindspore.nn as nn +from mindspore.common.initializer import TruncatedNormal +from mindspore.ops import operations as P +from mindspore import context +from .bert_model import BertModel + + +class BertCLSModel(nn.Cell): + """ + This class is responsible for classification task evaluation, i.e. XNLI(num_labels=3), + LCQMC(num_labels=2), Chnsenti(num_labels=2). The returned output represents the final + logits as the results of log_softmax is proportional to that of softmax. + """ + + def __init__(self, config, is_training, num_labels=2, dropout_prob=0.0, use_one_hot_embeddings=False, + assessment_method=""): + super(BertCLSModel, self).__init__() + if not is_training: + config.hidden_dropout_prob = 0.0 + config.hidden_probs_dropout_prob = 0.0 + self.bert = BertModel(config, is_training, use_one_hot_embeddings) + self.cast = P.Cast() + self.weight_init = TruncatedNormal(config.initializer_range) + self.log_softmax = P.LogSoftmax(axis=-1) + self.dtype = config.dtype + self.num_labels = num_labels + self.dense_1 = nn.Dense(config.hidden_size, self.num_labels, weight_init=self.weight_init, + has_bias=True).to_float(config.compute_type) + self.dropout = nn.Dropout(1 - dropout_prob) + self.assessment_method = assessment_method + + def construct(self, input_ids, input_mask, token_type_id): + _, pooled_output, _ = self.bert(input_ids, token_type_id, input_mask) + cls = self.cast(pooled_output, self.dtype) + cls = self.dropout(cls) + logits = self.dense_1(cls) + logits = self.cast(logits, self.dtype) + if self.assessment_method != "spearman_correlation": + logits = self.log_softmax(logits) + return logits + + +class BertSquadModel(nn.Cell): + ''' + This class is responsible for SQuAD + ''' + + def __init__(self, config, is_training, num_labels=2, dropout_prob=0.0, use_one_hot_embeddings=False): + super(BertSquadModel, self).__init__() + if not is_training: + config.hidden_dropout_prob = 0.0 + config.hidden_probs_dropout_prob = 0.0 + self.bert = BertModel(config, is_training, use_one_hot_embeddings) + self.weight_init = TruncatedNormal(config.initializer_range) + self.dense1 = nn.Dense(config.hidden_size, num_labels, weight_init=self.weight_init, + has_bias=True).to_float(config.compute_type) + self.num_labels = num_labels + self.dtype = config.dtype + self.log_softmax = P.LogSoftmax(axis=1) + self.is_training = is_training + self.gpu_target = context.get_context("device_target") == "GPU" + self.cast = P.Cast() + self.reshape = P.Reshape() + self.transpose = P.Transpose() + self.shape = (-1, config.hidden_size) + self.origin_shape = (-1, config.seq_length, self.num_labels) + self.transpose_shape = (-1, self.num_labels, config.seq_length) + + def construct(self, input_ids, input_mask, token_type_id): + """Return the final logits as the results of log_softmax.""" + sequence_output, _, _ = self.bert(input_ids, token_type_id, input_mask) + sequence = self.reshape(sequence_output, self.shape) + logits = self.dense1(sequence) + logits = self.cast(logits, self.dtype) + logits = self.reshape(logits, self.origin_shape) + if self.gpu_target: + logits = self.transpose(logits, (0, 2, 1)) + logits = self.log_softmax(self.reshape(logits, (-1, self.transpose_shape[-1]))) + logits = self.transpose(self.reshape(logits, self.transpose_shape), (0, 2, 1)) + else: + logits = self.log_softmax(logits) + return logits + + +class BertNERModel(nn.Cell): + """ + This class is responsible for sequence labeling task evaluation, i.e. NER(num_labels=11). + The returned output represents the final logits as the results of log_softmax is proportional to that of softmax. + """ + + def __init__(self, config, is_training, num_labels=11, use_crf=False, with_lstm=False, + dropout_prob=0.0, use_one_hot_embeddings=False): + super(BertNERModel, self).__init__() + if not is_training: + config.hidden_dropout_prob = 0.0 + config.hidden_probs_dropout_prob = 0.0 + self.bert = BertModel(config, is_training, use_one_hot_embeddings) + self.cast = P.Cast() + self.weight_init = TruncatedNormal(config.initializer_range) + self.log_softmax = P.LogSoftmax(axis=-1) + self.dtype = config.dtype + self.num_labels = num_labels + self.dense_1 = nn.Dense(config.hidden_size, self.num_labels, weight_init=self.weight_init, + has_bias=True).to_float(config.compute_type) + if with_lstm: + self.lstm_hidden_size = config.hidden_size // 2 + self.lstm = nn.LSTM(config.hidden_size, self.lstm_hidden_size, batch_first=True, bidirectional=True) + self.dropout = nn.Dropout(1 - dropout_prob) + self.reshape = P.Reshape() + self.shape = (-1, config.hidden_size) + self.use_crf = use_crf + self.with_lstm = with_lstm + self.origin_shape = (-1, config.seq_length, self.num_labels) + + def construct(self, input_ids, input_mask, token_type_id): + """Return the final logits as the results of log_softmax.""" + sequence_output, _, _ = self.bert(input_ids, token_type_id, input_mask) + seq = self.dropout(sequence_output) + if self.with_lstm: + batch_size = input_ids.shape[0] + data_type = self.dtype + hidden_size = self.lstm_hidden_size + h0 = P.Zeros()((2, batch_size, hidden_size), data_type) + c0 = P.Zeros()((2, batch_size, hidden_size), data_type) + seq, _ = self.lstm(seq, (h0, c0)) + seq = self.reshape(seq, self.shape) + logits = self.dense_1(seq) + logits = self.cast(logits, self.dtype) + if self.use_crf: + return_value = self.reshape(logits, self.origin_shape) + else: + return_value = self.log_softmax(logits) + return return_value diff --git a/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_chinesener_mindrecord.py b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_chinesener_mindrecord.py new file mode 100644 index 0000000000000000000000000000000000000000..f74db8f9bc3eadfffcf95a0c846ef968e964ecdd --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_chinesener_mindrecord.py @@ -0,0 +1,296 @@ +# Copyright 2022 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 codecs +import pickle +import numpy as np +from mindspore.mindrecord import FileWriter +import tokenization + +__all__ = ['NerProcessor', 'write_tokens', 'convert_single_example', 'filed_based_convert_examples_to_features'] + + +class InputExample(): + """A single training/test example for simple sequence classification.""" + def __init__(self, guid=None, text=None, label=None): + """Constructs a InputExample. + Args: + guid: Unique id for the example. + text_a: string. The untokenized text of the first sequence. For single + sequence tasks, only this sequence must be specified. + label: (Optional) string. The label of the example. This should be + specified for train and dev examples, but not for test examples. + """ + self.guid = guid + self.text = text + self.label = label + + +class InputFeatures(): + """A single set of features of data.""" + def __init__(self, input_ids, input_mask, segment_ids, label_ids,): + self.input_ids = input_ids + self.input_mask = input_mask + self.segment_ids = segment_ids + self.label_ids = label_ids + + +class NerProcessor(): + def __init__(self, output_dir): + self.labels = set() + self.output_dir = output_dir + + def get_train_examples(self, data_dir): + return self._create_example( + self._read_data(os.path.join(data_dir, "example.train")), "train" + ) + + def get_dev_examples(self, data_dir): + return self._create_example( + self._read_data(os.path.join(data_dir, "example.dev")), "dev" + ) + + def get_test_examples(self, data_dir): + return self._create_example( + self._read_data(os.path.join(data_dir, "example.test")), "test") + + def get_labels(self, labels=None): + if labels is not None: + if os.path.exists(labels) and os.path.isfile(labels): + with codecs.open(labels, 'r', encoding='utf-8') as fd: + for line in fd: + self.labels.append(line.strip()) + else: + self.labels = labels.split(',') + self.labels = set(self.labels) + if os.path.exists(os.path.join(self.output_dir, 'label_list.pkl')): + with codecs.open(os.path.join(self.output_dir, 'label_list.pkl'), 'rb') as rf: + self.labels = pickle.load(rf) + else: + if self.labels: + self.labels = self.labels.union(set(["X", "[CLS]", "[SEP]"])) + with open(os.path.join(self.output_dir, 'label_list.txt'), 'w') as rf: + for label in self.labels: + rf.write(label + "\n") + else: + self.labels = ["O", 'B-TIM', 'I-TIM', "B-PER", "I-PER", "B-ORG", "I-ORG", "B-LOC", "I-LOC", "X", + "[CLS]", "[SEP]"] + return self.labels + + def _create_example(self, lines, set_type): + examples = [] + for (i, line) in enumerate(lines): + guid = "%s-%s" % (set_type, i) + text = tokenization.convert_to_unicode(line[1]) + label = tokenization.convert_to_unicode(line[0]) + examples.append(InputExample(guid=guid, text=text, label=label)) + return examples + + def _read_data(self, input_file): + """Reads a BIO data.""" + with codecs.open(input_file, 'r', encoding='utf-8') as f: + lines = [] + words = [] + labels = [] + for line in f: + contends = line.strip() + tokens = contends.split(' ') + if len(tokens) == 2: + words.append(tokens[0]) + labels.append(tokens[-1]) + else: + if not contends and words: + label = [] + word = [] + for l, w in zip(labels, words): + if l and w: + label.append(l) + self.labels.add(l) + word.append(w) + lines.append([' '.join(label), ' '.join(word)]) + words = [] + labels = [] + continue + if contends.startswith("-DOCSTART-"): + continue + return lines + + +def write_tokens(tokens, output_dir, mode): + """ + write token result to output txt + """ + if mode == "test": + path = os.path.join(output_dir, "token_" + mode + ".txt") + wf = codecs.open(path, 'a', encoding='utf-8') + for token in tokens: + if token != "**NULL**": + wf.write(token + '\n') + wf.close() + + +def convert_single_example(ex_index, example, label_list, max_seq_length, tokenizer, output_dir, mode): + """ + convert example to id single by single + """ + label_map = {} + for (i, label) in enumerate(label_list, 0): + label_map[label] = i + + textlist = example.text.split(' ') + labellist = example.label.split(' ') + tokens = [] + labels = [] + for i, word in enumerate(textlist): + token = tokenizer.tokenize(word) + tokens.extend(token) + label_1 = labellist[i] + for m in range(len(token)): + if m == 0: + labels.append(label_1) + else: + labels.append("X") + if len(tokens) >= max_seq_length - 1: + tokens = tokens[0:(max_seq_length - 2)] + labels = labels[0:(max_seq_length - 2)] + ntokens = [] + segment_ids = [] + label_ids = [] + ntokens.append("[CLS]") # add [CLS] begin of token + segment_ids.append(0) + label_ids.append(label_map["[CLS]"]) + for i, token in enumerate(tokens): + ntokens.append(token) + segment_ids.append(0) + label_ids.append(label_map[labels[i]]) + ntokens.append("[SEP]") # add [SEP] end of token + segment_ids.append(0) + label_ids.append(label_map["[SEP]"]) + input_ids = tokenization.convert_tokens_to_ids(args_opt.vocab_file, ntokens) # convert ntokens to ID format + input_mask = [1] * len(input_ids) + # padding + while len(input_ids) < max_seq_length: + input_ids.append(0) + input_mask.append(0) + segment_ids.append(0) + label_ids.append(0) + ntokens.append("**NULL**") + assert len(input_ids) == max_seq_length + assert len(input_mask) == max_seq_length + assert len(segment_ids) == max_seq_length + assert len(label_ids) == max_seq_length + + feature = InputFeatures( + input_ids=input_ids, + input_mask=input_mask, + segment_ids=segment_ids, + label_ids=label_ids, + ) + + write_tokens(ntokens, output_dir, mode) + return feature + + +def filed_based_convert_examples_to_features( + examples, label_list, max_seq_length, tokenizer, output_file, output_dir, mode=None): + """ + convert examples to mindrecord format + """ + schema = { + "input_ids": {"type": "int32", "shape": [-1]}, + "input_mask": {"type": "int32", "shape": [-1]}, + "segment_ids": {"type": "int32", "shape": [-1]}, + "label_ids": {"type": "int32", "shape": [-1]}, + } + writer = FileWriter(output_file, overwrite=True) + writer.add_schema(schema) + total_written = 0 + for (ex_index, example) in enumerate(examples): + all_data = [] + feature = convert_single_example(ex_index, example, label_list, max_seq_length, tokenizer, output_dir, mode) + input_ids = np.array(feature.input_ids, dtype=np.int32) + input_mask = np.array(feature.input_mask, dtype=np.int32) + segment_ids = np.array(feature.segment_ids, dtype=np.int32) + label_ids = np.array(feature.label_ids, dtype=np.int32) + data = {'input_ids': input_ids, + "input_mask": input_mask, + "segment_ids": segment_ids, + "label_ids": label_ids} + all_data.append(data) + if all_data: + writer.write_raw_data(all_data) + total_written += 1 + writer.commit() + print("Total instances is: ", total_written, flush=True) + +def main(args): + # check output dir exists + if not os.path.exists(args.output_dir): + os.makedirs(args.output_dir, exist_ok=True) + + processor = NerProcessor(args.output_dir) + + tokenizer = tokenization.FullTokenizer( + vocab_file=args.vocab_file, do_lower_case=args.do_lower_case) + + train_examples = None + eval_examples = None + + if args.do_train and args.do_eval: + train_examples = processor.get_train_examples(args.data_dir) + + print("***** Running training *****") + print(" Num examples is: ", len(train_examples), flush=True) + + eval_examples = processor.get_dev_examples(args.data_dir) + print("***** Running evaluation *****") + print(" Num examples is: ", len(eval_examples), flush=True) + + test_examples = processor.get_test_examples(args.data_dir) + print("***** Running test *****") + print(" Num examples is: ", len(test_examples), flush=True) + + label_list = processor.get_labels() + + if args.do_train and args.do_eval: + train_file = os.path.join(args.output_dir, "train.mind_record") + if not os.path.exists(train_file): + filed_based_convert_examples_to_features( + train_examples, label_list, args.max_seq_length, tokenizer, train_file, args.output_dir) + + eval_file = os.path.join(args.output_dir, "eval.mind_record") + if not os.path.exists(eval_file): + filed_based_convert_examples_to_features( + eval_examples, label_list, args.max_seq_length, tokenizer, eval_file, args.output_dir) + + test_file = os.path.join(args.output_dir, "test.mind_record") + if not os.path.exists(test_file): + filed_based_convert_examples_to_features( + test_examples, label_list, args.max_seq_length, tokenizer, test_file, args.output_dir) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Make dataset in mindrecord format.') + parser.add_argument('--data_dir', default=".", type=str, help='') + parser.add_argument('--max_seq_length', default=202, type=int, help='') + parser.add_argument('--do_train', default=True, type=bool, help='') + parser.add_argument('--do_eval', default=True, type=bool, help='') + parser.add_argument('--do_lower_case', default=True, type=bool, help='') + parser.add_argument('--vocab_file', default="./vocab.txt", type=str, help='') + parser.add_argument('--output_dir', default="./outputs", type=str, help='') + args_opt = parser.parse_args() + main(args_opt) diff --git a/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_cluener_mindrecord.py b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_cluener_mindrecord.py new file mode 100644 index 0000000000000000000000000000000000000000..f5e9dffcc433ee922ec1dbe71919309ce2cd6f12 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_cluener_mindrecord.py @@ -0,0 +1,370 @@ +# Copyright 2022 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. +# ============================================================================ +"""Generate MindRecord for BERT finetuning runner: CLUENER.""" +import os +import csv +import json +from argparse import ArgumentParser +import numpy as np +import tokenization +from mindspore.mindrecord import FileWriter + +def parse_args(): + parser = ArgumentParser(description="Generate MindRecord for bert task: CLUENER") + parser.add_argument("--data_dir", type=str, default="", + help="The input data dir. Should contain the .tsv files (or other data files) for the task.") + parser.add_argument("--task_name", type=str, default="ner", help="The name of the task to train.") + parser.add_argument("--vocab_file", type=str, default="", + help="The vocabulary file that the BERT model was trained on.") + parser.add_argument("--output_dir", type=str, default="", + help="The output directory where the mindrecord will be written.") + parser.add_argument("--do_lower_case", type=bool, default=True, help="Whether to lower case the input text. " + "Should be True for uncased models" + "and False for cased models.") + parser.add_argument("--max_seq_length", type=int, default=128, help="Maximum sequence length.") + parser.add_argument("--do_train", type=bool, default=True, help="Whether to run training.") + parser.add_argument("--do_eval", type=bool, default=True, help="Whether to run eval on the dev set.") + parser.add_argument("--do_predict", type=bool, default=True, + help="Whether to run the model in inference mode on the test set.") + args_opt = parser.parse_args() + return args_opt + + +class InputExample(): + """A single training/test example for simple sequence classification.""" + + def __init__(self, guid, text_a, text_b=None, label=None): + """Constructs a InputExample. + + Args: + guid: Unique id for the example. + text_a: string. The untokenized text of the first sequence. For single + sequence tasks, only this sequence must be specified. + text_b: (Optional) string. The untokenized text of the second sequence. + Only must be specified for sequence pair tasks. + label: (Optional) string. The label of the example. This should be + specified for train and dev examples, but not for test examples. + """ + self.guid = guid + self.text_a = text_a + self.text_b = text_b + self.label = label + + +class PaddingInputExample(): + """Fake example so the num input examples is a multiple of the batch size. + + When running eval/predict on the TPU, we need to pad the number of examples + to be a multiple of the batch size, because the TPU requires a fixed batch + size. The alternative is to drop the last batch, which is bad because it means + the entire output data won't be generated. + + We use this class instead of `None` because treating `None` as padding + battches could cause silent errors. + """ + + +class InputFeatures(): + """A single set of features of data.""" + + def __init__(self, + input_ids, + input_mask, + segment_ids, + label_id): + self.input_ids = input_ids + self.input_mask = input_mask + self.segment_ids = segment_ids + self.label_id = label_id + + +class DataProcessor(): + """Base class for data converters for sequence classification data sets.""" + + def get_train_examples(self, data_dir): + """Gets a collection of `InputExample`s for the train set.""" + raise NotImplementedError() + + def get_dev_examples(self, data_dir): + """Gets a collection of `InputExample`s for the dev set.""" + raise NotImplementedError() + + def get_test_examples(self, data_dir): + """Gets a collection of `InputExample`s for prediction.""" + raise NotImplementedError() + + def get_labels(self): + """Gets the list of labels for this data set.""" + raise NotImplementedError() + + @classmethod + def _read_tsv(cls, input_file, quotechar=None): + """Reads a tab separated value file.""" + with open(input_file, "r") as f: + reader = csv.reader(f, delimiter="\t", quotechar=quotechar) + lines = [] + for line in reader: + lines.append(line) + return lines + + @classmethod + def _read_json(cls, input_file, quotechar=None): + """Reads a tab separated value file.""" + with open(input_file, "r") as f: + lines = [] + for line in f.readlines(): + lines.append(json.loads(line.strip())) + return lines + + +class NerProcessor(DataProcessor): + """Processor for the CLUENER data set.""" + + def get_train_examples(self, data_dir): + """See base class.""" + return self._create_examples( + self._read_json(os.path.join(data_dir, "train.json"))) + + def get_dev_examples(self, data_dir): + """See base class.""" + return self._create_examples( + self._read_json(os.path.join(data_dir, "dev.json"))) + + def get_test_examples(self, data_dir): + """See base class.""" + return self._create_examples( + self._read_json(os.path.join(data_dir, "test.json"))) + + def get_labels(self): + clue_labels = ['address', 'book', 'company', 'game', 'government', 'movie', + 'name', 'organization', 'position', 'scene'] + with open(os.path.join(args.output_dir, 'label.txt'), 'w') as rf: + for label in clue_labels: + rf.write(label + "\n") + return ['O'] + [p + '-' + l for p in ['B', 'M', 'E', 'S'] for l in clue_labels] + + def generate_label(self, line, label): + """generate label""" + for l, words in line['label'].items(): + for _, indices in words.items(): + for index in indices: + if index[0] == index[1]: + label[index[0]] = 'S-' + l + else: + label[index[0]] = 'B-' + l + label[index[1]] = 'E-' + l + for j in range(index[0] + 1, index[1]): + label[j] = 'M-' + l + return label + + def _create_examples(self, lines): + """See base class.""" + examples = [] + for (i, line) in enumerate(lines): + guid = "%s" % (i) if 'id' not in line else line['id'] + text_a = tokenization.convert_to_unicode(line['text']) + label = ['O'] * len(text_a) + if 'label' in line: + label = self.generate_label(line, label) + examples.append(InputExample(guid=guid, text_a=text_a, label=label)) + return examples + +def convert_single_example(ex_index, example, label_list, max_seq_length, tokenizer): + """Converts a single `InputExample` into a single `InputFeatures`.""" + + if isinstance(example, PaddingInputExample): + return InputFeatures( + input_ids=[0] * max_seq_length, + input_mask=[0] * max_seq_length, + segment_ids=[0] * max_seq_length, + label_id=[0] * max_seq_length) + + label_map = {} + for (i, label) in enumerate(label_list): + label_map[label] = i + + tokens_a = example.text_a + + # Account for [CLS] and [SEP] with "- 2" + if len(tokens_a) > max_seq_length - 2: + tokens_a = tokens_a[0:(max_seq_length - 2)] + + # The convention in ALBERT is: + # (a) For sequence pairs: + # tokens: [CLS] is this jack ##son ##ville ? [SEP] no it is not . [SEP] + # type_ids: 0 0 0 0 0 0 0 0 1 1 1 1 1 1 + # (b) For single sequences: + # tokens: [CLS] the dog is hairy . [SEP] + # type_ids: 0 0 0 0 0 0 0 + # + # Where "type_ids" are used to indicate whether this is the first + # sequence or the second sequence. The embedding vectors for `type=0` and + # `type=1` were learned during pre-training and are added to the wordpiece + # embedding vector (and position vector). This is not *strictly* necessary + # since the [SEP] token unambiguously separates the sequences, but it makes + # it easier for the model to learn the concept of sequences. + # + # For classification tasks, the first vector (corresponding to [CLS]) is + # used as the "sentence vector". Note that this only makes sense because + # the entire model is fine-tuned. + + tokens = [] + segment_ids = [] + label_ids = [] + + tokens.append("[CLS]") + segment_ids.append(0) + label_ids.append(0) + for i, token in enumerate(tokens_a): + tokens.append(token) + segment_ids.append(0) + label_ids.append(label_map[example.label[i]]) + tokens.append("[SEP]") + segment_ids.append(0) + label_ids.append(0) + + input_ids = tokenization.convert_tokens_to_ids(args.vocab_file, tokens) + + # The mask has 1 for real tokens and 0 for padding tokens. Only real + # tokens are attended to. + input_mask = [1] * len(input_ids) + + # Zero-pad up to the sequence length. + while len(input_ids) < max_seq_length: + input_ids.append(0) + input_mask.append(0) + segment_ids.append(0) + label_ids.append(0) + + assert len(input_ids) == max_seq_length + assert len(input_mask) == max_seq_length + assert len(segment_ids) == max_seq_length + assert len(label_ids) == max_seq_length + + if ex_index < 1: + print("*** Example ***") + print("guid: %s" % (example.guid)) + print("tokens: %s" % " ".join( + [tokenization.printable_text(x) for x in tokens])) + print("input_ids: %s" % " ".join([str(x) for x in input_ids])) + print("input_mask: %s" % " ".join([str(x) for x in input_mask])) + print("segment_ids: %s" % " ".join([str(x) for x in segment_ids])) + print("label: %s (id = %s)" % (example.label, label_ids)) + + feature = InputFeatures( + input_ids=input_ids, + input_mask=input_mask, + segment_ids=segment_ids, + label_id=label_ids) + return feature + + +def file_based_convert_examples_to_features( + examples, label_list, max_seq_length, tokenizer, output_file): + """Convert a set of `InputExample`s to a MINDRecord file.""" + + schema = { + "input_ids": {"type": "int32", "shape": [-1]}, + "input_mask": {"type": "int32", "shape": [-1]}, + "segment_ids": {"type": "int32", "shape": [-1]}, + "label_ids": {"type": "int32", "shape": [-1]}, + } + writer = FileWriter(output_file, overwrite=True) + writer.add_schema(schema) + total_written = 0 + + for (ex_index, example) in enumerate(examples): + all_data = [] + feature = convert_single_example(ex_index, example, label_list, + max_seq_length, tokenizer) + + input_ids = np.array(feature.input_ids, dtype=np.int32) + input_mask = np.array(feature.input_mask, dtype=np.int32) + segment_ids = np.array(feature.segment_ids, dtype=np.int32) + label_ids = np.array(feature.label_id, dtype=np.int32) + data = {'input_ids': input_ids, + "input_mask": input_mask, + "segment_ids": segment_ids, + "label_ids": label_ids} + all_data.append(data) + if all_data: + writer.write_raw_data(all_data) + total_written += 1 + writer.commit() + print("Total instances is: ", total_written, flush=True) + + +def main(): + processors = { + "ner": NerProcessor + } + + if not args.do_train and not args.do_eval and not args.do_predict: + raise ValueError( + "At least one of `do_train`, `do_eval` or `do_predict' must be True.") + + if not os.path.exists(args.output_dir): + os.makedirs(args.output_dir, exist_ok=True) + + task_name = args.task_name.lower() + + if task_name not in processors: + raise ValueError("Task not found: %s" % (task_name)) + + processor = processors[task_name]() + + label_list = processor.get_labels() + + tokenizer = tokenization.FullTokenizer( + vocab_file=args.vocab_file, do_lower_case=args.do_lower_case) + + if args.do_train: + train_examples = processor.get_train_examples(args.data_dir) + train_file = os.path.join(args.output_dir, "train.mindrecord") + file_based_convert_examples_to_features( + train_examples, label_list, args.max_seq_length, tokenizer, train_file) + print("***** Running training *****") + print(" Num examples = %d", len(train_examples)) + + # ner task with CLUENER do eval with dev.mindrecord + if args.do_eval: + eval_examples = processor.get_dev_examples(args.data_dir) + num_actual_eval_examples = len(eval_examples) + eval_file = os.path.join(args.output_dir, "dev.mindrecord") + file_based_convert_examples_to_features(eval_examples, label_list, + args.max_seq_length, tokenizer, + eval_file) + print("***** Running prediction*****") + print(" Num examples = %d (%d actual, %d padding)", + len(eval_examples), num_actual_eval_examples, + len(eval_examples) - num_actual_eval_examples) + + if args.do_predict: + predict_examples = processor.get_test_examples(args.data_dir) + num_actual_predict_examples = len(predict_examples) + predict_file = os.path.join(args.output_dir, "predict.mindrecord") + file_based_convert_examples_to_features(predict_examples, label_list, + args.max_seq_length, tokenizer, + predict_file) + + print("***** Running prediction*****") + print(" Num examples = %d (%d actual, %d padding)", + len(predict_examples), num_actual_predict_examples, + len(predict_examples) - num_actual_predict_examples) + + +if __name__ == "__main__": + args = parse_args() + main() diff --git a/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_pretrain_mindrecord.py b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_pretrain_mindrecord.py new file mode 100644 index 0000000000000000000000000000000000000000..354195bcc5b71c145b6b6daac9a5f59c91fcc38e --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_pretrain_mindrecord.py @@ -0,0 +1,412 @@ +# Copyright 2022 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. +# ============================================================================ +"""Generate pretrain MindRecord for BERT.""" + +import collections +import random +from argparse import ArgumentParser +import tokenization +import numpy as np +from mindspore.mindrecord import FileWriter + + +def parse_args(): + parser = ArgumentParser(description="Generate MindRecord for bert") + parser.add_argument("--input_file", type=str, default="", + help="Input raw text file (or comma-separated list of files).") + parser.add_argument("--output_file", type=str, default="", + help="Output MindRecord file (or comma-separated list of files).") + parser.add_argument("--vocab_file", type=str, default="", + help="The vocabulary file that the BERT model was trained on.") + parser.add_argument("--do_lower_case", type=bool, default=True, + help="Whether to lower case the input text. " + "Should be True for uncased models and False for cased models.") + parser.add_argument("--do_whole_word_mask", type=bool, default=False, + help="Whether to use whole word masking rather than per-WordPiece masking.") + parser.add_argument("--max_seq_length", type=int, default=128, help="Maximum sequence length.") + parser.add_argument("--max_predictions_per_seq", type=int, default=20, + help="Maximum number of masked LM predictions per sequence.") + parser.add_argument("--random_seed", type=int, default=12345, help="Random seed for data generation.") + parser.add_argument("--dupe_factor", type=int, default=5, + help="Number of times to duplicate the input data (with different masks).") + parser.add_argument("--masked_lm_prob", type=float, default=0.15, help="Masked LM probability.") + parser.add_argument("--short_seq_prob", type=float, default=0.1, + help="Probability of creating sequences which are shorter than the maximum length.") + args_opt = parser.parse_args() + return args_opt + + +class TrainingInstance(): + """A single training instance (sentence pair).""" + def __init__(self, tokens, segment_ids, masked_lm_positions, masked_lm_labels, + is_random_next): + self.tokens = tokens + self.segment_ids = segment_ids + self.is_random_next = is_random_next + self.masked_lm_positions = masked_lm_positions + self.masked_lm_labels = masked_lm_labels + + def __str__(self): + s = "" + s += "tokens: %s\n" % (" ".join([tokenization.printable_text(x) for x in self.tokens])) + s += "segment_ids: %s\n" % (" ".join([str(x) for x in self.segment_ids])) + s += "is_random_next: %s\n" % self.is_random_next + s += "masked_lm_positions: %s\n" % (" ".join([str(x) for x in self.masked_lm_positions])) + s += "masked_lm_labels: %s\n" % (" ".join([tokenization.printable_text(x) for x in self.masked_lm_labels])) + s += "\n" + return s + + def __repr__(self): + return self.__str__() + + +def write_instance_to_example_files(instances, tokenizer, max_seq_length, + max_predictions_per_seq, output_file): + """Create TF example files from `TrainingInstance`s.""" + schema = { + "input_ids": {"type": "int32", "shape": [-1]}, + "input_mask": {"type": "int32", "shape": [-1]}, + "segment_ids": {"type": "int32", "shape": [-1]}, + "masked_lm_positions": {"type": "int32", "shape": [-1]}, + "masked_lm_ids": {"type": "int32", "shape": [-1]}, + "masked_lm_weights": {"type": "float32", "shape": [-1]}, + "next_sentence_label": {"type": "int32", "shape": [-1]}, + } + writer = FileWriter(output_file, overwrite=True) + writer.add_schema(schema) + + total_written = 0 + for (_, instance) in enumerate(instances): + all_data = [] + input_ids = tokenization.convert_tokens_to_ids(args.vocab_file, instance.tokens) + input_mask = [1] * len(input_ids) + segment_ids = list(instance.segment_ids) + assert len(input_ids) <= max_seq_length + + while len(input_ids) < max_seq_length: + input_ids.append(0) + input_mask.append(0) + segment_ids.append(0) + + assert len(input_ids) == max_seq_length + assert len(input_mask) == max_seq_length + assert len(segment_ids) == max_seq_length + + masked_lm_positions = list(instance.masked_lm_positions) + masked_lm_ids = tokenization.convert_tokens_to_ids(args.vocab_file, instance.masked_lm_labels) + masked_lm_weights = [1.0] * len(masked_lm_ids) + + while len(masked_lm_positions) < max_predictions_per_seq: + masked_lm_positions.append(0) + masked_lm_ids.append(0) + masked_lm_weights.append(0.0) + + next_sentence_label = 1 if instance.is_random_next else 0 + input_ids = np.array(input_ids, dtype=np.int32) + input_mask = np.array(input_mask, dtype=np.int32) + segment_ids = np.array(segment_ids, dtype=np.int32) + masked_lm_positions = np.array(masked_lm_positions, dtype=np.int32) + masked_lm_ids = np.array(masked_lm_ids, dtype=np.int32) + masked_lm_weights = np.array(masked_lm_weights, dtype=np.float32) + next_sentence_label = np.array(next_sentence_label, dtype=np.int32) + data = {'input_ids': input_ids, + "input_mask": input_mask, + "segment_ids": segment_ids, + "masked_lm_positions": masked_lm_positions, + "masked_lm_ids": masked_lm_ids, + "masked_lm_weights": masked_lm_weights, + "next_sentence_label": next_sentence_label} + all_data.append(data) + if all_data: + writer.write_raw_data(all_data) + total_written += 1 + + writer.commit() + print("Wrote %d total instances", total_written) + + +def create_training_instances(input_file, tokenizer, max_seq_length, + dupe_factor, short_seq_prob, masked_lm_prob, + max_predictions_per_seq, rng): + """Create `TrainingInstance`s from raw text.""" + all_documents = [[]] + + # Input file format: + # (1) One sentence per line. These should ideally be actual sentences, not + # entire paragraphs or arbitrary spans of text. (Because we use the + # sentence boundaries for the "next sentence prediction" task). + # (2) Blank lines between documents. Document boundaries are needed so + # that the "next sentence prediction" task doesn't span between documents. + with open(input_file, "r") as reader: + while True: + line = tokenization.convert_to_unicode(reader.readline()) + if not line: + break + line = line.strip() + + # Empty lines are used as document delimiters + if not line: + all_documents.append([]) + tokens = tokenizer.tokenize(line) + if tokens: + all_documents[-1].append(tokens) + + + # Remove empty documents + all_documents = [x for x in all_documents if x] + rng.shuffle(all_documents) + + vocab_words = list(tokenizer.vocab_dict.keys()) + instances = [] + for _ in range(dupe_factor): + for document_index in range(len(all_documents)): + instances.extend(create_instances_from_document(all_documents, document_index, max_seq_length, + short_seq_prob, masked_lm_prob, max_predictions_per_seq, + vocab_words, rng)) + + rng.shuffle(instances) + return instances + + +def create_instances_from_document(all_documents, document_index, max_seq_length, short_seq_prob, + masked_lm_prob, max_predictions_per_seq, vocab_words, rng): + """Creates `TrainingInstance`s for a single document.""" + document = all_documents[document_index] + + # Account for [CLS], [SEP], [SEP] + max_num_tokens = max_seq_length - 3 + + # We *usually* want to fill up the entire sequence since we are padding + # to `max_seq_length` anyways, so short sequences are generally wasted + # computation. However, we *sometimes* + # (i.e., short_seq_prob == 0.1 == 10% of the time) want to use shorter + # sequences to minimize the mismatch between pre-training and fine-tuning. + # The `target_seq_length` is just a rough target however, whereas + # `max_seq_length` is a hard limit. + target_seq_length = max_num_tokens + if rng.random() < short_seq_prob: + target_seq_length = rng.randint(2, max_num_tokens) + + # We DON'T just concatenate all of the tokens from a document into a long + # sequence and choose an arbitrary split point because this would make the + # next sentence prediction task too easy. Instead, we split the input into + # segments "A" and "B" based on the actual "sentences" provided by the user + # input. + instances = [] + current_chunk = [] + current_length = 0 + i = 0 + while i < len(document): + segment = document[i] + current_chunk.append(segment) + current_length += len(segment) + if i == len(document) - 1 or current_length >= target_seq_length: + if current_chunk: + (tokens_a, tokens_b, i, is_random_next) = init_tokena_and_tokenb(current_chunk, rng, target_seq_length, + all_documents, document_index, i) + truncate_seq_pair(tokens_a, tokens_b, max_num_tokens, rng) + + assert len(tokens_a) >= 1 + assert len(tokens_b) >= 1 + + tokens, segment_ids = init_tokens_and_segment_ids(tokens_a, tokens_b) + + (tokens, masked_lm_positions, masked_lm_labels) = create_masked_lm_predictions( + tokens, masked_lm_prob, max_predictions_per_seq, vocab_words, rng) + instance = TrainingInstance( + tokens=tokens, + segment_ids=segment_ids, + is_random_next=is_random_next, + masked_lm_positions=masked_lm_positions, + masked_lm_labels=masked_lm_labels) + instances.append(instance) + current_chunk = [] + current_length = 0 + i += 1 + return instances + +def init_tokena_and_tokenb(current_chunk, rng, target_seq_length, all_documents, document_index, i): + """init tokens_a and tokens_b""" + # `a_end` is how many segments from `current_chunk` go into the `A` + # (first) sentence. + a_end = 1 + if len(current_chunk) >= 2: + a_end = rng.randint(1, len(current_chunk) - 1) + + tokens_a = [] + for j in range(a_end): + tokens_a.extend(current_chunk[j]) + + tokens_b = [] + # Random next + is_random_next = False + if len(current_chunk) == 1 or rng.random() < 0.5: + is_random_next = True + target_b_length = target_seq_length - len(tokens_a) + + # This should rarely go for more than one iteration for large + # corpora. However, just to be careful, we try to make sure that + # the random document is not the same as the document + # we're processing. + for _ in range(10): + random_document_index = rng.randint(0, len(all_documents) - 1) + if random_document_index != document_index: + break + + random_document = all_documents[random_document_index] + random_start = rng.randint(0, len(random_document) - 1) + for j in range(random_start, len(random_document)): + tokens_b.extend(random_document[j]) + if len(tokens_b) >= target_b_length: + break + # We didn't actually use these segments so we "put them back" so + # they don't go to waste. + num_unused_segments = len(current_chunk) - a_end + i -= num_unused_segments + # Actual next + else: + is_random_next = False + for j in range(a_end, len(current_chunk)): + tokens_b.extend(current_chunk[j]) + return (tokens_a, tokens_b, i, is_random_next) + +def init_tokens_and_segment_ids(tokens_a, tokens_b): + """init tokens and segment_ids.""" + tokens = [] + segment_ids = [] + tokens.append("[CLS]") + segment_ids.append(0) + for token in tokens_a: + tokens.append(token) + segment_ids.append(0) + tokens.append("[SEP]") + segment_ids.append(0) + + for token in tokens_b: + tokens.append(token) + segment_ids.append(1) + tokens.append("[SEP]") + segment_ids.append(1) + return tokens, segment_ids + +MaskedLmInstance = collections.namedtuple("MaskedLmInstance", ["index", "label"]) + + +def create_masked_lm_predictions(tokens, masked_lm_prob, max_predictions_per_seq, vocab_words, rng): + """Creates the predictions for the masked LM objective.""" + cand_indexes = [] + for (i, token) in enumerate(tokens): + if token in ('[CLS]', '[SEP]'): + continue + # Whole Word Masking means that if we mask all of the wordpieces + # corresponding to an original word. When a word has been split into + # WordPieces, the first token does not have any marker and any subsequence + # tokens are prefixed with ##. So whenever we see the ## token, we + # append it to the previous set of word indexes. + # + # Note that Whole Word Masking does *not* change the training code + # at all -- we still predict each WordPiece independently, softmaxed + # over the entire vocabulary. + if (args.do_whole_word_mask and len(cand_indexes) >= 1 and token.startswith("##")): + cand_indexes[-1].append(i) + else: + cand_indexes.append([i]) + + rng.shuffle(cand_indexes) + + output_tokens = list(tokens) + + num_to_predict = min(max_predictions_per_seq, max(1, int(round(len(tokens) * masked_lm_prob)))) + + masked_lms = [] + covered_indexes = set() + for index_set in cand_indexes: + if len(masked_lms) >= num_to_predict: + break + # If adding a whole-word mask would exceed the maximum number of + # predictions, then just skip this candidate. + if len(masked_lms) + len(index_set) > num_to_predict: + continue + is_any_index_covered = False + for index in index_set: + if index in covered_indexes: + is_any_index_covered = True + break + if is_any_index_covered: + continue + for index in index_set: + covered_indexes.add(index) + + masked_token = None + # 80% of the time, replace with [MASK] + if rng.random() < 0.8: + masked_token = "[MASK]" + else: + # 10% of the time, keep original + if rng.random() < 0.5: + masked_token = tokens[index] + # 10% of the time, replace with random word + else: + masked_token = vocab_words[rng.randint(0, len(vocab_words) - 1)] + + output_tokens[index] = masked_token + + masked_lms.append(MaskedLmInstance(index=index, label=tokens[index])) + assert len(masked_lms) <= num_to_predict + masked_lms = sorted(masked_lms, key=lambda x: x.index) + + masked_lm_positions = [] + masked_lm_labels = [] + for p in masked_lms: + masked_lm_positions.append(p.index) + masked_lm_labels.append(p.label) + + return (output_tokens, masked_lm_positions, masked_lm_labels) + + +def truncate_seq_pair(tokens_a, tokens_b, max_num_tokens, rng): + """Truncates a pair of sequences to a maximum sequence length.""" + while True: + total_length = len(tokens_a) + len(tokens_b) + if total_length <= max_num_tokens: + break + + trunc_tokens = tokens_a if len(tokens_a) > len(tokens_b) else tokens_b + assert len(trunc_tokens) >= 1 + + # We want to sometimes truncate from the front and sometimes from the + # back to add more randomness and avoid biases. + if rng.random() < 0.5: + del trunc_tokens[0] + else: + trunc_tokens.pop() + +def main(): + tokenizer = tokenization.FullTokenizer(vocab_file=args.vocab_file, do_lower_case=args.do_lower_case) + rng = random.Random(args.random_seed) + print("before create_training_instances", flush=True) + instances = create_training_instances( + args.input_file, tokenizer, args.max_seq_length, args.dupe_factor, + args.short_seq_prob, args.masked_lm_prob, args.max_predictions_per_seq, + rng) + + print("done write_instance_to_example_files", flush=True) + write_instance_to_example_files(instances, tokenizer, args.max_seq_length, + args.max_predictions_per_seq, args.output_file) + + +if __name__ == "__main__": + args = parse_args() + main() diff --git a/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_pretrain_mindrecords.sh b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_pretrain_mindrecords.sh new file mode 100644 index 0000000000000000000000000000000000000000..afc10a8a83a0fa115112d5d929ae5236ac3ccfb6 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_pretrain_mindrecords.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# Copyright 2022 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. +# ============================================================================ + +echo "==============================================================================================================" +echo "Please run the script as: " +echo "bash ./generate_pretrain_mindrecords.sh INPUT_FILES_PATH OUTPUT_FILES_PATH VOCAB_FILE" +echo "for example: bash ./generate_pretrain_mindrecords.sh ./wiki-clean-aa ./output/ ./bert-base-uncased-vocab.txt" +echo "==============================================================================================================" + +if [ $# -ne 3 ] +then + echo "Usage: bash ./generate_pretrain_mindrecords.sh INPUT_FILES_PATH OUTPUT_FILES_PATH VOCAB_FILE" +exit 1 +fi + +get_real_path(){ + if [ -z "$1" ]; then + echo "" + elif [ "${1:0:1}" == "/" ]; then + echo "$1" + else + echo "$(realpath -m $PWD/$1)" + fi +} + +run_create_mindrecords(){ + rm -rf ./logs + mkdir ./logs + for file in $1/* + do + [[ -e "$file" ]] + input_file_name=$file + echo "============input_file_name is $input_file_name" + file_name=${input_file_name##*/} + output_file_name=$2/$file_name.mindrecord + echo "============output_file_name is $output_file_name" + echo "============vocab_file name is $3" + python generate_pretrain_mindrecord.py --input_file $input_file_name \ + --output_file $output_file_name \ + --vocab_file $3 &> ./logs/$file_name.txt & + done +} + +input_files_path=$(get_real_path $1) +output_files_path=$(get_real_path $2) +vocab_file=$(get_real_path $3) +run_create_mindrecords $input_files_path $output_files_path $vocab_file diff --git a/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_squad_mindrecord.py b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_squad_mindrecord.py new file mode 100644 index 0000000000000000000000000000000000000000..f7274c59690e35ed31340153e53eb3cbe6de83ba --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_squad_mindrecord.py @@ -0,0 +1,583 @@ +# Copyright 2022 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. +# ============================================================================ +"""Run BERT on SQuAD 1.1 and SQuAD 2.0.""" + +import collections +import json +import os +import random +from argparse import ArgumentParser +import six +import numpy as np +import tokenization +from mindspore.mindrecord import FileWriter + + +def parse_args(): + parser = ArgumentParser(description="Generate MindRecord for bert task: Squad") + parser.add_argument("--vocab_file", type=str, default="", + help="The vocabulary file that the BERT model was trained on.") + parser.add_argument("--output_dir", type=str, default="", + help="The output directory where the model checkpoints will be written.") + parser.add_argument("--train_file", type=str, default="", help="SQuAD json for training. E.g., train-v1.1.json") + parser.add_argument("--predict_file", type=str, default="", + help="SQuAD json for predictions. E.g., dev-v1.1.json or test-v1.1.json") + parser.add_argument("--do_lower_case", type=bool, default=True, help="Whether to lower case the input text. " + "Should be True for uncased models" + " and False for cased models.") + parser.add_argument("--max_seq_length", type=int, default=384, help="Maximum sequence length.") + parser.add_argument("--doc_stride", type=int, default=128, help="When splitting up a long document into chunks, " + "how much stride to take between chunks.") + parser.add_argument("--max_query_length", type=int, default=64, + help="The maximum number of tokens for the question, " + "Questions longer than this will be truncated to this length.") + parser.add_argument("--do_train", type=bool, default=True, help="Whether to run training.") + parser.add_argument("--do_predict", type=bool, default=True, help="Whether to run eval on the dev set.") + parser.add_argument("--version_2_with_negative", type=bool, default=False, + help="If true, the SQuAD examples contain some that do not have an answer.") + args_opt = parser.parse_args() + return args_opt + + +class SquadExample(): + """ + A single training/test example for simple sequence classification. + For examples without an answer, the start and end position are -1. + """ + + def __init__(self, + qas_id, + question_text, + doc_tokens, + orig_answer_text=None, + start_position=None, + end_position=None, + is_impossible=False): + self.qas_id = qas_id + self.question_text = question_text + self.doc_tokens = doc_tokens + self.orig_answer_text = orig_answer_text + self.start_position = start_position + self.end_position = end_position + self.is_impossible = is_impossible + + def __str__(self): + return self.__repr__() + + def __repr__(self): + s = "" + s += "qas_id: %s" % (tokenization.printable_text(self.qas_id)) + s += ", question_text: %s" % ( + tokenization.printable_text(self.question_text)) + s += ", doc_tokens: [%s]" % (" ".join(self.doc_tokens)) + if self.start_position: + s += ", start_position: %d" % (self.start_position) + if self.start_position: + s += ", end_position: %d" % (self.end_position) + if self.start_position: + s += ", is_impossible: %r" % (self.is_impossible) + return s + + +class InputFeatures(): + """A single set of features of data.""" + + def __init__(self, + unique_id, + example_index, + doc_span_index, + tokens, + token_to_orig_map, + token_is_max_context, + input_ids, + input_mask, + segment_ids, + start_position=None, + end_position=None, + is_impossible=None): + self.unique_id = unique_id + self.example_index = example_index + self.doc_span_index = doc_span_index + self.tokens = tokens + self.token_to_orig_map = token_to_orig_map + self.token_is_max_context = token_is_max_context + self.input_ids = input_ids + self.input_mask = input_mask + self.segment_ids = segment_ids + self.start_position = start_position + self.end_position = end_position + self.is_impossible = is_impossible + + +def read_squad_examples(input_file, is_training): + """Read a SQuAD json file into a list of SquadExample.""" + with open(input_file, "r") as reader: + input_data = json.load(reader)["data"] + + def is_whitespace(c): + if c == " " or c == "\t" or c == "\r" or c == "\n" or ord(c) == 0x202F: + return True + return False + + examples = [] + for entry in input_data: + for paragraph in entry["paragraphs"]: + paragraph_text = paragraph["context"] + doc_tokens = [] + char_to_word_offset = [] + prev_is_whitespace = True + for c in paragraph_text: + if is_whitespace(c): + prev_is_whitespace = True + else: + if prev_is_whitespace: + doc_tokens.append(c) + else: + doc_tokens[-1] += c + prev_is_whitespace = False + char_to_word_offset.append(len(doc_tokens) - 1) + + for qa in paragraph["qas"]: + one_example = process_one_example(qa, is_training, args.version_2_with_negative, doc_tokens, + char_to_word_offset) + if one_example is not None: + examples.append(one_example) + + return examples + +def process_one_example(qa, is_training, version_2_with_negative, doc_tokens, char_to_word_offset): + """generate one example from qa""" + qas_id = qa["id"] + question_text = qa["question"] + start_position = None + end_position = None + orig_answer_text = None + is_impossible = False + if is_training: + if version_2_with_negative: + is_impossible = qa["is_impossible"] + if (len(qa["answers"]) != 1) and (not is_impossible): + raise ValueError("For training, each question should have exactly 1 answer.") + if not is_impossible: + answer = qa["answers"][0] + orig_answer_text = answer["text"] + answer_offset = answer["answer_start"] + answer_length = len(orig_answer_text) + start_position = char_to_word_offset[answer_offset] + end_position = char_to_word_offset[answer_offset + answer_length - 1] + actual_text = " ".join(doc_tokens[start_position:(end_position + 1)]) + cleaned_answer_text = " ".join(tokenization.whitespace_tokenize(orig_answer_text)) + if actual_text.find(cleaned_answer_text) == -1: + print("Could not find answer:", actual_text, ", vs: ", cleaned_answer_text) + return None + else: + start_position = -1 + end_position = -1 + orig_answer_text = "" + + example = SquadExample( + qas_id=qas_id, + question_text=question_text, + doc_tokens=doc_tokens, + orig_answer_text=orig_answer_text, + start_position=start_position, + end_position=end_position, + is_impossible=is_impossible) + return example + +def convert_examples_to_features(examples, tokenizer, max_seq_length, + doc_stride, max_query_length, is_training, + output_fn): + """Loads a data file into a list of `InputBatch`s.""" + + unique_id = 1000000000 + + for (example_index, example) in enumerate(examples): + query_tokens = tokenizer.tokenize(example.question_text) + + if len(query_tokens) > max_query_length: + query_tokens = query_tokens[0:max_query_length] + + tok_to_orig_index = [] + orig_to_tok_index = [] + all_doc_tokens = [] + for (i, token) in enumerate(example.doc_tokens): + orig_to_tok_index.append(len(all_doc_tokens)) + sub_tokens = tokenizer.tokenize(token) + for sub_token in sub_tokens: + tok_to_orig_index.append(i) + all_doc_tokens.append(sub_token) + + tok_start_position = None + tok_end_position = None + if is_training and example.is_impossible: + tok_start_position = -1 + tok_end_position = -1 + if is_training and not example.is_impossible: + tok_start_position = orig_to_tok_index[example.start_position] + if example.end_position < len(example.doc_tokens) - 1: + tok_end_position = orig_to_tok_index[example.end_position + 1] - 1 + else: + tok_end_position = len(all_doc_tokens) - 1 + (tok_start_position, tok_end_position) = _improve_answer_span( + all_doc_tokens, tok_start_position, tok_end_position, tokenizer, + example.orig_answer_text) + + # The -3 accounts for [CLS], [SEP] and [SEP] + max_tokens_for_doc = max_seq_length - len(query_tokens) - 3 + + # We can have documents that are longer than the maximum sequence length. + # To deal with this we do a sliding window approach, where we take chunks + # of the up to our max length with a stride of `doc_stride`. + _DocSpan = collections.namedtuple( # pylint: disable=invalid-name + "DocSpan", ["start", "length"]) + doc_spans = [] + start_offset = 0 + while start_offset < len(all_doc_tokens): + length = len(all_doc_tokens) - start_offset + if length > max_tokens_for_doc: + length = max_tokens_for_doc + doc_spans.append(_DocSpan(start=start_offset, length=length)) + if start_offset + length == len(all_doc_tokens): + break + start_offset += min(length, doc_stride) + + for (doc_span_index, doc_span) in enumerate(doc_spans): + tokens = [] + token_to_orig_map = {} + token_is_max_context = {} + segment_ids = [] + tokens.append("[CLS]") + segment_ids.append(0) + for token in query_tokens: + tokens.append(token) + segment_ids.append(0) + tokens.append("[SEP]") + segment_ids.append(0) + + for i in range(doc_span.length): + split_token_index = doc_span.start + i + token_to_orig_map[len(tokens)] = tok_to_orig_index[split_token_index] + + is_max_context = _check_is_max_context(doc_spans, doc_span_index, + split_token_index) + token_is_max_context[len(tokens)] = is_max_context + tokens.append(all_doc_tokens[split_token_index]) + segment_ids.append(1) + tokens.append("[SEP]") + segment_ids.append(1) + + input_ids = tokenization.convert_tokens_to_ids(args.vocab_file, tokens) + + # The mask has 1 for real tokens and 0 for padding tokens. Only real + # tokens are attended to. + input_mask = [1] * len(input_ids) + + # Zero-pad up to the sequence length. + while len(input_ids) < max_seq_length: + input_ids.append(0) + input_mask.append(0) + segment_ids.append(0) + + assert len(input_ids) == max_seq_length + assert len(input_mask) == max_seq_length + assert len(segment_ids) == max_seq_length + + start_position = None + end_position = None + if is_training and not example.is_impossible: + # For training, if our document chunk does not contain an annotation + # we throw it out, since there is nothing to predict. + doc_start = doc_span.start + doc_end = doc_span.start + doc_span.length - 1 + out_of_span = False + if not (tok_start_position >= doc_start and + tok_end_position <= doc_end): + out_of_span = True + if out_of_span: + start_position = 0 + end_position = 0 + else: + doc_offset = len(query_tokens) + 2 + start_position = tok_start_position - doc_start + doc_offset + end_position = tok_end_position - doc_start + doc_offset + + if is_training and example.is_impossible: + start_position = 0 + end_position = 0 + + if example_index < 1: + print("*** Example ***") + print("unique_id: %s" % (unique_id)) + print("example_index: %s" % (example_index)) + print("doc_span_index: %s" % (doc_span_index)) + print("tokens: %s" % " ".join( + [tokenization.printable_text(x) for x in tokens])) + print("token_to_orig_map: %s" % " ".join( + ["%d:%d" % (x, y) for (x, y) in six.iteritems(token_to_orig_map)])) + print("token_is_max_context: %s" % " ".join([ + "%d:%s" % (x, y) for (x, y) in six.iteritems(token_is_max_context) + ])) + print("input_ids: %s" % " ".join([str(x) for x in input_ids])) + print( + "input_mask: %s" % " ".join([str(x) for x in input_mask])) + print( + "segment_ids: %s" % " ".join([str(x) for x in segment_ids])) + if is_training and example.is_impossible: + print("impossible example") + if is_training and not example.is_impossible: + answer_text = " ".join(tokens[start_position:(end_position + 1)]) + print("start_position: %d" % (start_position)) + print("end_position: %d" % (end_position)) + print( + "answer: %s" % (tokenization.printable_text(answer_text))) + + feature = InputFeatures( + unique_id=unique_id, + example_index=example_index, + doc_span_index=doc_span_index, + tokens=tokens, + token_to_orig_map=token_to_orig_map, + token_is_max_context=token_is_max_context, + input_ids=input_ids, + input_mask=input_mask, + segment_ids=segment_ids, + start_position=start_position, + end_position=end_position, + is_impossible=example.is_impossible) + + # Run callback + output_fn(feature) + + unique_id += 1 + + +def _improve_answer_span(doc_tokens, input_start, input_end, tokenizer, + orig_answer_text): + """Returns tokenized answer spans that better match the annotated answer.""" + + # The SQuAD annotations are character based. We first project them to + # whitespace-tokenized words. But then after WordPiece tokenization, we can + # often find a "better match". For example: + # + # Question: What year was John Smith born? + # Context: The leader was John Smith (1895-1943). + # Answer: 1895 + # + # The original whitespace-tokenized answer will be "(1895-1943).". However + # after tokenization, our tokens will be "( 1895 - 1943 ) .". So we can match + # the exact answer, 1895. + # + # However, this is not always possible. Consider the following: + # + # Question: What country is the top exporter of electornics? + # Context: The Japanese electronics industry is the lagest in the world. + # Answer: Japan + # + # In this case, the annotator chose "Japan" as a character sub-span of + # the word "Japanese". Since our WordPiece tokenizer does not split + # "Japanese", we just use "Japanese" as the annotation. This is fairly rare + # in SQuAD, but does happen. + tok_answer_text = " ".join(tokenizer.tokenize(orig_answer_text)) + + for new_start in range(input_start, input_end + 1): + for new_end in range(input_end, new_start - 1, -1): + text_span = " ".join(doc_tokens[new_start:(new_end + 1)]) + if text_span == tok_answer_text: + return (new_start, new_end) + + return (input_start, input_end) + + +def _check_is_max_context(doc_spans, cur_span_index, position): + """Check if this is the 'max context' doc span for the token.""" + + # Because of the sliding window approach taken to scoring documents, a single + # token can appear in multiple documents. E.g. + # Doc: the man went to the store and bought a gallon of milk + # Span A: the man went to the + # Span B: to the store and bought + # Span C: and bought a gallon of + # ... + # + # Now the word 'bought' will have two scores from spans B and C. We only + # want to consider the score with "maximum context", which we define as + # the *minimum* of its left and right context (the *sum* of left and + # right context will always be the same, of course). + # + # In the example the maximum context for 'bought' would be span C since + # it has 1 left context and 3 right context, while span B has 4 left context + # and 0 right context. + best_score = None + best_span_index = None + for (span_index, doc_span) in enumerate(doc_spans): + end = doc_span.start + doc_span.length - 1 + if position < doc_span.start: + continue + if position > end: + continue + num_left_context = position - doc_span.start + num_right_context = end - position + score = min(num_left_context, num_right_context) + 0.01 * doc_span.length + if best_score is None or score > best_score: + best_score = score + best_span_index = span_index + + return cur_span_index == best_span_index + + +RawResult = collections.namedtuple("RawResult", + ["unique_id", "start_logits", "end_logits"]) + + +class FeatureWriter(): + """Writes InputFeature to TF example file.""" + + def __init__(self, filename, is_training): + self.filename = filename + self.is_training = is_training + self.num_features = 0 + self._writer = FileWriter(filename, overwrite=True) + if self.is_training: + self.schema = { + "unique_ids": {"type": "int32", "shape": [-1]}, + "input_ids": {"type": "int32", "shape": [-1]}, + "input_mask": {"type": "int32", "shape": [-1]}, + "segment_ids": {"type": "int32", "shape": [-1]}, + "start_positions": {"type": "int32", "shape": [-1]}, + "end_positions": {"type": "int32", "shape": [-1]}, + "is_impossible": {"type": "int32", "shape": [-1]}, + } + else: + self.schema = { + "unique_ids": {"type": "int32", "shape": [-1]}, + "input_ids": {"type": "int32", "shape": [-1]}, + "input_mask": {"type": "int32", "shape": [-1]}, + "segment_ids": {"type": "int32", "shape": [-1]}, + } + self._writer.add_schema(self.schema) + self.total_written = 0 + + def process_feature(self, feature): + """Write a InputFeature to the TFRecordWriter as a tf.train.Example.""" + self.num_features += 1 + all_data = [] + unique_ids = np.array(feature.unique_id, dtype=np.int32) + input_ids = np.array(feature.input_ids, dtype=np.int32) + input_mask = np.array(feature.input_mask, dtype=np.int32) + segment_ids = np.array(feature.segment_ids, dtype=np.int32) + if self.is_training: + start_positions = np.array(feature.start_position, dtype=np.int32) + end_positions = np.array(feature.end_position, dtype=np.int32) + impossible = 0 + if feature.is_impossible: + impossible = 1 + is_impossible = np.array(impossible, dtype=np.int32) + + if self.is_training: + data = { + "unique_ids": unique_ids, + "input_ids": input_ids, + "input_mask": input_mask, + "segment_ids": segment_ids, + "start_positions": start_positions, + "end_positions": end_positions, + "is_impossible": is_impossible + } + else: + data = { + "unique_ids": unique_ids, + "input_ids": input_ids, + "input_mask": input_mask, + "segment_ids": segment_ids + } + all_data.append(data) + if all_data: + self._writer.write_raw_data(all_data) + self.total_written += 1 + + def commit(self): + self._writer.commit() + +def main(): + if not os.path.exists(args.output_dir): + os.makedirs(args.output_dir, exist_ok=True) + + tokenizer = tokenization.FullTokenizer( + vocab_file=args.vocab_file, do_lower_case=args.do_lower_case) + + train_examples = None + if args.do_train: + train_examples = read_squad_examples( + input_file=args.train_file, is_training=True) + + # Pre-shuffle the input to avoid having to make a very large shuffle + # buffer in in the `input_fn`. + rng = random.Random(12345) + rng.shuffle(train_examples) + + if args.do_train: + # We write to a temporary file to avoid storing very large constant tensors + # in memory. + train_writer = FeatureWriter( + filename=os.path.join(args.output_dir, "train.mindrecord"), + is_training=True) + convert_examples_to_features( + examples=train_examples, + tokenizer=tokenizer, + max_seq_length=args.max_seq_length, + doc_stride=args.doc_stride, + max_query_length=args.max_query_length, + is_training=True, + output_fn=train_writer.process_feature) + train_writer.commit() + + print("***** Running training *****") + print(" Total instances is: ", train_writer.total_written, flush=True) + print(" Num orig examples is: ", len(train_examples), flush=True) + print(" Num split examples is: ", train_writer.num_features, flush=True) + del train_examples + + if args.do_predict: + eval_examples = read_squad_examples( + input_file=args.predict_file, is_training=False) + + eval_writer = FeatureWriter( + filename=os.path.join(args.output_dir, "eval.mindrecord"), + is_training=False) + eval_features = [] + + def append_feature(feature): + eval_features.append(feature) + eval_writer.process_feature(feature) + + convert_examples_to_features( + examples=eval_examples, + tokenizer=tokenizer, + max_seq_length=args.max_seq_length, + doc_stride=args.doc_stride, + max_query_length=args.max_query_length, + is_training=False, + output_fn=append_feature) + eval_writer.commit() + + print("***** Running predictions *****") + print(" Total instances is: ", eval_writer.total_written, flush=True) + print(" Num orig examples is: ", len(eval_examples), flush=True) + print(" Num split examples is: ", len(eval_features), flush=True) + +if __name__ == "__main__": + args = parse_args() + main() diff --git a/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_tnews_mindrecord.py b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_tnews_mindrecord.py new file mode 100644 index 0000000000000000000000000000000000000000..5ae5b773d58aa83338c7e2392186d6ad4ed6dc42 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/generate_tnews_mindrecord.py @@ -0,0 +1,435 @@ +# Copyright 2022 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. +# ============================================================================ +"""Generate MindRecord for BERT finetuning runner: TNEWS.""" +import os +import json +import csv +from argparse import ArgumentParser +import six +import numpy as np +import tokenization +from mindspore.mindrecord import FileWriter + +def parse_args(): + parser = ArgumentParser(description="Generate MindRecord for bert task: TNEWS") + parser.add_argument("--data_dir", type=str, default="", + help="The input data dir. Should contain the .tsv files (or other data files) for the task.") + parser.add_argument("--task_name", type=str, default="tnews", help="The name of the task to train.") + parser.add_argument("--vocab_file", type=str, default="", + help="The vocabulary file that the BERT model was trained on.") + parser.add_argument("--output_dir", type=str, default="", + help="The output directory where the mindrecord will be written.") + parser.add_argument("--do_lower_case", type=bool, default=True, help="Whether to lower case the input text. " + "Should be True for uncased models " + "and False for cased models.") + parser.add_argument("--max_seq_length", type=int, default=128, help="Maximum sequence length.") + parser.add_argument("--do_train", type=bool, default=True, help="Whether to run training.") + parser.add_argument("--do_eval", type=bool, default=True, help="Whether to run eval on the dev set.") + parser.add_argument("--do_predict", type=bool, default=True, + help="Whether to run the model in inference mode on the test set.") + args_opt = parser.parse_args() + return args_opt + + +class PaddingInputExample(): + """Fake example so the num input examples is a multiple of the batch size. + When running eval/predict on the TPU, we need to pad the number of examples + to be a multiple of the batch size, because the TPU requires a fixed batch + size. The alternative is to drop the last batch, which is bad because it means + the entire output data won't be generated. + We use this class instead of `None` because treating `None` as padding + battches could cause silent errors. + """ + + +class InputFeatures(): + """A single set of features of data.""" + + def __init__(self, + input_ids, + input_mask, + segment_ids, + label_id, + is_real_example=True): + self.input_ids = input_ids + self.input_mask = input_mask + self.segment_ids = segment_ids + self.label_id = label_id + self.is_real_example = is_real_example + + +def convert_single_example(ex_index, example, label_list, max_seq_length, + tokenizer): + """Converts a single `InputExample` into a single `InputFeatures`.""" + + if isinstance(example, PaddingInputExample): + return InputFeatures( + input_ids=[0] * max_seq_length, + input_mask=[0] * max_seq_length, + segment_ids=[0] * max_seq_length, + label_id=0, + is_real_example=False) + + label_map = {} + for (i, label) in enumerate(label_list): + label_map[label] = i + + tokens_a = tokenizer.tokenize(example.text_a) + tokens_b = None + if example.text_b: + tokens_b = tokenizer.tokenize(example.text_b) + + if tokens_b: + # Modifies `tokens_a` and `tokens_b` in place so that the total + # length is less than the specified length. + # Account for [CLS], [SEP], [SEP] with "- 3" + _truncate_seq_pair(tokens_a, tokens_b, max_seq_length - 3) + else: + # Account for [CLS] and [SEP] with "- 2" + if len(tokens_a) > max_seq_length - 2: + tokens_a = tokens_a[0:(max_seq_length - 2)] + + # The convention in BERT is: + # (a) For sequence pairs: + # tokens: [CLS] is this jack ##son ##ville ? [SEP] no it is not . [SEP] + # type_ids: 0 0 0 0 0 0 0 0 1 1 1 1 1 1 + # (b) For single sequences: + # tokens: [CLS] the dog is hairy . [SEP] + # type_ids: 0 0 0 0 0 0 0 + # + # Where "type_ids" are used to indicate whether this is the first + # sequence or the second sequence. The embedding vectors for `type=0` and + # `type=1` were learned during pre-training and are added to the wordpiece + # embedding vector (and position vector). This is not *strictly* necessary + # since the [SEP] token unambiguously separates the sequences, but it makes + # it easier for the model to learn the concept of sequences. + # + # For classification tasks, the first vector (corresponding to [CLS]) is + # used as the "sentence vector". Note that this only makes sense because + # the entire model is fine-tuned. + tokens = [] + segment_ids = [] + tokens.append("[CLS]") + segment_ids.append(0) + for token in tokens_a: + tokens.append(token) + segment_ids.append(0) + tokens.append("[SEP]") + segment_ids.append(0) + + if tokens_b: + for token in tokens_b: + tokens.append(token) + segment_ids.append(1) + tokens.append("[SEP]") + segment_ids.append(1) + + input_ids = tokenization.convert_tokens_to_ids(args.vocab_file, tokens) + + # The mask has 1 for real tokens and 0 for padding tokens. Only real + # tokens are attended to. + input_mask = [1] * len(input_ids) + + # Zero-pad up to the sequence length. + while len(input_ids) < max_seq_length: + input_ids.append(0) + input_mask.append(0) + segment_ids.append(0) + + assert len(input_ids) == max_seq_length + assert len(input_mask) == max_seq_length + assert len(segment_ids) == max_seq_length + + label_id = label_map[example.label] + if ex_index < 1: + print("*** Example ***") + print("guid: %s" % (example.guid)) + print("tokens: %s" % " ".join( + [tokenization.printable_text(x) for x in tokens])) + print("input_ids: %s" % " ".join([str(x) for x in input_ids])) + print("input_mask: %s" % " ".join([str(x) for x in input_mask])) + print("segment_ids: %s" % " ".join([str(x) for x in segment_ids])) + print("label: %s (id = %d)" % (example.label, label_id)) + + feature = InputFeatures( + input_ids=input_ids, + input_mask=input_mask, + segment_ids=segment_ids, + label_id=label_id, + is_real_example=True) + return feature + + +def file_based_convert_examples_to_features( + examples, label_list, max_seq_length, tokenizer, output_file): + """Convert a set of `InputExample`s to a MindRecord file.""" + + schema = { + "input_ids": {"type": "int32", "shape": [-1]}, + "input_mask": {"type": "int32", "shape": [-1]}, + "segment_ids": {"type": "int32", "shape": [-1]}, + "label_ids": {"type": "int32", "shape": [-1]}, + "is_real_example": {"type": "int32", "shape": [-1]}, + } + writer = FileWriter(output_file, overwrite=True) + writer.add_schema(schema) + total_written = 0 + for (ex_index, example) in enumerate(examples): + all_data = [] + feature = convert_single_example(ex_index, example, label_list, + max_seq_length, tokenizer) + + input_ids = np.array(feature.input_ids, dtype=np.int32) + input_mask = np.array(feature.input_mask, dtype=np.int32) + segment_ids = np.array(feature.segment_ids, dtype=np.int32) + label_ids = np.array(feature.label_id, dtype=np.int32) + is_real_example = np.array(feature.is_real_example, dtype=np.int32) + data = {'input_ids': input_ids, + "input_mask": input_mask, + "segment_ids": segment_ids, + "label_ids": label_ids, + "is_real_example": is_real_example} + all_data.append(data) + if all_data: + writer.write_raw_data(all_data) + total_written += 1 + writer.commit() + print("Total instances is: ", total_written, flush=True) + + +def _truncate_seq_pair(tokens_a, tokens_b, max_length): + """Truncates a sequence pair in place to the maximum length.""" + + # This is a simple heuristic which will always truncate the longer sequence + # one token at a time. This makes more sense than truncating an equal percent + # of tokens from each, since if one sequence is very short then each token + # that's truncated likely contains more information than a longer sequence. + while True: + total_length = len(tokens_a) + len(tokens_b) + if total_length <= max_length: + break + if len(tokens_a) > len(tokens_b): + tokens_a.pop() + else: + tokens_b.pop() + + +# This function is not used by this file but is still used by the Colab and people who depend on it. +def convert_examples_to_features(examples, label_list, max_seq_length, + tokenizer): + """Convert a set of `InputExample`s to a list of `InputFeatures`.""" + + features = [] + for (ex_index, example) in enumerate(examples): + feature = convert_single_example(ex_index, example, label_list, + max_seq_length, tokenizer) + + features.append(feature) + return features + + +class DataProcessor(): + """Base class for data converters for sequence classification data sets.""" + + def get_train_examples(self, data_dir): + """Gets a collection of `InputExample`s for the train set.""" + raise NotImplementedError() + + def get_dev_examples(self, data_dir): + """Gets a collection of `InputExample`s for the dev set.""" + raise NotImplementedError() + + def get_test_examples(self, data_dir): + """Gets a collection of `InputExample`s for prediction.""" + raise NotImplementedError() + + def get_labels(self): + """Gets the list of labels for this data set.""" + raise NotImplementedError() + + @classmethod + def _read_tsv(cls, input_file, delimiter="\t", quotechar=None): + """Reads a tab separated value file.""" + with open(input_file, "r") as f: + reader = csv.reader(f, delimiter=delimiter, quotechar=quotechar) + lines = [] + for line in reader: + lines.append(line) + return lines + + @classmethod + def _read_txt(cls, input_file): + """Reads a tab separated value file.""" + with open(input_file, "r") as f: + reader = f.readlines() + lines = [] + for line in reader: + lines.append(line.strip().split("_!_")) + return lines + + @classmethod + def _read_json(cls, input_file): + """Reads a tab separated value file.""" + with open(input_file, "r") as f: + reader = f.readlines() + lines = [] + for line in reader: + lines.append(json.loads(line.strip())) + return lines + + +def convert_to_unicode(text): + """Converts `text` to Unicode (if it's not already), assuming utf-8 input.""" + if six.PY3: + if isinstance(text, str): + return text + if isinstance(text, bytes): + return text.decode("utf-8", "ignore") + raise ValueError("Unsupported string type: %s" % (type(text))) + if six.PY2: + if isinstance(text, str): + return text.decode("utf-8", "ignore") + if isinstance(text, unicode): + return text + raise ValueError("Unsupported string type: %s" % (type(text))) + raise ValueError("Not running on Python2 or Python 3?") + + +class InputExample(): + """A single training/test example for simple sequence classification.""" + + def __init__(self, guid, text_a, text_b=None, label=None): + """Constructs a InputExample. + Args: + guid: Unique id for the example. + text_a: string. The untokenized text of the first sequence. For single + sequence tasks, only this sequence must be specified. + text_b: (Optional) string. The untokenized text of the second sequence. + Only must be specified for sequence pair tasks. + label: (Optional) string. The label of the example. This should be + specified for train and dev examples, but not for test examples. + """ + self.guid = guid + self.text_a = text_a + self.text_b = text_b + self.label = label + + +class TnewsProcessor(DataProcessor): + """Processor for the MRPC data set (GLUE version).""" + + def get_train_examples(self, data_dir): + """See base class.""" + return self._create_examples( + self._read_json(os.path.join(data_dir, "train.json")), "train") + + def get_dev_examples(self, data_dir): + """See base class.""" + return self._create_examples( + self._read_json(os.path.join(data_dir, "dev.json")), "dev") + + def get_test_examples(self, data_dir): + """See base class.""" + return self._create_examples( + self._read_json(os.path.join(data_dir, "test.json")), "test") + + def get_labels(self): + """See base class.""" + labels = [] + for i in range(17): + if i in (5, 11): + continue + labels.append(str(100 + i)) + return labels + + def _create_examples(self, lines, set_type): + """Creates examples for the training and dev sets.""" + examples = [] + for (i, line) in enumerate(lines): + guid = "%s-%s" % (set_type, i) + text_a = convert_to_unicode(line['sentence']) + text_b = None + label = convert_to_unicode(line['label']) if set_type != 'test' else "100" + examples.append( + InputExample(guid=guid, text_a=text_a, text_b=text_b, label=label)) + return examples + + +def main(): + processors = { + "tnews": TnewsProcessor, + } + + if not args.do_train and not args.do_eval and not args.do_predict: + raise ValueError( + "At least one of `do_train`, `do_eval` or `do_predict' must be True.") + + if not os.path.exists(args.output_dir): + os.makedirs(args.output_dir, exist_ok=True) + + task_name = args.task_name.lower() + + if task_name not in processors: + raise ValueError("Task not found: %s" % (task_name)) + + processor = processors[task_name]() + + label_list = processor.get_labels() + + tokenizer = tokenization.FullTokenizer( + vocab_file=args.vocab_file, do_lower_case=args.do_lower_case) + + if args.do_train: + print("data_dir:", args.data_dir) + train_examples = processor.get_train_examples(args.data_dir) + train_file = os.path.join(args.output_dir, "train.mindrecord") + file_based_convert_examples_to_features( + train_examples, label_list, args.max_seq_length, tokenizer, train_file) + + print("***** Running training *****") + print(" Num examples = %d", len(train_examples), flush=True) + + if args.do_eval: + # dev dataset + eval_examples = processor.get_dev_examples(args.data_dir) + num_actual_eval_examples = len(eval_examples) + + eval_file = os.path.join(args.output_dir, "dev.mindrecord") + file_based_convert_examples_to_features( + eval_examples, label_list, args.max_seq_length, tokenizer, eval_file) + + print("***** Running evaluation *****") + print(" Num examples = %d (%d actual, %d padding)", + len(eval_examples), num_actual_eval_examples, + len(eval_examples) - num_actual_eval_examples, flush=True) + + if args.do_predict: + predict_examples = processor.get_test_examples(args.data_dir) + num_actual_predict_examples = len(predict_examples) + + predict_file = os.path.join(args.output_dir, "predict.mindrecord") + file_based_convert_examples_to_features(predict_examples, label_list, + args.max_seq_length, tokenizer, + predict_file) + + print("***** Running prediction*****") + print(" Num examples = %d (%d actual, %d padding)", + len(predict_examples), num_actual_predict_examples, + len(predict_examples) - num_actual_predict_examples, flush=True) + + +if __name__ == "__main__": + args = parse_args() + main() diff --git a/nlp/language_model/bert/MindSpore/src/generate_mindrecord/tokenization.py b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/tokenization.py new file mode 100644 index 0000000000000000000000000000000000000000..d153e5de9d82e37e71f0fb97dd63656b247a20b5 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/generate_mindrecord/tokenization.py @@ -0,0 +1,345 @@ +# Copyright 2022 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. +# ============================================================================ + +""" +Tokenization. +""" + +import unicodedata +import collections +import six + +def convert_to_unicode(text): + """ + Convert text into unicode type. + Args: + text: input str. + + Returns: + input str in unicode. + """ + ret = text + if isinstance(text, str): + ret = text + elif isinstance(text, bytes): + ret = text.decode("utf-8", "ignore") + else: + raise ValueError("Unsupported string type: %s" % (type(text))) + return ret + + +def printable_text(text): + """Returns text encoded in a way suitable for print""" + # These functions want `str` for both Python2 and Python3, but in one case + # it's a Unicode string and in the other it's a byte string. + if six.PY3: + if isinstance(text, str): + return text + if isinstance(text, bytes): + return text.decode("utf-8", "ignore") + raise ValueError("Unsupported string type: %s" % (type(text))) + if six.PY2: + if isinstance(text, str): + return text + if isinstance(text, unicode): + return text.encode("utf-8") + raise ValueError("Unsupported string type: %s" % (type(text))) + raise ValueError("Not running on Python2 or Python 3?") + + +def vocab_to_dict_key_token(vocab_file): + """Loads a vocab file into a dict, key is token.""" + vocab = collections.OrderedDict() + index = 0 + with open(vocab_file, "r") as reader: + while True: + token = convert_to_unicode(reader.readline()) + if not token: + break + token = token.strip() + vocab[token] = index + index += 1 + return vocab + + +def vocab_to_dict_key_id(vocab_file): + """Loads a vocab file into a dict, key is id.""" + vocab = collections.OrderedDict() + index = 0 + with open(vocab_file, "r") as reader: + while True: + token = convert_to_unicode(reader.readline()) + if not token: + break + token = token.strip() + vocab[index] = token + index += 1 + return vocab + + +def whitespace_tokenize(text): + """Runs basic whitespace cleaning and splitting on a piece of text.""" + text = text.strip() + if not text: + return [] + tokens = text.split() + return tokens + +def convert_by_vocab(vocab_file, items): + """Converts a sequence of [tokens|ids] using the vocab.""" + vocab_dict = vocab_to_dict_key_token(vocab_file) + output = [] + for item in items: + if item in vocab_dict: + output.append(vocab_dict[item]) + else: + output.append(vocab_dict['[UNK]']) + return output + +def convert_tokens_to_ids(vocab_file, tokens): + return convert_by_vocab(vocab_file, tokens) + +def convert_ids_to_tokens(vocab_file, ids): + """ + Convert ids to tokens. + Args: + vocab_file: path to vocab.txt. + ids: list of ids. + + Returns: + list of tokens. + """ + vocab_dict = vocab_to_dict_key_id(vocab_file) + output = [] + for _id in ids: + output.append(vocab_dict[_id]) + return output + + +class FullTokenizer(): + """ + Full tokenizer + """ + def __init__(self, vocab_file, do_lower_case=True): + self.vocab_dict = vocab_to_dict_key_token(vocab_file) + self.do_lower_case = do_lower_case + self.basic_tokenize = BasicTokenizer(do_lower_case) + self.wordpiece_tokenize = WordpieceTokenizer(self.vocab_dict) + + def tokenize(self, text): + """ + Do full tokenization. + Args: + text: str of text. + + Returns: + list of tokens. + """ + tokens_ret = [] + text = convert_to_unicode(text) + for tokens in self.basic_tokenize.tokenize(text): + wordpiece_tokens = self.wordpiece_tokenize.tokenize(tokens) + tokens_ret.extend(wordpiece_tokens) + return tokens_ret + + +class BasicTokenizer(): + """ + Basic tokenizer + """ + def __init__(self, do_lower_case=True): + self.do_lower_case = do_lower_case + + def tokenize(self, text): + """ + Do basic tokenization. + Args: + text: text in unicode. + + Returns: + a list of tokens split from text + """ + text = self._clean_text(text) + text = self._tokenize_chinese_chars(text) + + orig_tokens = whitespace_tokenize(text) + split_tokens = [] + for token in orig_tokens: + if self.do_lower_case: + token = token.lower() + token = self._run_strip_accents(token) + aaa = self._run_split_on_punc(token) + split_tokens.extend(aaa) + + output_tokens = whitespace_tokenize(" ".join(split_tokens)) + return output_tokens + + def _run_strip_accents(self, text): + """Strips accents from a piece of text.""" + text = unicodedata.normalize("NFD", text) + output = [] + for char in text: + cat = unicodedata.category(char) + if cat == "Mn": + continue + output.append(char) + return "".join(output) + + def _run_split_on_punc(self, text): + """Splits punctuation on a piece of text.""" + i = 0 + start_new_word = True + output = [] + for char in text: + if _is_punctuation(char): + output.append([char]) + start_new_word = True + else: + if start_new_word: + output.append([]) + start_new_word = False + output[-1].append(char) + i += 1 + return ["".join(x) for x in output] + + def _clean_text(self, text): + """Performs invalid character removal and whitespace cleanup on text.""" + output = [] + for char in text: + cp = ord(char) + if cp == 0 or cp == 0xfffd or _is_control(char): + continue + if _is_whitespace(char): + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _tokenize_chinese_chars(self, text): + """Adds whitespace around any CJK character.""" + output = [] + for char in text: + cp = ord(char) + if self._is_chinese_char(cp): + output.append(" ") + output.append(char) + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _is_chinese_char(self, cp): + """Checks whether CP is the codepoint of a CJK character.""" + # This defines a "chinese character" as anything in the CJK Unicode block: + # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) + # + # Note that the CJK Unicode block is NOT all Japanese and Korean characters, + # despite its name. The modern Korean Hangul alphabet is a different block, + # as is Japanese Hiragana and Katakana. Those alphabets are used to write + # space-separated words, so they are not treated specially and handled + # like the all of the other languages. + if ((0x4E00 <= cp <= 0x9FFF) or + (0x3400 <= cp <= 0x4DBF) or + (0x20000 <= cp <= 0x2A6DF) or + (0x2A700 <= cp <= 0x2B73F) or + (0x2B740 <= cp <= 0x2B81F) or + (0x2B820 <= cp <= 0x2CEAF) or + (0xF900 <= cp <= 0xFAFF) or + (0x2F800 <= cp <= 0x2FA1F)): + return True + + return False + + +class WordpieceTokenizer(): + """ + Wordpiece tokenizer + """ + def __init__(self, vocab): + self.vocab_dict = vocab + + def tokenize(self, tokens): + """ + Do word-piece tokenization + Args: + tokens: a word. + + Returns: + a list of tokens that can be found in vocab dict. + """ + output_tokens = [] + tokens = convert_to_unicode(tokens) + for token in whitespace_tokenize(tokens): + chars = list(token) + len_chars = len(chars) + start = 0 + end = len_chars + while start < len_chars: + while start < end: + substr = "".join(token[start:end]) + if start != 0: + substr = "##" + substr + if substr in self.vocab_dict: + output_tokens.append(substr) + start = end + end = len_chars + else: + end = end - 1 + if start == end and start != len_chars: + output_tokens.append("[UNK]") + break + return output_tokens + + +def _is_whitespace(char): + """Checks whether `chars` is a whitespace character.""" + # \t, \n, and \r are technically control characters but we treat them + # as whitespace since they are generally considered as such. + whitespace_char = [" ", "\t", "\n", "\r"] + if char in whitespace_char: + return True + cat = unicodedata.category(char) + if cat == "Zs": + return True + return False + + +def _is_control(char): + """Checks whether `chars` is a control character.""" + # These are technically control characters but we count them as whitespace + # characters. + control_char = ["\t", "\n", "\r"] + if char in control_char: + return False + cat = unicodedata.category(char) + if cat in ("Cc", "Cf"): + return True + return False + + +def _is_punctuation(char): + """Checks whether `chars` is a punctuation character.""" + cp = ord(char) + # We treat all non-letter/number ASCII as punctuation. + # Characters such as "^", "$", and "`" are not in the Unicode + # Punctuation class but we treat them as punctuation anyways, for + # consistency. + if ((33 <= cp <= 47) or (58 <= cp <= 64) or + (91 <= cp <= 96) or (123 <= cp <= 126)): + return True + cat = unicodedata.category(char) + if cat.startswith("P"): + return True + return False diff --git a/nlp/language_model/bert/MindSpore/src/model_utils/config.py b/nlp/language_model/bert/MindSpore/src/model_utils/config.py new file mode 100644 index 0000000000000000000000000000000000000000..5451f4398db3d31b09947007a60745d79a10df51 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/model_utils/config.py @@ -0,0 +1,214 @@ +# 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. +# ============================================================================ + +"""Parse arguments""" + +import os +import ast +import argparse +from pprint import pformat +import yaml +import mindspore.common.dtype as mstype +from src.bert_model import BertConfig + + +class Config: + """ + Configuration namespace. Convert dictionary to members. + """ + def __init__(self, cfg_dict): + for k, v in cfg_dict.items(): + if isinstance(v, (list, tuple)): + setattr(self, k, [Config(x) if isinstance(x, dict) else x for x in v]) + else: + setattr(self, k, Config(v) if isinstance(v, dict) else v) + + def __str__(self): + return pformat(self.__dict__) + + def __repr__(self): + return self.__str__() + + +def parse_cli_to_yaml(parser, cfg, helper=None, choices=None, cfg_path="pretrain_base_config.yaml"): + """ + Parse command line arguments to the configuration according to the default yaml. + + Args: + parser: Parent parser. + cfg: Base configuration. + helper: Helper description. + cfg_path: Path to the default yaml config. + """ + parser = argparse.ArgumentParser(description="[REPLACE THIS at config.py]", + parents=[parser]) + helper = {} if helper is None else helper + choices = {} if choices is None else choices + for item in cfg: + if not isinstance(cfg[item], list) and not isinstance(cfg[item], dict): + help_description = helper[item] if item in helper else "Please reference to {}".format(cfg_path) + choice = choices[item] if item in choices else None + if isinstance(cfg[item], bool): + parser.add_argument("--" + item, type=ast.literal_eval, default=cfg[item], choices=choice, + help=help_description) + else: + parser.add_argument("--" + item, type=type(cfg[item]), default=cfg[item], choices=choice, + help=help_description) + args = parser.parse_args() + return args + + +def parse_yaml(yaml_path): + """ + Parse the yaml config file. + + Args: + yaml_path: Path to the yaml config. + """ + with open(yaml_path, 'r') as fin: + try: + cfgs = yaml.load_all(fin.read(), Loader=yaml.FullLoader) + cfgs = [x for x in cfgs] + if len(cfgs) == 1: + cfg_helper = {} + cfg = cfgs[0] + cfg_choices = {} + elif len(cfgs) == 2: + cfg, cfg_helper = cfgs + cfg_choices = {} + elif len(cfgs) == 3: + cfg, cfg_helper, cfg_choices = cfgs + else: + raise ValueError("At most 3 docs (config, description for help, choices) are supported in config yaml") + # print(cfg_helper) + except: + raise ValueError("Failed to parse yaml") + return cfg, cfg_helper, cfg_choices + + +def merge(args, cfg): + """ + Merge the base config from yaml file and command line arguments. + + Args: + args: Command line arguments. + cfg: Base configuration. + """ + args_var = vars(args) + for item in args_var: + cfg[item] = args_var[item] + return cfg + + +def parse_dtype(dtype): + if dtype not in ["mstype.float32", "mstype.float16"]: + raise ValueError("Not supported dtype") + + if dtype == "mstype.float32": + return mstype.float32 + if dtype == "mstype.float16": + return mstype.float16 + return None + +def extra_operations(cfg): + """ + Do extra work on config + + Args: + config: Object after instantiation of class 'Config'. + """ + def create_filter_fun(keywords): + return lambda x: not (True in [key in x.name.lower() for key in keywords]) + + if cfg.description == 'run_pretrain': + cfg.AdamWeightDecay.decay_filter = create_filter_fun(cfg.AdamWeightDecay.decay_filter) + cfg.Lamb.decay_filter = create_filter_fun(cfg.Lamb.decay_filter) + cfg.base_net_cfg.dtype = parse_dtype(cfg.base_net_cfg.dtype) + cfg.base_net_cfg.compute_type = parse_dtype(cfg.base_net_cfg.compute_type) + cfg.nezha_net_cfg.dtype = parse_dtype(cfg.nezha_net_cfg.dtype) + cfg.nezha_net_cfg.compute_type = parse_dtype(cfg.nezha_net_cfg.compute_type) + cfg.large_net_cfg.dtype = parse_dtype(cfg.large_net_cfg.dtype) + cfg.large_net_cfg.compute_type = parse_dtype(cfg.large_net_cfg.compute_type) + cfg.large_boost_net_cfg.dtype = parse_dtype(cfg.large_boost_net_cfg.dtype) + cfg.large_boost_net_cfg.compute_type = parse_dtype(cfg.large_boost_net_cfg.compute_type) + if cfg.bert_network == 'base': + cfg.batch_size = cfg.base_batch_size + _bert_net_cfg = cfg.base_net_cfg + elif cfg.bert_network == 'nezha': + cfg.batch_size = cfg.nezha_batch_size + _bert_net_cfg = cfg.nezha_net_cfg + elif cfg.bert_network == 'large': + cfg.batch_size = cfg.large_batch_size + _bert_net_cfg = cfg.large_net_cfg + elif cfg.bert_network == 'large_boost': + cfg.batch_size = cfg.large_boost_batch_size + _bert_net_cfg = cfg.large_boost_net_cfg + else: + pass + cfg.bert_net_cfg = BertConfig(**_bert_net_cfg.__dict__) + elif cfg.description == 'run_ner': + cfg.optimizer_cfg.AdamWeightDecay.decay_filter = \ + create_filter_fun(cfg.optimizer_cfg.AdamWeightDecay.decay_filter) + cfg.optimizer_cfg.Lamb.decay_filter = create_filter_fun(cfg.optimizer_cfg.Lamb.decay_filter) + cfg.bert_net_cfg.dtype = mstype.float32 + cfg.bert_net_cfg.compute_type = mstype.float16 + cfg.bert_net_cfg = BertConfig(**cfg.bert_net_cfg.__dict__) + + elif cfg.description == 'run_squad': + cfg.optimizer_cfg.AdamWeightDecay.decay_filter = \ + create_filter_fun(cfg.optimizer_cfg.AdamWeightDecay.decay_filter) + cfg.optimizer_cfg.Lamb.decay_filter = create_filter_fun(cfg.optimizer_cfg.Lamb.decay_filter) + cfg.bert_net_cfg.dtype = mstype.float32 + cfg.bert_net_cfg.compute_type = mstype.float16 + cfg.bert_net_cfg = BertConfig(**cfg.bert_net_cfg.__dict__) + + elif cfg.description == 'run_classifier': + cfg.optimizer_cfg.AdamWeightDecay.decay_filter = \ + create_filter_fun(cfg.optimizer_cfg.AdamWeightDecay.decay_filter) + cfg.optimizer_cfg.Lamb.decay_filter = create_filter_fun(cfg.optimizer_cfg.Lamb.decay_filter) + cfg.bert_net_cfg.dtype = mstype.float32 + cfg.bert_net_cfg.compute_type = mstype.float16 + cfg.bert_net_cfg = BertConfig(**cfg.bert_net_cfg.__dict__) + else: + pass + + +def get_config(): + """ + Get Config according to the yaml file and cli arguments. + """ + def get_abs_path(path_relative): + current_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_dir, path_relative) + parser = argparse.ArgumentParser(description="default name", add_help=False) + parser.add_argument("--config_path", type=get_abs_path, default="../../pretrain_config.yaml", + help="Config file path") + path_args, _ = parser.parse_known_args() + default, helper, choices = parse_yaml(path_args.config_path) + args = parse_cli_to_yaml(parser=parser, cfg=default, helper=helper, choices=choices, cfg_path=path_args.config_path) + final_config = merge(args, default) + config_obj = Config(final_config) + extra_operations(config_obj) + return config_obj + + +config = get_config() +bert_net_cfg = config.bert_net_cfg +if config.description in ('run_classifier', 'run_ner', 'run_squad'): + optimizer_cfg = config.optimizer_cfg + + +if __name__ == '__main__': + print(config) diff --git a/nlp/language_model/bert/MindSpore/src/model_utils/device_adapter.py b/nlp/language_model/bert/MindSpore/src/model_utils/device_adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..9c3d21d5e47c22617170887df9da97beff668495 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/model_utils/device_adapter.py @@ -0,0 +1,27 @@ +# 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. +# ============================================================================ + +"""Device adapter for ModelArts""" + +from src.model_utils.config import config + +if config.enable_modelarts: + from src.model_utils.moxing_adapter import get_device_id, get_device_num, get_rank_id, get_job_id +else: + from src.model_utils.local_adapter import get_device_id, get_device_num, get_rank_id, get_job_id + +__all__ = [ + "get_device_id", "get_device_num", "get_rank_id", "get_job_id" +] diff --git a/nlp/language_model/bert/MindSpore/src/model_utils/local_adapter.py b/nlp/language_model/bert/MindSpore/src/model_utils/local_adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..769fa6dc78e59eb66dbc8e6773accdc1d08b649e --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/model_utils/local_adapter.py @@ -0,0 +1,36 @@ +# 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. +# ============================================================================ + +"""Local adapter""" + +import os + +def get_device_id(): + device_id = os.getenv('DEVICE_ID', '0') + return int(device_id) + + +def get_device_num(): + device_num = os.getenv('RANK_SIZE', '1') + return int(device_num) + + +def get_rank_id(): + global_rank_id = os.getenv('RANK_ID', '0') + return int(global_rank_id) + + +def get_job_id(): + return "Local Job" diff --git a/nlp/language_model/bert/MindSpore/src/model_utils/moxing_adapter.py b/nlp/language_model/bert/MindSpore/src/model_utils/moxing_adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..09cb0f0cf0fb88ba809d5ba9a40432b644d789b3 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/model_utils/moxing_adapter.py @@ -0,0 +1,123 @@ +# 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. +# ============================================================================ + +"""Moxing adapter for ModelArts""" + +import os +import functools +from mindspore import context +from mindspore.profiler import Profiler +from src.model_utils.config import config + +_global_sync_count = 0 + +def get_device_id(): + device_id = os.getenv('DEVICE_ID', '0') + return int(device_id) + + +def get_device_num(): + device_num = os.getenv('RANK_SIZE', '1') + return int(device_num) + + +def get_rank_id(): + global_rank_id = os.getenv('RANK_ID', '0') + return int(global_rank_id) + + +def get_job_id(): + job_id = os.getenv('JOB_ID') + job_id = job_id if job_id != "" else "default" + return job_id + +def sync_data(from_path, to_path): + """ + Download data from remote obs to local directory if the first url is remote url and the second one is local path + Upload data from local directory to remote obs in contrast. + """ + import moxing as mox + import time + global _global_sync_count + sync_lock = "/tmp/copy_sync.lock" + str(_global_sync_count) + _global_sync_count += 1 + + # Each server contains 8 devices as most. + if get_device_id() % min(get_device_num(), 8) == 0 and not os.path.exists(sync_lock): + print("from path: ", from_path) + print("to path: ", to_path) + mox.file.copy_parallel(from_path, to_path) + print("===finish data synchronization===") + try: + os.mknod(sync_lock) + # print("os.mknod({}) success".format(sync_lock)) + except IOError: + pass + print("===save flag===") + + while True: + if os.path.exists(sync_lock): + break + time.sleep(1) + + print("Finish sync data from {} to {}.".format(from_path, to_path)) + + +def moxing_wrapper(pre_process=None, post_process=None): + """ + Moxing wrapper to download dataset and upload outputs. + """ + def wrapper(run_func): + @functools.wraps(run_func) + def wrapped_func(*args, **kwargs): + # Download data from data_url + if config.enable_modelarts: + if config.data_url: + sync_data(config.data_url, config.data_path) + print("Dataset downloaded: ", os.listdir(config.data_path)) + if config.checkpoint_url: + sync_data(config.checkpoint_url, config.load_path) + print("Preload downloaded: ", os.listdir(config.load_path)) + if config.train_url: + sync_data(config.train_url, config.output_path) + print("Workspace downloaded: ", os.listdir(config.output_path)) + + context.set_context(save_graphs_path=os.path.join(config.output_path, str(get_rank_id()))) + config.device_num = get_device_num() + config.device_id = get_device_id() + if not os.path.exists(config.output_path): + os.makedirs(config.output_path) + + if pre_process: + pre_process() + + if config.enable_profiling: + profiler = Profiler() + + run_func(*args, **kwargs) + + if config.enable_profiling: + profiler.analyse() + + # Upload data to train_url + if config.enable_modelarts: + if post_process: + post_process() + + if config.train_url: + print("Start to copy output directory") + sync_data(config.output_path, config.train_url) + return wrapped_func + return wrapper diff --git a/nlp/language_model/bert/MindSpore/src/sample_process.py b/nlp/language_model/bert/MindSpore/src/sample_process.py new file mode 100644 index 0000000000000000000000000000000000000000..80b001f8eaffea28f64a39e58906ee8e3d6b59eb --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/sample_process.py @@ -0,0 +1,100 @@ +# Copyright 2020 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. +# ============================================================================ + +"""process txt""" + +import re +from src.tokenization import convert_tokens_to_ids + +def process_one_example_p(tokenizer, vocab, text, max_seq_len=128): + """process one testline""" + textlist = list(text) + tokens = [] + for _, word in enumerate(textlist): + token = tokenizer.tokenize(word) + tokens.extend(token) + if len(tokens) >= max_seq_len - 1: + tokens = tokens[0:(max_seq_len - 2)] + ntokens = [] + segment_ids = [] + label_ids = [] + ntokens.append("[CLS]") + segment_ids.append(0) + for _, token in enumerate(tokens): + ntokens.append(token) + segment_ids.append(0) + ntokens.append("[SEP]") + segment_ids.append(0) + input_ids = convert_tokens_to_ids(vocab, ntokens) + input_mask = [1] * len(input_ids) + while len(input_ids) < max_seq_len: + input_ids.append(0) + input_mask.append(0) + segment_ids.append(0) + label_ids.append(0) + ntokens.append("**NULL**") + assert len(input_ids) == max_seq_len + assert len(input_mask) == max_seq_len + assert len(segment_ids) == max_seq_len + + feature = (input_ids, input_mask, segment_ids) + return feature + +def label_generation(text="", probs=None, tag_to_index=None): + """generate label""" + data = [text] + probs = [probs] + result = [] + label2id = tag_to_index + id2label = [k for k, v in label2id.items()] + + for index, prob in enumerate(probs): + for v in prob[1:len(data[index]) + 1]: + result.append(id2label[int(v)]) + + labels = {} + start = None + index = 0 + for _, t in zip("".join(data), result): + if re.search("^[BS]", t): + if start is not None: + label = result[index - 1][2:] + if labels.get(label): + te_ = text[start:index] + labels[label][te_] = [[start, index - 1]] + else: + te_ = text[start:index] + labels[label] = {te_: [[start, index - 1]]} + start = index + if re.search("^O", t): + if start is not None: + label = result[index - 1][2:] + if labels.get(label): + te_ = text[start:index] + labels[label][te_] = [[start, index - 1]] + else: + te_ = text[start:index] + labels[label] = {te_: [[start, index - 1]]} + start = None + index += 1 + if start is not None: + label = result[start][2:] + if labels.get(label): + te_ = text[start:index] + labels[label][te_] = [[start, index - 1]] + else: + te_ = text[start:index] + labels[label] = {te_: [[start, index - 1]]} + return labels diff --git a/nlp/language_model/bert/MindSpore/src/score.py b/nlp/language_model/bert/MindSpore/src/score.py new file mode 100644 index 0000000000000000000000000000000000000000..08a7e9b69519663507d481e61947b16023ddb034 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/score.py @@ -0,0 +1,79 @@ +# Copyright 2020 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. +# ============================================================================ + +""" +Calculate average F1 score among labels. +""" + +import json + +def get_f1_score_for_each_label(pre_lines, gold_lines, label): + """ + Get F1 score for each label. + Args: + pre_lines: listed label info from pre_file. + gold_lines: listed label info from gold_file. + label: + + Returns: + F1 score for this label. + """ + TP = 0 + FP = 0 + FN = 0 + index = 0 + while index < len(pre_lines): + pre_line = pre_lines[index].get(label, {}) + gold_line = gold_lines[index].get(label, {}) + for sample in pre_line: + if sample in gold_line: + TP += 1 + else: + FP += 1 + for sample in gold_line: + if sample not in pre_line: + FN += 1 + index += 1 + f1 = 2 * TP / (2 * TP + FP + FN) + return f1 + + +def get_f1_score(labels, pre_file, gold_file): + """ + Get F1 scores for each label. + Args: + labels: list of labels. + pre_file: prediction file. + gold_file: ground truth file. + + Returns: + average F1 score on all labels. + """ + pre_lines = [json.loads(line.strip())['label'] for line in open(pre_file) if line.strip()] + gold_lines = [json.loads(line.strip())['label'] for line in open(gold_file) if line.strip()] + if len(pre_lines) != len(gold_lines): + raise ValueError("pre file and gold file have different line count.") + f1_sum = 0 + for label in labels: + f1 = get_f1_score_for_each_label(pre_lines, gold_lines, label) + print('label: %s, F1: %.6f' % (label, f1)) + f1_sum += f1 + + return f1_sum/len(labels) + + +def get_result(labels, pre_file, gold_file): + avg = get_f1_score(labels, pre_file=pre_file, gold_file=gold_file) + print("avg F1: %.6f" % avg) diff --git a/nlp/language_model/bert/MindSpore/src/squad_get_predictions.py b/nlp/language_model/bert/MindSpore/src/squad_get_predictions.py new file mode 100644 index 0000000000000000000000000000000000000000..6a7e947f0dd6054068fc7ba199c3f919e9078895 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/squad_get_predictions.py @@ -0,0 +1,258 @@ +# Copyright 2020 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. +# ============================================================================ + +"""get predictions for squad""" + +import math +import collections +import six +from src import tokenization + + +def get_prelim_predictions(features, unique_id_to_result, n_best_size, max_answer_length): + """get prelim predictions""" + _PrelimPrediction = collections.namedtuple( + "PrelimPrediction", + ["feature_index", "start_index", "end_index", "start_logit", "end_logit"]) + prelim_predictions = [] + # keep track of the minimum score of null start+end of position 0 + for (feature_index, feature) in enumerate(features): + if feature.unique_id not in unique_id_to_result: + continue + result = unique_id_to_result[feature.unique_id] + start_indexes = _get_best_indexes(result.start_logits, n_best_size) + end_indexes = _get_best_indexes(result.end_logits, n_best_size) + # if we could have irrelevant answers, get the min score of irrelevant + for start_index in start_indexes: + for end_index in end_indexes: + # We could hypothetically create invalid predictions, e.g., predict + # that the start of the span is in the question. We throw out all + # invalid predictions. + if start_index >= len(feature.tokens): + continue + if end_index >= len(feature.tokens): + continue + if start_index not in feature.token_to_orig_map: + continue + if end_index not in feature.token_to_orig_map: + continue + if not feature.token_is_max_context.get(start_index, False): + continue + if end_index < start_index: + continue + length = end_index - start_index + 1 + if length > max_answer_length: + continue + prelim_predictions.append( + _PrelimPrediction( + feature_index=feature_index, + start_index=start_index, + end_index=end_index, + start_logit=result.start_logits[start_index], + end_logit=result.end_logits[end_index])) + + prelim_predictions = sorted( + prelim_predictions, + key=lambda x: (x.start_logit + x.end_logit), + reverse=True) + return prelim_predictions + + +def get_nbest(prelim_predictions, features, example, n_best_size, do_lower_case): + """get nbest predictions""" + _NbestPrediction = collections.namedtuple( + "NbestPrediction", ["text", "start_logit", "end_logit"]) + + seen_predictions = {} + nbest = [] + for pred in prelim_predictions: + if len(nbest) >= n_best_size: + break + feature = features[pred.feature_index] + if pred.start_index > 0: # this is a non-null prediction + tok_tokens = feature.tokens[pred.start_index:(pred.end_index + 1)] + orig_doc_start = feature.token_to_orig_map[pred.start_index] + orig_doc_end = feature.token_to_orig_map[pred.end_index] + orig_tokens = example.doc_tokens[orig_doc_start:(orig_doc_end + 1)] + tok_text = " ".join(tok_tokens) + + # De-tokenize WordPieces that have been split off. + tok_text = tok_text.replace(" ##", "") + tok_text = tok_text.replace("##", "") + + # Clean whitespace + tok_text = tok_text.strip() + tok_text = " ".join(tok_text.split()) + orig_text = " ".join(orig_tokens) + final_text = get_final_text(tok_text, orig_text, do_lower_case) + if final_text in seen_predictions: + continue + + seen_predictions[final_text] = True + else: + final_text = "" + seen_predictions[final_text] = True + + nbest.append( + _NbestPrediction( + text=final_text, + start_logit=pred.start_logit, + end_logit=pred.end_logit)) + + # In very rare edge cases we could have no valid predictions. So we + # just create a nonce prediction in this case to avoid failure. + if not nbest: + nbest.append(_NbestPrediction(text="empty", start_logit=0.0, end_logit=0.0)) + + assert len(nbest) >= 1 + return nbest + + +def get_predictions(all_examples, all_features, all_results, n_best_size, max_answer_length, do_lower_case): + """Get final predictions""" + example_index_to_features = collections.defaultdict(list) + for feature in all_features: + example_index_to_features[feature.example_index].append(feature) + + unique_id_to_result = {} + for result in all_results: + unique_id_to_result[result.unique_id] = result + all_predictions = collections.OrderedDict() + + for (example_index, example) in enumerate(all_examples): + features = example_index_to_features[example_index] + prelim_predictions = get_prelim_predictions(features, unique_id_to_result, n_best_size, max_answer_length) + nbest = get_nbest(prelim_predictions, features, example, n_best_size, do_lower_case) + + total_scores = [] + best_non_null_entry = None + for entry in nbest: + total_scores.append(entry.start_logit + entry.end_logit) + if not best_non_null_entry: + if entry.text: + best_non_null_entry = entry + + probs = _compute_softmax(total_scores) + + nbest_json = [] + for (i, entry) in enumerate(nbest): + output = collections.OrderedDict() + output["text"] = entry.text + output["probability"] = probs[i] + output["start_logit"] = entry.start_logit + output["end_logit"] = entry.end_logit + nbest_json.append(output) + + assert len(nbest_json) >= 1 + + all_predictions[example.qas_id] = nbest_json[0]["text"] + return all_predictions + + +def write_predictions(all_examples, all_features, all_results, n_best_size, + max_answer_length, do_lower_case): + """Write final predictions to the json file and log-odds of null if needed.""" + + all_predictions = get_predictions(all_examples, all_features, all_results, + n_best_size, max_answer_length, do_lower_case) + return all_predictions + + +def get_final_text(pred_text, orig_text, do_lower_case): + """Project the tokenized prediction back to the original text.""" + def _strip_spaces(text): + ns_chars = [] + ns_to_s_map = collections.OrderedDict() + for (i, c) in enumerate(text): + if c == " ": + continue + ns_to_s_map[len(ns_chars)] = i + ns_chars.append(c) + ns_text = "".join(ns_chars) + return (ns_text, ns_to_s_map) + + tokenizer = tokenization.BasicTokenizer(do_lower_case=do_lower_case) + tok_text = " ".join(tokenizer.tokenize(orig_text)) + + start_position = tok_text.find(pred_text) + if start_position == -1: + return orig_text + end_position = start_position + len(pred_text) - 1 + + (orig_ns_text, orig_ns_to_s_map) = _strip_spaces(orig_text) + (tok_ns_text, tok_ns_to_s_map) = _strip_spaces(tok_text) + + if len(orig_ns_text) != len(tok_ns_text): + return orig_text + + tok_s_to_ns_map = {} + for (i, tok_index) in six.iteritems(tok_ns_to_s_map): + tok_s_to_ns_map[tok_index] = i + + orig_start_position = None + if start_position in tok_s_to_ns_map: + ns_start_position = tok_s_to_ns_map[start_position] + if ns_start_position in orig_ns_to_s_map: + orig_start_position = orig_ns_to_s_map[ns_start_position] + + if orig_start_position is None: + return orig_text + + orig_end_position = None + if end_position in tok_s_to_ns_map: + ns_end_position = tok_s_to_ns_map[end_position] + if ns_end_position in orig_ns_to_s_map: + orig_end_position = orig_ns_to_s_map[ns_end_position] + + if orig_end_position is None: + return orig_text + + output_text = orig_text[orig_start_position:(orig_end_position + 1)] + return output_text + + +def _get_best_indexes(logits, n_best_size): + """Get the n-best logits from a list.""" + index_and_score = sorted(enumerate(logits), key=lambda x: x[1], reverse=True) + + best_indexes = [] + for (i, score) in enumerate(index_and_score): + if i >= n_best_size: + break + best_indexes.append(score[0]) + return best_indexes + + +def _compute_softmax(scores): + """Compute softmax probability over raw logits.""" + if not scores: + return [] + + max_score = None + for score in scores: + if max_score is None or score > max_score: + max_score = score + + exp_scores = [] + total_sum = 0.0 + for score in scores: + x = math.exp(score - max_score) + exp_scores.append(x) + total_sum += x + + probs = [] + for score in exp_scores: + probs.append(score / total_sum) + return probs diff --git a/nlp/language_model/bert/MindSpore/src/squad_postprocess.py b/nlp/language_model/bert/MindSpore/src/squad_postprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..6ecb7c8327607e3365a8b26d564c449906a3ac10 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/squad_postprocess.py @@ -0,0 +1,97 @@ +# Copyright 2020 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 script for SQuAD v1.1""" + +from collections import Counter +import string +import re +import json +import sys + +def normalize_answer(s): + """Lower text and remove punctuation, articles and extra whitespace.""" + def remove_articles(text): + return re.sub(r'\b(a|an|the)\b', ' ', text) + + def white_space_fix(text): + return ' '.join(text.split()) + + def remove_punc(text): + exclude = set(string.punctuation) + return ''.join(ch for ch in text if ch not in exclude) + + def lower(text): + return text.lower() + + return white_space_fix(remove_articles(remove_punc(lower(s)))) + +def f1_score(prediction, ground_truth): + """calculate f1 score""" + prediction_tokens = normalize_answer(prediction).split() + ground_truth_tokens = normalize_answer(ground_truth).split() + common = Counter(prediction_tokens) & Counter(ground_truth_tokens) + num_same = sum(common.values()) + if num_same == 0: + return 0 + precision = 1.0 * num_same / len(prediction_tokens) + recall = 1.0 * num_same / len(ground_truth_tokens) + f1 = (2 * precision * recall) / (precision + recall) + return f1 + +def exact_match_score(prediction, ground_truth): + return normalize_answer(prediction) == normalize_answer(ground_truth) + +def metric_max_over_ground_truths(metric_fn, prediction, ground_truths): + scores_for_ground_truths = [] + for ground_truth in ground_truths: + score = metric_fn(prediction, ground_truth) + scores_for_ground_truths.append(score) + return max(scores_for_ground_truths) + +def evaluate(dataset, predictions): + """do evaluation""" + f1 = exact_match = total = 0 + for article in dataset: + for paragraph in article['paragraphs']: + for qa in paragraph['qas']: + total += 1 + if qa['id'] not in predictions: + message = 'Unanswered question ' + qa['id'] + \ + ' will receive score 0.' + print(message, file=sys.stderr) + continue + ground_truths = list(map(lambda x: x['text'], qa['answers'])) + if not ground_truths: + continue + prediction = predictions[qa['id']] + exact_match += metric_max_over_ground_truths( + exact_match_score, prediction, ground_truths) + f1 += metric_max_over_ground_truths( + f1_score, prediction, ground_truths) + + exact_match = 100.0 * exact_match / total + f1 = 100.0 * f1 / total + return {'exact_match': exact_match, 'f1': f1} + + +def SQuad_postprocess(dataset_file, all_predictions, output_metrics="output.json"): + with open(dataset_file) as ds: + dataset_json = json.load(ds) + dataset = dataset_json['data'] + re_json = evaluate(dataset, all_predictions) + print(json.dumps(re_json)) + with open(output_metrics, 'w') as wr: + wr.write(json.dumps(re_json)) diff --git a/nlp/language_model/bert/MindSpore/src/tokenization.py b/nlp/language_model/bert/MindSpore/src/tokenization.py new file mode 100644 index 0000000000000000000000000000000000000000..49b11279a8abe9b601d3670d99cde7bca334f9e4 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/tokenization.py @@ -0,0 +1,329 @@ +# Copyright 2020 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. +# ============================================================================ + +""" +Tokenization. +""" + +import unicodedata +import collections + +def convert_to_unicode(text): + """ + Convert text into unicode type. + Args: + text: input str. + + Returns: + input str in unicode. + """ + ret = text + if isinstance(text, str): + ret = text + elif isinstance(text, bytes): + ret = text.decode("utf-8", "ignore") + else: + raise ValueError("Unsupported string type: %s" % (type(text))) + return ret + + +def vocab_to_dict_key_token(vocab_file): + """Loads a vocab file into a dict, key is token.""" + vocab = collections.OrderedDict() + index = 0 + with open(vocab_file, "r") as reader: + while True: + token = convert_to_unicode(reader.readline()) + if not token: + break + token = token.strip() + vocab[token] = index + index += 1 + return vocab + + +def vocab_to_dict_key_id(vocab_file): + """Loads a vocab file into a dict, key is id.""" + vocab = collections.OrderedDict() + index = 0 + with open(vocab_file, "r") as reader: + while True: + token = convert_to_unicode(reader.readline()) + if not token: + break + token = token.strip() + vocab[index] = token + index += 1 + return vocab + + +def whitespace_tokenize(text): + """Runs basic whitespace cleaning and splitting on a piece of text.""" + text = text.strip() + if not text: + return [] + tokens = text.split() + return tokens + + +def convert_tokens_to_ids(vocab_file, tokens): + """ + Convert tokens to ids. + Args: + vocab_file: path to vocab.txt. + tokens: list of tokens. + + Returns: + list of ids. + """ + vocab_dict = vocab_to_dict_key_token(vocab_file) + output = [] + for token in tokens: + output.append(vocab_dict[token]) + return output + + +def convert_ids_to_tokens(vocab_file, ids): + """ + Convert ids to tokens. + Args: + vocab_file: path to vocab.txt. + ids: list of ids. + + Returns: + list of tokens. + """ + vocab_dict = vocab_to_dict_key_id(vocab_file) + output = [] + for _id in ids: + output.append(vocab_dict[_id]) + return output + + +class FullTokenizer(): + """ + Full tokenizer + """ + def __init__(self, vocab_file, do_lower_case=True): + self.vocab_dict = vocab_to_dict_key_token(vocab_file) + self.do_lower_case = do_lower_case + self.basic_tokenize = BasicTokenizer(do_lower_case) + self.wordpiece_tokenize = WordpieceTokenizer(self.vocab_dict) + + def tokenize(self, text): + """ + Do full tokenization. + Args: + text: str of text. + + Returns: + list of tokens. + """ + tokens_ret = [] + text = convert_to_unicode(text) + for tokens in self.basic_tokenize.tokenize(text): + wordpiece_tokens = self.wordpiece_tokenize.tokenize(tokens) + tokens_ret.extend(wordpiece_tokens) + return tokens_ret + + +class BasicTokenizer(): + """ + Basic tokenizer + """ + def __init__(self, do_lower_case=True): + self.do_lower_case = do_lower_case + + def tokenize(self, text): + """ + Do basic tokenization. + Args: + text: text in unicode. + + Returns: + a list of tokens split from text + """ + text = self._clean_text(text) + text = self._tokenize_chinese_chars(text) + + orig_tokens = whitespace_tokenize(text) + split_tokens = [] + for token in orig_tokens: + if self.do_lower_case: + token = token.lower() + token = self._run_strip_accents(token) + aaa = self._run_split_on_punc(token) + split_tokens.extend(aaa) + + output_tokens = whitespace_tokenize(" ".join(split_tokens)) + return output_tokens + + def _run_strip_accents(self, text): + """Strips accents from a piece of text.""" + text = unicodedata.normalize("NFD", text) + output = [] + for char in text: + cat = unicodedata.category(char) + if cat == "Mn": + continue + output.append(char) + return "".join(output) + + def _run_split_on_punc(self, text): + """Splits punctuation on a piece of text.""" + i = 0 + start_new_word = True + output = [] + for char in text: + if _is_punctuation(char): + output.append([char]) + start_new_word = True + else: + if start_new_word: + output.append([]) + start_new_word = False + output[-1].append(char) + i += 1 + return ["".join(x) for x in output] + + def _clean_text(self, text): + """Performs invalid character removal and whitespace cleanup on text.""" + output = [] + for char in text: + cp = ord(char) + if cp == 0 or cp == 0xfffd or _is_control(char): + continue + if _is_whitespace(char): + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _tokenize_chinese_chars(self, text): + """Adds whitespace around any CJK character.""" + output = [] + for char in text: + cp = ord(char) + if self._is_chinese_char(cp): + output.append(" ") + output.append(char) + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _is_chinese_char(self, cp): + """Checks whether CP is the codepoint of a CJK character.""" + # This defines a "chinese character" as anything in the CJK Unicode block: + # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) + # + # Note that the CJK Unicode block is NOT all Japanese and Korean characters, + # despite its name. The modern Korean Hangul alphabet is a different block, + # as is Japanese Hiragana and Katakana. Those alphabets are used to write + # space-separated words, so they are not treated specially and handled + # like the all of the other languages. + if ((0x4E00 <= cp <= 0x9FFF) or + (0x3400 <= cp <= 0x4DBF) or + (0x20000 <= cp <= 0x2A6DF) or + (0x2A700 <= cp <= 0x2B73F) or + (0x2B740 <= cp <= 0x2B81F) or + (0x2B820 <= cp <= 0x2CEAF) or + (0xF900 <= cp <= 0xFAFF) or + (0x2F800 <= cp <= 0x2FA1F)): + return True + + return False + + +class WordpieceTokenizer(): + """ + Wordpiece tokenizer + """ + def __init__(self, vocab): + self.vocab_dict = vocab + + def tokenize(self, tokens): + """ + Do word-piece tokenization + Args: + tokens: a word. + + Returns: + a list of tokens that can be found in vocab dict. + """ + output_tokens = [] + tokens = convert_to_unicode(tokens) + for token in whitespace_tokenize(tokens): + chars = list(token) + len_chars = len(chars) + start = 0 + end = len_chars + while start < len_chars: + while start < end: + substr = "".join(token[start:end]) + if start != 0: + substr = "##" + substr + if substr in self.vocab_dict: + output_tokens.append(substr) + start = end + end = len_chars + else: + end = end - 1 + if start == end and start != len_chars: + output_tokens.append("[UNK]") + break + return output_tokens + + +def _is_whitespace(char): + """Checks whether `chars` is a whitespace character.""" + # \t, \n, and \r are technically control characters but we treat them + # as whitespace since they are generally considered as such. + whitespace_char = [" ", "\t", "\n", "\r"] + if char in whitespace_char: + return True + cat = unicodedata.category(char) + if cat == "Zs": + return True + return False + + +def _is_control(char): + """Checks whether `chars` is a control character.""" + # These are technically control characters but we count them as whitespace + # characters. + control_char = ["\t", "\n", "\r"] + if char in control_char: + return False + cat = unicodedata.category(char) + if cat in ("Cc", "Cf"): + return True + return False + + +def _is_punctuation(char): + """Checks whether `chars` is a punctuation character.""" + cp = ord(char) + # We treat all non-letter/number ASCII as punctuation. + # Characters such as "^", "$", and "`" are not in the Unicode + # Punctuation class but we treat them as punctuation anyways, for + # consistency. + if ((33 <= cp <= 47) or (58 <= cp <= 64) or + (91 <= cp <= 96) or (123 <= cp <= 126)): + return True + cat = unicodedata.category(char) + if cat.startswith("P"): + return True + return False diff --git a/nlp/language_model/bert/MindSpore/src/tools/parallel_tfrecord_to_mindrecord.py b/nlp/language_model/bert/MindSpore/src/tools/parallel_tfrecord_to_mindrecord.py new file mode 100644 index 0000000000000000000000000000000000000000..c65d5f8e52bdec2bee12e65db8f937d8a545027a --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/tools/parallel_tfrecord_to_mindrecord.py @@ -0,0 +1,80 @@ +# Copyright 2022 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 os +from multiprocessing import Pool +from argparse import ArgumentParser +import mindspore.dataset as ds +from mindspore.mindrecord import FileWriter + +def tf_2_mr(item): + item_path = item + if not os.path.exists(args.output_mindrecord_dir): + os.makedirs(args.output_mindrecord_dir, exist_ok=True) + mindrecord_path = args.output_mindrecord_dir + item[item.rfind('/') + 1:item.rfind('.')] + '.mindrecord' + print("Start convert {} to {}.".format(item_path, mindrecord_path)) + writer = FileWriter(file_name=mindrecord_path, shard_num=1, overwrite=True) + nlp_schema = {"input_ids": {"type": "int64", "shape": [-1]}, + "input_mask": {"type": "int64", "shape": [-1]}, + "segment_ids": {"type": "int64", "shape": [-1]}, + "next_sentence_labels": {"type": "int64", "shape": [-1]}, + "masked_lm_positions": {"type": "int64", "shape": [-1]}, + "masked_lm_ids": {"type": "int64", "shape": [-1]}, + "masked_lm_weights": {"type": "float32", "shape": [-1]}} + writer.add_schema(nlp_schema, "it is a preprocessed nlp dataset") + + tf_objs = ds.TFRecordDataset(item_path, shuffle=False) + data = [] + index = 0 + for tf_obj in tf_objs.create_dict_iterator(output_numpy=True): + sample = {"input_ids": tf_obj["input_ids"], + "input_mask": tf_obj["input_mask"], + "segment_ids": tf_obj["segment_ids"], + "next_sentence_labels": tf_obj["next_sentence_labels"], + "masked_lm_positions": tf_obj["masked_lm_positions"], + "masked_lm_ids": tf_obj["masked_lm_ids"], + "masked_lm_weights": tf_obj["masked_lm_weights"]} + data.append(sample) + index += 1 + if index % 2000 == 0: + writer.write_raw_data(data) + data = [] + if data: + writer.write_raw_data(data) + + writer.commit() + print("Convert {} to {} success.".format(item_path, mindrecord_path)) + +def parse_args(): + parser = ArgumentParser(description="Parallel tfrecord to mindrecord") + parser.add_argument("--pool_nums", type=int, default="8", + help="pool nums convert tfrecord to mindrecord, can increase this value to speed up.") + parser.add_argument("--input_tfrecord_dir", type=str, default="", + help="The input data dir contain the .tfrecord files, it is best to use an absolute path.") + parser.add_argument("--output_mindrecord_dir", type=str, default="", + help="The output data dir to save mindrecord, it is best to use an absolute path.") + args_opt = parser.parse_args() + return args_opt + +args = parse_args() +if __name__ == "__main__": + pool = Pool(args.pool_nums) + files = os.listdir(args.input_tfrecord_dir) + items = [] + for file_name in files: + items.append(os.path.join(args.input_tfrecord_dir, file_name)) + for single_file in items: + pool.apply_async(tf_2_mr, (single_file,)) + pool.close() + pool.join() diff --git a/nlp/language_model/bert/MindSpore/src/tools/vis_tfrecord_or_mindrecord.py b/nlp/language_model/bert/MindSpore/src/tools/vis_tfrecord_or_mindrecord.py new file mode 100644 index 0000000000000000000000000000000000000000..b984a59b65e01cfef40335fdf3c866ef693abf29 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/tools/vis_tfrecord_or_mindrecord.py @@ -0,0 +1,57 @@ +# Copyright 2022 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. +# ============================================================================ +from argparse import ArgumentParser +import tensorflow as tf +import mindspore.mindrecord as mm + +def vis_tfrecord(file_name): + tfe = tf.contrib.eager + tfe.enable_eager_execution() + raw_dataset = tf.data.TFRecordDataset(file_name) + # raw_dataset is iterator: you can use raw_dataset.take(n) to get n data + for raw_record in raw_dataset: + example = tf.train.Example() + example.ParseFromString(raw_record.numpy()) + print("Start print tfrecord example:", example, flush=True) + + +def vis_mindrecord(file_name): + fr = mm.FileReader(file_name, num_consumer=4) + for data in fr.get_next(): + print("value is:", data) + # you can use break here to get one data + fr.close() + + +def main(): + """ + vis tfrecord or vis mindrecord + """ + parser = ArgumentParser(description='vis tfrecord or vis mindrecord.') + parser.add_argument("--file_name", type=str, default='./output/AE_wiki_00.tfrecord', help="the file name.") + parser.add_argument("--vis_option", type=str, default='vis_tfrecord', choices=['vis_tfrecord', 'vis_mindrecord'], + help="option of transfer vis_tfrecord or vis_mindrecord, default is vis_tfrecord.") + args = parser.parse_args() + if args.vis_option == 'vis_tfrecord': + print("start vis tfrecord: ", args.file_name, flush=True) + vis_tfrecord(args.file_name) + elif args.vis_option == 'vis_mindrecord': + print("start vis mindrecord: ", args.file_name, flush=True) + vis_mindrecord(args.file_name) + else: + raise ValueError("Unsupported vis option: ", args.vis_option) + +if __name__ == "__main__": + main() diff --git a/nlp/language_model/bert/MindSpore/src/utils.py b/nlp/language_model/bert/MindSpore/src/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..5491f778bac58184761a24f977739495bb81dc0d --- /dev/null +++ b/nlp/language_model/bert/MindSpore/src/utils.py @@ -0,0 +1,282 @@ +# Copyright 2020 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. +# ============================================================================ + +""" +Functional Cells used in Bert finetune and evaluation. +""" + +import os +import math +import collections +import numpy as np +import mindspore.nn as nn +from mindspore import log as logger +from mindspore.ops import operations as P +from mindspore.common.tensor import Tensor +from mindspore.common import dtype as mstype +from mindspore.train.callback import Callback +from mindspore.nn.metrics import Metric +from mindspore.nn.learning_rate_schedule import LearningRateSchedule, PolynomialDecayLR, WarmUpLR + + +class CrossEntropyCalculation(nn.Cell): + """ + Cross Entropy loss + """ + def __init__(self, is_training=True): + super(CrossEntropyCalculation, self).__init__() + self.onehot = P.OneHot() + self.on_value = Tensor(1.0, mstype.float32) + self.off_value = Tensor(0.0, mstype.float32) + self.reduce_sum = P.ReduceSum() + self.reduce_mean = P.ReduceMean() + self.reshape = P.Reshape() + self.last_idx = (-1,) + self.neg = P.Neg() + self.cast = P.Cast() + self.is_training = is_training + + def construct(self, logits, label_ids, num_labels): + if self.is_training: + label_ids = self.reshape(label_ids, self.last_idx) + one_hot_labels = self.onehot(label_ids, num_labels, self.on_value, self.off_value) + per_example_loss = self.neg(self.reduce_sum(one_hot_labels * logits, self.last_idx)) + loss = self.reduce_mean(per_example_loss, self.last_idx) + return_value = self.cast(loss, mstype.float32) + else: + return_value = logits * 1.0 + return return_value + + +def make_directory(path: str): + """Make directory.""" + if path is None or not isinstance(path, str) or path.strip() == "": + logger.error("The path(%r) is invalid type.", path) + raise TypeError("Input path is invalid type") + + # convert the relative paths + path = os.path.realpath(path) + logger.debug("The abs path is %r", path) + + # check the path is exist and write permissions? + if os.path.exists(path): + real_path = path + else: + # All exceptions need to be caught because create directory maybe have some limit(permissions) + logger.debug("The directory(%s) doesn't exist, will create it", path) + try: + os.makedirs(path, exist_ok=True) + real_path = path + except PermissionError as e: + logger.error("No write permission on the directory(%r), error = %r", path, e) + raise TypeError("No write permission on the directory.") + return real_path + +class LossCallBack(Callback): + """ + Monitor the loss in training. + If the loss in NAN or INF terminating training. + Note: + if per_print_times is 0 do not print loss. + Args: + per_print_times (int): Print loss every times. Default: 1. + """ + def __init__(self, dataset_size=-1): + super(LossCallBack, self).__init__() + self._dataset_size = dataset_size + def step_end(self, run_context): + """ + Print loss after each step + """ + cb_params = run_context.original_args() + if self._dataset_size > 0: + percent, epoch_num = math.modf(cb_params.cur_step_num / self._dataset_size) + if percent == 0: + percent = 1 + epoch_num -= 1 + print("epoch: {}, current epoch percent: {}, step: {}, outputs are {}" + .format(int(epoch_num), "%.3f" % percent, cb_params.cur_step_num, str(cb_params.net_outputs)), + flush=True) + else: + print("epoch: {}, step: {}, outputs are {}".format(cb_params.cur_epoch_num, cb_params.cur_step_num, + str(cb_params.net_outputs)), flush=True) + + +def LoadNewestCkpt(load_finetune_checkpoint_dir, prefix): + """ + Find the ckpt finetune generated and load it into eval network. + """ + files = os.listdir(load_finetune_checkpoint_dir) + max_time = 0 + for filename in files: + if filename.startswith(prefix) and filename.endswith(".ckpt"): + full_path = os.path.join(load_finetune_checkpoint_dir, filename) + mtime = os.path.getmtime(full_path) + if mtime > max_time: + max_time = mtime + load_finetune_checkpoint_path = full_path + print("Find the newest checkpoint: ", load_finetune_checkpoint_path) + return load_finetune_checkpoint_path + + +class BertLearningRate(LearningRateSchedule): + """ + Warmup-decay learning rate for Bert network. + """ + def __init__(self, learning_rate, end_learning_rate, warmup_steps, decay_steps, power): + super(BertLearningRate, self).__init__() + self.warmup_flag = False + if warmup_steps > 0: + self.warmup_flag = True + self.warmup_lr = WarmUpLR(learning_rate, warmup_steps) + self.decay_lr = PolynomialDecayLR(learning_rate, end_learning_rate, decay_steps, power) + self.warmup_steps = Tensor(np.array([warmup_steps]).astype(np.float32)) + + self.greater = P.Greater() + self.one = Tensor(np.array([1.0]).astype(np.float32)) + self.cast = P.Cast() + + def construct(self, global_step): + decay_lr = self.decay_lr(global_step) + if self.warmup_flag: + is_warmup = self.cast(self.greater(self.warmup_steps, global_step), mstype.float32) + warmup_lr = self.warmup_lr(global_step) + lr = (self.one - is_warmup) * decay_lr + is_warmup * warmup_lr + else: + lr = decay_lr + return lr + + +def convert_labels_to_index(label_list): + """ + Convert label_list to indices for NER task. + """ + label2id = collections.OrderedDict() + label2id["O"] = 0 + prefix = ["S_", "B_", "M_", "E_"] + index = 0 + for label in label_list: + for pre in prefix: + index += 1 + sub_label = pre + label + label2id[sub_label] = index + return label2id + +def _get_poly_lr(global_step, lr_init, lr_end, lr_max, warmup_steps, total_steps, poly_power): + """ + generate learning rate array + + Args: + global_step(int): current step + lr_init(float): init learning rate + lr_end(float): end learning rate + lr_max(float): max learning rate + warmup_steps(int): number of warmup epochs + total_steps(int): total epoch of training + poly_power(int): poly learning rate power + + Returns: + np.array, learning rate array + """ + lr_each_step = [] + if warmup_steps != 0: + inc_each_step = (float(lr_max) - float(lr_init)) / float(warmup_steps) + else: + inc_each_step = 0 + for i in range(total_steps): + if i < warmup_steps: + lr = float(lr_init) + inc_each_step * float(i) + else: + base = (1.0 - (float(i) - float(warmup_steps)) / (float(total_steps) - float(warmup_steps))) + lr = float(lr_max - lr_end) * (base ** poly_power) + lr = lr + lr_end + if lr < 0.0: + lr = 0.0 + lr_each_step.append(lr) + + learning_rate = np.array(lr_each_step).astype(np.float32) + current_step = global_step + learning_rate = learning_rate[current_step:] + return learning_rate + + +def get_bert_thor_lr(lr_max=0.0034, lr_min=3.244e-05, lr_power=1.0, lr_total_steps=30000): + learning_rate = _get_poly_lr(global_step=0, lr_init=0.0, lr_end=lr_min, lr_max=lr_max, warmup_steps=0, + total_steps=lr_total_steps, poly_power=lr_power) + return Tensor(learning_rate) + + +def get_bert_thor_damping(damping_max=5e-2, damping_min=1e-6, damping_power=1.0, damping_total_steps=30000): + damping = _get_poly_lr(global_step=0, lr_init=0.0, lr_end=damping_min, lr_max=damping_max, warmup_steps=0, + total_steps=damping_total_steps, poly_power=damping_power) + return Tensor(damping) + + +class EvalCallBack(Callback): + """ + Evaluate after a certain amount of training samples. + Args: + model (Model): The network model. + eval_ds (Dataset): The eval dataset. + global_batch (int): The batchsize of the sum of all devices. + eval_samples (int): The number of eval interval samples. + """ + def __init__(self, model, eval_ds, global_batch, eval_samples): + super(EvalCallBack, self).__init__() + self.model = model + self.eval_ds = eval_ds + self.global_batch = global_batch + self.eval_samples = eval_samples + self.last_eval_step = 0 + + def epoch_end(self, run_context): + """ + Evaluate after training a certain number of samples. + """ + cb_params = run_context.original_args() + num_samples = (cb_params.cur_step_num - self.last_eval_step) * self.global_batch + if num_samples < self.eval_samples: + return + self.last_eval_step = cb_params.cur_step_num + total_sumples = cb_params.cur_step_num * self.global_batch + res = self.model.eval(self.eval_ds, dataset_sink_mode=True) + res = res['bert_acc'] + print("====================================", flush=True) + print("Accuracy is: ", "%.6f" % res, ", current samples is: ", total_sumples) + print("====================================", flush=True) + +class BertMetric(Metric): + """ + The metric of bert network. + Args: + batch_size (int): The batchsize of each device. + """ + def __init__(self, batch_size): + super(BertMetric, self).__init__() + self.clear() + self.batch_size = batch_size + + def clear(self): + self.mlm_total = 0 + self.mlm_acc = 0 + + def update(self, *inputs): + mlm_acc = self._convert_data(inputs[0]) + mlm_total = self._convert_data(inputs[1]) + self.mlm_acc += mlm_acc + self.mlm_total += mlm_total + + def eval(self): + return self.mlm_acc / self.mlm_total diff --git a/nlp/language_model/bert/MindSpore/task_classifier_config.yaml b/nlp/language_model/bert/MindSpore/task_classifier_config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6b75084a74e54eff433e5a9fe588b5910570bcfe --- /dev/null +++ b/nlp/language_model/bert/MindSpore/task_classifier_config.yaml @@ -0,0 +1,113 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path" +device_target: "Ascend" +enable_profiling: False + +# ============================================================================== +description: "run_classifier" +assessment_method: "Accuracy" +do_train: "false" +do_eval: "false" +device_id: 0 +epoch_num: 3 +num_class: 2 +train_data_shuffle: "true" +eval_data_shuffle: "false" +train_batch_size: 32 +eval_batch_size: 1 +save_finetune_checkpoint_path: "./classifier_finetune/ckpt/" +load_pretrain_checkpoint_path: "" +load_finetune_checkpoint_path: "" +train_data_file_path: "" +eval_data_file_path: "" +schema_file_path: "" +# export related +export_batch_size: 1 +export_ckpt_file: '' +export_file_name: 'bert_classifier' +file_format: 'MINDIR' + +optimizer_cfg: + optimizer: 'Lamb' + AdamWeightDecay: + learning_rate: 0.00002 # 2e-5 + end_learning_rate: 0.0000000001 # 1e-10 + power: 1.0 + weight_decay: 0.00001 # 1e-5 + decay_filter: ['layernorm', 'bias'] + eps: 0.000001 # 1e-6 + Lamb: + learning_rate: 0.00002 # 2e-5, + end_learning_rate: 0.0000000001 # 1e-10 + power: 1.0 + weight_decay: 0.01 + decay_filter: ['layernorm', 'bias'] + Momentum: + learning_rate: 0.00002 # 2e-5 + momentum: 0.9 + +bert_net_cfg: + seq_length: 128 + vocab_size: 21128 + hidden_size: 768 + num_hidden_layers: 12 + num_attention_heads: 12 + intermediate_size: 3072 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Url for modelarts" +train_url: "Url for modelarts" +data_path: "The location of the input data." +output_path: "The location of the output file." +device_target: "Running platform, choose from Ascend, GPU or CPU(On the CPU, only the export and inference of the onnx model is supported), and default is Ascend." +enable_profiling: 'Whether enable profiling while training, default: False' + +assessment_method: "assessment_method including [Mcc, Spearman_correlation, Accuracy, F1], default is Accuracy" +do_train: "Enable train, default is false" +do_eval: "Enable eval, default is false" +device_id: "Device id, default is 0." +epoch_num: "Epoch number, default is 3." +num_class: "The number of class, default is 2." +train_data_shuffle: "Enable train data shuffle, default is true" +eval_data_shuffle: "Enable eval data shuffle, default is false" +train_batch_size: "Train batch size, default is 32" +eval_batch_size: "Eval batch size, default is 1" +save_finetune_checkpoint_path: "Save checkpoint path" +load_pretrain_checkpoint_path: "Load checkpoint file path" +load_finetune_checkpoint_path: "Load checkpoint file path" +train_data_file_path: "Data path, it is better to use absolute path" +eval_data_file_path: "Data path, it is better to use absolute path" +schema_file_path: "Schema path, it is better to use absolute path" + +export_batch_size: "export batch size." +export_ckpt_file: "Bert ckpt file." +export_file_name: "bert output mindir name." +file_format: "file format" +--- +# chocies +device_target: ['Ascend', 'GPU', 'CPU'] +assessment_method: ["Mcc", "Spearman_correlation", "Accuracy", "F1"] +do_train: ["true", "false"] +do_eval: ["true", "false"] +train_data_shuffle: ["true", "false"] +eval_data_shuffle: ["true", "false"] +file_format: ["AIR", "ONNX", "MINDIR"] \ No newline at end of file diff --git a/nlp/language_model/bert/MindSpore/task_ner_config.yaml b/nlp/language_model/bert/MindSpore/task_ner_config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7243bf2ef780ae76b14583ed190ebf1819764035 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/task_ner_config.yaml @@ -0,0 +1,123 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +data_path: "/cache/data" +output_path: "/cache/train" +load_path: "/cache/checkpoint_path" +device_target: "Ascend" +enable_profiling: False + +# ============================================================================== +description: "run_ner" +assessment_method: "BF1" +do_train: "false" +do_eval: "false" +use_crf: "false" +with_lstm: "false" +device_id: 0 +epoch_num: 5 +train_data_shuffle: "true" +eval_data_shuffle: "false" +train_batch_size: 32 +eval_batch_size: 1 +vocab_file_path: "" +label_file_path: "" +save_finetune_checkpoint_path: "./ner_finetune/ckpt/" +load_pretrain_checkpoint_path: "" +load_finetune_checkpoint_path: "" +train_data_file_path: "" +eval_data_file_path: "" +dataset_format: "mindrecord" +schema_file_path: "" +# export related +export_batch_size: 1 +export_ckpt_file: '' +export_file_name: 'bert_ner' +file_format: 'MINDIR' + +optimizer_cfg: + optimizer: 'Lamb' + AdamWeightDecay: + learning_rate: 0.00002 # 2e-5 + end_learning_rate: 0.0000000001 # 1e-10 + power: 1.0 + weight_decay: 0.00001 # 1e-5 + decay_filter: ['layernorm', 'bias'] + eps: 0.000001 # 1e-6 + Lamb: + learning_rate: 0.00002 # 2e-5, + end_learning_rate: 0.0000000001 # 1e-10 + power: 1.0 + weight_decay: 0.01 + decay_filter: ['layernorm', 'bias'] + Momentum: + learning_rate: 0.00002 # 2e-5 + momentum: 0.9 + +bert_net_cfg: + seq_length: 128 + vocab_size: 21128 + hidden_size: 768 + num_hidden_layers: 12 + num_attention_heads: 12 + intermediate_size: 3072 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Url for modelarts" +train_url: "Url for modelarts" +data_path: "The location of the input data." +output_path: "The location of the output file." +device_target: "Running platform, choose from Ascend or CPU, and default is Ascend." +enable_profiling: 'Whether enable profiling while training, default: False' + +assessment_method: "assessment_method include: [BF1, clue_benchmark, MF1], default is BF1" +do_train: "Eable train, default is false" +do_eval: "Eable eval, default is false" +use_crf: "Use crf, default is false" +device_id: "Device id, default is 0." +epoch_num: "Epoch number, default is 5." +train_data_shuffle: "Enable train data shuffle, default is true" +eval_data_shuffle: "Enable eval data shuffle, default is false" +train_batch_size: "Train batch size, default is 32" +eval_batch_size: "Eval batch size, default is 1" +vocab_file_path: "Vocab file path, used in clue benchmark" +label_file_path: "label file path, used in clue benchmark" +save_finetune_checkpoint_path: "Save checkpoint path" +load_pretrain_checkpoint_path: "Load checkpoint file path" +load_finetune_checkpoint_path: "Load checkpoint file path" +train_data_file_path: "Data path, it is better to use absolute path" +eval_data_file_path: "Data path, it is better to use absolute path" +dataset_format: "Dataset format, support mindrecord or tfrecord" +schema_file_path: "Schema path, it is better to use absolute path" + +export_batch_size: "export batch size." +export_ckpt_file: "Bert ckpt file." +export_file_name: "bert output mindir name." +file_format: "file format" +--- +# chocies +device_target: ['Ascend', 'GPU'] +assessment_method: ["BF1", "clue_benchmark", "MF1"] +do_train: ["true", "false"] +do_eval: ["true", "false"] +use_crf: ["true", "false"] +with_lstm: ["true", "false"] +train_data_shuffle: ["true", "false"] +eval_data_shuffle: ["true", "false"] +dataset_format: ["mindrecord", "tfrecord"] +file_format: ["AIR", "ONNX", "MINDIR"] diff --git a/nlp/language_model/bert/MindSpore/task_squad_config.yaml b/nlp/language_model/bert/MindSpore/task_squad_config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e9fb147579dec7b03a6bdf9e2e19f9291697c775 --- /dev/null +++ b/nlp/language_model/bert/MindSpore/task_squad_config.yaml @@ -0,0 +1,112 @@ +# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing) +enable_modelarts: False +# Url for modelarts +data_url: "" +train_url: "" +checkpoint_url: "" +# Path for local +data_path: "." +output_path: "." +load_path: "." +device_target: "GPU" +enable_profiling: False + +# ============================================================================== +description: "run_squad" +do_train: "false" +do_eval: "false" +device_id: 0 +epoch_num: 3 +num_class: 2 +train_data_shuffle: "true" +eval_data_shuffle: "false" +train_batch_size: 32 +eval_batch_size: 1 +vocab_file_path: "" +eval_json_path: "" +save_finetune_checkpoint_path: "./squad_finetune/ckpt/" +load_pretrain_checkpoint_path: "" +load_finetune_checkpoint_path: "" +train_data_file_path: "" +schema_file_path: "" +# export related +export_batch_size: 1 +export_ckpt_file: '' +export_file_name: 'bert_squad' +file_format: 'MINDIR' + +optimizer_cfg: + optimizer: 'AdamWeightDecay' + AdamWeightDecay: + learning_rate: 0.0001 # 1e-4 + end_learning_rate: 0.00000000001 # 1e-11 + power: 5.0 + weight_decay: 0.001 # 1e-3 + decay_filter: ['layernorm', 'bias'] + eps: 0.000001 # 1e-6 + Lamb: + learning_rate: 0.0001 # 1e-4, + end_learning_rate: 0.00000000001 # 1e-11 + power: 5.0 + weight_decay: 0.01 + decay_filter: ['layernorm', 'bias'] + Momentum: + learning_rate: 0.0001 # 1e-4 + momentum: 0.9 + +bert_net_cfg: + seq_length: 512 + vocab_size: 30522 + hidden_size: 1024 + num_hidden_layers: 24 + num_attention_heads: 16 + intermediate_size: 4096 + hidden_act: "gelu" + hidden_dropout_prob: 0.1 + attention_probs_dropout_prob: 0.1 + max_position_embeddings: 512 + type_vocab_size: 2 + initializer_range: 0.02 + use_relative_positions: False + dtype: mstype.float32 + compute_type: mstype.float16 + +--- +# Help description for each configuration +enable_modelarts: "Whether training on modelarts, default: False" +data_url: "Url for modelarts" +train_url: "Url for modelarts" +data_path: "The location of the input data." +output_path: "The location of the output file." +device_target: "Running platform, choose from Ascend or CPU, and default is Ascend." +enable_profiling: 'Whether enable profiling while training, default: False' + +do_train: "Eable train, default is false" +do_eval: "Eable eval, default is false" +device_id: "Device id, default is 0." +epoch_num: "Epoch number, default is 1." +num_class: "The number of class, default is 2." +train_data_shuffle: "Enable train data shuffle, default is true" +eval_data_shuffle: "Enable eval data shuffle, default is false" +train_batch_size: "Train batch size, default is 32" +eval_batch_size: "Eval batch size, default is 1" +vocab_file_path: "Vocab file path" +eval_json_path: "Evaluation json file path, can be eval.json" +save_finetune_checkpoint_path: "Save checkpoint path" +load_pretrain_checkpoint_path: "Load checkpoint file path" +load_finetune_checkpoint_path: "Load checkpoint file path" +train_data_file_path: "Data path, it is better to use absolute path" +schema_file_path: "Schema path, it is better to use absolute path" + +export_batch_size: "export batch size." +export_ckpt_file: "Bert ckpt file." +export_file_name: "bert output mindir name." +file_format: "file format" +--- +# chocies +device_target: ['Ascend', 'GPU'] +do_train: ["true", "false"] +do_eval: ["true", "false"] +train_data_shuffle: ["true", "false"] +eval_data_shuffle: ["true", "false"] +file_format: ["AIR", "ONNX", "MINDIR"]