From bddddc5164479f27646260fbea657d09d0bc72b7 Mon Sep 17 00:00:00 2001 From: Lin Manhui Date: Mon, 8 Aug 2022 14:25:10 +0800 Subject: [PATCH] [TIPC] Initialize TIPC (#8) * Init TIPC * Update gitignore * Fix bugs * Add docs and more models * Fix bugs --- .gitignore | 4 +- paddlers/tasks/__init__.py | 17 +- paddlers/tasks/base.py | 9 +- paddlers/transforms/batch_operators.py | 5 +- test_tipc/.gitignore | 3 + test_tipc/README.md | 62 +++ test_tipc/common_func.sh | 131 +++++++ test_tipc/config_utils.py | 252 ++++++++++++ test_tipc/configs/cd/_base_/airchange.yaml | 70 ++++ test_tipc/configs/cd/bit/bit.yaml | 8 + .../configs/cd/bit/train_infer_python.txt | 53 +++ test_tipc/configs/clas/_base_/ucmerced.yaml | 72 ++++ test_tipc/configs/clas/hrnet/hrnet.yaml | 10 + .../configs/clas/hrnet/train_infer_python.txt | 53 +++ test_tipc/configs/det/_base_/sarship.yaml | 74 ++++ test_tipc/configs/det/ppyolo/ppyolo.yaml | 10 + .../configs/det/ppyolo/train_infer_python.txt | 53 +++ test_tipc/configs/seg/_base_/rsseg.yaml | 68 ++++ .../configs/seg/unet/train_infer_python.txt | 53 +++ test_tipc/configs/seg/unet/unet.yaml | 11 + test_tipc/docs/overview.png | Bin 0 -> 141626 bytes test_tipc/docs/test_train_inference_python.md | 89 +++++ test_tipc/infer.py | 316 +++++++++++++++ test_tipc/prepare.sh | 43 ++ test_tipc/run_task.py | 103 +++++ test_tipc/test_train_inference.sh | 369 ++++++++++++++++++ 26 files changed, 1929 insertions(+), 9 deletions(-) create mode 100644 test_tipc/.gitignore create mode 100644 test_tipc/README.md create mode 100644 test_tipc/common_func.sh create mode 100644 test_tipc/config_utils.py create mode 100644 test_tipc/configs/cd/_base_/airchange.yaml create mode 100644 test_tipc/configs/cd/bit/bit.yaml create mode 100644 test_tipc/configs/cd/bit/train_infer_python.txt create mode 100644 test_tipc/configs/clas/_base_/ucmerced.yaml create mode 100644 test_tipc/configs/clas/hrnet/hrnet.yaml create mode 100644 test_tipc/configs/clas/hrnet/train_infer_python.txt create mode 100644 test_tipc/configs/det/_base_/sarship.yaml create mode 100644 test_tipc/configs/det/ppyolo/ppyolo.yaml create mode 100644 test_tipc/configs/det/ppyolo/train_infer_python.txt create mode 100644 test_tipc/configs/seg/_base_/rsseg.yaml create mode 100644 test_tipc/configs/seg/unet/train_infer_python.txt create mode 100644 test_tipc/configs/seg/unet/unet.yaml create mode 100644 test_tipc/docs/overview.png create mode 100644 test_tipc/docs/test_train_inference_python.md create mode 100644 test_tipc/infer.py create mode 100644 test_tipc/prepare.sh create mode 100644 test_tipc/run_task.py create mode 100644 test_tipc/test_train_inference.sh diff --git a/.gitignore b/.gitignore index f615de4..d0991ff 100644 --- a/.gitignore +++ b/.gitignore @@ -132,9 +132,11 @@ dmypy.json # Pyre type checker .pyre/ -# testdata +# test data tutorials/train/change_detection/DataSet/ tutorials/train/classification/DataSet/ optic_disc_seg.tar optic_disc_seg/ output/ + +/log \ No newline at end of file diff --git a/paddlers/tasks/__init__.py b/paddlers/tasks/__init__.py index d7b20d9..5c1f428 100644 --- a/paddlers/tasks/__init__.py +++ b/paddlers/tasks/__init__.py @@ -12,9 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -import paddlers.tasks.object_detector as det -import paddlers.tasks.segmenter as seg -import paddlers.tasks.change_detector as cd -import paddlers.tasks.classifier as clas -import paddlers.tasks.image_restorer as res +import paddlers.tasks.object_detector as detector +import paddlers.tasks.segmenter as segmenter +import paddlers.tasks.change_detector as change_detector +import paddlers.tasks.classifier as classifier +import paddlers.tasks.image_restorer as restorer from .load_model import load_model + +# Shorter aliases +det = detector +seg = segmenter +cd = change_detector +clas = classifier +res = restorer diff --git a/paddlers/tasks/base.py b/paddlers/tasks/base.py index 0e2e901..c8642c7 100644 --- a/paddlers/tasks/base.py +++ b/paddlers/tasks/base.py @@ -194,10 +194,15 @@ class BaseModel(metaclass=ModelMeta): info['Transforms'] = list() for op in self.test_transforms.transforms: name = op.__class__.__name__ - if name.startswith('Arrange'): - continue attr = op.__dict__ info['Transforms'].append({name: attr}) + arrange = self.test_transforms.arrange + if arrange is not None: + info['Transforms'].append({ + arrange.__class__.__name__: { + 'mode': 'test' + } + }) info['completed_epochs'] = self.completed_epochs return info diff --git a/paddlers/transforms/batch_operators.py b/paddlers/transforms/batch_operators.py index c2496c5..2c723d1 100644 --- a/paddlers/transforms/batch_operators.py +++ b/paddlers/transforms/batch_operators.py @@ -62,7 +62,10 @@ class BatchCompose(Transform): for i in range(len(samples)): tmp_data.append(samples[i][k]) if not 'gt_' in k and not 'is_crowd' in k and not 'difficult' in k: - tmp_data = np.stack(tmp_data, axis=0) + # This if assumes that all elements in tmp_data has the same type. + if len(tmp_data) == 0 or not isinstance(tmp_data[0], + (str, bytes)): + tmp_data = np.stack(tmp_data, axis=0) batch_data[k] = tmp_data return batch_data diff --git a/test_tipc/.gitignore b/test_tipc/.gitignore new file mode 100644 index 0000000..8edf5d4 --- /dev/null +++ b/test_tipc/.gitignore @@ -0,0 +1,3 @@ +/scripts/ +/output/ +/data/ \ No newline at end of file diff --git a/test_tipc/README.md b/test_tipc/README.md new file mode 100644 index 0000000..76affc3 --- /dev/null +++ b/test_tipc/README.md @@ -0,0 +1,62 @@ +# 飞桨训推一体全流程(TIPC) + +## 1. 简介 + +飞桨除了基本的模型训练和预测,还提供了支持多端多平台的高性能推理部署工具。本文档提供了飞桨训推一体全流程(Training and Inference Pipeline Criterion(TIPC))信息和测试工具,方便用户查阅每种模型的训练推理部署打通情况,并可以进行一键测试。 + +
+ +
+ +## 2. 汇总信息 + +打通情况汇总如下,已填写的部分表示可以使用本工具进行一键测试,未填写的表示正在支持中。 + +**字段说明:** +- 基础训练预测:指Linux GPU/CPU环境下的模型训练、Paddle Inference Python预测。 +- 更多训练方式:包括多机多卡、混合精度训练。 +- 更多部署方式:包括C++预测、Serving服务化部署、ARM端侧部署等多种部署方式,具体列表见[3.3节](#3.3) +- Slim训练部署:包括PACT在线量化、离线量化。 +- 更多训练环境:包括Windows GPU/CPU、Linux NPU、Linux DCU等多种环境。 + + +| 任务类别 | 模型名称 | 基础
训练预测 | 更多
训练方式 | 更多
部署方式 | Slim
训练部署 | 更多
训练环境 | +| :--- | :--- | :----: | :--------: | :----: | :----: | :----: | :----: | +| 变化检测 | BIT | 支持 | - | - | - | - | +| 场景分类 | HRNet | 支持 | - | - | - | - | +| 目标检测 | PP-YOLO | 支持 | - | - | - | - | +| 图像分割 | UNet | 支持 | - | - | - | - | + + +## 3. 测试工具简介 + +### 3.1 目录介绍 + +``` +test_tipc + |--configs # 配置目录 + | |--task_name # 任务名称 + | |--model_name # 模型名称 + | |--train_infer_python.txt # 基础训练推理测试配置文件 + |--docs # 文档目录 + | |--test_train_inference_python.md # 基础训练推理测试说明文档 + |----README.md # TIPC说明文档 + |----prepare.sh # TIPC基础训练推理测试数据准备脚本 + |----test_train_inference_python.sh # TIPC基础训练推理测试解析脚本 + |----common_func.sh # TIPC基础训练推理测试常用函数 +``` + +### 3.2 测试流程概述 + +使用本工具,可以测试不同功能的支持情况。测试过程包含: + +1. 准备数据与环境 +2. 运行测试脚本,观察不同配置是否运行成功。 + + +### 3.3 开始测试 + +请参考相应文档,完成指定功能的测试。 + +- 基础训练预测测试: + - [Linux GPU/CPU 基础训练推理测试](docs/test_train_inference_python.md) diff --git a/test_tipc/common_func.sh b/test_tipc/common_func.sh new file mode 100644 index 0000000..506ac82 --- /dev/null +++ b/test_tipc/common_func.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +function func_parser_key() { + strs=$1 + IFS=":" + array=(${strs}) + tmp=${array[0]} + echo ${tmp} +} + +function func_parser_value() { + strs=$1 + IFS=":" + array=(${strs}) + tmp=${array[1]} + echo ${tmp} +} + +function func_parser_value_lite() { + strs=$1 + IFS=$2 + array=(${strs}) + tmp=${array[1]} + echo ${tmp} +} + +function func_set_params() { + key=$1 + value=$2 + if [ ${key}x = "null"x ];then + echo " " + elif [[ ${value} = "null" ]] || [[ ${value} = " " ]] || [ ${#value} -le 0 ];then + echo " " + else + echo "${key}=${value}" + fi +} + +function func_parser_params() { + strs=$1 + IFS=":" + array=(${strs}) + key=${array[0]} + tmp=${array[1]} + IFS="|" + res="" + for _params in ${tmp[*]}; do + IFS="=" + array=(${_params}) + mode=${array[0]} + value=${array[1]} + if [[ ${mode} = ${MODE} ]]; then + IFS="|" + #echo $(func_set_params "${mode}" "${value}") + echo $value + break + fi + IFS="|" + done + echo ${res} +} + +function status_check() { + local last_status=$1 # the exit code + local run_command=$2 + local run_log=$3 + local model_name=$4 + + if [ $last_status -eq 0 ]; then + echo -e "\033[33m Run successfully with command - ${model_name} - ${run_command}! \033[0m" | tee -a ${run_log} + else + echo -e "\033[33m Run failed with command - ${model_name} - ${run_command}! \033[0m" | tee -a ${run_log} + fi +} + +function download_and_unzip_dataset() { + local ds_dir="$1" + local ds_name="$2" + local url="$3" + local clear="${4-True}" + + local ds_path="${ds_dir}/${ds_name}" + local zip_name="${url##*/}" + + if [ ${clear} = 'True' ]; then + rm -rf "${ds_path}" + fi + + wget -nc -P "${ds_dir}" "${url}" --no-check-certificate + cd "${ds_dir}" && unzip "${zip_name}" && cd - \ + && echo "Successfully downloaded ${zip_name} from ${url}. File saved in ${ds_path}. " +} + +function parse_extra_args() { + local lines=("$@") + local last_idx=$((${#lines[@]}-1)) + local IFS=';' + extra_args=(${lines[last_idx]}) +} + +function add_suffix() { + local ori_path="$1" + local suffix=$2 + local ext="${ori_path##*.}" + echo "${ori_path%.*}${suffix}.${ext}" +} + +function parse_first_value() { + local key_values=$1 + local IFS=":" + local arr=(${key_values}) + echo ${arr[1]} +} + +function parse_second_value() { + local key_values=$1 + local IFS=":" + local arr=(${key_values}) + echo ${arr[2]} +} + +function run_command() { + local cmd="$1" + local log_path="$2" + if [ -n "${log_path}" ]; then + eval ${cmd} | tee "${log_path}" + test ${PIPESTATUS[0]} -eq 0 + else + eval ${cmd} + fi +} diff --git a/test_tipc/config_utils.py b/test_tipc/config_utils.py new file mode 100644 index 0000000..fa137f8 --- /dev/null +++ b/test_tipc/config_utils.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python + +import argparse +import os.path as osp +from collections.abc import Mapping + +import yaml + + +def _chain_maps(*maps): + chained = dict() + keys = set().union(*maps) + for key in keys: + vals = [m[key] for m in maps if key in m] + if isinstance(vals[0], Mapping): + chained[key] = _chain_maps(*vals) + else: + chained[key] = vals[0] + return chained + + +def read_config(config_path): + with open(config_path, 'r', encoding='utf-8') as f: + cfg = yaml.safe_load(f) + return cfg or {} + + +def parse_configs(cfg_path, inherit=True): + if inherit: + cfgs = [] + cfgs.append(read_config(cfg_path)) + while cfgs[-1].get('_base_'): + base_path = cfgs[-1].pop('_base_') + curr_dir = osp.dirname(cfg_path) + cfgs.append( + read_config(osp.normpath(osp.join(curr_dir, base_path)))) + return _chain_maps(*cfgs) + else: + return read_config(cfg_path) + + +def _cfg2args(cfg, parser, prefix=''): + node_keys = set() + for k, v in cfg.items(): + opt = prefix + k + if isinstance(v, list): + if len(v) == 0: + parser.add_argument( + '--' + opt, type=object, nargs='*', default=v) + else: + # Only apply to homogeneous lists + if isinstance(v[0], CfgNode): + node_keys.add(opt) + parser.add_argument( + '--' + opt, type=type(v[0]), nargs='*', default=v) + elif isinstance(v, dict): + # Recursively parse a dict + _, new_node_keys = _cfg2args(v, parser, opt + '.') + node_keys.update(new_node_keys) + elif isinstance(v, CfgNode): + node_keys.add(opt) + _, new_node_keys = _cfg2args(v.to_dict(), parser, opt + '.') + node_keys.update(new_node_keys) + elif isinstance(v, bool): + parser.add_argument('--' + opt, action='store_true', default=v) + else: + parser.add_argument('--' + opt, type=type(v), default=v) + return parser, node_keys + + +def _args2cfg(cfg, args, node_keys): + args = vars(args) + for k, v in args.items(): + pos = k.find('.') + if pos != -1: + # Iteratively parse a dict + dict_ = cfg + while pos != -1: + dict_.setdefault(k[:pos], {}) + dict_ = dict_[k[:pos]] + k = k[pos + 1:] + pos = k.find('.') + dict_[k] = v + else: + cfg[k] = v + + for k in node_keys: + pos = k.find('.') + if pos != -1: + # Iteratively parse a dict + dict_ = cfg + while pos != -1: + dict_.setdefault(k[:pos], {}) + dict_ = dict_[k[:pos]] + k = k[pos + 1:] + pos = k.find('.') + v = dict_[k] + dict_[k] = [CfgNode(v_) for v_ in v] if isinstance( + v, list) else CfgNode(v) + else: + v = cfg[k] + cfg[k] = [CfgNode(v_) for v_ in v] if isinstance( + v, list) else CfgNode(v) + + return cfg + + +def parse_args(*args, **kwargs): + cfg_parser = argparse.ArgumentParser(add_help=False) + cfg_parser.add_argument('--config', type=str, default='') + cfg_parser.add_argument('--inherit_off', action='store_true') + cfg_args = cfg_parser.parse_known_args()[0] + cfg_path = cfg_args.config + inherit_on = not cfg_args.inherit_off + + # Main parser + parser = argparse.ArgumentParser( + conflict_handler='resolve', parents=[cfg_parser]) + # Global settings + parser.add_argument('cmd', choices=['train', 'eval']) + parser.add_argument('task', choices=['cd', 'clas', 'det', 'seg']) + + # Data + parser.add_argument('--datasets', type=dict, default={}) + parser.add_argument('--transforms', type=dict, default={}) + parser.add_argument('--download_on', action='store_true') + parser.add_argument('--download_url', type=str, default='') + parser.add_argument('--download_path', type=str, default='./') + + # Optimizer + parser.add_argument('--optimizer', type=dict, default={}) + + # Training related + parser.add_argument('--num_epochs', type=int, default=100) + parser.add_argument('--train_batch_size', type=int, default=8) + parser.add_argument('--save_interval_epochs', type=int, default=1) + parser.add_argument('--log_interval_steps', type=int, default=1) + parser.add_argument('--save_dir', default='../exp/') + parser.add_argument('--learning_rate', type=float, default=0.01) + parser.add_argument('--early_stop', action='store_true') + parser.add_argument('--early_stop_patience', type=int, default=5) + parser.add_argument('--use_vdl', action='store_true') + parser.add_argument('--resume_checkpoint', type=str) + parser.add_argument('--train', type=dict, default={}) + + # Loss + parser.add_argument('--losses', type=dict, nargs='+', default={}) + + # Model + parser.add_argument('--model', type=dict, default={}) + + if osp.exists(cfg_path): + cfg = parse_configs(cfg_path, inherit_on) + parser, node_keys = _cfg2args(cfg, parser, '') + args = parser.parse_args(*args, **kwargs) + return _args2cfg(dict(), args, node_keys) + elif cfg_path != '': + raise FileNotFoundError + else: + args = parser.parse_args() + return _args2cfg(dict(), args, set()) + + +class _CfgNodeMeta(yaml.YAMLObjectMetaclass): + def __call__(cls, obj): + if isinstance(obj, CfgNode): + return obj + return super(_CfgNodeMeta, cls).__call__(obj) + + +class CfgNode(yaml.YAMLObject, metaclass=_CfgNodeMeta): + yaml_tag = u'!Node' + yaml_loader = yaml.SafeLoader + # By default use a lexical scope + ctx = globals() + + def __init__(self, dict_): + super().__init__() + self.type = dict_['type'] + self.args = dict_.get('args', []) + self.module = self._get_module(dict_.get('module', '')) + + @classmethod + def set_context(cls, ctx): + # TODO: Implement dynamic scope with inspect.stack() + old_ctx = cls.ctx + cls.ctx = ctx + return old_ctx + + def build_object(self, mod=None): + if mod is None: + mod = self.module + cls = getattr(mod, self.type) + if isinstance(self.args, list): + args = build_objects(self.args) + obj = cls(*args) + elif isinstance(self.args, dict): + args = build_objects(self.args) + obj = cls(**args) + else: + raise NotImplementedError + return obj + + def _get_module(self, s): + mod = None + while s: + idx = s.find('.') + if idx == -1: + next_ = s + s = '' + else: + next_ = s[:idx] + s = s[idx + 1:] + if mod is None: + mod = self.ctx[next_] + else: + mod = getattr(mod, next_) + return mod + + @staticmethod + def build_objects(cfg, mod=None): + if isinstance(cfg, list): + return [CfgNode.build_objects(c, mod=mod) for c in cfg] + elif isinstance(cfg, CfgNode): + return cfg.build_object(mod=mod) + elif isinstance(cfg, dict): + return { + k: CfgNode.build_objects( + v, mod=mod) + for k, v in cfg.items() + } + else: + return cfg + + def __repr__(self): + return f"(type={self.type}, args={self.args}, module={self.module or ' '})" + + @classmethod + def from_yaml(cls, loader, node): + map_ = loader.construct_mapping(node) + return cls(map_) + + def items(self): + yield from [('type', self.type), ('args', self.args), ('module', + self.module)] + + def to_dict(self): + return dict(self.items()) + + +def build_objects(cfg, mod=None): + return CfgNode.build_objects(cfg, mod=mod) diff --git a/test_tipc/configs/cd/_base_/airchange.yaml b/test_tipc/configs/cd/_base_/airchange.yaml new file mode 100644 index 0000000..38ec406 --- /dev/null +++ b/test_tipc/configs/cd/_base_/airchange.yaml @@ -0,0 +1,70 @@ +# Basic configurations of AirChange dataset + +datasets: + train: !Node + type: CDDataset + args: + data_dir: ./test_tipc/data/airchange/ + file_list: ./test_tipc/data/airchange/train.txt + label_list: null + num_workers: 0 + shuffle: True + with_seg_labels: False + binarize_labels: True + eval: !Node + type: CDDataset + args: + data_dir: ./test_tipc/data/airchange/ + file_list: ./test_tipc/data/airchange/eval.txt + label_list: null + num_workers: 0 + shuffle: False + with_seg_labels: False + binarize_labels: True +transforms: + train: + - !Node + type: DecodeImg + - !Node + type: RandomCrop + args: + crop_size: 256 + aspect_ratio: [0.5, 2.0] + scaling: [0.2, 1.0] + - !Node + type: RandomHorizontalFlip + args: + prob: 0.5 + - !Node + type: Normalize + args: + mean: [0.5, 0.5, 0.5] + std: [0.5, 0.5, 0.5] + - !Node + type: ArrangeChangeDetector + args: ['train'] + eval: + - !Node + type: DecodeImg + - !Node + type: Normalize + args: + mean: [0.5, 0.5, 0.5] + std: [0.5, 0.5, 0.5] + - !Node + type: ArrangeChangeDetector + args: ['eval'] +download_on: False +download_url: https://paddlers.bj.bcebos.com/datasets/airchange.zip +download_path: ./test_tipc/data/ + +num_epochs: 5 +train_batch_size: 4 +save_interval_epochs: 3 +log_interval_steps: 50 +save_dir: ./test_tipc/output/cd/ +learning_rate: 0.01 +early_stop: False +early_stop_patience: 5 +use_vdl: False +resume_checkpoint: '' \ No newline at end of file diff --git a/test_tipc/configs/cd/bit/bit.yaml b/test_tipc/configs/cd/bit/bit.yaml new file mode 100644 index 0000000..735a318 --- /dev/null +++ b/test_tipc/configs/cd/bit/bit.yaml @@ -0,0 +1,8 @@ +# Basic configurations of BIT + +_base_: ../_base_/airchange.yaml + +save_dir: ./test_tipc/output/cd/bit/ + +model: !Node + type: BIT \ No newline at end of file diff --git a/test_tipc/configs/cd/bit/train_infer_python.txt b/test_tipc/configs/cd/bit/train_infer_python.txt new file mode 100644 index 0000000..69ddeae --- /dev/null +++ b/test_tipc/configs/cd/bit/train_infer_python.txt @@ -0,0 +1,53 @@ +===========================train_params=========================== +model_name:cd:bit +python:python +gpu_list:0|0,1 +use_gpu:null|null +--precision:null +--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=10 +--save_dir:adaptive +--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=4 +--model_path:null +train_model_name:best_model +train_infer_file_list:./test_tipc/data/airchange/:./test_tipc/data/airchange/eval.txt +null:null +## +trainer:norm +norm_train:test_tipc/run_task.py train cd --config ./test_tipc/configs/cd/bit/bit.yaml +pact_train:null +fpgm_train:null +distill_train:null +null:null +null:null +## +===========================eval_params=========================== +eval:null +null:null +## +===========================export_params=========================== +--save_dir:adaptive +--model_dir:adaptive +--fixed_input_shape:[-1,3,256,256] +norm_export:deploy/export/export_model.py +quant_export:null +fpgm_export:null +distill_export:null +export1:null +export2:null +===========================infer_params=========================== +infer_model:null +infer_export:null +infer_quant:False +inference:test_tipc/infer.py +--device:cpu|gpu +--enable_mkldnn:True +--cpu_threads:6 +--batch_size:1 +--use_trt:False +--precision:fp32 +--model_dir:null +--file_list:null:null +--save_log_path:null +--benchmark:True +--model_name:bit +null:null \ No newline at end of file diff --git a/test_tipc/configs/clas/_base_/ucmerced.yaml b/test_tipc/configs/clas/_base_/ucmerced.yaml new file mode 100644 index 0000000..424f45e --- /dev/null +++ b/test_tipc/configs/clas/_base_/ucmerced.yaml @@ -0,0 +1,72 @@ +# Basic configurations of UCMerced dataset + +datasets: + train: !Node + type: ClasDataset + args: + data_dir: ./test_tipc/data/ucmerced/ + file_list: ./test_tipc/data/ucmerced/train.txt + label_list: ./test_tipc/data/ucmerced/labels.txt + num_workers: 0 + shuffle: True + eval: !Node + type: ClasDataset + args: + data_dir: ./test_tipc/data/ucmerced/ + file_list: ./test_tipc/data/ucmerced/val.txt + label_list: ./test_tipc/data/ucmerced/labels.txt + num_workers: 0 + shuffle: False +transforms: + train: + - !Node + type: DecodeImg + - !Node + type: Resize + args: + target_size: 256 + - !Node + type: RandomHorizontalFlip + args: + prob: 0.5 + - !Node + type: RandomVerticalFlip + args: + prob: 0.5 + - !Node + type: Normalize + args: + mean: [0.5, 0.5, 0.5] + std: [0.5, 0.5, 0.5] + - !Node + type: ArrangeClassifier + args: ['train'] + eval: + - !Node + type: DecodeImg + - !Node + type: Resize + args: + target_size: 256 + - !Node + type: Normalize + args: + mean: [0.5, 0.5, 0.5] + std: [0.5, 0.5, 0.5] + - !Node + type: ArrangeClassifier + args: ['eval'] +download_on: False +download_url: https://paddlers.bj.bcebos.com/datasets/ucmerced.zip +download_path: ./test_tipc/data/ + +num_epochs: 2 +train_batch_size: 16 +save_interval_epochs: 5 +log_interval_steps: 50 +save_dir: e./test_tipc/output/clas/ +learning_rate: 0.01 +early_stop: False +early_stop_patience: 5 +use_vdl: False +resume_checkpoint: '' \ No newline at end of file diff --git a/test_tipc/configs/clas/hrnet/hrnet.yaml b/test_tipc/configs/clas/hrnet/hrnet.yaml new file mode 100644 index 0000000..2f0a000 --- /dev/null +++ b/test_tipc/configs/clas/hrnet/hrnet.yaml @@ -0,0 +1,10 @@ +# Basic configurations of HRNet + +_base_: ../_base_/ucmerced.yaml + +save_dir: ./test_tipc/output/clas/hrnet/ + +model: !Node + type: HRNet_W18_C + args: + num_classes: 21 \ No newline at end of file diff --git a/test_tipc/configs/clas/hrnet/train_infer_python.txt b/test_tipc/configs/clas/hrnet/train_infer_python.txt new file mode 100644 index 0000000..23f3820 --- /dev/null +++ b/test_tipc/configs/clas/hrnet/train_infer_python.txt @@ -0,0 +1,53 @@ +===========================train_params=========================== +model_name:clas:hrnet +python:python +gpu_list:0|0,1 +use_gpu:null|null +--precision:null +--num_epochs:lite_train_lite_infer=3|lite_train_whole_infer=3|whole_train_whole_infer=10 +--save_dir:adaptive +--train_batch_size:lite_train_lite_infer=16|lite_train_whole_infer=16|whole_train_whole_infer=16 +--model_path:null +train_model_name:best_model +train_infer_file_list:./test_tipc/data/ucmerced/:./test_tipc/data/ucmerced/val.txt +null:null +## +trainer:norm +norm_train:test_tipc/run_task.py train clas --config ./test_tipc/configs/clas/hrnet/hrnet.yaml +pact_train:null +fpgm_train:null +distill_train:null +null:null +null:null +## +===========================eval_params=========================== +eval:null +null:null +## +===========================export_params=========================== +--save_dir:adaptive +--model_dir:adaptive +--fixed_input_shape:[-1,3,256,256] +norm_export:deploy/export/export_model.py +quant_export:null +fpgm_export:null +distill_export:null +export1:null +export2:null +===========================infer_params=========================== +infer_model:null +infer_export:null +infer_quant:False +inference:test_tipc/infer.py +--device:cpu|gpu +--enable_mkldnn:True +--cpu_threads:6 +--batch_size:1 +--use_trt:False +--precision:fp32 +--model_dir:null +--file_list:null:null +--save_log_path:null +--benchmark:True +--model_name:hrnet +null:null \ No newline at end of file diff --git a/test_tipc/configs/det/_base_/sarship.yaml b/test_tipc/configs/det/_base_/sarship.yaml new file mode 100644 index 0000000..c7c6afe --- /dev/null +++ b/test_tipc/configs/det/_base_/sarship.yaml @@ -0,0 +1,74 @@ +# Basic configurations of SARShip dataset + +datasets: + train: !Node + type: VOCDetDataset + args: + data_dir: ./test_tipc/data/sarship/ + file_list: ./test_tipc/data/sarship/train.txt + label_list: ./test_tipc/data/sarship/labels.txt + shuffle: True + eval: !Node + type: VOCDetDataset + args: + data_dir: ./test_tipc/data/sarship/ + file_list: ./test_tipc/data/sarship/eval.txt + label_list: ./test_tipc/data/sarship/labels.txt + shuffle: False +transforms: + train: + - !Node + type: DecodeImg + - !Node + type: RandomDistort + - !Node + type: RandomExpand + - !Node + type: RandomCrop + - !Node + type: RandomHorizontalFlip + - !Node + type: BatchRandomResize + args: + target_sizes: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608] + interp: RANDOM + - !Node + type: Normalize + args: + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + - !Node + type: ArrangeDetector + args: ['train'] + eval: + - !Node + type: DecodeImg + - !Node + type: Resize + args: + target_size: 608 + interp: CUBIC + - !Node + type: Normalize + args: + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + - !Node + type: ArrangeDetector + args: ['eval'] +download_on: False +download_url: https://paddlers.bj.bcebos.com/datasets/sarship.zip +download_path: ./test_tipc/data/ + +num_epochs: 10 +train_batch_size: 4 +save_interval_epochs: 5 +log_interval_steps: 4 +save_dir: ./test_tipc/output/det/ +learning_rate: 0.0005 +use_vdl: False +resume_checkpoint: '' +train: + pretrain_weights: COCO + warmup_steps: 0 + warmup_start_lr: 0.0 \ No newline at end of file diff --git a/test_tipc/configs/det/ppyolo/ppyolo.yaml b/test_tipc/configs/det/ppyolo/ppyolo.yaml new file mode 100644 index 0000000..6d9ef3d --- /dev/null +++ b/test_tipc/configs/det/ppyolo/ppyolo.yaml @@ -0,0 +1,10 @@ +# Basic configurations of PP-YOLO + +_base_: ../_base_/sarship.yaml + +save_dir: ./test_tipc/output/det/ppyolo/ + +model: !Node + type: PPYOLO + args: + num_classes: 1 \ No newline at end of file diff --git a/test_tipc/configs/det/ppyolo/train_infer_python.txt b/test_tipc/configs/det/ppyolo/train_infer_python.txt new file mode 100644 index 0000000..43a47fa --- /dev/null +++ b/test_tipc/configs/det/ppyolo/train_infer_python.txt @@ -0,0 +1,53 @@ +===========================train_params=========================== +model_name:det:ppyolo +python:python +gpu_list:0|0,1 +use_gpu:null|null +--precision:null +--num_epochs:lite_train_lite_infer=3|lite_train_whole_infer=3|whole_train_whole_infer=10 +--save_dir:adaptive +--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=4 +--model_path:null +train_model_name:best_model +train_infer_file_list:./test_tipc/data/sarship/:./test_tipc/data/sarship/eval.txt +null:null +## +trainer:norm +norm_train:test_tipc/run_task.py train det --config ./test_tipc/configs/det/ppyolo/ppyolo.yaml +pact_train:null +fpgm_train:null +distill_train:null +null:null +null:null +## +===========================eval_params=========================== +eval:null +null:null +## +===========================export_params=========================== +--save_dir:adaptive +--model_dir:adaptive +--fixed_input_shape:[-1,3,608,608] +norm_export:deploy/export/export_model.py +quant_export:null +fpgm_export:null +distill_export:null +export1:null +export2:null +===========================infer_params=========================== +infer_model:null +infer_export:null +infer_quant:False +inference:test_tipc/infer.py +--device:cpu|gpu +--enable_mkldnn:True +--cpu_threads:6 +--batch_size:1 +--use_trt:False +--precision:fp32 +--model_dir:null +--file_list:null:null +--save_log_path:null +--benchmark:True +--model_name:ppyolo +null:null \ No newline at end of file diff --git a/test_tipc/configs/seg/_base_/rsseg.yaml b/test_tipc/configs/seg/_base_/rsseg.yaml new file mode 100644 index 0000000..2f1d588 --- /dev/null +++ b/test_tipc/configs/seg/_base_/rsseg.yaml @@ -0,0 +1,68 @@ +# Basic configurations of RSSeg dataset + +datasets: + train: !Node + type: SegDataset + args: + data_dir: ./test_tipc/data/rsseg/ + file_list: ./test_tipc/data/rsseg/train.txt + label_list: ./test_tipc/data/rsseg/labels.txt + num_workers: 0 + shuffle: True + eval: !Node + type: SegDataset + args: + data_dir: ./test_tipc/data/rsseg/ + file_list: ./test_tipc/data/rsseg/val.txt + label_list: ./test_tipc/data/rsseg/labels.txt + num_workers: 0 + shuffle: False +transforms: + train: + - !Node + type: DecodeImg + - !Node + type: Resize + args: + target_size: 512 + - !Node + type: RandomHorizontalFlip + args: + prob: 0.5 + - !Node + type: Normalize + args: + mean: [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] + std: [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] + - !Node + type: ArrangeSegmenter + args: ['train'] + eval: + - !Node + type: DecodeImg + - !Node + type: Resize + args: + target_size: 512 + - !Node + type: Normalize + args: + mean: [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] + std: [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] + - !Node + type: ArrangeSegmenter + args: ['eval'] +download_on: False +download_url: https://paddlers.bj.bcebos.com/datasets/rsseg.zip +download_path: ./test_tipc/data/ + +num_epochs: 10 +train_batch_size: 4 +save_interval_epochs: 5 +log_interval_steps: 4 +save_dir: ./test_tipc/output/seg/ +learning_rate: 0.001 +early_stop: False +early_stop_patience: 5 +use_vdl: False +resume_checkpoint: '' \ No newline at end of file diff --git a/test_tipc/configs/seg/unet/train_infer_python.txt b/test_tipc/configs/seg/unet/train_infer_python.txt new file mode 100644 index 0000000..1a548e1 --- /dev/null +++ b/test_tipc/configs/seg/unet/train_infer_python.txt @@ -0,0 +1,53 @@ +===========================train_params=========================== +model_name:seg:unet +python:python +gpu_list:0|0,1 +use_gpu:null|null +--precision:null +--num_epochs:lite_train_lite_infer=3|lite_train_whole_infer=3|whole_train_whole_infer=10 +--save_dir:adaptive +--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=4 +--model_path:null +train_model_name:best_model +train_infer_file_list:./test_tipc/data/rsseg/:./test_tipc/data/rsseg/val.txt +null:null +## +trainer:norm +norm_train:test_tipc/run_task.py train seg --config ./test_tipc/configs/seg/unet/unet.yaml +pact_train:null +fpgm_train:null +distill_train:null +null:null +null:null +## +===========================eval_params=========================== +eval:null +null:null +## +===========================export_params=========================== +--save_dir:adaptive +--model_dir:adaptive +--fixed_input_shape:[-1,10,512,512] +norm_export:deploy/export/export_model.py +quant_export:null +fpgm_export:null +distill_export:null +export1:null +export2:null +===========================infer_params=========================== +infer_model:null +infer_export:null +infer_quant:False +inference:test_tipc/infer.py +--device:cpu|gpu +--enable_mkldnn:True +--cpu_threads:6 +--batch_size:1 +--use_trt:False +--precision:fp32 +--model_dir:null +--file_list:null:null +--save_log_path:null +--benchmark:True +--model_name:unet +null:null \ No newline at end of file diff --git a/test_tipc/configs/seg/unet/unet.yaml b/test_tipc/configs/seg/unet/unet.yaml new file mode 100644 index 0000000..8d2af9c --- /dev/null +++ b/test_tipc/configs/seg/unet/unet.yaml @@ -0,0 +1,11 @@ +# Basic configurations of UNet + +_base_: ../_base_/rsseg.yaml + +save_dir: ./test_tipc/output/seg/unet/ + +model: !Node + type: UNet + args: + input_channel: 10 + num_classes: 5 \ No newline at end of file diff --git a/test_tipc/docs/overview.png b/test_tipc/docs/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..319ac819daff38ed77e84cdff2b122e8bc4a8e5f GIT binary patch literal 141626 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�~fIt_UDE{M{ zFtE5D`OhLS@7)3LjT(C{C4Yq{F{fvQ!wxx zPQx_V|9`&%@?Qlo9?J*1FaF~s#FNPIjbmX&Z+|`RaZK}>f{Q-Bw{IY=bBa=oZy+&%1*+}Ga~XS1Bw^|(KmNaL-BQi{uA=1yQqhey_RoDx(;=d} z*y4UJhyV3S^Y~+dph|!X$NYES`k$kw?Fk-a!3?Ek;LS(;>sL*&m$<%E9PdR z_4l*TPT-MTuhiRo@Xtm6b;M%~hZtt96e;TO|M=T=5x4l(AbY~z{GJ2F_2YC|rop>$ zDg%`WaFFJ|Noh;KHA8MZFdL?ZVML5e9S41o2;F(}Kcr znElkoV<232-~>H8+z3O)_I(2wAPWNu8X-U%&tYRJYuDqBZ#y7yF0_&-1otG9pR6wS zyPa(y;&C{*vw~`9s%~rWfKlauCn4t~cHB#T-_Sy>Z|Z^5UqDHv6+l83woXvXZY~uC zLWk+c(25puuuB&i2wnRbG9O#iPKbdB)8a#aj*3h!D07x@QV_Ww8|n7(m2)!S4k6*v zM3Q$#54@CP)IZnX94MLKQZHJx$WW{FWxY6E+e+p2jMzwfGo=DGCjqTQA%cV%G!onw z*;ALGjUW^Wv*}H>Q$TjN0e>yT=udtcQ4}oNy4Yo)Ds*$49>AjRQYsY|Bh}$E8%Gvf zOesmHi*I~kv{@#%+SB(zs&l$HfO3pJ$zpN4j5Q3;0eo@(!GU7HI#$0&IBxODVzLVF z#{;*xT#Z^fO!7}5SqgbAd)-4vh&8)qK8a99zBYiUFB6Ot`+5O0i)L;B-lYX3wW!Z#6~; zkXoXFoVt))oE zu5P|{s`Y3p@Q_W~6V$HiL^`7bg@NE3$0-mLK$b~Y!74QwYD-q52EF5t1MsV7OE%OM zK%2-Tz!M8C4~WokW$qn|dTCKEo2^LM>zYe&2pZ8A+OVy1kdGbu(olCN>}R^krINn) zet8|?-aIKTs6mI12#7M;8W#2Mrq*BAZsAj;=K1gseT~Xh^}As0t6%N~K_V$@Pjwmi1z!6$;T6rJXy`J(RAC~MT>J_8o*@0!-R zV+PVUEa1HM))b} zbwMq-SDEw4wGAfjZ3S{KMz4Y{RiV$D)Hp&tIVT`re6q4I>2h((eB=w$z1Vrik3)Ap zu|JrY0?ZP|bj!ARkwN!o99n##(c+c)qp1iH4>%9KncbyeKo2pY{?5&@snZo3)tfmW zl2&6eC6OW=W=sev);`%K1*;XcZ!kj!EkhwMXvh_VT8&ZDh$pNXHT4jH1J!+;Kq7Q^ z(it+};DxgL4GlBJ^`Z&n@6V~($125PWU%QUFK$t7+ zMznikH|7mUM%)mXBn33?pb>rs%j-70RKBJbn0($tH9#;WIEvBjnHd`Qd08lhWr7Mf zDsnW7WwuN@UQqG*UN@CVOv6$~fqrsZc<0%9+H0nZNDjxU_a7#U^-=Dl)*+|dB|INc z8+rHa=~F(i+ULimyW=kak*})DQTGnm^)m>7?=77%kF8Gkh48p9A5tggxaocqb(U3i zJv9nft#N7ff5>JN>d5VS9tn8%)!il|7htUBiULhMZ&!~1VCJ&(bjWy_#XQ*7 z^y+1qXm-KIf+rf6-RKi`A72ruIl*|L6#omTNu@cdVzL135LI4tMZj!{$&6Anz)n|> zHOj3TSBb~~%CPq!J)xUUZSsC+N*b(UZoL=i53ypGh++t9cA!G{5hdPraSZC@VZS*t z1ros03?Frr>HId!$^_w}F{pvnGgm;nS3?Enu1!{-sRBslnQeiloxRDPZPHjHHihJ{ z(V%;Fe!gT64WHd0AolOR$&l9TXkR(rsvjvzkAum+jjDba^^tnE%x8&Ofi41Q*2b(x zXxMds#VWA4JVyXr6g!M8iKEF7^>A7nJ z(1!@&FkzuLEvVGiqtw9N@k75zZ~*y5==(y=dJX{CL;!W0hGG)|nh95+DZwo6Hbi?j zPWT@B_LIcQu;{>|1TH3^xc1A)q2pf1l713l%r}p?*AZYCPF4l*jT4GP!~7n&+fw6N z3}%Ndi?r83oKs9k-Yp&j6ehnkXRpIw4}Yr;jlE8&NRRW(R4tHteYE8DCqmBIe&@A0 zCV`phrhtb4>v+ZZ3ToM(t(Ch_)Xh~`=myzTCdFgaw_-oC1Jp)&qsb}{(}{{!CX?-J z!3F{o3!SO~{>A&29j6D*B=U}&ATp@2*}4Q~yHM&rnW!$oQE8!#t?lV*1=MVeJ>bMd(eJeqr|z_;J{Ek#TxI;xY2{1 zEYX9*K3Oz@YoxpbuqPLW3zFq=ve(5Y;@EzON&)loyl*IHOcE58HZ3zisWPNMr}pq` zU>7o~%|ni3YJ)bgq%5EG*D_EyN3cuA`6HB3pgh=OM0jiZGi* z;C8I_?h&h1%hve8DswmxeN?QGwMB2Ol=FPz5;-dqp!o0x#zs%X+gXGUMlv~{q?wRfeNy&Y&+I0lP+$v_=7llDIrfEVU>mfFQF@zl@@a9U>*n6`o?C^akgb24z>qn zTZ%qd+3T*g>4FfPAI~wB+fcf+vuTW>g;O87H<#MgELILURew-pBF@%nez_sj8cOLG z%VyiadSw50eHbIXT)8AYXFBP*ehg-S!RBwy;Qr4W4MCe9Q|oQo&ELgW+D(DeBeHX&d!-h@M9kf~Z!% z>p-wCjW$l_-k(B-b5Jx}nDUnY&7SepVf>g*2hlLgk`vHwb%gGXagJ0uCYLOX>qCve z*5)KYBKS;xbmRm1Y65IzS#+1^BmZs#J2C z{I({t!6#5~z$c%`eA^L8*;{VSt^_E$y4~@8@Xpnz#tuMR$^8~Q90XDk>s(kYZ&-5o`mx}{jD2H33hcL)8Y%4$Lvy6gz%ZM`Atvh|ekG|$P?;E?cOn~G& zPEbjbmpdI~0*(d=Abh-y=^Oi~t^-IKX+oY3aFn156@-GDL=UquUj8hwT|Ps)b98je zc?I}Zko~mk8v72n-mB{6$pF~{dnf)~X0y>e^Li0+9rrs@mObk-x%^O!N6!)i_qV3T zWM7&d`-akAY1q+MT9jKIshF&pBD6wuOOg7WP?^^HGt#Se1e`%0{BdHy(c;Ino=^gp zC&!^8wh8;UYH`SSPdvuOXj+nVnNGEg*a*z=oY@B+ zZxG&PFH^7SNZd4L#FN)%Zi|o=RZc~qlBZx$osPDvOv33khARRg(m=kAj&>4_T=ann^LwQs zv8MqKEAyI-w^f)T1sQ4hX~41wwWvj%I?V?WHQ#m z4L;NHTJ_QCv2hw9Y#toEhNIq>nOU0#&Ugz-Xy$qCgN29EU=F)jl|iAR2dIc-Rat2?&*QQUt)Za))CJ+0k64I_QGs5 znul}(uE25cg+A2;iTJJj!}SqLxAW&iPaYVY?zfVGYAV$9PQz`fyuE)ZTnsSi5|9Rv;;8ZD4TRwNIlJ3Qm!~ zU4#`3dgx3Z))Rs40&sIQtBLeR3i)@ZJxRrG)2PXG#Y&+i?UvB3{t2-BZ^+Px91dTS z$)M|QkuvJJj;JUaTh`EYaxke#D{#zl6`;XY3HH@S_QrxVtLCD*Eg2q9Ty!zWk061klAWp5(-0u3WpEC8@zaW4c;Dq)yuXd z{=J1CUv8zfx6{|I=4B0)PPv{5?JwlHobTMZjJC+yh_kN{0b&O!Aj%-)*|aP5wW$vJ zTK2VEe-ED}KMzDSJyDA*=?NhiPnx0w2Jo&%;YZGp7QNu3p_$XUoAXI&Ag{bSDMA>W8V~%4dd3Ek-S+H9& zhT-rwwj(IbEwYG3UzHL8Z(J{5*IgM*!Y^FZ8E207pa3-d3eu@pX=a9yiBTaSWBCp@ zvR+Ml(k+&W$K4L5*KkdrSe=FL5M6BfXL`ieK+r8M_h+j&7ZEU&3SL_pB@pBfTHqvMql?E zkgft+m!5u>AZn)ZWK~ID?j8L1sVY~TdmSjfU%r_Xnok&q@SCmlddbV@NQ|Ef9PlON zU5p3=vx}FT@T5|ln&d2-?;v{r4^*DnYm|3qhO$WDH)mc(3X4h%=1{}QnYCiFy zH(5{vN(Ty$VFznh1@FuoZWWB$s37&~ifD|Z356z8 zjb)v(3@-c4*w(64a#Z{(8oLj)ZaO5T8)MMs*4vAtrH*^Imuwc=7C=r*xiro-Ty9+n zaTuDCh+|u^RpA3uv0^#=b2O~U!D?8Os^=cCw%VmnY}6Ei zb(pZPw^8}02%cfW*^9e(AH)^uEtb(r*0}KFvF$%h@B`avcmvM%k79+){YWPB@eYG2 zK+O>bahg~qH1R8l_+NocYt_0c@56o4&1(c&`0Ox8tn_ImkTOXS0@&8NOv2XT3uizi z@RQz-5HB;IFsB|1*s^&E*mT7VP9@KWE)U0ql2kSuQ>*|A1h^+qkb+tT!epyh_S9uX zN(1pDO3)T#KPLT*_uc!!L2sl^T4QHGBeVw8$NNjA7UN}`kF0Mbnm<3HM}l?W!P~D@NLD%JFDEj8s6;)} z^3NE&`-Z;eI2cODwFcNSWY@WK_n{AJmckWwLEGH82270L5)9Qj!u@O8gT!=AUKulm z&Rs(k846oQJ&C#C>jJ=jn=M{$KofH6vKz@-gt_VMl!^^KkmiBG51;{^h)j{{`fu3`D^vqQ0qBvzX?b?RK*QrS z_Q`w`s!(X;754!W->rYvxcd_xnDE#2e_)SIQzq8ag5LBny>^`TyxOjC?yK{OQ;i&Wqu&2NDw1#>;GV+$210IXU;Fc8O8F)_6f3Ybu>WtPdh^DSQnUsyMH z$O4_n&mOnDqeT@hst1$-8T5&U+l3cWu+vhUc@kc(DwL~A>%wa`lyWMjpv0Jy9AF{- zy|lI~yERp3o6fGy9kC~#*zkkj_oJAMNP4oS`Uad{?b*c7< z?~7&su*CUD{A50)MqCXr*<fKD{7 z^;ce3v#}Z-g_t8!kxMMpuD+xf%5-WK6v+apFPl%lF-Nv%%!9T}#IhwzzXqNP5_A-J zCY3L)&EB}(5|yQKoYoChcIF!NBDhyH3sf9*8vMLd2yrI020YLkbRO3AnGu~XMy2oTW*q*4#`n8%1j#f2-t|Xs`fr$ zzuo`hay*kRZs^35C5Rya*A)`fhageQ| z$c*fn%`vjKLkb~#?`#>z-ZOh|GL9pAAA67IK7D`H_xJps=f7UPdX+x+eZR-`zOL&X zeZPDo8PH2X@gCKT^sFxX29#?}Q$Zovjus6vzlB zo%V*k(1%G8G#;_w8>rTObW8T;+qd z@^}ARc~N4LFun}~>7hcek_cVhgLFD{wb-#672zo(sPY2T3gvk33q9j@6L#jAjP&zj zOiI62g>xDiWYnFjE?m}9$T`fnS?Uvoc!opUgYRc_#q3q#4XupAGuo$ZcbDFkF)M*Y zJ61R!^i3hrifZFPxvV3W6Z#D)I7Ok9qdb#44vojjLr5==6(#1V?39I4b&aaWd`Xo_ zlxgDJ8Og0Q%AfeA0E?C|7FJJr2`|@B`~tR^umT`Of1!&(`cj0Q$-tZfv3pvBn$Y>v z?y>7~p7W5-v2O}Yl2tfZq>OKy%rp@jz&6r8m-d^TV$dkQVv-^u(~hEJnc%+oLYImt zR?lfTK(SAt=>!;cpgldB|N4ab%96cR-xg(1FShBbd$$4B*8c}wp;bh8v`o~*+#n$SI4jf9N<$a{5M33u zYK;(oa*pU6CBV-oLW3AG0KpQ`~+?E%NKi=*H674tDqehnD=8ev|!zi%l3y!%4;q6rH=G>23uTKl5` z{P*~nwku^JT5iXhAK;o1uFhrnfDh#E;O#Zn6@GEOyQ}!`6WCG!PvG!)PKEnVcKSU7 z5Mk`AI&c>|aaaXZc}6IH{r*%8W?n46W9euuuiH1?0Clj#+#E{j z%DgT~LvBlH9egvAjjO%K1c0QiwriwgQ6L*s(7;adRBBy3UlE17A_{b2k0;a8io--pb-{DOUuhM2hFCTUBd_ z8qw?2KOR=>+`8Gl@iu+(eh9n1HU^TCJ0~nRR>5!stjdOacc3xC?@XkZn=5{PSAwr3R*N=nzd zjq*x{D@Wt`xT0pyP8!P&hU+WFiduLNeB-q$Sw$bA__>4sy{K?RLB4TWpi>p4L z?K=hTzF)*fgU77NyNr?LX0|!&6gjE$2NgnMoukfT72AO^J3c~?>6O8A zs=3`PUFpHvnhCx3J#>+YV?D!+YE3BOo=-8 z)E_Ee-+GtZyzPetZrgY4qREz}GnP`5bZPRt7tX6M5~0?VHn`G|!C69L@91cWaHl@J z`f(#{|Lz3NmUUleJB!i}gIs*J`nl6we3zKg;e!>%|AM(Ud_g|7{}k=_{~95nf6r1) zc$P3X>iSj8E2m6EcU%+#s1X)DknC^`NJ%(mabxo+NZ5#63=qqEgAy^no0BB%*O!#pZV{egoQ zlJ6r7nzj{c?~K1tWIpNcs0;@S4=q?Il!1{029MiXP)>27iPUYOHq(<;Xf^z}uTOS) zbkTOK!nLMk-HO+EQ%;RRGx{T$mZ_V@D|f1&)?HV(<8b z#^rl^SBc(MZ*qeX@HYv4MRUQnW8~$p((T&3U^`{2%=(8R+e8coz%%Wh2GM`t@qhiQ zKK>T%;fnMMyS?eH+uTk`A1BLMfn>|JbhqkV{Ke_4Ls6VkjD1*!38$5sYQfBWDzx|K zz&he3o%ZtL7_z-nim!Yx+6Y_Ocr)Cb?8n!^j1U0pXVgemc&CZ=vkzP}a_et>=aH^i zZLnj?7xRPz#P$JI)irhuZ9C@;e(c=bYxpr{M6X=>2lK8Ux;D^o#7$=h3PwKw#gkpj z0-Lh>u0rN}26C?Q*we?x<@~2c-D+2Nt?0hAU=ipHMn)j2xMDBm#8qr}5)ZF9s6XU_ z59&U7`}Z!0_z8b7ivA@2KXCo;7lQ769B7j!u2KsYkItq|V45pmM4mpeh_NZb%#1jt zyfnNtZx_PqN_XG-c*xRm^XJNTn{G*}$(Liz5|Ao%#!DCV~~42tMp43SJkoZIzD$};k4oP@Z<-$I-i15s@JWO4-Lt3AnX>DvYmEZ zGai~_DVt^*b)+sFfj8&^Ti9Tsi>T%CTTB77oxHlujnj0$kxP%;4}@`YZtp%WC;6Q# zFL^jKJ+?U4x5nc9mR?=P{eAdGITof%9_kXK{JlRdbd!Wf%By|xNPA6xn{_}x4^r*r zvh1`9XK^++?ypo!)dg0U`p0*K?D}$1>0e;6J2MZN#P;tvBF5EaAfbw^Q2g&N6lJ5J zcwqczrZyV$jsDxPt=aa|yf;N(O%c`ODN{<#2?({hnNLMGZ-ogn3MvmPam#C!o~9I9 zHfj|*4L|3msq@qk({)iXy!LZ}>-VvED1$afF~{-k?R5OF);RgD$6*2Fz^Q|^fqWF^a)PK)J!@nCmBipT9V zXgOo7RK4>vY_-UMfUwWX%+De2{wqNMNrcj0mjL6ch?#Nzuz%B3W3eYba7BlE5e{SHh@7j0Z-asD^=k?y>oA;qK)NM6qDG$j^13r}c1k`~(rg9X=Pe^Y zWlNSrlct!Q&AQ)S0E`8#YCl7g*W2Y&cb9aP)ayu1X>fMUd*hosdiK`vyRj%z;?2FL z!bEO5gNW71i{<8S^LLQ(7|h+aeu*r8UztBu;_sKd@u5EClTh?DTE!|}mZ0_eXL4GN zCg+1WuTdw2)BBWRtMT!|$AwlaLvI3E^VG-^c&wjWrMIE_Tl1dU4p>(s7S=V&G=|Wn zsAKX9{#0et=gPyvo~K*+%6|yD@5!)lPBud=8U8%G|M@Whh%j~46mcA=;`|Ah@#I?V z!1al;ygidJfmfRHb-EM0;O%UUzhVMyIbD^V`L>ZvJY9UMWOU1r#Z+-e3Zg`3HG(rQ zBba*Pzz+DX^Em0%AQH8k+^*;|TGp#wu`m_ztKVM=eLbD&-=F??X1ygDU360YnFSOy+iBJ{r9#^dEYOMi zw^kdI3@Qa{+;G=oD-ge}mS=e2>%&m4$9p?&@yx(zYc0- zMzVJg!VWu>xIy|#@~7S_$jKiuvRGU!+7U3ha`h^^()EgE@;TDT5ywD>3g+JroroUI z90?5!7yTdR$bZU9do5aiy`LLekxci9)GmXS6ME*hdr16;hbtt36C?H6cN1+RAjTXH z#m1uF-WsPGNV@i|PkhBHO# z)xTPw+fKs|dGuz+2Z)Kdq5Wefgdueiy`j;AwZR~ml%12Y#f8FO?E>XZp7@Aq#e9FO zNkvI(SSWyZqSeMK%xW@UHQiJiA-B|s`^y}ktQ1aGqv;U)=c1U)xHEH0c`X@)Hfm+08_r^ zGGJ>SDTC7a6UV%s?13sneE8+w+P+ECt&C^V6gdj{vf>QdH8<(RjLi7T-`H*ZFj}aA zzZH!>L}&-{$SIQ-kK5mQ)QDpn`eju~thH=KELcbXp<&*s)!e3F(b>;=CbD@%o~INI z2(^77b=sDW%i;U%`ZJHC&l|;5%yUa*gXNMjr08Yt1cR8Uf}s_eD_mH-ZI{veKxO-u zhxB?sDl~AyFFvay<;io=(ltlD>7=Y1Ca#I-5|~7o>ousq*e;+78bG`MfQLD;2jLy_ z%-%Y_78OGJ=T&(`p=tk;P85^)zpu%k9QIE>AdYHb*u(jl%vXnbovC=wcJlEc3Btb7c7&5G%6O{ zoYx=nsWtdQ(WDodlyrGpZ_jC4C*?vM?6_WEQF9{1$0vBY#KHWWF0YMMIY}Gu2nnu3 zoQiHPZ4MSX28l=9OPk2ADv>Vpv-+ItY2y~Zt*AkTkm!M^TLVLu-n{8)DrI; zd=Z``nRaH_7n+#X=CbuYlL;n4e~)1N2NI;t7uM@5wm&blY+++Rs=V3~W!IJp4j$K8 z_$8 z&W=TSe;q+rDWDAkX|C{N)CA2-;{h1o>b~pA!Fmwb1ry7HYdzOR56A#j;aNJnQ6O4I z9TIIX&RtmR%vk?S_$O5R1Y*9qh<9;$tJD2EVEy^;{_9YMA$Ut8j-cCOhfl<_ON_)N z!_`0Fo1gu29 z2Ik**d(GwX9~UJb%v(iF5K{iOpUQt;64jA+vh6nF%9`drgr z$CX-_eq=@f%9HqP%ieq|jo~pB@V?P=+^C2F7-5;)$r03L_KTt%e5wejfU#kSJLK}d8hlg%^aYnXZI7cFK_E1NK`v3YiW_UputEGpx ze*q3eT9?1)z*;CNC`>dx^2Rpcx+$s)UC|E#n?6z@Td@Q1^0#b@EwY91ZC z0t$<3cbY`lBL+b3^4&J;ise>Hmx{fo@Qt5=gmDt(f3ZIS*B{M`2Qkk%1nlR?NchWP z_9$mdf+Y*NKqRkI;f2PHd8S4gH2EG|aLx+eV)|PFp?mt73ipe;{?Cg~|EVQu-L4wt zKWple2P??fZ_`W11(9>4V_!~jEjisAwJHc)_n>%7@eBOE{K(ZrhcdN_?Qc+St1G9ljTTy;j|MhHuS}E#?qu{)X zaW$}oj!43@;7XIPt4s*rf76aOiqVjPYShuMkf9UrZlHg>R)nF6QY(@>GTGs+{|2zY z%2%NV-rFSZC0*l%vRI|tbM32o+D98z1>3;wB25WSnC7&X!M{)yNiY~Id zqmy9_#QHN$sJY-*)S>M99ZvB!BRS-SI?e2Z=_)x^VAS4kFhM67oVGDly!Vr?8+5yb}DTz+0NDLbbHD zhtdNk4Pj1wn|KJ=Cl`4Bc2dp5ai{U1yJ9qP$2q%x_&*_ZTKcL{&;ZK=kJ`{On^)v6=1e+5?)&tDP9cI6j78-Oj7R_q_K5XaVRw(-Nn`iUdEG4;Y zHPgx~m6+Iq3EE=s1tP|<1W)^PF0?_~fXDeB;OTiX*{_dpSzH=cW^!23t>Q_t@2*Zm zS{ysgeEv32eI&6kIcm3CfH}Kl=j&7sn zfo8f(OwRjvsLjl&G^Bxgto5=sh?_5{U3V=bNs-Rvf%5@tzczi9kr2zy!kLMgE-Cfg){wl+M0ZaWj*F47k{+-{ zkqiM7jSMxk1Z_StmUj^d&mUtbSCYg)Ys}X(F_h0g491D;G+;y<=rp|W{aTj!%%>*D z)SgAP`W{fThc|c%m6X!CAS=|muZ~vP%k*DUtkgGL>_NnHje3H?;#iy_{5y7HuJ}`*l|(ChmVyB7LWt#jQd40Ki=c6f zdQj<_4BaZVMS^T2>HZNaBtXqRXPwb8p{QG_Fh&FGvOYfK?MwQ0aR!fv8}tJ>06pqj zwVrLUwWfYD7^p@sO$0b%z==7i)QqofF_AC~kW_U)Jf(qE`>LFQ39A%Q+WGPMv?rmf zo6?=9`MtY!sM>)A#AskwG)HWD%Bx&hB`h{g*yFi@SEDzfxR{vFE8%&t@=>oRPKB;& z6uetWJ643b!l)Wt6Dtu~V6lp-Q|b`9$cZqUDD^S70I=K(z;XjQiWiFk1X+^qYw5F` zw-w6thIkkioJ4?7fU515;0Yj;jGK~VTcgHHz<8ttBsn>-E2bbZzPADL_d_>HVi1%c zbMJ)Xl@B~nmPWuF3m`%f3M{yU-t8VQzJ~A_Vluq(_g8+h^VLe}zrT6>4Im2bDQEx1 zy%4@b^3-lEQ!-}PRXBUF_6=k)Inu)A8}@jda$TL!IW=VzVt$k|pQ3AbHS7=_0lt8xQC$D093QWu}-5 z5_EOSP%Sbto2nLPd|R#sY}1#9B3U(Bcmf=fFmx$gzRm*>9SnPYRBs^M7T zvn?$x#8K}C6f-GD`%!$)N5N>vi*8x{GsmaZa7C}t=ml7ytu!8|l!2hLbZ+S)=WO?y z-3Sv@njTB>(6)Q>CmFjyx5L>x5^MYs(Y@XUaPX>dS|%bUAK{$$)NcP>A6sgInb6Y< z9~D}3(I;$k^$xF1+hq)q#W>zb^Ysi2(pum6WfLGHO2$yW;b#Drv}VOFk!q^~D>|i} z4Hhg#U}Ykrr3x{szg}-Lw#_LX$r_(^u%DusJ8nKfZbC{;Q2$MFodJ}!WYq*)nI~rl z$gWcVW?zznD|f*9ySQZ(ikhD;6%)X1n^Vp-ZA@^;t?tPoxRwQ&IN$AOxq_;KhZUQS zwJfgKQZ|0VZ}fz@*cDRjR*7QqmMptFX^BjL_fsVM9gHJd% zK%P0J*EVn|A7rWl7n+(2H~VOI{q%4F#uR~}>VuWxM!o4lR52-wYOKFP45x$uBxSho zt0#b;Qq6S%UfQE zA0eiPA<>I7&3=Wttz6_hR^@=YBBokq&Wd#7n=*YuISlRl10VWGVIEi22N4nI9J4vR zMhTtr^9wVc@}EBCztpjj?IF$EC&=ynbCE9U^()nfUDtgE+*liwMo`ZhJ+pVu>cqdV z7Q>?%U9PSw`;l8rmCPV3)G^B-SP@g9=jV!xLwm~)_oy3-bUzt>FND752+C|MF8C6<`qr&c7J{7OT3*ltbgezEd9AohLK+{ns9Ogvki4 zvw`Zxa`hVq85l4(f{ho3nf=q*=xXo-KLlfEdsG6T&0`U04o_$MlF)<&3Y`zJTzOq5BN5K1i zYN6b#@h?}`uVll>jHjMAj_Ty4Hs%Ua^o5VVUQ z#4kPed&A3slCi~Q}F>tRma?r=%@!ysO|cAzuxhHq$;{mYG`O! zNE_|jgR37!w!Z|s!`;d{P3-bt(c~^EX3#HaaX1gjc?eGuXTDv&(a*95@e3hEI6Zz9 zaHp)M4`|kSkWT^<%)uG>7GywRpB7+>t8TBOjQ~p(PE* zrTMv_Til`Au)Xw0+APdWny(vn@2E_;z~~c&Y+4x3F`!qfEh>9>?(7g2N<3}HZx`BZrLksXt%B(5D zOr7M+&#Bvn6QBb{Qc>osBr1wb@xqJ4fGmG=XJg&lygkhW%dY>D4|oX5YK~vtPw;k` z*A3ev(l=`kcw=*kZT&b1MEU?`B@WHbt27@30x)^vkNQd`ij>YLqE)Oj&Ng}&zE2Hp z;zEt-cyC3&z}`n8>0uXVk)BVS_a%Yo*8^|dhB4)R(la?NmnULidxXhLu#0f;vh7jq z^CEt1x!2*0X8Nluj1+x(k-(BJmPKwIEb-#8_A;ykp6p43qII1kxoXg!MA+2X$@k@5 zl!u#^6MnsF&9TK`3Q+C?4cwGZW1Mw&QyO`RdYyOtOB!4zMP?3`Kmo;OlzmYpuxEHF zohZlxBvfIbr8(MFJ1>L;3Ph8t85)Mu;n6bJu> z=YHW;L^No?I#4E_^B3GZS?F%yzKpzRbZ>F)Bqg@gP8Rw60BqDr_Q0kvnm;DH00;ov z@c_H-ONS(prmfl@Kx;LR0s7{j*?#TCY0)MSwGl9J1;Xv?ibTrXQIphhE11nd$K%+baJN!Pj^QpT(c^*hbY z2=;@FXnm;o(`NzG-$s69Os&wk`IW)$TEiq~lKN=49B@J+;*kgtJS zJ*)RPOBb)Rwv82mJp^9bXmqMb8Z-Y?y#J58T+;&#zD>rqtL23OjVT3P1S_Zv`Lt`H zI-*yqeZ^(`o&P<_z_`2-4*L0n*9oh?N)uqG1W++Rg%x_aT2r?-GBHx>6`pJ>O%O2< z!Wh2S`@ZHD;^uqs1q?cKK>4rdWVHFwe2dd*_Yq*>#I-&m_|T`#P-R3)#ZoDn6Qpvl~JQoUsnG zn~f7L%yUHt67$h^z2X@D9Vvu!$b8gzlFkXLt+q8j(SIKhG9hDPR&2?>TyXAKaOuwz z_|?G!kr0R|PIsbz3CU>_u7rUqy5r@}JK^l%^;5{8gx>sDZ|Z;?iLi07a!9|~chjr@ zs1|2z*P@=JNWLE|>aC@<15`2qJK6y@-#w;GM*gbsRc(TW7$Mkad9Xsn=m^yG1`-X6 z3C;~!2X@mehaGA{D}cZ)koCGZZ+WMU4)m`AOkaVPGALf4+I3GL}(z z#@*H#j3eZFek9CvXk2MLZ0d}t+zT7Zl6PLFI#ZxfKI(tvD%lrhZhO2t*xLr+p5z|4CYOW`4({@;S2M)lxr7V{K!K+Gtj9AS5VQn<6qWAFWp^2VGoL%>M z+ClDE6$QkIxl-SRwiyWy#y^Abclad=#a6#5(mXfeKTx9*OBA!oq@nhjX;z{Cxn8yJ zP1^XsI5c|9=79Fo;&<$0CjN!$I$ef1&=zXrp1dAEFefD_bO3OzfgcmWm9~(Feq$qS zGXkJ~76W?XyKJ>T+Q#L&T>ZWSVXiW{6^_a*HM+4Rwp?O~hnB{Iwg(3?GG!vkS9fF( z>&~HVi#qq1107`&v=0vhhRONO$-HP0ei3gnn7eYk?~0U!dn8K5K&)8-2*}M;ZXF-T z?>Mka&b7vrSf{wga*d2tUHii)NT>q%sPPpWEe&jSr+OdYAwYKX!pnsLs3`ZHRQR$J zped`R9`rH^`RCu6>YzmvTLrqW5IyU}&euU>R%o@GdB9Ws1?{u3R}(5>Ly7+JRgrb) zSZ`{&Ez4FHx(Ry&Zp#ZU+B=`A|5ih9RJ)Tn)0Fv^&}zs5=nfB!iubI6WjHT>_S z2X9cfVhYWYz8hdTZnqX zlZ$|Rv=qo8l=BRr!Xw*^%GArnbCBb`_nr{jCR>;we}Gv6Snt^3=MVTxQ`j5=lkIMJ z?$&IpSdjrs_0&3<)-_Us+5z`Iw=oEj4R_)%Fsa=_(+GA(IwjQmf>gvkD-QR zX|tuG#HowvhQt_C6&BTE(vt+2>q&q8lW1RFNqtSCr(b=uJy^ipEa&$9_Z4{m^?+#u z@Ek5Cyq}heMR=myLn01r9TS}E-6Mq#ThF|~857JV@<#->aLXj@k2X{6Q zI%K6A8?dP==Y_7WXzLynvy$D>26v6?@%Qom0#%!=T2yW9B1ewXUQB-oi)bWCmBP_Z z@48%-9pZ}NcIB{3*-CFTMj@VbO|kxz5pW?7ZD zqG=eD=RcMn3`&}2wgy_8>{leZsp?pyJ0EC%HLxEP7AGhi61oo(V;I={`J-OK!UI4e z9XTo-X7Y)GXF3lLU(2`w@YI>$=NZrNJSiEAs&*^(#Xz+qKH#3WZx1UCrWp?|aD^&u zK8X?Nbmmb&o#RqFh|x(M%StEkGk}Rlu>`Ht0iUfcdUjAm`88Lw*P`wWSU;RUS|1mn zTwwnQO3ZL+J^zA$I$BB6ESPYe3x=k6rasfz%4bk5Mm>NT)rg2G%-? zac2($Ow<14w0P$xpR(tdf0(p_(71jQWI*O8nWb7%+0CCRQNNG6@LfYx(Kr31*xg$% zL;lzYyDT>ex$b`aH?f}94tsy&Ou|4q_EC*pf=k;q%V5t5+sLzwN%&CVqUZcrGK){Ia6$ zeRlrmx?Y6qEc36^B38l+Kb1$Y)j1&b+yrlab+l0rw~Ag{?e;+2w~;jRgyes-+vDGE zaqlVAJptR!3(Qic+?q8H&Pwp36FCLykoeZ`Z&O(=^L(`Y4Ef6+5GlAso}YX>Z28w^ zI@J>P3Va=(UgnuTh^G9hRcK<8oTUK;_;bkka=OR})b)y1KC>C4Q%Z7`cOC zd)fhDKEuinLiru79MAR|7J5bbcpNs9{3-TI@${S!)F~LxKvz~|ix|mPVw$Xo>-mHa z0~r3m)2cUx!_yg`@E0f2j$^pY9w>Z!%YhLI%G}^a1H9%vfQQ({#%G&N>~8}{9Q^s)J`k|33uLLfJWXwT}-T7ANW++g!H+gyA}n^ioBvyfRD=lDTUE6 zT#M!;K_yv);l!;FzzIbkrwzbeiUICcDpSrDrarTc(52z*z&ip?B9PUe9KFTC+w)9O zlFFavw&x|dE-9g8Btq%BL08-QJ=XV+pICA&4+~sF z8fqF;*&kd7&b$UhAR)D<8lt|2+->Y8^oct@_-NyBjDhCJ)WBN;OSg~CL>;)RP>BdS zN>st7*N2fdG4HhA`GwCVnVkT0Z`}E)s$N&zu)uJ|MbGQlh=Z}SjKpOIshASm6LW=r!lmx` zv#cZ9K5WP7hF0*ohc@m~MD4ZD+3U^VTC|@KFN(Lz%}>_$F?uW*P6DOUSe^vg3%7jw zV%_IZwf#;P=Rl6y(>^_~-uulGVR%m@bL-9wz8N1>N1qkXO%UnEdEFC5UEdj>?XZ5I z7tZp#Y;qUg1vV5UXDxBRa$0@wWps^rd^OjIj;>qUdS!@#*apHX(n3nWxH6I(dL*jx zvgqxI?gGsY(_nPU4=fq60C4 zg1$uV+Yo~5{XMWC{w`)UWK(DD$3b_9hO1fr2-ww{YS2rB0l$A+p1&19QBq0YFH=5B zZZO$fVHMF?xf;g*a)j<#9*F-1LEpi36Y!*oEUMNCXP&602Q+0fAo zXV2V?eY8r5ca=gc4cJ`ibe zZK7gF=42}KnQ4rrOy_K%&^xUL`hF-Od}+WN)NW)Nq;gocoPCl)h1-e4 zo~u}>Uxeya9L>|PUHt49K3RbZsgh7C9IY7#V_d~(s#^{b^cK&u-ZqDdO29=qIs$JC zC|J#PF@@6CnsMmaa(B`MIHH)c>@QOK?JAU5wIUK>$5TBpi*;`x4^aP_03A`1H+7CF zUjCxnOr(1~dj8SDSy5-{Xy!1jNZlZjQi7+ofa$-^9_?zl#JzCsR}1UL{Qh{?_-G+^ zELlYg6lBI&*bp^89KI!?a$GiwC{D2ZQ>DVH!ewvRrb>MT7$iwfH{9yK#uh`uR6P5k zm)Hf_uALU0a!=KUJP!%e()EVYDw{^^t&X;@jcK6=(9D%xsxAKH-Gv`q=W9<^M3xF| zcp7`+tH!PYOZ@7)Lg(dTpvUj0hD*Ezsx*t3^D9yvv|z~n2BX_wDO2{{No6Q8R5l&Q z1|i-*-Mw^WddPX`VB&{cZw)bJ09Xk2`#5N8uLAOjhCz>tuHb`UwL-n_yQu0jU&(05 z_bGF;P*(d&4P>eo?R#=Z2T$gI?(=}{x}7dRw!ZBy?UzelIHiFtnev*>B`F+3<~5Pp>gN;p z9BW_vNLTna61-2vFY(w(Cb>b0tmncoR+axK&OTC2C?5vTQT>Uh3Z2*xb^O{YT-KXq zvp&>>qdnhY8yNRTE6KBR3GvzBnp~Z%gdj#{>HHHpcl{Zbx7jkz;F5R&*?6M_%tWSYe?LhNRoT zAE+Z;igNv?_fIM~d6i9tpwwz!C)C(0astg-3Ic;{(UncGr#AXwYs(K^$dw#Gg(}TU zv@#t?jaISB30(^D3}#7S<{ezK`Uo1OaCDcU6+=Rwhy5-8eJd3?Ff>+b&)5Zv7A`m&W8%drvjp zO>pXzbCq~%Ut`&HVn0bA4{Xf7Z>BaRU;xpSy5C)dxO0-vX#!jarwk2fKzx7?`w{oakV#*155{k06YLSXPuFYNO z3YP(Mpa?tzp7ss}6N!rL;k8h;Q$J1V1Zdd{S|QlwgE{ch?MT z)XDXyItkE-cPT`U<|+x}xUOQ(p%j;?GEMM{wc7->D~OZuiH^$oI1vum zG86kl&`E4;0_0c%02XE=*}$JhVkGD5#l0?pX;PA!i~uUW2~(X=I#*gC6#HV`pj1|l zUHLcA{7NlML_`)Se@cl*m@I1c!8}ERH@dxsvLu9z4@}t{t*7y~-)6JQ)h5%=_E%SE zwKWlXtAxA`NBI?|?Ys- zJrr@)qllZw1>*o{A)$9-c+G^LMVrQvpIAx@ig zGxk_hgN9C|^F^_gc=vWlLuUelPtt&?H)JoK_n@dQRIO~En_RcLr22ULspB^(rIX2c zT|?=_rME8-f~Q8=vJ(~=Pbw1{*VXY`|AHrzwE!e7vuF+ebu3$Ng@p>9I&40!($m~o zU;&IaQ?MzWLU^y8UcTGP%i1(w7LaXT#B%@~n=a{5;L=Ey(aIq=@{WNVQY#Gf*fBCU zrZzXOm*hQ@1-@@j7>uwZC(Sb3lySXIeX95|VD#4R ze5bP@`M14IUmUBwRr?*{te;=f*!HnmwMcnf-agQxev)`K;B58hKir{Toe$u{tYGi9 zJ`JVy`XC&4tMB2``H%dLn$kVGORWsx?X_K=cQ~&QiOfII9w@WgtVddD#e1(=*~bJM zug7Qs(^Ja1PPQq+4*y$^Dj`3UzJ|sb>$t3*RfW>P#-Qh?pDu7^VKlZ=%#EYp*71XD zM{Yjs6kUoUSXJ~PzBas8uz!O$y+G-4D2{3Og{60|W$d}~eHlo$&ct}}Dz%QWa1AQt zlR%v*SXL3;TTq$OE9u8d`D1O%(9~5<_EJ2Kjlg(Lu2D-iE~UV`lkdmd!~Wu2x=Q^z z2Y`H$_MD1TDd9yX*{l11i{xT7_b0bnG5{TCrs0@qGe(e)M3?8GvT}pz-Hq`@jytWQ zTJK1@&r=)J->87CK=+51ch!%5m$x^nXj*m|c(krvQRjZ*saA^Jp;d*MrtNX5J&{c~ zg-5@%2{>QC_@LcrE?X1STs})sQq9k#8~UH(@2MR?YIwNzf&7l zka9d>+**AQ9#%QA<59wy?G~Y=wRQQ1Bd)eFQ3%dwmJ74sU8m&*+@C8$UG!s8Nk4WF z#}ezjUs4@DvkzSCS-!EX_}cSRukLjySIy*waFJ}QNajvUK-Oo)yy=aDErwipibV`zVS@8sX~ z;l`Mx($=Yvc^9;EBLuaKa=YfykB@NMqnRwC?Dxi6b@k7{W~6;wRG)C(#oiV(G}Bf3 zC2KAjt6YbM$@|5gSPvmORmm!|v0}@TVyMRG2%jKQX&_aK9x#6*?t33hjDohWqcoRt zZjd3xGr;FG{dsfDK(c=8W5Q~Otq-T0XV)e!g4*fA?}@e%?w^$#<(YDk=YEKsbi^0) zqff-2GCd%q8DA`2ys9tWn5`r^zMeD_kFk2Zx60)i&6=I5zw1-GeVok=*DBH~F+;u( zjCrhH?Ya3O{i-#`-p+xDmh|%3_QbYnYvh`_y2JGz+k(G!kdF#lAvF?P^hfD|Cr3-$ z-#sxRv!q!=`UtjhElk51(~K+UJ2ut>$L(0%O!S}Zf0*P~ct?o(QEPyoUiUq@oW~x@ z{rcV2zdx$5`b~!lB;NqKs0opdVOAs@VPaGQy1Eml(RdrrPe6KXQgruh6JZn0y=XmF z{w~d#5|~{ScOr+&4=})}D0YHcbw2P*w%*TE`194PCAoT8+kzO%hCQdN0TPLKL)Glr z^^863ahM6TtvYI5tJ3tctgP0S?q-OC9>ciEp*!}ZVuEzc*T9~T?GhCHCG#ItW@IG> zA4kxrgN`|x$5qEiUMa`MA_~yl?xe~;?{v3qX1c^=yHeaEOWYKp4EE13fppPaYD~(6 zN`3~mf$DFXSI#MKk=gU31^(c)pMoSMT73;@)+H>w5I|C60hVpD64Sb zzAi}q+~#AOk$2}GW|8}i#~0ygz)%T0Bxbi03@;Z~WhQ~6<>)*fjazD(V)uZJP#XZm zj(0O8qsLigMsr(*c`Q$T_{Va-Ey>xlsM!YRA+&bzJ{E>Xv4IhEsN}O}sxhLgyII7g z5{s{+8TIz(cb6@2RF>;GGHNjMtB)bsq4L#O`Gqn1n`l7>+8O2S8HRFeAeEihYei!B zhX#8h-BTphOE=wMsIx(?RZedb^@wg}{o9(B5j4!onIFSglsK!u2Ct}9=zOM1R&$Th3l7h=oop!yp$owg0}1c+Ck-IPgAg z4_Jr&c(@LhOzQ^%bK2*b4NBuY@7pe)3>=RmBgJFsQN!P^kqe4an+|;+X*`+HtAA~V zSrJ$Ngo@_2L!$t*B``nMfVGUwA3=glkU~{vRXA689zsXrY#Q<@L~sJAy`}OmC}Ji9Y==LE!R?2tN9)_i`u>5?sR?1u1MhaAdaGzY4@^annNclPbIIyA8yxsI1B$3 zksQ$xX)5kN9~V3{JZa&JyDd2CW4D=Qro+2G>5p?VMJ&>oNMmBsjP1rQrgs}7^r+tX|?!~h9?o_uY6fY^uSZw?H7n~NT+>nBJ+&4 z2j7JoCBfsko!M)fE*+}aQoZvHz?!V&hYOd<4;oDS)2?tBbeXm;_vX^Tb_4vW$9m~G zB5;faMyH6&Q?bB8wnz6{U6wh@HcpRs2uoRl@uI$I?zoj*G<`nk5aIG%cnLjneJz}D84}jVviDH zyE+9dTlq08$4N&*KCEz=im^E>7tH9-70~mR%P$oTCuM`Fcc;Be!!|t9Z^rgEobYD;MZ%kM+)>CL-Tle9zv@O> zk1G#e&*2phObo-qDvnU>beNsc-u+cF`F)z|yC;N~#d_}CYnGxFN%v2%t3^MYH+Pdz zFOCU~6OvJ)+*qQ4RD`!&^*e7qdgyz@Xa32|Z@Vzs=?lz^?n1ZOmTCiwRF~$MLbr8` zQo$j;G^c{qSp(zmf%A1m8S@&zD@8PN;pBY6nB8$^q7b^MII(c1zt@pARb{8bm*|xg zuLVo6fF6Y9>Ub7krx|C{eYIC+-5XOT?MuqEZv6vz;^>`>5R~Q$dn60Gj@Eq0fc$8$ zGMWyI0CUS9xuF`ILmK>6YZfd+q7Wj>}D7P7hmij z1|Nww3&Bt!z^7JhK(8G!on1Je-N`}aG?8QI>QqwNYc#nuy0-nvS&}R7$wEcEko`yx zVkfQaIkQwIRl(k!HPNkqHS{BwT`ugXp^*)r|Mpk+?rVESL4p{5FsZO3Ram3^k9uxv ztoLTEDoSjwvAzn3e!?vhS3p5IKeQKhdc@z#qg8e>AKk{&a@sST@S@Dgw1Qn)*38nf~JlLg_GnAX4RRaFbhwo9N2Av z?_CtJm@Zn*%M;wsT!q-lk4yzJdu!R2)dG#VFZc}Db*OmkUKAPgS>_dD0v@kXVCeX2_OGv1AQb4yvJ zh@l)0Z{8Ej3kTLdDQBvXrbsLb_w! zB7eR(H;jlr)le;2rgO#h55@vB72GbKj_7OJ=v9lsjapp2uylcimM^Q@rV}{n8%I<= zT>FQ}AXzZeW|LtSz!l*B*GccAH?0Xc_xzG_p{o)RRqgecUsMKy9?BFHU_SG>Od%p$ zk~}y&X8uE`^jRHEaifjWY{CaGgc?ic&Em0lh3l*Qb~(gR+PJt;!a2gk?zTIVPCf>i z19mLWD0>y{ySC;{15sok^4dY&Jr`ZL;cXm&0+YMOo00N)u~gPq&_9gNO$=AQE#QHR z{cen#+?5bwA((J6VNJ!^ko8K6HUh`pgqD>2D0%M2W4zyW6HyB0V7eQv1Zi(cgM5wT zLNeSya+~J@UjP@Z8xmbPzOLN zGM!tq-s}5$RaYl+4m<90Yt=RD^1 zxE|Ma-S2maFa*BVP!M?yZXZ2pp$?fAfAWPUibFyiuzDogje6JO;eM~I+Fc0^keIno z2P&1q#~7RzK0NvC*e6xUVLo(?*du?Y{X_!s`ka~Ps7+@FpNpYZJ2!x4=BZR@&hs_< zml&)Z)8O3C2+(k3Bq9ou&wgH&p59BP51-Bk=BETN_YCsb+v@v%o5`(>OJAq93Sd9>hPq zZossRVEfJ<#f4#wAm#D5^}@-0!|TJARWAq$aF|c?hC^>I=JV;6RnYQ zOiOK=J65XPgEAY~_ijygvqZ~xM0klsS}7!%raU%x17z(<4{K*OrsO(?T_``-bL?Hk z;AUO?)MTd=k>xb^o$@`sec7{`fkMDIt}&`{Fq4uDoHoM|4i)U zb&|wjx<#3yO}Sifq`&&u*QRotgI?qktNq-U5wJ9VDiohBjvq^SrByB8j;avfW#tMD z2PIF#$9V>#^=rO644Mxbt{Wd2(THMkv)aG7>fL3(^zG^4wJgmNv&S|oZ2VSJ*IbMr zY9u_{&wXX5ofL&V!;U1|{S;<}be5eG-tNRV@@a~XS;PmGdMKMRB11;w3q~mX z`aTE6HS_4Ff78d2isLo*Xu)kcJ-8N9k0w5tgnYWY@oaIbb6zadq+oieGLQ|fMHqKK z3}&>RnoS6IrihqJhvS732eUniM}8(kTVj16QF|Q@Y0r;e+CL1Vcj{=cYTtDd(>V$9 zPSfqg^ZIr<*E@W?ThnjX9r!DL)H+->d< zw?!^r)o)vf``WaYI4ngG5X@_UT#W;qe;{K!TpPvWG{iA7>UdR~{05eLSeN<$28Ei^ zpjDp)MfwyV!WDLU-_B>(>#l|vS3>z(Ncc)ue&{_FN#BTj7_Uir1fNu@#OY#1k;BAH z&}dLR-!SxZ&NWGriW7C+vncJjU)qzhDiYF{uHs|YW>@G~TLowi4$xhOo&oWI`OcgG zaGzD&a+WG|9DCbGRLtJm{i?wu+`2dC^r)gokEo2gjnM583ykSd?{1PpbQTEziv}$! z==|V#5fe-O`>+I@!q6a?_}Y7{^B~wWIM@Sm1*ufenaGkzmHNDLrRLi+Jr&@7dg9C$ z`NZSLcdwiLM9B!PPQkCUO7}GrA&NsY4k;UY3!8Pi#uEy!6KFZ^^ho(^o`-&9sMbb5TBYN?6Pt4}Wk|K|E;8JGVjz?lhY@=*8PF`9jTJ;TCM9U3+ws?iyxnI4q zAnU=fZAK1miuR?WnFdzfv}&LrK|0nUtH^nMzO0Y|oQB=yU-MQTq$E7llk7^`@`zwZ zbR&8ka5sNoVXNZX_I2>2XOe z-lvgE@@dn<4s!Zm9*>KI9HujU$)mPI^Z4HVP4J$!agzG01=-9;E@>JmK^+fPVPH|s?Rk`;#e@xA$3MgR69I6N{WKkh zYCPiTzR1mDvGSm%Tu63;K1e9%{Z{%zo)A|*4}NY;<@~x$D#yx>wktcTgiXk(1wl$X zJ-e}?N(ZrFZO(xeLODXg_XW9+lP3y)$A%&BWa!+1lnF(yM|(HVGE0X+hN9fF#UlM32vB*VrHT zB3;gB`f7phKznAP7PDoNnC+(FwkZF!v;I(*LTg2^uHm1bl4^)b0JKf*VL;&=7;bA{Pp z#JNDtnl{K~FLX=Qzab!6?57VIIJ6Hg8rxB>s|V5HcbB5W8PNFrClK#F-xv?zUYo;* zQ?7~SwZ0qg5c_ZhEBl$>olNxov%AG|z*oHjH*?%rr!@G0R%3&#dTH@6I1i{OhOGNm ze|oT%Q4D`}`>4X`Jgt{SUo(UNlx-t4Mne>XSw&Y^!;Z^89SUL{qO)1YNy06X#JL0< zx;CF4DiW5nD+4xxdtvD#v&hBiib}q>>83H1L;2ip&aYPy%Py>-bQj2BaJ@ogDX=*7 z*tIFF)bCLZ8C<}>?P8X^`6aIo-bj`lSY`a3vZGxK`b_@yAhn6X2pyp=We5m3cG^Yqme@o+c7ii2hY3w*atZQ;tB%F3o zI0CM_&^gdpNK}N;XQ+h(9*kR1P-UNesXl03Pjx62cNLhoN9d8>G zR{3oj%||PxpFa0y#XbhP?s+@IwkWxSEj+3lR}sRN2PV|Xn#*-M9@Wp(R9IfY-Q)I2tSR9}) zs?pqybpNDEu}19%A>q{}DckINS)^ZcgL(OBQ%K5I$qkLSG7%F!w<4zjCCAa@u{&$! zJd#&pC|t_;qJ+dU(zG@2Mph~vg-p;v!k8FAg_I)i+1 zB8$;Cb(S)P4`0ZqK!`urbbt*Q+7h53FFzEmPh>ja^38xJdaXvyYHJO4-=N)a=a~i; zlWC!PVQd}zOEkRww5^4c#B%nhS^LLcl*M#lHmtQSXM>^j9N*j!)z1m61Bu{o0m?qO zeeM=C3iiQlD58)*1KFsMX0r-!N6Y65nduBam0~ZvW>P=_&1BtAV^@a?t$6yXE78d= zlh=PGxNb2-0NjEK2S2X3#YgsL^@zx(h~hns(`yQe&QP2tWpP~$x3xOjd&;#FL}8u? zj=Dn4Z+b05IX`Zcm@siUL`;%516O5y%_2BFv+Uc%XAl@W$RBTN2T_sR6>M}0>3!=> zBdlCT`w!7X(j8W>+~jRD$h$qUZ+$wr?l~5v=b3HuIKt=X*~ae1GP~TemD_NUISXtI zI|+C8ZZ!(UVVE4XLZwo!eg}Ejr`|%9YL<`T_M-0}VcItQ3ng7epRGBb; zCFEJ-hSnHBX!Z!8^cFX%mfmqN8?HOSM#l=)0UCRLrbNRWvMRUak@K?yT@*6nupzDz zk|ZWhBGqhwr`=c9@vQ799#Nw&wT~YFs%|z#lJb=&b9B^EZ7wr8U-6cBM%igywY4z} zxJ~1rg#JzM$)h#WrLVkVdDd8#C>~Mc#NJwJ;Bv4#{)6o>CGl5C1O#-TrOi{ zP{Gia5O!Y|3z{ZqyAG;69gMfPPz$xHj1<#CDBlj5I)JN@pt>IyW2jiIH$jm~3Krcv zeb1qCZ}32JTU;SSg%%nN*Y+whhy6+hU=trBu&>q2=+?i=Y8^(;HP>Xe$m92JR<+IC zX#BgI1y6>rE5lq1Ra97pU;{7HfL>*@b~s;eB`!r{#@i-;%<>mk^^CP7c)ceEzMS#e zpA8NF5(+#}A|++CV}RtHhalIb+{uxCj59w;waz5w3lGSogaakc-C}6a6F&8&vzKXqM!4I_enTao6v46LzX$Mg$4{>$ zvWQSy&(MDGM=AsO264dcZOp~*Mog%P!x*0pgwdMqK=s&wGwz7J==I||t2HSSZ&oD% zV@qiUTY-|kXRC5bp=s(6w>f0&*OEWY=?cI5=QytFAJXu*0)a3d^4wGFPv>hI?&d^M zJMz>+>z_i;C&cj&ud|xS7iNPRVQEt){o*$B(+T6hdyD9b;$6o(>QE0M zuoFtZyYX79(peOWR^(!_mN1&G0?F7)mqW7%d%HWCT+5sk3stZkG6$UPdK1+H&Ij*f`ZI^c zf=Gm8Mv*K7@VO{D_{P77|6sjxNj8+o00B+~f!d&k;|$DwZYzM~0~8t7&W%Fy(^3136HKMjblR2sB!0<>Wn zVUU=tMqX1WD6XP@gU6n~y;`YuZ+mgva?$xA3}la#(Q4{hR}xG`3#ir9-&^}I_Cyw_ zj6k6w#6vL;0p#dfUbbq%f(1-?r5nfUQ^|>l_UAxo#|gib75NaHG#(31x#4nXbi_rJqZ2B&QlGX$Z=f zKD2+VQTY%i=zaZl<$Ku*N3;}^qTTURcVP3{`+6JorP~#S&q~$WIH|(f>a=6Mj+z-$d zOeVd`ak?zl5kLuRV;?@|;F`|W->&94tMkQ)fbOFM@>l0D0dyY|2;eSi@?+SsO3|ot z%~U~IQ!2}bMUph2Zo9o%bM=S0Q(II)qQEP$mHU8~+h$R<%bk|;o@whldo!4DdW3#} z8|Jd;&&VEu$p8v4vL=9-diDi4f{0vEmxk?y;MxxEm-ddo=g1=iwj$TK=Xw_)>Jg40 z>Pr#{AmEv114~QH>pH&KuG^t`LG*MpKDvC@4-!=tJSFezQowzT;j%}&>C9?5rVgstC%(b2BFP`ELVovF+feRt^s=C+%HnmTsz-_n!YkvB(G*CyQV#Jwma~e8A52D0Vk>daX)Y0CF!nv>(?Pnr-h+ky>3jU2k}#2A~I?$ncr> zU}0e`w3NfV&hYdBN0~FoC1;FoK<|~s8W2!Usq#L)vw^@#V9dqdzkIWO*sj!iHcKR!ES8YG z^7KRvFqkJeMS+F-&GGGB4&l7{R|+hQ;#bP@SuBoHU5qsV>K;~kv}`w6;1!B-9-a9R zxB1vCZy2D;@$R{;^Qk7a<`BqPg1k5q74GkDKvJ=Z_NW)0L_&|VOG~<%59Ag$0%q$J zpy|4GS@&yqV;iEF^r@+|Dm_NK^a~5khfNmuH)kUNY1wcoBIi^`oH!`*o8h=Ox7Z_9 zv@v+2QVx`450-8-#LYmlAs+*HrwWZfdxQfx8YPF7UhXzHgui9FLahXQXM$8*r7Vcm z76piXgVT#b9`+(QMSsW9gV7lYH~_lzB>)=~Z!hXrz2g52A?>$H*Y0Pp>Jd%f-6+EYU$wLf}xQ3m|8=4uFhv3&XfAjho}jlUXg z=^6+Sl|z+)qrk^tum$WxhZ`yrZ*OEK;C!}FfNR5L^TVQpQf)NS_Te5_d2!aOP~Qho zw4$isvkd&7Ywco2h?=2Tek)mupG12TJMfb49}uHr9Ze8$gX1Vot*Ej$OQQi4^a9O6 z;Vaj_&;Tl*8+mFGY$UIP^a(NFy`{g4Ft!R*qw*DDt zLw{TCkJBR*0c+z+&;BjBErcSv6%m+)h#p#?xRmEof@Uur$vkW7WYZi zO>JStVf!w~F`ZL-{yvbO3xMm+gY`SbNYL#Bc}X7+xeAJYH=f=D2pTA~E#IW?3vJDI z$ne1D?}8S|)J6A|r^{AnULy*=R0^lpAQ2BgGV&gAvNRV3z>62Pqr4SRA;!`}RZxKO zvMlbN3`vnB*gQva@h{eIBj_5vk(AfB!*=hx-*NH6aF2T%BSVn)P*me<%N+mbcY`Cz`4%{x}#$26J zRji)9+FIPr&alk{tJ<)1r_JBCgNNMgcxzs6<#`k< zBVa#TIRO^U%%wAaK<~@TUH!HwGz-EDqWbeid}$Y^g9?H^vUCG|f?t-?=QGrxO>GZo z75s48+KaPU+Vjs$0kY*|&erd^k8No1=B6YbnDiob4a5LxsGgQcX2YpB?1j{CaS1R!|JeOl`X)!hYRJ(2{@j7R!cv(-yz0W%Y$csIP* zSoyv>5!z0=!Xj_p$by(LK%t`LT8(MFzorJ|uB0{};QM|;l=9I`7V^UCNn4hS%g zw!`PTwE^>95cp6rAR^!D%(3*&7n4S`z@*O{k2a(olT1k@w{oFhNdDj;kcQDMj(O&#~71Y)-^GBli#w?E(+G>bS5qMI#ShF;%)N<%zvV6`$7~{tM1q{!k=%p(a|1zwb}(ZosRX z*6Q&4bXJh%V+c0@B%Z-JP-LGpnC*d$W){WxG}rvP`FsY4wLxSOJYmssH2W!4R3CMM zuxA?L4FcSSHGr=YT^(a+DYZ2P=BwGxA5p?)`M@I++!xec59W)c1CoHJaij&{u47ml zGnTt68o7oY78pJby|c+I_n7k&a8kjomQp2GZ+-q`DujydFpRAdfM!8>DMQK)XxzH& zc&^H)%d2cnYuF(70TAIg3YY8*ty5PZ932Gw(2nx#_P|@@VvaXb&dRpW>=!zS`)2D? z%<&>h*8Nd(eP*L|p&LHu>X+%WYbF^jr0z8a6k)2jzo8GD#wU7!E0!3B_vA(Hd(Lf3 zA3Jkpuzk&?LMYwPNH*sf0s#}!U+;W~d01q>`rfUWoasB{rP z83vLF`+5z5>hcq!PN?NBZZ)cUg!XQzWHXzwL$1UMGflH18M(7piH=J$g3S47ogzUc z4UhUc9ywB04dyZx7r6|e_U3Q5q>0|M-f*S2Ue#$uNRpEusYgJ_rCN#3?O=hSIbTD zq5&X<+ivOBda@rbVbds%w3f7`yaD@hjSm6>F5g;}O1I)pO8PTixE!x zTA+8m8gL7j7HkQuVLosr-(Ra;?@pD`1=^%EWn1wEiQtTjHHx2!2}j1@WrV07FS7u6 zU+{n?9vL9{&5g_k%7Z!{)jI%d&lW-Gs@&S`Y46+iADqxd9D(SWI7F!EZp|;+l>||K z=4XMg1SPN{aW`6-@xRFTFDZVguxZMiAO{LbAKt$4b@!f_1(^%jHkKcH(a3IO6-aeF zkfsp6ke(EVckby`dOJ%FOlIGZ@b2M`h1o6i4!*Sw$L_u6q~*rb131K-dPYjd#R0sn zHhMDL1=K@_fk4DN{cB}IIk@m_E(f$t09rcA0E~xqCaJm0V6Ik@VrF_S@HW-jTmB-4 z`O|1mNWwew6}nO~KmX0*KT^mKC~|AL?WUUxh!0$kp69TbAAB}q6aZ7H*YI{3%JNe( z8_Tq@KifkNA|%9bk;m3f4lzJ{OX_%?awrfmxJ&xYrx;icz6E!{T z5&b#9oexE?Eo!@v{tz(GgkL2zBO7;SevF9br~$nc%ywyK>@2GR{+KwC4+5{g#2l_x z?Uo*oL6W0#vek-ar+3E(KxC!zVD6*T)R@z%b%Uw!_$Oe?$a|8X6x1kzy8;ueT7^V- z1|$KZef$5kzP|L^Q~No5ln%iDmN2V5jN~UHkP`61Sb|!cEvlA!?EHZif zIbnaBXMPV15ca9zv9dU$`>rNLVEJaUKKY6XF!`wDPCa%)g;I>wC^>A-V7jzLZpI*+ z80mRpMzK0Uc?jek!Li*7SiWY%Sr_{NFPihvmmxq2;67{%_`C-zoQlc_HOT?+i0uJw z`TZ?oz-Wdg5_&IaYvni>uptIEx%NGRMwvh*I0#$$dKEuML*fC90#Z%@up~4q0=Sq! zm~4{=K(w8vYj04FGleMbuJ9xQvSC0|fC1c2Uc5lzwXhraxqjbm_(M_AoiV2qCC=rM ze(uf*N??xnn>;6(&O)TOvZ~cAD~y>`pM+kyYJn(dY!H!M8AStNWY3?naP>hq6$oYw z=R_?FYhltT25VMm1{<^yyy-X8?mUA~&$l_athlJ7|XSjw+ax}FF<1s;oz z!!kSNAy0VEwG(Xsf1gC;abQ5Z)?ZdIr^p({TS9;FADZv4r;&$!@|G9fF`CVSn<^1%S8FZ&I$=aq8QqJqOlIio5){wxLJ97RqAt#p0O0*j9+%Q7}X2~9Fo!3=sUzgTuXr9c82hkJm5t!0CE-@%kr*qL=)rw zJ1GFdKTfN(mzjr0Hw==dLM`PdEL4#ONAt$dGk-6RvtSed(XD%dT#T-x==Ohx3hSOj z+{Gg-)3+9W;u^OraA!Wk#SZ}Mcx|zG@A^-+;)@BocvUS z;4k4Gq8e~d57Q=KWxpubyZB`2E1@`kqAsN_@0Qj-It$o4Rf$C3S8~!{lGTRXxordj z_t#?I$PuIG!O`a6-Z&YhhO(DVc|`HvrUZV>*2?8#HXnd=uN&1VZ#0LqIgkA?f! zpZ~LXFLfKDhDuhljsED;7X>n&83W7whZViZKfFp>;K{3ZZ9N=EMt&ctmmdDvKK^S^ zMF_+k4$OZy`9J>v9RmhfChf1;vrVJWcU{T(lX0pX)z!_&09Xh^l+o+*NV|`t!o&N( zBOV-^KfChxpa`jgDGQ?b@jtx+$OO3BB49r~C#n4z*(3S@U7OOeEV~y9p8pu`Kie)t z4AaNc zXKRE$LGu^QE$8Xs|FO8wy9Jt+E$1WrpT?#JZ6qdU2-M+ zY}Dl7RDiaSwDa-PjO^ZY;W{Awlt z$SowsE+xqG#o%JxzlI84M+nT<{V%PeKer|Zup~y*`CqYNncC_iD4Sd^_0*kT2m$VO zrSCn3{D5JnKy|&Yx2?z^uOW#9&5t@y)_G2l{F(@Ur0U7&OCtf%hQlSoRYZ@$;Kp4- zufGbJ_yJjqSdE()Z&(B{we6#mg79W+zRM3=s)17WY?Nj?mV7-`S*DI?ln9- z960!EET56Wd__MS^;GP&_TWi|NP+U~LA95QyG9c~o+B+D)*L!{abB7U@bTmD{T5ra zPD(knRaS484O?!#%sGaCzz57hD2zCIeJIQ9FaAu38ZudFAI!o3wq?a({O*(|3yQUK zmn5vuhK6Vi7+{()9$AZXpzL2O#m50^e99e^4au%3Dl_E+tduhb$d@`hC5-%HQ#_&J zN2EPj$j_}{L$}A;!BQMhGj7qV{yp?Q1JKC8 zVna*YUz_`U5#1+67uiy1<+r|2%Q|ZpzJW+GL`X}RmFl9t^yfO-dkuQ%x)EP2ad~q8)vt@c`JC0MR~UZ5{9ir% z?;3TF11H@y|)WQfK`80U0QNk(EHa<>ld0iS_^cfD{XYy|99QUP5xoIQ}(L=f4sG z5(oFNXK_6C7csHFn)u&e?ZGtnVMTvoHfNA{#`yNyxrQH|8^PJYbAu>x<1q& zrT^S~(xC2bkN4-&0R&%nxEs)lY>A5_82DfFQb!DNe_%rgYFeH@i652ggVqH; z)l}S=q9U^nzZ*<$DhX7I%UNEmA$Jviy9Xc%p`wJ>a#QG)Q$rJ*vG-vzK;dej@Mem5 z67{w-+~Blfzn;-`EEmhG}ZPC*AQI_LD_>bmRH#V z^-_y}wcSSwPOrvSN-D(ZbvB$qfi$vzHhY5$F**VqWQNZG0%7;iRNZv}q5m2K{{!7x ztH|}~vhI(jx+-Nev-ch_%cWMc=cP2RXsGFpuJW~5R=M7|sOtrDObrxu?@jCdB;LFj zF6b*rDpsdkJQi`bqO&) z`mYt3L&v|SV8#dAeg@A+KL!p&VyJq_=CIOq`u0>`L~Jmq0cdPB{*kokCzx;wH@S}! z#dGpr1LeNUfkZI#fX4cfMG4P&uORQRw2{BO!FwL;W0Vm3=v zy5XXXFrKoarm#;DNn1P$)3LdIiPGO?zZ1;b#PV2+DQ2qEI<>6>b;4T}W8M1c0F%W^ z2IW}zJUkT3>mu8qp{g@GX1Ky2SWj!EbfO~*q%zZ#OLolL96$lO%K9)|Ug~pq7K;s= zRBJQk)94J%9A!{paBud6ixh`hKP*GVx~ZZ7PrPeVUx3H!(fM$~!pm3hS}wjjKcg>? zD*7xuSkvu1x9E`|((rJtgniZ3(ws&DFp%L}#a7d~JoEs24Y>Gq;y4W4SY>9MkAwD^ z?Q2t9R%4g#+}1rJHn1eOnO|bkdbW)m}-l*70zA99$$g(^n-E6VZV(Sca!E=Rr7;|L+= z%{OnY=DQJ^wBn0_!t%#9iLlbY!6zRCZ=7MG8YyHPN;Rq=Se{#Di2l) z4Ub2k*;IoOR@SM_!B7&9VbS$eMboVy=9=RoGe|0b%}n+OBFi@`%-0m^B?hNH)MqBW zShotMk%HxLS$LVDu^ECa5lvK;tx3%+0i)E?KT66%d5IqQRyg$JT6C~3y1Zh3+5-^b zKzE+D5Aa7ls}m^}gaGhSqsMAhe%@^wnf93Fg+7{%WA?bgW47fJ${XBUiNh&jlLkvP zSL(&b$E2D8KzZuWAx`{LAUP92w3BL-O7)%wL_9ixe;7ukqydoXq_ty9)Kw=(=03W$rkzfQX3a==&V9wATn>|I z7Awy*B<};EPBy4wiSeTha=*&L8rhZj;fy(1-{GRnyvJ3YKj`9;5c6gmN#76{>qd|O zYKe4VsnVOnk)Mg*zTRk2xpJ% z_s^;1lETByHA8_4FxQitL+$LahM-0)e2HU2Y_oyum7_#(qG-}+9>Z`YVdb$vmPQ5c zLUAJt;6850&)W1>q*lsgC@^6U-@sf6lRG6qHAgbPm~&)xaF0bk!gC+~`V^LIzXiFFA;ply+ zb;TcM|8~r|9bK~gK>?S=S)kBT124g}WrnpdQz#7udN2?l#2~iSg;8of<@jtF(PPuH zOt=^^d>@m#FZ?sy2OIt%z*Pk}=Kxy^My~a&0hLVd5#8){OZO;)rr;h#xA_h4Wv(sE zwyEigCEZ(j<+>^K+1~7EE`Rh5CIkKit$O(%UjK8AYD6srBgBmAV%Ic4OU^s-F|`!& zdfZECLCW4vr&SGU-wmC|^0%dBHoDYi zf?zsRyS%noivpzr)nj<5vc<=bAN7BDg;{BzT5#y-_i;X-OdYHQ;vM9JRc;aUb|*(1 zWo3Y6MNLO1q0ROtO^duvlCqw0cH32*SvvJ^m=n_la%TEDI!PfwIMROAj(H=Y0Q38* z_zXE)0YH^&f$~5Lp2Mt9*Q>kn(46f&{0X|`067F@w*Ne(!^UmKlY&LI5ZnS>KMRP+ zC5;QB`@sFa)$`#u9QE9n0~>*e5#FZ)#ecV&8eK$){|A}wo;~YL;wbcI$RnqnFlhr5 zr8G00)t@->_yQ3&D;Z%0DbC5%uP8Gn#6V_=P342lVr6Hi1e6oT!Ma;IKzJId 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

lX*RIM*de zWp$;XCqo-oq(Jx3fO!^jC4yNSlU7;2IW&_juSyMn)fFUCD{K}WKc*3~>Gzwqvh@WBjhuBI*5kegFL3Zm)m3osQ=@=ei!( zV?Q3(<1U+(GD$2h2#InB#+MkLE1o^nFMi@U#n&t_pO!A&(xcRCx zZ2YbJjhGSK8(~E-6Wu9P^;9K<7XY;t`uNF$bzaWid}D6nM2tEnD?n`qN@$+$A=@e0 zFaHGOpL5vBv>%Tr(FxipP`j-FRp?=)#LMq5RLx$FnwIrGoKY`1^V}{u=Fo+PgSZUJ zeUTJPmb4VR`x~ct2ctwduH@CONsU~)ku0}6(YZTv+BbC7(cY&1xfyRL?EO8^I%J{~ z36e(wJC>K!vo(2O?`eOf1s13cd`Fj249^y@ovE{y8_Zo zjKByAVarS&*Apmk7qoUszVUJY#sVnH>V0bE_1!f4!7{@Gh4JG}O562$ejKHILSTW8S5a^wZ))SC;8@7QLzq+& zA0DkyTz~n(VpVsw>6Vt8v$R(2ww89! ze8s%|`mSS~$nJyixsgYn*EmUeBVgpb;Smm9$sFdJk%ogA{f$!5+OEmrE#adHjEkXt z5xoJ;%S#`2o42~cdkHc;i8yY8sgJ%@bn5(7ql5~*~ zC1co4;I_IGPc;WUewGrs@5BEO*Yz*M)`r z(z!?jP6lkfC^X_d9$hfYhpfuk{>k7&md53b|Ae}AGfP5d4+ z;quo3pP_zZiENcHE)uQTVz^!RwSl2Neoh1uY0bJ2IN41EzP*A#wZ&*msm2M<7m0?Z zaXG-<9Jt{07O56iCFudVL|@UftG~?8lXwT`5(ein87;oYyqqiyPB=6ZYU)F)cCs*D z>O7cw>aLkd_X^DP>86H#+_FG=wC#w@$IuLs?q5qM^%g@WD&lGVYmZ*TL8cKrT=pbc zZ$3XOKQrODa_!qmETeU&Z7Ip7Z+9>ZIxa?oL6KJRzQ!-})%U2kY=IDnWvg|Wv<`;C z_Bm&}T_2_z!NJfG2(B8*>>J_foAw*m=q#U4S-S;m8GO$6s%a*mimoZS@%Zs$S?koa ztgjIx<6~ar!hPm4F)E1L=1Rqg6z=$iGop*RWn!fU<6|v&wFa>ELcg6t%f}_#kkl)j z=C6UZ9LQ|9-Ci$i5b4Qo@rH!?A0Ah9lM*Lb^E~S>_x7hDMGDEN97wBe`9*|$OI61d zajz4u$FA-=-ZU9TS=n^K9~&~+ExH^B5D`M@96CdY=Vg++-WjCKpGl7aPJlc6cI(>} z&2syR#u;Dj*QZIG?(26M-vqM;LBpTey$<8P5>-|EEdo8@fvwG$y#yUcULF2E6h*0D zX@%kSAIaDp!#Be6)%?S~2C15^UiQQ5xfJl&mE%+uxPJKHPJT z$R_<#X6@20L>L|KLqfheJ)1nEL6u`MO>b=!c;vUnhDzl|u9Nf1T0jsonwp7E=M~MC zDpcb+GaKK9Fs1pCOgWNSOgT{P8!5t=$U6-?;^FO*afW0;yF>a4dvFuFG2eW!V=m@_ z?dmE%C4#~-rU;ngFm>tnIjcMhOY+q(<6xMIwg>M#A)bF9!8>5B+Pv%wEoXeLWzZ?< zoxy$^p`^Z?&h@!{1h3%yJ5cqg%k3Gu)st5sNXTS-J&0bd?B&CUw-0Sg;3V+ZB!6Dl zIrInQekH|SXfUK&{DZ!Ep(qcz^LGq-r&-gn+9RWT$QyBnI9p5E*Q<83w#uzQJ1iKO z=@i=`9j$$8YLFA858@m5zX-FKZ@Q>VT)WDkfBjUrQ=I{+OqiW0KbPx^lH2rL_7CIA z)_tyJ=dpK24g5134~ssfHA=4$wsn!scDJiin}|aEny>3=6&?*Y4?erIz?bu|oEek^ zR?=O2m(|iV0$hBpT1%A5Pkg9Wsyi6tr^}R%?muEBTOnfsSxT}D!d%`ai+|Fuq>&n# z-O}g{3TgJ(J3`G4YX`tL0wc&$#v;6-Kl`r%$c z*m~w{&XojO#gv#HZmOY4)0*0VeA>ov4&^-^t-+$~PoE^qc`UqGN>sqL%ofZXLQ

  • (sCwS#4?nGpp7$SMwN=KaeN@xGw-hrsl>I4M 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 literal 0 HcmV?d00001 diff --git a/test_tipc/docs/test_train_inference_python.md b/test_tipc/docs/test_train_inference_python.md new file mode 100644 index 0000000..4d98215 --- /dev/null +++ b/test_tipc/docs/test_train_inference_python.md @@ -0,0 +1,89 @@ +# Linux GPU/CPU 基础训练推理测试 + +Linux GPU/CPU 基础训练推理测试的主程序为`test_train_inference_python.sh`,可以测试基于Python的模型训练、评估、推理等基本功能。 + +## 1. 测试结论汇总 + +- 训练相关: + +| 任务类别 | 模型名称 | 单机单卡 | 单机多卡 | +| :----: | :----: | :----: | :----: | +| 变化检测 | BIT | 正常训练 | 正常训练 | +| 场景分类 | HRNet | 正常训练 | 正常训练 | +| 目标检测 | PP-YOLO | 正常训练 | 正常训练 | +| 图像分割 | UNet | 正常训练 | 正常训练 | + +- 推理相关: + +| 任务类别 | 模型名称 | device_CPU | device_GPU | batchsize | +| :----: | :----: | :----: | :----: | :----: | +| 变化检测 | BIT | 支持 | 支持 | 1 | +| 场景分类 | HRNet | 支持 | 支持 | 1 | +| 目标检测 | YOLO | 支持 | 支持 | 1 | +| 图像分割 | UNet | 支持 | 支持 | 1 | + + +## 2. 测试流程 + +### 2.1 环境配置 + +除了安装PaddleRS以外,您还需要安装规范化日志输出工具AutoLog: +``` +pip install https://paddleocr.bj.bcebos.com/libs/auto_log-1.2.0-py3-none-any.whl +``` + +### 2.2 功能测试 + +先运行`test_tipc/prepare.sh`准备数据和模型,然后运行`test_tipc/test_train_inference_python.sh`进行测试。测试过程中生成的日志文件均存储在`test_tipc/output/`目录。 + +`test_tipc/test_train_inference_python.sh`支持4种运行模式,分别是: + +- 模式1:lite_train_lite_infer,使用少量数据训练,用于快速验证训练到预测的流程是否能走通,不验证精度和速度; +```shell +bash ./test_tipc/prepare.sh test_tipc/configs/clas/hrnet/train_infer_python.txt lite_train_lite_infer +bash ./test_tipc/test_train_inference.sh test_tipc/configs/clas/hrnet/train_infer_python.txt lite_train_lite_infer +``` + +- 模式2:lite_train_whole_infer,使用少量数据训练,全量数据预测,用于验证训练后的模型执行预测时预测速度是否合理; +```shell +bash ./test_tipc/prepare.sh test_tipc/configs/clas/hrnet/train_infer_python.txt lite_train_whole_infer +bash ./test_tipc/test_train_inference.sh test_tipc/configs/clas/hrnet/train_infer_python.txt lite_train_whole_infer +``` + +- 模式3:whole_infer,不训练,使用全量数据预测,验证模型动转静是否正常,检查模型的预测时间和精度; +```shell +bash ./test_tipc/prepare.sh test_tipc/configs/clas/hrnet/train_infer_python.txt whole_infer +# 用法1: +bash ./test_tipc/test_train_inference.sh test_tipc/configs/clas/hrnet/train_infer_python.txt whole_infer +# 用法2: 在指定GPU上执行预测,第三个传入参数为GPU编号 +bash ./test_tipc/test_train_inference.sh test_tipc/configs/clas/hrnet/train_infer_python.txt whole_infer '1' +``` + +- 模式4:whole_train_whole_infer,CE: 全量数据训练,全量数据预测,验证模型训练精度、预测精度、预测速度; +```shell +bash ./test_tipc/prepare.sh test_tipc/configs/clas/hrnet/train_infer_python.txt whole_train_whole_infer +bash ./test_tipc/test_train_inference.sh test_tipc/configs/clas/hrnet/train_infer_python.txt whole_train_whole_infer +``` + +运行相应指令后,在`test_tipc/output`目录中会自动保存运行日志。如lite_train_lite_infer模式下,该目录中可能存在以下文件: +``` +test_tipc/output/[task name]/[model name]/ +|- results_python.log # 存储指令执行状态的日志 +|- norm_gpus_0_autocast_null/ # GPU 0号卡上的训练日志和模型保存目录 +...... +|- python_infer_cpu_usemkldnn_True_threads_6_precision_fp32_batchsize_1.log # CPU上开启mkldnn,线程数设置为6,测试batch_size=1条件下的预测运行日志 +|- python_infer_gpu_usetrt_True_precision_fp16_batchsize_1.log # GPU上开启TensorRT,测试batch_size=1的半精度预测运行日志 +...... +``` + +其中`results_python.log`中保存了每条指令的执行状态。如果指令运行成功,输出信息如下所示: +``` + Run successfully with command - hrnet - python test_tipc/infer.py --file_list ./test_tipc/data/ucmerced/ ./test_tipc/data/ucmerced/val.txt --device=gpu --use_trt=False --precision=fp32 --model_dir=./test_tipc/output/clas/hrnet/lite_train_lite_infer/norm_gpus_0,1_autocast_null/static/ --batch_size=1 --benchmark=True ! +...... +``` + +如果运行失败,输出信息如下所示: +``` + Run failed with command - hrnet - python test_tipc/infer.py --file_list ./test_tipc/data/ucmerced/ ./test_tipc/data/ucmerced/val.txt --device=gpu --use_trt=False --precision=fp32 --model_dir=./test_tipc/output/clas/hrnet/lite_train_lite_infer/norm_gpus_0,1_autocast_null/static/ --batch_size=1 --benchmark=True ! +...... +``` diff --git a/test_tipc/infer.py b/test_tipc/infer.py new file mode 100644 index 0000000..9ad6123 --- /dev/null +++ b/test_tipc/infer.py @@ -0,0 +1,316 @@ +#!/usr/bin/env python + +import os +import os.path as osp +import argparse +from operator import itemgetter + +import numpy as np +import paddle +from paddle.inference import Config +from paddle.inference import create_predictor +from paddle.inference import PrecisionType +from paddlers.tasks import load_model +from paddlers.utils import logging + + +class _bool(object): + def __new__(cls, x): + if isinstance(x, str): + if x.lower() == 'false': + return False + elif x.lower() == 'true': + return True + return bool.__new__(x) + + +class TIPCPredictor(object): + def __init__(self, + model_dir, + device='cpu', + gpu_id=0, + cpu_thread_num=1, + use_mkl=True, + mkl_thread_num=4, + use_trt=False, + memory_optimize=True, + trt_precision_mode='fp32', + benchmark=False, + model_name='', + batch_size=1): + self.model_dir = model_dir + self._model = load_model(model_dir, with_net=False) + + if trt_precision_mode.lower() == 'fp32': + trt_precision_mode = PrecisionType.Float32 + elif trt_precision_mode.lower() == 'fp16': + trt_precision_mode = PrecisionType.Float16 + else: + logging.error( + "TensorRT precision mode {} is invalid. Supported modes are fp32 and fp16." + .format(trt_precision_mode), + exit=True) + + self.config = self.get_config( + device=device, + gpu_id=gpu_id, + cpu_thread_num=cpu_thread_num, + use_mkl=use_mkl, + mkl_thread_num=mkl_thread_num, + use_trt=use_trt, + use_glog=False, + memory_optimize=memory_optimize, + max_trt_batch_size=1, + trt_precision_mode=trt_precision_mode) + self.predictor = create_predictor(self.config) + + self.batch_size = batch_size + + if benchmark: + import auto_log + pid = os.getpid() + self.autolog = auto_log.AutoLogger( + model_name=model_name, + model_precision=trt_precision_mode, + batch_size=batch_size, + data_shape='dynamic', + save_path=None, + inference_config=self.config, + pids=pid, + process_name=None, + gpu_ids=0, + time_keys=[ + 'preprocess_time', 'inference_time', 'postprocess_time' + ], + warmup=0, + logger=logging) + self.benchmark = benchmark + + def get_config(self, device, gpu_id, cpu_thread_num, use_mkl, + mkl_thread_num, use_trt, use_glog, memory_optimize, + max_trt_batch_size, trt_precision_mode): + config = Config( + osp.join(self.model_dir, 'model.pdmodel'), + osp.join(self.model_dir, 'model.pdiparams')) + + if device == 'gpu': + config.enable_use_gpu(200, gpu_id) + config.switch_ir_optim(True) + if use_trt: + if self._model.model_type == 'segmenter': + logging.warning( + "Semantic segmentation models do not support TensorRT acceleration, " + "TensorRT is forcibly disabled.") + elif 'RCNN' in self._model.__class__.__name__: + logging.warning( + "RCNN models do not support TensorRT acceleration, " + "TensorRT is forcibly disabled.") + else: + config.enable_tensorrt_engine( + workspace_size=1 << 10, + max_batch_size=max_trt_batch_size, + min_subgraph_size=3, + precision_mode=trt_precision_mode, + use_static=False, + use_calib_mode=False) + else: + config.disable_gpu() + config.set_cpu_math_library_num_threads(cpu_thread_num) + if use_mkl: + if self._model.__class__.__name__ == 'MaskRCNN': + logging.warning( + "MaskRCNN does not support MKL-DNN, MKL-DNN is forcibly disabled" + ) + else: + try: + # Cache 10 different shapes for mkldnn to avoid memory leak + config.set_mkldnn_cache_capacity(10) + config.enable_mkldnn() + config.set_cpu_math_library_num_threads(mkl_thread_num) + except Exception as e: + logging.warning( + "The current environment does not support MKL-DNN, MKL-DNN is disabled." + ) + pass + + if not use_glog: + config.disable_glog_info() + if memory_optimize: + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + return config + + def preprocess(self, images, transforms): + preprocessed_samples = self._model._preprocess( + images, transforms, to_tensor=False) + if self._model.model_type == 'classifier': + preprocessed_samples = {'image': preprocessed_samples[0]} + elif self._model.model_type == 'segmenter': + preprocessed_samples = { + 'image': preprocessed_samples[0], + 'ori_shape': preprocessed_samples[1] + } + elif self._model.model_type == 'detector': + pass + elif self._model.model_type == 'change_detector': + preprocessed_samples = { + 'image': preprocessed_samples[0], + 'image2': preprocessed_samples[1], + 'ori_shape': preprocessed_samples[2] + } + else: + logging.error( + "Invalid model type {}".format(self._model.model_type), + exit=True) + return preprocessed_samples + + def postprocess(self, net_outputs, topk=1, ori_shape=None, transforms=None): + if self._model.model_type == 'classifier': + true_topk = min(self._model.num_classes, topk) + if self._model._postprocess is None: + self._model.build_postprocess_from_labels(topk) + # XXX: Convert ndarray to tensor as self._model._postprocess requires + assert len(net_outputs) == 1 + net_outputs = paddle.to_tensor(net_outputs[0]) + outputs = self._model._postprocess(net_outputs) + class_ids = map(itemgetter('class_ids'), outputs) + scores = map(itemgetter('scores'), outputs) + label_names = map(itemgetter('label_names'), outputs) + preds = [{ + 'class_ids_map': l, + 'scores_map': s, + 'label_names_map': n, + } for l, s, n in zip(class_ids, scores, label_names)] + elif self._model.model_type in ('segmenter', 'change_detector'): + label_map, score_map = self._model._postprocess( + net_outputs, + batch_origin_shape=ori_shape, + transforms=transforms.transforms) + preds = [{ + 'label_map': l, + 'score_map': s + } for l, s in zip(label_map, score_map)] + elif self._model.model_type == 'detector': + net_outputs = { + k: v + for k, v in zip(['bbox', 'bbox_num', 'mask'], net_outputs) + } + preds = self._model._postprocess(net_outputs) + else: + logging.error( + "Invalid model type {}.".format(self._model.model_type), + exit=True) + + return preds + + def _run(self, images, topk=1, transforms=None, time_it=False): + if self.benchmark and time_it: + self.autolog.times.start() + + preprocessed_input = self.preprocess(images, transforms) + + input_names = self.predictor.get_input_names() + for name in input_names: + input_tensor = self.predictor.get_input_handle(name) + input_tensor.copy_from_cpu(preprocessed_input[name]) + + if self.benchmark and time_it: + self.autolog.times.stamp() + + self.predictor.run() + + output_names = self.predictor.get_output_names() + net_outputs = [] + for name in output_names: + output_tensor = self.predictor.get_output_handle(name) + net_outputs.append(output_tensor.copy_to_cpu()) + + if self.benchmark and time_it: + self.autolog.times.stamp() + + res = self.postprocess( + net_outputs, + topk, + ori_shape=preprocessed_input.get('ori_shape', None), + transforms=transforms) + + if self.benchmark and time_it: + self.autolog.times.end(stamp=True) + + return res + + def predict(self, data_dir, file_list, topk=1, warmup_iters=5): + transforms = self._model.test_transforms + + # Warm up + iters = 0 + while True: + for images in self._parse_lines(data_dir, file_list): + if iters >= warmup_iters: + break + self._run( + images=images, + topk=topk, + transforms=transforms, + time_it=False) + iters += 1 + else: + continue + break + + results = [] + for images in self._parse_lines(data_dir, file_list): + res = self._run( + images=images, topk=topk, transforms=transforms, time_it=True) + results.append(res) + return results + + def _parse_lines(self, data_dir, file_list): + with open(file_list, 'r') as f: + batch = [] + for line in f: + items = line.strip().split() + items = [osp.join(data_dir, item) for item in items] + if self._model.model_type == 'change_detector': + batch.append((items[0], items[1])) + else: + batch.append(items[0]) + if len(batch) == self.batch_size: + yield batch + batch.clear() + if 0 < len(batch) < self.batch_size: + yield batch + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument('--file_list', type=str, nargs=2) + parser.add_argument('--model_dir', type=str, default='./') + parser.add_argument( + '--device', type=str, choices=['cpu', 'gpu'], default='cpu') + parser.add_argument('--enable_mkldnn', type=_bool, default=False) + parser.add_argument('--cpu_threads', type=int, default=10) + parser.add_argument('--use_trt', type=_bool, default=False) + parser.add_argument( + '--precision', type=str, choices=['fp32', 'fp16'], default='fp16') + parser.add_argument('--batch_size', type=int, default=1) + parser.add_argument('--benchmark', type=_bool, default=False) + parser.add_argument('--model_name', type=str, default='') + + args = parser.parse_args() + + predictor = TIPCPredictor( + args.model_dir, + device=args.device, + cpu_thread_num=args.cpu_threads, + use_mkl=args.enable_mkldnn, + mkl_thread_num=args.cpu_threads, + use_trt=args.use_trt, + trt_precision_mode=args.precision, + benchmark=args.benchmark) + + predictor.predict(args.file_list[0], args.file_list[1]) + + if args.benchmark: + predictor.autolog.report() diff --git a/test_tipc/prepare.sh b/test_tipc/prepare.sh new file mode 100644 index 0000000..cc4c807 --- /dev/null +++ b/test_tipc/prepare.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +source test_tipc/common_func.sh + +set -o errexit +set -o nounset + +FILENAME=$1 +# $MODE must be one of ('lite_train_lite_infer' 'lite_train_whole_infer' 'whole_train_whole_infer', 'whole_infer') +MODE=$2 + +dataline=$(cat ${FILENAME}) + +# Parse params +IFS=$'\n' +lines=(${dataline}) +task_name=$(parse_first_value "${lines[1]}") +model_name=$(parse_second_value "${lines[1]}") + +# Download pretrained weights +if [ ${MODE} = 'whole_infer' ]; then + : +fi + +# Download datasets +DATA_DIR='./test_tipc/data/' +mkdir -p "${DATA_DIR}" +if [[ ${MODE} == 'lite_train_lite_infer' \ + || ${MODE} == 'lite_train_whole_infer' \ + || ${MODE} == 'whole_train_whole_infer' \ + || ${MODE} == 'whole_infer' ]]; then + + if [[ ${task_name} == 'cd' ]]; then + download_and_unzip_dataset "${DATA_DIR}" airchange https://paddlers.bj.bcebos.com/datasets/airchange.zip + elif [[ ${task_name} == 'clas' ]]; then + download_and_unzip_dataset "${DATA_DIR}" ucmerced https://paddlers.bj.bcebos.com/datasets/ucmerced.zip + elif [[ ${task_name} == 'det' ]]; then + download_and_unzip_dataset "${DATA_DIR}" sarship https://paddlers.bj.bcebos.com/datasets/sarship.zip + elif [[ ${task_name} == 'seg' ]]; then + download_and_unzip_dataset "${DATA_DIR}" rsseg https://paddlers.bj.bcebos.com/datasets/rsseg_mini.zip + fi + +fi diff --git a/test_tipc/run_task.py b/test_tipc/run_task.py new file mode 100644 index 0000000..c4cf6a1 --- /dev/null +++ b/test_tipc/run_task.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +import os + +# Import cv2 and sklearn before paddlers to solve the +# "ImportError: dlopen: cannot load any more object with static TLS" issue. +import cv2 +import sklearn +import paddle +import paddlers +from paddlers import transforms as T + +from config_utils import parse_args, build_objects, CfgNode + + +def format_cfg(cfg, indent=0): + s = '' + if isinstance(cfg, dict): + for i, (k, v) in enumerate(sorted(cfg.items())): + s += ' ' * indent + str(k) + ': ' + if isinstance(v, (dict, list, CfgNode)): + s += '\n' + format_cfg(v, indent=indent + 1) + else: + s += str(v) + if i != len(cfg) - 1: + s += '\n' + elif isinstance(cfg, list): + for i, v in enumerate(cfg): + s += ' ' * indent + '- ' + if isinstance(v, (dict, list, CfgNode)): + s += '\n' + format_cfg(v, indent=indent + 1) + else: + s += str(v) + if i != len(cfg) - 1: + s += '\n' + elif isinstance(cfg, CfgNode): + s += ' ' * indent + f"type: {cfg.type}" + '\n' + s += ' ' * indent + f"module: {cfg.module}" + '\n' + s += ' ' * indent + 'args: \n' + format_cfg(cfg.args, indent + 1) + return s + + +if __name__ == '__main__': + CfgNode.set_context(globals()) + + cfg = parse_args() + print(format_cfg(cfg)) + + # Automatically download data + if cfg['download_on']: + paddlers.utils.download_and_decompress( + cfg['download_url'], path=cfg['download_path']) + + if cfg['cmd'] == 'train': + train_dataset = build_objects( + cfg['datasets']['train'], mod=paddlers.datasets) + train_transforms = T.Compose( + build_objects( + cfg['transforms']['train'], mod=T)) + # XXX: Late binding of transforms + train_dataset.transforms = train_transforms + eval_dataset = build_objects(cfg['datasets']['eval'], mod=paddlers.datasets) + eval_transforms = T.Compose(build_objects(cfg['transforms']['eval'], mod=T)) + # XXX: Late binding of transforms + eval_dataset.transforms = eval_transforms + + model = build_objects( + cfg['model'], mod=getattr(paddlers.tasks, cfg['task'])) + + if cfg['cmd'] == 'train': + if cfg['optimizer']: + if len(cfg['optimizer'].args) == 0: + cfg['optimizer'].args = {} + if not isinstance(cfg['optimizer'].args, dict): + raise TypeError + if cfg['optimizer'].args.get('parameters', None) is not None: + raise ValueError + cfg['optimizer'].args['parameters'] = model.net.parameters() + optimizer = build_objects(cfg['optimizer'], mod=paddle.optimizer) + else: + optimizer = None + + model.train( + num_epochs=cfg['num_epochs'], + train_dataset=train_dataset, + train_batch_size=cfg['train_batch_size'], + eval_dataset=eval_dataset, + optimizer=optimizer, + save_interval_epochs=cfg['save_interval_epochs'], + log_interval_steps=cfg['log_interval_steps'], + save_dir=cfg['save_dir'], + learning_rate=cfg['learning_rate'], + early_stop=cfg['early_stop'], + early_stop_patience=cfg['early_stop_patience'], + use_vdl=cfg['use_vdl'], + resume_checkpoint=cfg['resume_checkpoint'] or None, + **cfg['train']) + elif cfg['cmd'] == 'eval': + state_dict = paddle.load( + os.path.join(cfg['resume_checkpoint'], 'model.pdparams')) + model.net.set_state_dict(state_dict) + res = model.evaluate(eval_dataset) + print(res) diff --git a/test_tipc/test_train_inference.sh b/test_tipc/test_train_inference.sh new file mode 100644 index 0000000..3fb300b --- /dev/null +++ b/test_tipc/test_train_inference.sh @@ -0,0 +1,369 @@ +#!/usr/bin/env bash + +source test_tipc/common_func.sh + +FILENAME=$1 +# $MODE be one of {'lite_train_lite_infer' 'lite_train_whole_infer' 'whole_train_whole_infer', 'whole_infer'} +MODE=$2 + +dataline=$(awk 'NR>=1{print}' $FILENAME) + +# Parse params +IFS=$'\n' +lines=(${dataline}) + +# Training params +task_name=$(parse_first_value "${lines[1]}") +model_name=$(parse_second_value "${lines[1]}") +python=$(func_parser_value "${lines[2]}") +gpu_list=$(func_parser_value "${lines[3]}") +train_use_gpu_key=$(func_parser_key "${lines[4]}") +train_use_gpu_value=$(func_parser_value "${lines[4]}") +autocast_list=$(func_parser_value "${lines[5]}") +autocast_key=$(func_parser_key "${lines[5]}") +epoch_key=$(func_parser_key "${lines[6]}") +epoch_num=$(func_parser_params "${lines[6]}") +save_model_key=$(func_parser_key "${lines[7]}") +train_batch_key=$(func_parser_key "${lines[8]}") +train_batch_value=$(func_parser_params "${lines[8]}") +pretrain_model_key=$(func_parser_key "${lines[9]}") +pretrain_model_value=$(func_parser_value "${lines[9]}") +train_model_name=$(func_parser_value "${lines[10]}") +train_infer_img_dir=$(parse_first_value "${lines[11]}") +train_infer_img_file_list=$(parse_second_value "${lines[11]}") +train_param_key1=$(func_parser_key "${lines[12]}") +train_param_value1=$(func_parser_value "${lines[12]}") + +trainer_list=$(func_parser_value "${lines[14]}") +trainer_norm=$(func_parser_key "${lines[15]}") +norm_trainer=$(func_parser_value "${lines[15]}") +pact_key=$(func_parser_key "${lines[16]}") +pact_trainer=$(func_parser_value "${lines[16]}") +fpgm_key=$(func_parser_key "${lines[17]}") +fpgm_trainer=$(func_parser_value "${lines[17]}") +distill_key=$(func_parser_key "${lines[18]}") +distill_trainer=$(func_parser_value "${lines[18]}") +trainer_key1=$(func_parser_key "${lines[19]}") +trainer_value1=$(func_parser_value "${lines[19]}") +trainer_key2=$(func_parser_key "${lines[20]}") +trainer_value2=$(func_parser_value "${lines[20]}") + +eval_py=$(func_parser_value "${lines[23]}") +eval_key1=$(func_parser_key "${lines[24]}") +eval_value1=$(func_parser_value "${lines[24]}") + +save_infer_key=$(func_parser_key "${lines[27]}") +export_weight=$(func_parser_key "${lines[28]}") +export_shape_key=$(func_parser_key "${lines[29]}") +export_shape_value=$(func_parser_value "${lines[29]}") +norm_export=$(func_parser_value "${lines[30]}") +pact_export=$(func_parser_value "${lines[31]}") +fpgm_export=$(func_parser_value "${lines[32]}") +distill_export=$(func_parser_value "${lines[33]}") +export_key1=$(func_parser_key "${lines[34]}") +export_value1=$(func_parser_value "${lines[34]}") +export_key2=$(func_parser_key "${lines[35]}") +export_value2=$(func_parser_value "${lines[35]}") +inference_dir=$(func_parser_value "${lines[36]}") + +# Params of inference model +infer_model_dir_list=$(func_parser_value "${lines[37]}") +infer_export_list=$(func_parser_value "${lines[38]}") +infer_is_quant=$(func_parser_value "${lines[39]}") +# Inference params +inference_py=$(func_parser_value "${lines[40]}") +use_gpu_key=$(func_parser_key "${lines[41]}") +use_gpu_list=$(func_parser_value "${lines[41]}") +use_mkldnn_key=$(func_parser_key "${lines[42]}") +use_mkldnn_list=$(func_parser_value "${lines[42]}") +cpu_threads_key=$(func_parser_key "${lines[43]}") +cpu_threads_list=$(func_parser_value "${lines[43]}") +batch_size_key=$(func_parser_key "${lines[44]}") +batch_size_list=$(func_parser_value "${lines[44]}") +use_trt_key=$(func_parser_key "${lines[45]}") +use_trt_list=$(func_parser_value "${lines[45]}") +precision_key=$(func_parser_key "${lines[46]}") +precision_list=$(func_parser_value "${lines[46]}") +infer_model_key=$(func_parser_key "${lines[47]}") +file_list_key=$(func_parser_key "${lines[48]}") +infer_img_dir=$(parse_first_value "${lines[48]}") +infer_img_file_list=$(parse_second_value "${lines[48]}") +save_log_key=$(func_parser_key "${lines[49]}") +benchmark_key=$(func_parser_key "${lines[50]}") +benchmark_value=$(func_parser_value "${lines[50]}") +infer_key1=$(func_parser_key "${lines[51]}") +infer_value1=$(func_parser_value "${lines[51]}") +infer_key2=$(func_parser_key "${lines[52]}") +infer_value2=$(func_parser_value "${lines[52]}") + +OUT_PATH="./test_tipc/output/${task_name}/${model_name}/${MODE}" +mkdir -p ${OUT_PATH} +status_log="${OUT_PATH}/results_python.log" +echo "------------------------ ${MODE} ------------------------" >> "${status_log}" + +# Parse extra args +parse_extra_args "${lines[@]}" +for params in ${extra_args[*]}; do + IFS=':' + arr=(${params}) + key=${arr[0]} + value=${arr[1]} + : +done + +function func_inference() { + local IFS='|' + local _python=$1 + local _script="$2" + local _model_dir="$3" + local _log_path="$4" + local _img_dir="$5" + local _file_list="$6" + + # Do inference + for use_gpu in ${use_gpu_list[*]}; do + if [ ${use_gpu} = 'False' ] || [ ${use_gpu} = 'cpu' ]; then + for use_mkldnn in ${use_mkldnn_list[*]}; do + if [ ${use_mkldnn} = 'False' ]; then + continue + fi + for threads in ${cpu_threads_list[*]}; do + for batch_size in ${batch_size_list[*]}; do + for precision in ${precision_list[*]}; do + if [ ${use_mkldnn} = 'False' ] && [ ${precision} = 'fp16' ]; then + continue + fi # Skip when enable fp16 but disable mkldnn + + set_precision=$(func_set_params "${precision_key}" "${precision}") + + _save_log_path="${_log_path}/python_infer_cpu_usemkldnn_${use_mkldnn}_threads_${threads}_precision_${precision}_batchsize_${batch_size}.log" + infer_value1="${_log_path}/python_infer_cpu_usemkldnn_${use_mkldnn}_threads_${threads}_precision_${precision}_batchsize_${batch_size}_results" + set_device=$(func_set_params "${use_gpu_key}" "${use_gpu}") + set_mkldnn=$(func_set_params "${use_mkldnn_key}" "${use_mkldnn}") + set_benchmark=$(func_set_params "${benchmark_key}" "${benchmark_value}") + set_batchsize=$(func_set_params "${batch_size_key}" "${batch_size}") + set_cpu_threads=$(func_set_params "${cpu_threads_key}" "${threads}") + set_model_dir=$(func_set_params "${infer_model_key}" "${_model_dir}") + set_infer_params1=$(func_set_params "${infer_key1}" "${infer_value1}") + set_infer_params2=$(func_set_params "${infer_key2}" "${infer_value2}") + + cmd="${_python} ${_script} ${file_list_key} ${_img_dir} ${_file_list} ${set_device} ${set_mkldnn} ${set_cpu_threads} ${set_model_dir} ${set_batchsize} ${set_benchmark} ${set_precision} ${set_infer_params1} ${set_infer_params2}" + echo ${cmd} + run_command "${cmd}" "${_save_log_path}" + + last_status=${PIPESTATUS[0]} + status_check ${last_status} "${cmd}" "${status_log}" "${model_name}" + done + done + done + done + elif [ ${use_gpu} = 'True' ] || [ ${use_gpu} = 'gpu' ]; then + for use_trt in ${use_trt_list[*]}; do + for precision in ${precision_list[*]}; do + if [ ${precision} = 'fp16' ] && [ ${use_trt} = 'False' ]; then + continue + fi # Skip when enable fp16 but disable trt + + for batch_size in ${batch_size_list[*]}; do + _save_log_path="${_log_path}/python_infer_gpu_usetrt_${use_trt}_precision_${precision}_batchsize_${batch_size}.log" + infer_value1="${_log_path}/python_infer_gpu_usetrt_${use_trt}_precision_${precision}_batchsize_${batch_size}_results" + set_device=$(func_set_params "${use_gpu_key}" "${use_gpu}") + set_benchmark=$(func_set_params "${benchmark_key}" "${benchmark_value}") + set_batchsize=$(func_set_params "${batch_size_key}" "${batch_size}") + set_tensorrt=$(func_set_params "${use_trt_key}" "${use_trt}") + set_precision=$(func_set_params "${precision_key}" "${precision}") + set_model_dir=$(func_set_params "${infer_model_key}" "${_model_dir}") + set_infer_params1=$(func_set_params "${infer_key1}" "${infer_value1}") + set_infer_params2=$(func_set_params "${infer_key2}" "${infer_value2}") + + cmd="${_python} ${_script} ${file_list_key} ${_img_dir} ${_file_list} ${set_device} ${set_tensorrt} ${set_precision} ${set_model_dir} ${set_batchsize} ${set_benchmark} ${set_infer_params2}" + echo ${cmd} + run_command "${cmd}" "${_save_log_path}" + + last_status=${PIPESTATUS[0]} + status_check $last_status "${cmd}" "${status_log}" "${model_name}" + + done + done + done + else + echo "Currently, hardwares other than CPU and GPU are not supported!" + fi + done +} + +if [ ${MODE} = 'whole_infer' ]; then + GPUID=$3 + if [ ${#GPUID} -le 0 ]; then + env="" + else + env="export CUDA_VISIBLE_DEVICES=${GPUID}" + fi + if [ ${infer_model_dir_list} == 'null' ]; then + echo -e "\033[33m No inference model is specified! \033[0m" + exit 1 + fi + # Set CUDA_VISIBLE_DEVICES + eval ${env} + export count=0 + IFS='|' + infer_run_exports=(${infer_export_list}) + for infer_model in ${infer_model_dir_list[*]}; do + # Run export + if [ ${infer_run_exports[count]} != 'null' ]; then + save_infer_dir="${infer_model}/static" + set_export_weight=$(func_set_params "${export_weight}" "${infer_model}") + set_export_shape=$(func_set_params "${export_shape_key}" "${export_shape_value}") + set_save_infer_key=$(func_set_params "${save_infer_key}" "${save_infer_dir}") + + export_cmd="${python} ${infer_run_exports[count]} ${set_export_weight} ${set_save_infer_key} ${set_export_shape}" + echo ${infer_run_exports[count]} + eval ${export_cmd} + + status_export=$? + status_check ${status_export} "${export_cmd}" "${status_log}" "${model_name}" + else + save_infer_dir=${infer_model} + fi + # Run inference + func_inference "${python}" "${inference_py}" "${save_infer_dir}" "${OUT_PATH}" "${infer_img_dir}" "${infer_img_file_list}" + count=$((${count} + 1)) + done +else + IFS='|' + export count=0 + USE_GPU_KEY=(${train_use_gpu_value}) + for gpu in ${gpu_list[*]}; do + train_use_gpu=${USE_GPU_KEY[count]} + count=$((${count} + 1)) + ips="" + if [ ${gpu} = '-1' ]; then + env="" + elif [ ${#gpu} -le 1 ]; then + env="export CUDA_VISIBLE_DEVICES=${gpu}" + eval ${env} + elif [ ${#gpu} -le 15 ]; then + IFS=',' + array=(${gpu}) + env="export CUDA_VISIBLE_DEVICES=${array[0]}" + IFS='|' + else + IFS=';' + array=(${gpu}) + ips=${array[0]} + gpu=${array[1]} + IFS='|' + env="" + fi + for autocast in ${autocast_list[*]}; do + if [ ${autocast} = 'amp' ]; then + set_amp_config="Global.use_amp=True Global.scale_loss=1024.0 Global.use_dynamic_loss_scaling=True" + else + set_amp_config="" + fi + for trainer in ${trainer_list[*]}; do + if [ ${trainer} = ${pact_key} ]; then + run_train=${pact_trainer} + run_export=${pact_export} + elif [ ${trainer} = "${fpgm_key}" ]; then + run_train=${fpgm_trainer} + run_export=${fpgm_export} + elif [ ${trainer} = "${distill_key}" ]; then + run_train=${distill_trainer} + run_export=${distill_export} + elif [ ${trainer} = ${trainer_key1} ]; then + run_train=${trainer_value1} + run_export=${export_value1} + elif [[ ${trainer} = ${trainer_key2} ]]; then + run_train=${trainer_value2} + run_export=${export_value2} + else + run_train=${norm_trainer} + run_export=${norm_export} + fi + + if [ ${run_train} = 'null' ]; then + continue + fi + set_autocast=$(func_set_params "${autocast_key}" "${autocast}") + set_epoch=$(func_set_params "${epoch_key}" "${epoch_num}") + set_pretrain=$(func_set_params "${pretrain_model_key}" "${pretrain_model_value}") + set_batchsize=$(func_set_params "${train_batch_key}" "${train_batch_value}") + set_train_params1=$(func_set_params "${train_param_key1}" "${train_param_value1}") + set_use_gpu=$(func_set_params "${train_use_gpu_key}" "${train_use_gpu}") + # If length of ips >= 15, then it is seen as multi-machine. + # 15 is the min length of ips info for multi-machine: 0.0.0.0,0.0.0.0 + if [ ${#ips} -le 15 ]; then + save_dir="${OUT_PATH}/${trainer}_gpus_${gpu}_autocast_${autocast}" + nodes=1 + else + IFS=',' + ips_array=(${ips}) + IFS='|' + nodes=${#ips_array[@]} + save_dir="${OUT_PATH}/${trainer}_gpus_${gpu}_autocast_${autocast}_nodes_${nodes}" + fi + log_path="${OUT_PATH}/${trainer}_gpus_${gpu}_autocast_${autocast}_nodes_${nodes}.log" + + # Load pretrained model from norm training if current trainer is pact or fpgm trainer. + if ([ ${trainer} = ${pact_key} ] || [ ${trainer} = ${fpgm_key} ]) && [ ${nodes} -le 1 ]; then + set_pretrain="${load_norm_train_model}" + fi + + set_save_model=$(func_set_params "${save_model_key}" "${save_dir}") + if [ ${#gpu} -le 2 ]; then # Train with cpu or single gpu + cmd="${python} ${run_train} ${set_use_gpu} ${set_save_model} ${set_epoch} ${set_pretrain} ${set_autocast} ${set_batchsize} ${set_train_params1} ${set_amp_config}" + elif [ ${#ips} -le 15 ]; then # Train with multi-gpu + cmd="${python} -m paddle.distributed.launch --gpus=${gpu} ${run_train} ${set_use_gpu} ${set_save_model} ${set_epoch} ${set_pretrain} ${set_autocast} ${set_batchsize} ${set_train_params1} ${set_amp_config}" + else # Train with multi-machine + cmd="${python} -m paddle.distributed.launch --ips=${ips} --gpus=${gpu} ${run_train} ${set_use_gpu} ${set_save_model} ${set_pretrain} ${set_epoch} ${set_autocast} ${set_batchsize} ${set_train_params1} ${set_amp_config}" + fi + + echo ${cmd} + # Run train + run_command "${cmd}" "${log_path}" + status_check $? "${cmd}" "${status_log}" "${model_name}" + + if [[ "${cmd}" == *'paddle.distributed.launch'* ]]; then + cat log/workerlog.0 >> ${log_path} + fi + + set_eval_pretrain=$(func_set_params "${pretrain_model_key}" "${save_dir}/${train_model_name}/model.pdparams") + # Save norm trained models to set pretrain for pact training and fpgm training + if [ ${trainer} = ${trainer_norm} ] && [ ${nodes} -le 1 ]; then + load_norm_train_model=${set_eval_pretrain} + fi + # Run evaluation + if [ ${eval_py} != 'null' ]; then + log_path="${OUT_PATH}/${trainer}_gpus_${gpu}_autocast_${autocast}_nodes_${nodes}_eval.log" + set_eval_params1=$(func_set_params "${eval_key1}" "${eval_value1}") + eval_cmd="${python} ${eval_py} ${set_eval_pretrain} ${set_use_gpu} ${set_eval_params1}" + run_command "${eval_cmd}" "${log_path}" + status_check $? "${eval_cmd}" "${status_log}" "${model_name}" + fi + # Run export model + if [ ${run_export} != 'null' ]; then + log_path="${OUT_PATH}/${trainer}_gpus_${gpu}_autocast_${autocast}_nodes_${nodes}_export.log" + save_infer_path="${save_dir}/static" + set_export_weight=$(func_set_params "${export_weight}" "${save_dir}/${train_model_name}") + set_export_shape=$(func_set_params "${export_shape_key}" "${export_shape_value}") + set_save_infer_key=$(func_set_params "${save_infer_key}" "${save_infer_path}") + export_cmd="${python} ${run_export} ${set_export_weight} ${set_save_infer_key} ${set_export_shape}" + run_command "${export_cmd}" "${log_path}" + status_check $? "${export_cmd}" "${status_log}" "${model_name}" + + # Run inference + eval ${env} + if [[ ${inference_dir} != 'null' ]] && [[ ${inference_dir} != '##' ]]; then + infer_model_dir="${save_infer_path}/${inference_dir}" + else + infer_model_dir=${save_infer_path} + fi + func_inference "${python}" "${inference_py}" "${infer_model_dir}" "${OUT_PATH}" "${train_infer_img_dir}" "${train_infer_img_file_list}" + + eval "unset CUDA_VISIBLE_DEVICES" + fi + done # Done with: for trainer in ${trainer_list[*]}; do + done # Done with: for autocast in ${autocast_list[*]}; do + done # Done with: for gpu in ${gpu_list[*]}; do +fi # End if [ ${MODE} = 'infer' ]; then \ No newline at end of file