From 0eddba1af04a2188d63cb82214acdc92c01acbe9 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Tue, 9 Apr 2024 16:15:46 +0800 Subject: [PATCH 01/19] Add: densenet161 inference script. --- .../classification/densenet161/igie/README.md | 43 ++++ .../densenet161/igie/build_engine.py | 73 +++++++ .../classification/densenet161/igie/export.py | 74 +++++++ .../densenet161/igie/inference.py | 186 ++++++++++++++++++ .../infer_densenet161_fp16_accuracy.sh | 35 ++++ .../infer_densenet161_fp16_performance.sh | 36 ++++ 6 files changed, 447 insertions(+) create mode 100644 models/cv/classification/densenet161/igie/README.md create mode 100644 models/cv/classification/densenet161/igie/build_engine.py create mode 100644 models/cv/classification/densenet161/igie/export.py create mode 100644 models/cv/classification/densenet161/igie/inference.py create mode 100644 models/cv/classification/densenet161/igie/scripts/infer_densenet161_fp16_accuracy.sh create mode 100644 models/cv/classification/densenet161/igie/scripts/infer_densenet161_fp16_performance.sh diff --git a/models/cv/classification/densenet161/igie/README.md b/models/cv/classification/densenet161/igie/README.md new file mode 100644 index 00000000..aa9d0173 --- /dev/null +++ b/models/cv/classification/densenet161/igie/README.md @@ -0,0 +1,43 @@ +# DenseNet161 + +## Description +DenseNet161 is a convolutional neural network architecture that belongs to the family of Dense Convolutional Networks (DenseNets). Introduced as an extension to the previous DenseNet models, DenseNet161 offers improved performance and deeper network capacity, making it suitable for various computer vision tasks. + + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +``` +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight densenet161-8d451a50.pth --output densenet161.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_densenet161_fp16_accuracy.sh +# Performance +bash scripts/infer_densenet161_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +------------|-----------|----------|---------|---------|-------- +DenseNet161 | 32 | FP16 | 520.26 | 77.095 | 93.526 diff --git a/models/cv/classification/densenet161/igie/build_engine.py b/models/cv/classification/densenet161/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/densenet161/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/densenet161/igie/export.py b/models/cv/classification/densenet161/igie/export.py new file mode 100644 index 00000000..cb5466db --- /dev/null +++ b/models/cv/classification/densenet161/igie/export.py @@ -0,0 +1,74 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import torch +import torchvision +import argparse +import re + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = torchvision.models.densenet161(weights=False) + + state_dict = torch.load(args.weight) + + pattern = re.compile(r'^(.*denselayer\d+\.(?:norm|relu|conv))\.((?:[12])\.(?:weight|bias|running_mean|running_var))$' + ) + for key in list(state_dict.keys()): + res = pattern.match(key) + if res: + new_key = res.group(1) + res.group(2) + state_dict[new_key] = state_dict[key] + del state_dict[key] + + model.load_state_dict(state_dict) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/densenet161/igie/inference.py b/models/cv/classification/densenet161/igie/inference.py new file mode 100644 index 00000000..3aef3ec7 --- /dev/null +++ b/models/cv/classification/densenet161/igie/inference.py @@ -0,0 +1,186 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256, interpolation=InterpolationMode.BILINEAR), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/densenet161/igie/scripts/infer_densenet161_fp16_accuracy.sh b/models/cv/classification/densenet161/igie/scripts/infer_densenet161_fp16_accuracy.sh new file mode 100644 index 00000000..7ad5d57e --- /dev/null +++ b/models/cv/classification/densenet161/igie/scripts/infer_densenet161_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="densenet161.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path densenet161_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine densenet161_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/densenet161/igie/scripts/infer_densenet161_fp16_performance.sh b/models/cv/classification/densenet161/igie/scripts/infer_densenet161_fp16_performance.sh new file mode 100644 index 00000000..96548717 --- /dev/null +++ b/models/cv/classification/densenet161/igie/scripts/infer_densenet161_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="densenet161.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path densenet161_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine densenet161_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From a81c801d314f0a923fb6ce92fb6675261d3ed30d Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Tue, 9 Apr 2024 17:47:01 +0800 Subject: [PATCH 02/19] Add: efficientnet_b1 inference script. --- .../efficientnet_b1/igie/README.md | 43 ++++ .../efficientnet_b1/igie/build_engine.py | 73 +++++++ .../efficientnet_b1/igie/export.py | 61 ++++++ .../efficientnet_b1/igie/inference.py | 186 ++++++++++++++++++ .../infer_efficientnet_b1_fp16_accuracy.sh | 35 ++++ .../infer_efficientnet_b1_fp16_performance.sh | 36 ++++ 6 files changed, 434 insertions(+) create mode 100644 models/cv/classification/efficientnet_b1/igie/README.md create mode 100644 models/cv/classification/efficientnet_b1/igie/build_engine.py create mode 100644 models/cv/classification/efficientnet_b1/igie/export.py create mode 100644 models/cv/classification/efficientnet_b1/igie/inference.py create mode 100644 models/cv/classification/efficientnet_b1/igie/scripts/infer_efficientnet_b1_fp16_accuracy.sh create mode 100644 models/cv/classification/efficientnet_b1/igie/scripts/infer_efficientnet_b1_fp16_performance.sh diff --git a/models/cv/classification/efficientnet_b1/igie/README.md b/models/cv/classification/efficientnet_b1/igie/README.md new file mode 100644 index 00000000..a63b0197 --- /dev/null +++ b/models/cv/classification/efficientnet_b1/igie/README.md @@ -0,0 +1,43 @@ +# EfficientNet B1 + +## Description +EfficientNet B1 is a convolutional neural network architecture that falls under the EfficientNet family, known for its remarkable balance between model size and performance. Introduced as part of the EfficientNet series, EfficientNet B1 offers a compact yet powerful solution for various computer vision tasks, including image classification, object detection and segmentation. + + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +``` +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight efficientnet_b1-c27df63c.pth --output efficientnet_b1.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_efficientnet_b1_fp16_accuracy.sh +# Performance +bash scripts/infer_efficientnet_b1_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +----------------|-----------|----------|---------|---------|-------- +Efficientnet_b1 | 32 | FP16 | 1292.31 | 78.823 | 94.494 diff --git a/models/cv/classification/efficientnet_b1/igie/build_engine.py b/models/cv/classification/efficientnet_b1/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/efficientnet_b1/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/efficientnet_b1/igie/export.py b/models/cv/classification/efficientnet_b1/igie/export.py new file mode 100644 index 00000000..ec2d6de9 --- /dev/null +++ b/models/cv/classification/efficientnet_b1/igie/export.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import torch +import torchvision +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = torchvision.models.efficientnet_b1() + model.load_state_dict(torch.load(args.weight)) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/efficientnet_b1/igie/inference.py b/models/cv/classification/efficientnet_b1/igie/inference.py new file mode 100644 index 00000000..3aef3ec7 --- /dev/null +++ b/models/cv/classification/efficientnet_b1/igie/inference.py @@ -0,0 +1,186 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256, interpolation=InterpolationMode.BILINEAR), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/efficientnet_b1/igie/scripts/infer_efficientnet_b1_fp16_accuracy.sh b/models/cv/classification/efficientnet_b1/igie/scripts/infer_efficientnet_b1_fp16_accuracy.sh new file mode 100644 index 00000000..5d57cdf1 --- /dev/null +++ b/models/cv/classification/efficientnet_b1/igie/scripts/infer_efficientnet_b1_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="efficientnet_b1.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path efficientnet_b1_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine efficientnet_b1_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/efficientnet_b1/igie/scripts/infer_efficientnet_b1_fp16_performance.sh b/models/cv/classification/efficientnet_b1/igie/scripts/infer_efficientnet_b1_fp16_performance.sh new file mode 100644 index 00000000..69c2ad2f --- /dev/null +++ b/models/cv/classification/efficientnet_b1/igie/scripts/infer_efficientnet_b1_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="efficientnet_b1.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path efficientnet_b1_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine efficientnet_b1_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From 5a09f82a5414f38ced08e8f4a2e1922068e1615f Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Wed, 10 Apr 2024 11:18:51 +0800 Subject: [PATCH 03/19] Add: efficientnetv2_rw_t inference script. --- .../efficientnetv2_rw_t/igie/README.md | 44 +++++ .../efficientnetv2_rw_t/igie/build_engine.py | 73 +++++++ .../efficientnetv2_rw_t/igie/export.py | 58 ++++++ .../efficientnetv2_rw_t/igie/inference.py | 183 ++++++++++++++++++ ...infer_efficientnetv2_rw_t_fp16_accuracy.sh | 35 ++++ ...er_efficientnetv2_rw_t_fp16_performance.sh | 36 ++++ 6 files changed, 429 insertions(+) create mode 100644 models/cv/classification/efficientnetv2_rw_t/igie/README.md create mode 100644 models/cv/classification/efficientnetv2_rw_t/igie/build_engine.py create mode 100644 models/cv/classification/efficientnetv2_rw_t/igie/export.py create mode 100644 models/cv/classification/efficientnetv2_rw_t/igie/inference.py create mode 100644 models/cv/classification/efficientnetv2_rw_t/igie/scripts/infer_efficientnetv2_rw_t_fp16_accuracy.sh create mode 100644 models/cv/classification/efficientnetv2_rw_t/igie/scripts/infer_efficientnetv2_rw_t_fp16_performance.sh diff --git a/models/cv/classification/efficientnetv2_rw_t/igie/README.md b/models/cv/classification/efficientnetv2_rw_t/igie/README.md new file mode 100644 index 00000000..d9edb305 --- /dev/null +++ b/models/cv/classification/efficientnetv2_rw_t/igie/README.md @@ -0,0 +1,44 @@ +# EfficientNetv2_rw_t + +## Description +EfficientNetV2_rw_t is an enhanced version of the EfficientNet family of convolutional neural network architectures. It builds upon the success of its predecessors by introducing novel advancements aimed at further improving performance and efficiency in various computer vision tasks. + + +## Setup + +### Install +``` +pip3 install timm +pip3 install onnx +pip3 install tqdm +``` +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight efficientnetv2_t_agc-3620981a.pth --output efficientnetv2_rw_t.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_efficientnetv2_rw_t_b1_fp16_accuracy.sh +# Performance +bash scripts/infer_efficientnetv2_rw_t_b1_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +--------------------|-----------|----------|---------|---------|-------- +Efficientnetv2_rw_t | 32 | FP16 | 831.678 | 82.306 | 96.163 diff --git a/models/cv/classification/efficientnetv2_rw_t/igie/build_engine.py b/models/cv/classification/efficientnetv2_rw_t/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/efficientnetv2_rw_t/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/efficientnetv2_rw_t/igie/export.py b/models/cv/classification/efficientnetv2_rw_t/igie/export.py new file mode 100644 index 00000000..3c3f579c --- /dev/null +++ b/models/cv/classification/efficientnetv2_rw_t/igie/export.py @@ -0,0 +1,58 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import timm +import torch +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = timm.create_model('efficientnetv2_rw_t', checkpoint_path=args.weight) + model.eval() + + dummy_input = torch.randn([32, 3, 288, 288]) + + torch.onnx.export( + model, + dummy_input, + args.output, + opset_version=13, + do_constant_folding=True, + input_names=["input"], + output_names=["output"], + dynamic_axes={"input": {0: "batch"}, "output": {0: "batch"}} + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/efficientnetv2_rw_t/igie/inference.py b/models/cv/classification/efficientnetv2_rw_t/igie/inference.py new file mode 100644 index 00000000..e33c91fa --- /dev/null +++ b/models/cv/classification/efficientnetv2_rw_t/igie/inference.py @@ -0,0 +1,183 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +from timm.data import create_dataset, create_loader + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + datasets = create_dataset(root=data_path, name="") + + dataloader = create_loader( + datasets, + input_size=(3, 288, 288), + batch_size=batch_size, + interpolation='bicubic', + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225), + crop_pct=1.0, + use_prefetcher = False, + num_workers = num_workers + ) + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/efficientnetv2_rw_t/igie/scripts/infer_efficientnetv2_rw_t_fp16_accuracy.sh b/models/cv/classification/efficientnetv2_rw_t/igie/scripts/infer_efficientnetv2_rw_t_fp16_accuracy.sh new file mode 100644 index 00000000..e1e98414 --- /dev/null +++ b/models/cv/classification/efficientnetv2_rw_t/igie/scripts/infer_efficientnetv2_rw_t_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="efficientnetv2_rw_t.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,288,288 \ + --precision fp16 \ + --engine_path efficientnetv2_rw_t_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine efficientnetv2_rw_t_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/efficientnetv2_rw_t/igie/scripts/infer_efficientnetv2_rw_t_fp16_performance.sh b/models/cv/classification/efficientnetv2_rw_t/igie/scripts/infer_efficientnetv2_rw_t_fp16_performance.sh new file mode 100644 index 00000000..797791fe --- /dev/null +++ b/models/cv/classification/efficientnetv2_rw_t/igie/scripts/infer_efficientnetv2_rw_t_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="efficientnetv2_rw_t.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,288,288 \ + --precision fp16 \ + --engine_path efficientnetv2_rw_t_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine efficientnetv2_rw_t_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From 04c0f8e1acba24e57db01d09a0bfa5bdc4283296 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Wed, 10 Apr 2024 17:15:23 +0800 Subject: [PATCH 04/19] Add: mobilenet_v3 inference script. --- .../mobilenet_v3/igie/README.md | 42 ++++ .../mobilenet_v3/igie/build_engine.py | 73 +++++++ .../mobilenet_v3/igie/export.py | 61 ++++++ .../mobilenet_v3/igie/inference.py | 186 ++++++++++++++++++ .../infer_mobilenetv3_small_fp16_accuracy.sh | 35 ++++ ...nfer_mobilenetv3_small_fp16_performance.sh | 36 ++++ 6 files changed, 433 insertions(+) create mode 100644 models/cv/classification/mobilenet_v3/igie/README.md create mode 100644 models/cv/classification/mobilenet_v3/igie/build_engine.py create mode 100644 models/cv/classification/mobilenet_v3/igie/export.py create mode 100644 models/cv/classification/mobilenet_v3/igie/inference.py create mode 100644 models/cv/classification/mobilenet_v3/igie/scripts/infer_mobilenetv3_small_fp16_accuracy.sh create mode 100644 models/cv/classification/mobilenet_v3/igie/scripts/infer_mobilenetv3_small_fp16_performance.sh diff --git a/models/cv/classification/mobilenet_v3/igie/README.md b/models/cv/classification/mobilenet_v3/igie/README.md new file mode 100644 index 00000000..ac62b380 --- /dev/null +++ b/models/cv/classification/mobilenet_v3/igie/README.md @@ -0,0 +1,42 @@ +# MobileNetV3_Small + +## Description +MobileNetV3_Small is a lightweight convolutional neural network architecture designed for efficient mobile and embedded devices. It is part of the MobileNet family, renowned for its compact size and high performance, making it ideal for applications with limited computational resources.The key focus of MobileNetV3_Small is to achieve a balance between model size, speed, and accuracy. + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +``` +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight mobilenet_v3_small-047dcff4.pth --output mobilenetv3_small.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_mobilenetv3_small_fp16_accuracy.sh +# Performance +bash scripts/infer_mobilenetv3_small_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +------------------|-----------|----------|---------|---------|-------- +MobileNetV3_Small | 32 | FP16 | 6837.86 | 67.612 | 87.404 diff --git a/models/cv/classification/mobilenet_v3/igie/build_engine.py b/models/cv/classification/mobilenet_v3/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/mobilenet_v3/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v3/igie/export.py b/models/cv/classification/mobilenet_v3/igie/export.py new file mode 100644 index 00000000..e02485e4 --- /dev/null +++ b/models/cv/classification/mobilenet_v3/igie/export.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import torch +import torchvision +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = torchvision.models.mobilenet_v3_small() + model.load_state_dict(torch.load(args.weight)) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/mobilenet_v3/igie/inference.py b/models/cv/classification/mobilenet_v3/igie/inference.py new file mode 100644 index 00000000..3aef3ec7 --- /dev/null +++ b/models/cv/classification/mobilenet_v3/igie/inference.py @@ -0,0 +1,186 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256, interpolation=InterpolationMode.BILINEAR), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v3/igie/scripts/infer_mobilenetv3_small_fp16_accuracy.sh b/models/cv/classification/mobilenet_v3/igie/scripts/infer_mobilenetv3_small_fp16_accuracy.sh new file mode 100644 index 00000000..95a26480 --- /dev/null +++ b/models/cv/classification/mobilenet_v3/igie/scripts/infer_mobilenetv3_small_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="mobilenetv3_small.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path mobilenetv3_small_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine mobilenetv3_small_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v3/igie/scripts/infer_mobilenetv3_small_fp16_performance.sh b/models/cv/classification/mobilenet_v3/igie/scripts/infer_mobilenetv3_small_fp16_performance.sh new file mode 100644 index 00000000..05b9e1ec --- /dev/null +++ b/models/cv/classification/mobilenet_v3/igie/scripts/infer_mobilenetv3_small_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="mobilenetv3_small.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path mobilenetv3_small_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine mobilenetv3_small_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From ee86ad397519d71fc93aa6dcddcf8b08250c66d5 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Wed, 10 Apr 2024 17:35:21 +0800 Subject: [PATCH 05/19] Add: mobilenet_v3_large inference script. --- .../mobilenet_v3_large/igie/README.md | 42 ++++ .../mobilenet_v3_large/igie/build_engine.py | 73 +++++++ .../mobilenet_v3_large/igie/export.py | 61 ++++++ .../mobilenet_v3_large/igie/inference.py | 186 ++++++++++++++++++ .../infer_mobilenetv3_large_fp16_accuracy.sh | 35 ++++ ...nfer_mobilenetv3_large_fp16_performance.sh | 36 ++++ 6 files changed, 433 insertions(+) create mode 100644 models/cv/classification/mobilenet_v3_large/igie/README.md create mode 100644 models/cv/classification/mobilenet_v3_large/igie/build_engine.py create mode 100644 models/cv/classification/mobilenet_v3_large/igie/export.py create mode 100644 models/cv/classification/mobilenet_v3_large/igie/inference.py create mode 100644 models/cv/classification/mobilenet_v3_large/igie/scripts/infer_mobilenetv3_large_fp16_accuracy.sh create mode 100644 models/cv/classification/mobilenet_v3_large/igie/scripts/infer_mobilenetv3_large_fp16_performance.sh diff --git a/models/cv/classification/mobilenet_v3_large/igie/README.md b/models/cv/classification/mobilenet_v3_large/igie/README.md new file mode 100644 index 00000000..2d76704d --- /dev/null +++ b/models/cv/classification/mobilenet_v3_large/igie/README.md @@ -0,0 +1,42 @@ +# MobileNetV3_Large + +## Description +MobileNetV3_Large builds upon the success of its predecessors by incorporating several innovative design strategies to enhance performance. It features larger model capacity and computational resources compared to MobileNetV3_Small, allowing for deeper network architectures and more complex feature representations. + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +``` +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight mobilenet_v3_large-8738ca79.pth --output mobilenetv3_large.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_mobilenetv3_large_fp16_accuracy.sh +# Performance +bash scripts/infer_mobilenetv3_large_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +------------------|-----------|----------|---------|---------|-------- +MobileNetV3_Large | 32 | FP16 | 3644.08 | 74.042 | 91.303 diff --git a/models/cv/classification/mobilenet_v3_large/igie/build_engine.py b/models/cv/classification/mobilenet_v3_large/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/mobilenet_v3_large/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v3_large/igie/export.py b/models/cv/classification/mobilenet_v3_large/igie/export.py new file mode 100644 index 00000000..1e6c82ea --- /dev/null +++ b/models/cv/classification/mobilenet_v3_large/igie/export.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import torch +import torchvision +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = torchvision.models.mobilenet_v3_large() + model.load_state_dict(torch.load(args.weight)) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/mobilenet_v3_large/igie/inference.py b/models/cv/classification/mobilenet_v3_large/igie/inference.py new file mode 100644 index 00000000..3aef3ec7 --- /dev/null +++ b/models/cv/classification/mobilenet_v3_large/igie/inference.py @@ -0,0 +1,186 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256, interpolation=InterpolationMode.BILINEAR), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v3_large/igie/scripts/infer_mobilenetv3_large_fp16_accuracy.sh b/models/cv/classification/mobilenet_v3_large/igie/scripts/infer_mobilenetv3_large_fp16_accuracy.sh new file mode 100644 index 00000000..dff18e9b --- /dev/null +++ b/models/cv/classification/mobilenet_v3_large/igie/scripts/infer_mobilenetv3_large_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="mobilenetv3_large.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path mobilenetv3_large_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine mobilenetv3_large_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v3_large/igie/scripts/infer_mobilenetv3_large_fp16_performance.sh b/models/cv/classification/mobilenet_v3_large/igie/scripts/infer_mobilenetv3_large_fp16_performance.sh new file mode 100644 index 00000000..49380c0a --- /dev/null +++ b/models/cv/classification/mobilenet_v3_large/igie/scripts/infer_mobilenetv3_large_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="mobilenetv3_large.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path mobilenetv3_large_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine mobilenetv3_large_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From 702f95213cb08a87a49d6931ea9ce7fb9b7b7e19 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Wed, 10 Apr 2024 18:46:10 +0800 Subject: [PATCH 06/19] Add: regnet_x_1_6gf inference script. --- .../regnet_x_1_6gf/igie/README.md | 42 ++++ .../regnet_x_1_6gf/igie/build_engine.py | 73 +++++++ .../regnet_x_1_6gf/igie/export.py | 61 ++++++ .../regnet_x_1_6gf/igie/inference.py | 186 ++++++++++++++++++ .../infer_regnet_x_1_6gf_fp16_accuracy.sh | 35 ++++ .../infer_regnet_x_1_6gf_fp16_performance.sh | 36 ++++ 6 files changed, 433 insertions(+) create mode 100644 models/cv/classification/regnet_x_1_6gf/igie/README.md create mode 100644 models/cv/classification/regnet_x_1_6gf/igie/build_engine.py create mode 100644 models/cv/classification/regnet_x_1_6gf/igie/export.py create mode 100644 models/cv/classification/regnet_x_1_6gf/igie/inference.py create mode 100644 models/cv/classification/regnet_x_1_6gf/igie/scripts/infer_regnet_x_1_6gf_fp16_accuracy.sh create mode 100644 models/cv/classification/regnet_x_1_6gf/igie/scripts/infer_regnet_x_1_6gf_fp16_performance.sh diff --git a/models/cv/classification/regnet_x_1_6gf/igie/README.md b/models/cv/classification/regnet_x_1_6gf/igie/README.md new file mode 100644 index 00000000..1869ecfa --- /dev/null +++ b/models/cv/classification/regnet_x_1_6gf/igie/README.md @@ -0,0 +1,42 @@ +# RegNet_x_1_6gf + +## Description +RegNet is a family of models designed for image classification tasks, as described in the paper "Designing Network Design Spaces". The RegNet design space provides simple and fast networks that work well across a wide range of computational budgets.The architecture of RegNet models is based on the principle of designing network design spaces, which allows for a more systematic exploration of possible network architectures. This makes it easier to understand and modify the architecture.RegNet_x_1_6gf is a specific model within the RegNet family, designed for image classification tasks + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +``` +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight regnet_x_1_6gf-a12f2b72.pth --output regnet_x_1_6gf.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_regnet_x_1_6gf_fp16_accuracy.sh +# Performance +bash scripts/infer_regnet_x_1_6gf_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +------------------|-----------|----------|---------|---------|-------- +RegNet_x_1_6gf | 32 | FP16 | 487.749 | 79.303 | 94.624 diff --git a/models/cv/classification/regnet_x_1_6gf/igie/build_engine.py b/models/cv/classification/regnet_x_1_6gf/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/regnet_x_1_6gf/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/regnet_x_1_6gf/igie/export.py b/models/cv/classification/regnet_x_1_6gf/igie/export.py new file mode 100644 index 00000000..3e60b43a --- /dev/null +++ b/models/cv/classification/regnet_x_1_6gf/igie/export.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import torch +import torchvision +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = torchvision.models.regnet_x_1_6gf() + model.load_state_dict(torch.load(args.weight)) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/regnet_x_1_6gf/igie/inference.py b/models/cv/classification/regnet_x_1_6gf/igie/inference.py new file mode 100644 index 00000000..3aef3ec7 --- /dev/null +++ b/models/cv/classification/regnet_x_1_6gf/igie/inference.py @@ -0,0 +1,186 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256, interpolation=InterpolationMode.BILINEAR), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/regnet_x_1_6gf/igie/scripts/infer_regnet_x_1_6gf_fp16_accuracy.sh b/models/cv/classification/regnet_x_1_6gf/igie/scripts/infer_regnet_x_1_6gf_fp16_accuracy.sh new file mode 100644 index 00000000..4dda13dc --- /dev/null +++ b/models/cv/classification/regnet_x_1_6gf/igie/scripts/infer_regnet_x_1_6gf_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="regnet_x_1_6gf.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path regnet_x_1_6gf_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine regnet_x_1_6gf_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/regnet_x_1_6gf/igie/scripts/infer_regnet_x_1_6gf_fp16_performance.sh b/models/cv/classification/regnet_x_1_6gf/igie/scripts/infer_regnet_x_1_6gf_fp16_performance.sh new file mode 100644 index 00000000..2fa72b64 --- /dev/null +++ b/models/cv/classification/regnet_x_1_6gf/igie/scripts/infer_regnet_x_1_6gf_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="regnet_x_1_6gf.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path regnet_x_1_6gf_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine regnet_x_1_6gf_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From 34b972006c6994c6abc3eef53754caae0ca06f52 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Mon, 15 Apr 2024 11:04:33 +0800 Subject: [PATCH 07/19] Add: resnet101 inference script. --- .../classification/resnet101/igie/README.md | 52 +++++ .../resnet101/igie/build_engine.py | 73 +++++++ .../classification/resnet101/igie/export.py | 61 ++++++ .../resnet101/igie/inference.py | 186 ++++++++++++++++++ .../classification/resnet101/igie/quantize.py | 105 ++++++++++ .../scripts/infer_resnet101_fp16_accuracy.sh | 35 ++++ .../infer_resnet101_fp16_performance.sh | 36 ++++ .../scripts/infer_resnet101_int8_accuracy.sh | 43 ++++ .../infer_resnet101_int8_performance.sh | 44 +++++ 9 files changed, 635 insertions(+) create mode 100644 models/cv/classification/resnet101/igie/README.md create mode 100644 models/cv/classification/resnet101/igie/build_engine.py create mode 100644 models/cv/classification/resnet101/igie/export.py create mode 100644 models/cv/classification/resnet101/igie/inference.py create mode 100644 models/cv/classification/resnet101/igie/quantize.py create mode 100644 models/cv/classification/resnet101/igie/scripts/infer_resnet101_fp16_accuracy.sh create mode 100644 models/cv/classification/resnet101/igie/scripts/infer_resnet101_fp16_performance.sh create mode 100644 models/cv/classification/resnet101/igie/scripts/infer_resnet101_int8_accuracy.sh create mode 100644 models/cv/classification/resnet101/igie/scripts/infer_resnet101_int8_performance.sh diff --git a/models/cv/classification/resnet101/igie/README.md b/models/cv/classification/resnet101/igie/README.md new file mode 100644 index 00000000..0ca92625 --- /dev/null +++ b/models/cv/classification/resnet101/igie/README.md @@ -0,0 +1,52 @@ +# ResNet101 + +## Description +ResNet101 is a convolutional neural network architecture that belongs to the ResNet (Residual Network) family.With a total of 101 layers, ResNet101 comprises multiple residual blocks, each containing convolutional layers with batch normalization and rectified linear unit (ReLU) activations. These residual blocks allow the network to effectively capture complex features at different levels of abstraction, leading to superior performance on image recognition tasks. + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +``` + +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight resnet101-63fe2227.pth --output resnet101.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_resnet101_fp16_accuracy.sh +# Performance +bash scripts/infer_resnet101_fp16_performance.sh +``` + +### INT8 +```bash +# Accuracy +bash scripts/infer_resnet101_int8_accuracy.sh +# Performance +bash scripts/infer_resnet101_int8_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +----------|-----------|----------|----------|----------|-------- +ResNet101 | 32 | FP16 | 2507.074 | 77.331 | 93.520 +ResNet101 | 32 | INT8 | 5458.890 | 76.719 | 93.348 diff --git a/models/cv/classification/resnet101/igie/build_engine.py b/models/cv/classification/resnet101/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/resnet101/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/resnet101/igie/export.py b/models/cv/classification/resnet101/igie/export.py new file mode 100644 index 00000000..a9cd5b05 --- /dev/null +++ b/models/cv/classification/resnet101/igie/export.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import torch +import torchvision +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = torchvision.models.resnet101() + model.load_state_dict(torch.load(args.weight)) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/resnet101/igie/inference.py b/models/cv/classification/resnet101/igie/inference.py new file mode 100644 index 00000000..3aef3ec7 --- /dev/null +++ b/models/cv/classification/resnet101/igie/inference.py @@ -0,0 +1,186 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256, interpolation=InterpolationMode.BILINEAR), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/resnet101/igie/quantize.py b/models/cv/classification/resnet101/igie/quantize.py new file mode 100644 index 00000000..1ead4321 --- /dev/null +++ b/models/cv/classification/resnet101/igie/quantize.py @@ -0,0 +1,105 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import onnx +import psutil +import argparse +import numpy as np +from inference import get_dataloader +from onnxruntime.quantization import (CalibrationDataReader, QuantFormat, + quantize_static, QuantType, + CalibrationMethod) + +class CalibrationDataLoader(CalibrationDataReader): + def __init__(self, input_name, dataloader, cnt_limit=100): + self.cnt = 0 + self.input_name = input_name + self.cnt_limit = cnt_limit + self.iter = iter(dataloader) + + # avoid oom + @staticmethod + def _exceed_memory_upper_bound(upper_bound=80): + info = psutil.virtual_memory() + total_percent = info.percent + if total_percent >= upper_bound: + return True + return False + + def get_next(self): + if self._exceed_memory_upper_bound() or self.cnt >= self.cnt_limit: + return None + self.cnt += 1 + print(f"onnx calibration data count: {self.cnt}") + input_info = next(self.iter) + + ort_input = {k: np.array(v) for k, v in zip(self.input_name, input_info)} + return ort_input + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--out_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="calibration datasets path.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + model = onnx.load(args.model_path) + input_names = [input.name for input in model.graph.input] + + dataloader = get_dataloader(args.datasets, batch_size=1, num_workers=args.num_workers) + calibration = CalibrationDataLoader(input_names, dataloader, cnt_limit=20) + + quantize_static(args.model_path, + args.out_path, + calibration_data_reader=calibration, + quant_format=QuantFormat.QOperator, + per_channel=False, + activation_type=QuantType.QInt8, + weight_type=QuantType.QInt8, + optimize_model=False, + use_external_data_format=False, + calibrate_method=CalibrationMethod.Percentile, + nodes_to_exclude=['/Softmax'], + extra_options = { + 'ActivationSymmetric': True, + 'WeightSymmetric': True + } + ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/resnet101/igie/scripts/infer_resnet101_fp16_accuracy.sh b/models/cv/classification/resnet101/igie/scripts/infer_resnet101_fp16_accuracy.sh new file mode 100644 index 00000000..3175d05b --- /dev/null +++ b/models/cv/classification/resnet101/igie/scripts/infer_resnet101_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnet101.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path resnet101_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine resnet101_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/resnet101/igie/scripts/infer_resnet101_fp16_performance.sh b/models/cv/classification/resnet101/igie/scripts/infer_resnet101_fp16_performance.sh new file mode 100644 index 00000000..6d88a9e2 --- /dev/null +++ b/models/cv/classification/resnet101/igie/scripts/infer_resnet101_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnet101.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path resnet101_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine resnet101_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file diff --git a/models/cv/classification/resnet101/igie/scripts/infer_resnet101_int8_accuracy.sh b/models/cv/classification/resnet101/igie/scripts/infer_resnet101_int8_accuracy.sh new file mode 100644 index 00000000..23fff27a --- /dev/null +++ b/models/cv/classification/resnet101/igie/scripts/infer_resnet101_int8_accuracy.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnet101.onnx" +quantized_model_path="resnet101_int8.onnx" +datasets_path=${DATASETS_DIR} + +if [ ! -e $quantized_model_path ]; then + # quantize model to int8 + python3 quantize.py \ + --model_path ${model_path} \ + --out_path ${quantized_model_path} \ + --datasets ${datasets_path} +fi + +# build engine +python3 build_engine.py \ + --model_path ${quantized_model_path} \ + --input input:${batchsize},3,224,224 \ + --precision int8 \ + --engine_path resnet101_bs_${batchsize}_int8.so + +# inference +python3 inference.py \ + --engine resnet101_bs_${batchsize}_int8.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/resnet101/igie/scripts/infer_resnet101_int8_performance.sh b/models/cv/classification/resnet101/igie/scripts/infer_resnet101_int8_performance.sh new file mode 100644 index 00000000..c8b30b9c --- /dev/null +++ b/models/cv/classification/resnet101/igie/scripts/infer_resnet101_int8_performance.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnet101.onnx" +quantized_model_path="resnet101_int8.onnx" +datasets_path=${DATASETS_DIR} + +if [ ! -e $quantized_model_path ]; then + # quantize model to int8 + python3 quantize.py \ + --model_path ${model_path} \ + --out_path ${quantized_model_path} \ + --datasets ${datasets_path} +fi + +# build engine +python3 build_engine.py \ + --model_path ${quantized_model_path} \ + --input input:${batchsize},3,224,224 \ + --precision int8 \ + --engine_path resnet101_bs_${batchsize}_int8.so + +# inference +python3 inference.py \ + --engine resnet101_bs_${batchsize}_int8.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True -- Gitee From ddf0a4eff24cabd0951219bfd3fa3317ff9526b3 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Mon, 15 Apr 2024 11:10:51 +0800 Subject: [PATCH 08/19] Update: efficientnetv2_rw_t README. --- models/cv/classification/efficientnetv2_rw_t/igie/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/cv/classification/efficientnetv2_rw_t/igie/README.md b/models/cv/classification/efficientnetv2_rw_t/igie/README.md index d9edb305..581f6ed9 100644 --- a/models/cv/classification/efficientnetv2_rw_t/igie/README.md +++ b/models/cv/classification/efficientnetv2_rw_t/igie/README.md @@ -32,9 +32,9 @@ export DATASETS_DIR=/Path/to/imagenet_val/ ```bash # Accuracy -bash scripts/infer_efficientnetv2_rw_t_b1_fp16_accuracy.sh +bash scripts/infer_efficientnetv2_rw_t_fp16_accuracy.sh # Performance -bash scripts/infer_efficientnetv2_rw_t_b1_fp16_performance.sh +bash scripts/infer_efficientnetv2_rw_t_fp16_performance.sh ``` ## Results -- Gitee From 0833150d9e09dcc480b5ffea748ee54c74f14001 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Mon, 15 Apr 2024 14:21:33 +0800 Subject: [PATCH 09/19] Add: resnet152 inference script. --- .../classification/resnet152/igie/README.md | 52 +++++ .../resnet152/igie/build_engine.py | 73 +++++++ .../classification/resnet152/igie/export.py | 61 ++++++ .../resnet152/igie/inference.py | 186 ++++++++++++++++++ .../classification/resnet152/igie/quantize.py | 105 ++++++++++ .../scripts/infer_resnet152_fp16_accuracy.sh | 35 ++++ .../infer_resnet152_fp16_performance.sh | 36 ++++ .../scripts/infer_resnet152_int8_accuracy.sh | 43 ++++ .../infer_resnet152_int8_performance.sh | 44 +++++ 9 files changed, 635 insertions(+) create mode 100644 models/cv/classification/resnet152/igie/README.md create mode 100644 models/cv/classification/resnet152/igie/build_engine.py create mode 100644 models/cv/classification/resnet152/igie/export.py create mode 100644 models/cv/classification/resnet152/igie/inference.py create mode 100644 models/cv/classification/resnet152/igie/quantize.py create mode 100644 models/cv/classification/resnet152/igie/scripts/infer_resnet152_fp16_accuracy.sh create mode 100644 models/cv/classification/resnet152/igie/scripts/infer_resnet152_fp16_performance.sh create mode 100644 models/cv/classification/resnet152/igie/scripts/infer_resnet152_int8_accuracy.sh create mode 100644 models/cv/classification/resnet152/igie/scripts/infer_resnet152_int8_performance.sh diff --git a/models/cv/classification/resnet152/igie/README.md b/models/cv/classification/resnet152/igie/README.md new file mode 100644 index 00000000..301b0774 --- /dev/null +++ b/models/cv/classification/resnet152/igie/README.md @@ -0,0 +1,52 @@ +# ResNet152 + +## Description +ResNet152 is a convolutional neural network architecture that is part of the ResNet (Residual Network) family, Comprising 152 layers, At the core of ResNet152 is the innovative residual learning framework, which addresses the challenges associated with training very deep neural networks. + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +``` + +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight resnet152-394f9c45.pth --output resnet152.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_resnet152_fp16_accuracy.sh +# Performance +bash scripts/infer_resnet152_fp16_performance.sh +``` + +### INT8 +```bash +# Accuracy +bash scripts/infer_resnet152_int8_accuracy.sh +# Performance +bash scripts/infer_resnet152_int8_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +----------|-----------|----------|----------|----------|-------- +ResNet152 | 32 | FP16 | 1768.348 | 78.285 | 94.022 +ResNet152 | 32 | INT8 | 3864.913 | 77.637 | 93.728 diff --git a/models/cv/classification/resnet152/igie/build_engine.py b/models/cv/classification/resnet152/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/resnet152/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/resnet152/igie/export.py b/models/cv/classification/resnet152/igie/export.py new file mode 100644 index 00000000..1386e31d --- /dev/null +++ b/models/cv/classification/resnet152/igie/export.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import torch +import torchvision +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = torchvision.models.resnet152() + model.load_state_dict(torch.load(args.weight)) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/resnet152/igie/inference.py b/models/cv/classification/resnet152/igie/inference.py new file mode 100644 index 00000000..3aef3ec7 --- /dev/null +++ b/models/cv/classification/resnet152/igie/inference.py @@ -0,0 +1,186 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256, interpolation=InterpolationMode.BILINEAR), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/resnet152/igie/quantize.py b/models/cv/classification/resnet152/igie/quantize.py new file mode 100644 index 00000000..1ead4321 --- /dev/null +++ b/models/cv/classification/resnet152/igie/quantize.py @@ -0,0 +1,105 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import onnx +import psutil +import argparse +import numpy as np +from inference import get_dataloader +from onnxruntime.quantization import (CalibrationDataReader, QuantFormat, + quantize_static, QuantType, + CalibrationMethod) + +class CalibrationDataLoader(CalibrationDataReader): + def __init__(self, input_name, dataloader, cnt_limit=100): + self.cnt = 0 + self.input_name = input_name + self.cnt_limit = cnt_limit + self.iter = iter(dataloader) + + # avoid oom + @staticmethod + def _exceed_memory_upper_bound(upper_bound=80): + info = psutil.virtual_memory() + total_percent = info.percent + if total_percent >= upper_bound: + return True + return False + + def get_next(self): + if self._exceed_memory_upper_bound() or self.cnt >= self.cnt_limit: + return None + self.cnt += 1 + print(f"onnx calibration data count: {self.cnt}") + input_info = next(self.iter) + + ort_input = {k: np.array(v) for k, v in zip(self.input_name, input_info)} + return ort_input + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--out_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="calibration datasets path.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + model = onnx.load(args.model_path) + input_names = [input.name for input in model.graph.input] + + dataloader = get_dataloader(args.datasets, batch_size=1, num_workers=args.num_workers) + calibration = CalibrationDataLoader(input_names, dataloader, cnt_limit=20) + + quantize_static(args.model_path, + args.out_path, + calibration_data_reader=calibration, + quant_format=QuantFormat.QOperator, + per_channel=False, + activation_type=QuantType.QInt8, + weight_type=QuantType.QInt8, + optimize_model=False, + use_external_data_format=False, + calibrate_method=CalibrationMethod.Percentile, + nodes_to_exclude=['/Softmax'], + extra_options = { + 'ActivationSymmetric': True, + 'WeightSymmetric': True + } + ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/resnet152/igie/scripts/infer_resnet152_fp16_accuracy.sh b/models/cv/classification/resnet152/igie/scripts/infer_resnet152_fp16_accuracy.sh new file mode 100644 index 00000000..625abd36 --- /dev/null +++ b/models/cv/classification/resnet152/igie/scripts/infer_resnet152_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnet152.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path resnet152_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine resnet152_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/resnet152/igie/scripts/infer_resnet152_fp16_performance.sh b/models/cv/classification/resnet152/igie/scripts/infer_resnet152_fp16_performance.sh new file mode 100644 index 00000000..541d3b20 --- /dev/null +++ b/models/cv/classification/resnet152/igie/scripts/infer_resnet152_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnet152.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path resnet152_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine resnet152_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file diff --git a/models/cv/classification/resnet152/igie/scripts/infer_resnet152_int8_accuracy.sh b/models/cv/classification/resnet152/igie/scripts/infer_resnet152_int8_accuracy.sh new file mode 100644 index 00000000..f19ffad3 --- /dev/null +++ b/models/cv/classification/resnet152/igie/scripts/infer_resnet152_int8_accuracy.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnet152.onnx" +quantized_model_path="resnet152_int8.onnx" +datasets_path=${DATASETS_DIR} + +if [ ! -e $quantized_model_path ]; then + # quantize model to int8 + python3 quantize.py \ + --model_path ${model_path} \ + --out_path ${quantized_model_path} \ + --datasets ${datasets_path} +fi + +# build engine +python3 build_engine.py \ + --model_path ${quantized_model_path} \ + --input input:${batchsize},3,224,224 \ + --precision int8 \ + --engine_path resnet152_bs_${batchsize}_int8.so + +# inference +python3 inference.py \ + --engine resnet152_bs_${batchsize}_int8.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/resnet152/igie/scripts/infer_resnet152_int8_performance.sh b/models/cv/classification/resnet152/igie/scripts/infer_resnet152_int8_performance.sh new file mode 100644 index 00000000..9a8fe016 --- /dev/null +++ b/models/cv/classification/resnet152/igie/scripts/infer_resnet152_int8_performance.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnet152.onnx" +quantized_model_path="resnet152_int8.onnx" +datasets_path=${DATASETS_DIR} + +if [ ! -e $quantized_model_path ]; then + # quantize model to int8 + python3 quantize.py \ + --model_path ${model_path} \ + --out_path ${quantized_model_path} \ + --datasets ${datasets_path} +fi + +# build engine +python3 build_engine.py \ + --model_path ${quantized_model_path} \ + --input input:${batchsize},3,224,224 \ + --precision int8 \ + --engine_path resnet152_bs_${batchsize}_int8.so + +# inference +python3 inference.py \ + --engine resnet152_bs_${batchsize}_int8.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True -- Gitee From a7966ebe0d05f7186d1a9a587604b13c4e898b2e Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Mon, 15 Apr 2024 16:09:29 +0800 Subject: [PATCH 10/19] Add: shufflenetv2_x0_5 inference script. --- .../shufflenetv2_x0_5/igie/README.md | 43 ++++ .../shufflenetv2_x0_5/igie/build_engine.py | 73 +++++++ .../shufflenetv2_x0_5/igie/export.py | 61 ++++++ .../shufflenetv2_x0_5/igie/inference.py | 186 ++++++++++++++++++ .../infer_shufflenetv2_x0_5_fp16_accuracy.sh | 35 ++++ ...nfer_shufflenetv2_x0_5_fp16_performance.sh | 36 ++++ 6 files changed, 434 insertions(+) create mode 100644 models/cv/classification/shufflenetv2_x0_5/igie/README.md create mode 100644 models/cv/classification/shufflenetv2_x0_5/igie/build_engine.py create mode 100644 models/cv/classification/shufflenetv2_x0_5/igie/export.py create mode 100644 models/cv/classification/shufflenetv2_x0_5/igie/inference.py create mode 100644 models/cv/classification/shufflenetv2_x0_5/igie/scripts/infer_shufflenetv2_x0_5_fp16_accuracy.sh create mode 100644 models/cv/classification/shufflenetv2_x0_5/igie/scripts/infer_shufflenetv2_x0_5_fp16_performance.sh diff --git a/models/cv/classification/shufflenetv2_x0_5/igie/README.md b/models/cv/classification/shufflenetv2_x0_5/igie/README.md new file mode 100644 index 00000000..4503f8eb --- /dev/null +++ b/models/cv/classification/shufflenetv2_x0_5/igie/README.md @@ -0,0 +1,43 @@ +# ShuffleNetV2_x0_5 + +## Description +ShuffleNetV2_x0_5 is a lightweight convolutional neural network architecture designed for efficient image classification and feature extraction, it also incorporates other design optimizations such as depthwise separable convolutions, group convolutions, and efficient building blocks to further reduce computational complexity and improve efficiency. + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +``` + +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight shufflenetv2_x0.5-f707e7126e.pth --output shufflenetv2_x0_5.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_shufflenetv2_x0_5_fp16_accuracy.sh +# Performance +bash scripts/infer_shufflenetv2_x0_5_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +------------------|-----------|----------|----------|----------|-------- +ShuffleNetV2_x0_5 | 32 | FP16 | 11677.55 | 60.501 | 81.702 diff --git a/models/cv/classification/shufflenetv2_x0_5/igie/build_engine.py b/models/cv/classification/shufflenetv2_x0_5/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/shufflenetv2_x0_5/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/shufflenetv2_x0_5/igie/export.py b/models/cv/classification/shufflenetv2_x0_5/igie/export.py new file mode 100644 index 00000000..da1a97d6 --- /dev/null +++ b/models/cv/classification/shufflenetv2_x0_5/igie/export.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import torch +import torchvision +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = torchvision.models.shufflenet_v2_x0_5() + model.load_state_dict(torch.load(args.weight)) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/shufflenetv2_x0_5/igie/inference.py b/models/cv/classification/shufflenetv2_x0_5/igie/inference.py new file mode 100644 index 00000000..3aef3ec7 --- /dev/null +++ b/models/cv/classification/shufflenetv2_x0_5/igie/inference.py @@ -0,0 +1,186 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256, interpolation=InterpolationMode.BILINEAR), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/shufflenetv2_x0_5/igie/scripts/infer_shufflenetv2_x0_5_fp16_accuracy.sh b/models/cv/classification/shufflenetv2_x0_5/igie/scripts/infer_shufflenetv2_x0_5_fp16_accuracy.sh new file mode 100644 index 00000000..4be39d2f --- /dev/null +++ b/models/cv/classification/shufflenetv2_x0_5/igie/scripts/infer_shufflenetv2_x0_5_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="shufflenetv2_x0_5.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path shufflenetv2_x0_5_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine shufflenetv2_x0_5_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/shufflenetv2_x0_5/igie/scripts/infer_shufflenetv2_x0_5_fp16_performance.sh b/models/cv/classification/shufflenetv2_x0_5/igie/scripts/infer_shufflenetv2_x0_5_fp16_performance.sh new file mode 100644 index 00000000..26f92011 --- /dev/null +++ b/models/cv/classification/shufflenetv2_x0_5/igie/scripts/infer_shufflenetv2_x0_5_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="shufflenetv2_x0_5.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path shufflenetv2_x0_5_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine shufflenetv2_x0_5_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From 5de9ccf86fd8106458957a5358a861a1aaee29b6 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Mon, 15 Apr 2024 17:04:43 +0800 Subject: [PATCH 11/19] Add: wide_resnet50 inference script. --- .../wide_resnet50/igie/README.md | 52 +++++ .../wide_resnet50/igie/build_engine.py | 73 +++++++ .../wide_resnet50/igie/export.py | 61 ++++++ .../wide_resnet50/igie/inference.py | 186 ++++++++++++++++++ .../wide_resnet50/igie/quantize.py | 105 ++++++++++ .../infer_wide_resnet50_fp16_accuracy.sh | 35 ++++ .../infer_wide_resnet50_fp16_performance.sh | 36 ++++ .../infer_wide_resnet50_int8_accuracy.sh | 43 ++++ .../infer_wide_resnet50_int8_performance.sh | 44 +++++ 9 files changed, 635 insertions(+) create mode 100644 models/cv/classification/wide_resnet50/igie/README.md create mode 100644 models/cv/classification/wide_resnet50/igie/build_engine.py create mode 100644 models/cv/classification/wide_resnet50/igie/export.py create mode 100644 models/cv/classification/wide_resnet50/igie/inference.py create mode 100644 models/cv/classification/wide_resnet50/igie/quantize.py create mode 100644 models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_fp16_accuracy.sh create mode 100644 models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_fp16_performance.sh create mode 100644 models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_int8_accuracy.sh create mode 100644 models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_int8_performance.sh diff --git a/models/cv/classification/wide_resnet50/igie/README.md b/models/cv/classification/wide_resnet50/igie/README.md new file mode 100644 index 00000000..448cad00 --- /dev/null +++ b/models/cv/classification/wide_resnet50/igie/README.md @@ -0,0 +1,52 @@ +# WideResNet50 + +## Description +The distinguishing feature of Wide ResNet50 lies in its widened architecture compared to traditional ResNet models. By increasing the width of the residual blocks, Wide ResNet50 enhances the capacity of the network to capture richer and more diverse feature representations, leading to improved performance on various visual recognition tasks. + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +``` + +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +python3 export.py --weight wide_resnet50_2-95faca4d.pth --output wide_resnet50.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` + +### FP16 + +```bash +# Accuracy +bash scripts/infer_wide_resnet50_fp16_accuracy.sh +# Performance +bash scripts/infer_wide_resnet50_fp16_performance.sh +``` + +### INT8 +```bash +# Accuracy +bash scripts/infer_wide_resnet50_int8_accuracy.sh +# Performance +bash scripts/infer_wide_resnet50_int8_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +-------------|-----------|----------|----------|----------|-------- +WideResNet50 | 32 | FP16 | 2312.383 | 78.459 | 94.052 +WideResNet50 | 32 | INT8 | 5195.654 | 77.957 | 93.798 diff --git a/models/cv/classification/wide_resnet50/igie/build_engine.py b/models/cv/classification/wide_resnet50/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/wide_resnet50/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/wide_resnet50/igie/export.py b/models/cv/classification/wide_resnet50/igie/export.py new file mode 100644 index 00000000..1d3c64c8 --- /dev/null +++ b/models/cv/classification/wide_resnet50/igie/export.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import torch +import torchvision +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = torchvision.models.wide_resnet50_2() + model.load_state_dict(torch.load(args.weight)) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/wide_resnet50/igie/inference.py b/models/cv/classification/wide_resnet50/igie/inference.py new file mode 100644 index 00000000..3aef3ec7 --- /dev/null +++ b/models/cv/classification/wide_resnet50/igie/inference.py @@ -0,0 +1,186 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256, interpolation=InterpolationMode.BILINEAR), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/wide_resnet50/igie/quantize.py b/models/cv/classification/wide_resnet50/igie/quantize.py new file mode 100644 index 00000000..1ead4321 --- /dev/null +++ b/models/cv/classification/wide_resnet50/igie/quantize.py @@ -0,0 +1,105 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import onnx +import psutil +import argparse +import numpy as np +from inference import get_dataloader +from onnxruntime.quantization import (CalibrationDataReader, QuantFormat, + quantize_static, QuantType, + CalibrationMethod) + +class CalibrationDataLoader(CalibrationDataReader): + def __init__(self, input_name, dataloader, cnt_limit=100): + self.cnt = 0 + self.input_name = input_name + self.cnt_limit = cnt_limit + self.iter = iter(dataloader) + + # avoid oom + @staticmethod + def _exceed_memory_upper_bound(upper_bound=80): + info = psutil.virtual_memory() + total_percent = info.percent + if total_percent >= upper_bound: + return True + return False + + def get_next(self): + if self._exceed_memory_upper_bound() or self.cnt >= self.cnt_limit: + return None + self.cnt += 1 + print(f"onnx calibration data count: {self.cnt}") + input_info = next(self.iter) + + ort_input = {k: np.array(v) for k, v in zip(self.input_name, input_info)} + return ort_input + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--out_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="calibration datasets path.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + model = onnx.load(args.model_path) + input_names = [input.name for input in model.graph.input] + + dataloader = get_dataloader(args.datasets, batch_size=1, num_workers=args.num_workers) + calibration = CalibrationDataLoader(input_names, dataloader, cnt_limit=20) + + quantize_static(args.model_path, + args.out_path, + calibration_data_reader=calibration, + quant_format=QuantFormat.QOperator, + per_channel=False, + activation_type=QuantType.QInt8, + weight_type=QuantType.QInt8, + optimize_model=False, + use_external_data_format=False, + calibrate_method=CalibrationMethod.Percentile, + nodes_to_exclude=['/Softmax'], + extra_options = { + 'ActivationSymmetric': True, + 'WeightSymmetric': True + } + ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_fp16_accuracy.sh b/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_fp16_accuracy.sh new file mode 100644 index 00000000..54098ac4 --- /dev/null +++ b/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="wide_resnet50.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path wide_resnet50_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine wide_resnet50_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_fp16_performance.sh b/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_fp16_performance.sh new file mode 100644 index 00000000..7fa6ba8c --- /dev/null +++ b/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="wide_resnet50.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path wide_resnet50_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine wide_resnet50_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file diff --git a/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_int8_accuracy.sh b/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_int8_accuracy.sh new file mode 100644 index 00000000..047a5ecb --- /dev/null +++ b/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_int8_accuracy.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="wide_resnet50.onnx" +quantized_model_path="wide_resnet50_int8.onnx" +datasets_path=${DATASETS_DIR} + +if [ ! -e $quantized_model_path ]; then + # quantize model to int8 + python3 quantize.py \ + --model_path ${model_path} \ + --out_path ${quantized_model_path} \ + --datasets ${datasets_path} +fi + +# build engine +python3 build_engine.py \ + --model_path ${quantized_model_path} \ + --input input:${batchsize},3,224,224 \ + --precision int8 \ + --engine_path wide_resnet50_bs_${batchsize}_int8.so + +# inference +python3 inference.py \ + --engine wide_resnet50_bs_${batchsize}_int8.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_int8_performance.sh b/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_int8_performance.sh new file mode 100644 index 00000000..e36dfc1a --- /dev/null +++ b/models/cv/classification/wide_resnet50/igie/scripts/infer_wide_resnet50_int8_performance.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="wide_resnet50.onnx" +quantized_model_path="wide_resnet50_int8.onnx" +datasets_path=${DATASETS_DIR} + +if [ ! -e $quantized_model_path ]; then + # quantize model to int8 + python3 quantize.py \ + --model_path ${model_path} \ + --out_path ${quantized_model_path} \ + --datasets ${datasets_path} +fi + +# build engine +python3 build_engine.py \ + --model_path ${quantized_model_path} \ + --input input:${batchsize},3,224,224 \ + --precision int8 \ + --engine_path wide_resnet50_bs_${batchsize}_int8.so + +# inference +python3 inference.py \ + --engine wide_resnet50_bs_${batchsize}_int8.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True -- Gitee From 7b390bc655eadc3dcc250c4d0c0e3a765fe6ff08 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Tue, 16 Apr 2024 11:26:27 +0800 Subject: [PATCH 12/19] Add: res2net50 inference script. --- .../classification/res2net50/igie/README.md | 58 ++++++ .../res2net50/igie/build_engine.py | 73 +++++++ .../classification/res2net50/igie/export.py | 78 ++++++++ .../res2net50/igie/inference.py | 185 ++++++++++++++++++ .../scripts/infer_res2net50_fp16_accuracy.sh | 35 ++++ .../infer_res2net50_fp16_performance.sh | 36 ++++ 6 files changed, 465 insertions(+) create mode 100644 models/cv/classification/res2net50/igie/README.md create mode 100644 models/cv/classification/res2net50/igie/build_engine.py create mode 100644 models/cv/classification/res2net50/igie/export.py create mode 100644 models/cv/classification/res2net50/igie/inference.py create mode 100644 models/cv/classification/res2net50/igie/scripts/infer_res2net50_fp16_accuracy.sh create mode 100644 models/cv/classification/res2net50/igie/scripts/infer_res2net50_fp16_performance.sh diff --git a/models/cv/classification/res2net50/igie/README.md b/models/cv/classification/res2net50/igie/README.md new file mode 100644 index 00000000..6921a608 --- /dev/null +++ b/models/cv/classification/res2net50/igie/README.md @@ -0,0 +1,58 @@ +# Res2Net50 + +## Description +Res2Net50 is a convolutional neural network architecture that introduces the concept of "Residual-Residual Networks" (Res2Nets) to enhance feature representation and model expressiveness, particularly in image recognition tasks.The key innovation of Res2Net50 lies in its hierarchical feature aggregation mechanism, which enables the network to capture multi-scale features more effectively. Unlike traditional ResNet architectures, Res2Net50 incorporates multiple parallel pathways within each residual block, allowing the network to dynamically adjust the receptive field size and aggregate features across different scales. + +## Setup + +### Install +``` +yum install mesa-libGL +pip3 install onnx +pip3 install tqdm +pip3 install onnxsim +pip3 install mmcv==1.5.3 +pip3 install mmcls +``` + +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +# git clone mmpretrain +git clone -b v0.24.0 https://github.com/open-mmlab/mmpretrain.git + +# export onnx model +python3 export.py --cfg mmpretrain/configs/res2net/res2net50-w14-s8_8xb32_in1k.py --weight res2net50-w14-s8_3rdparty_8xb32_in1k_20210927-bc967bf1.pth --output res2net50.onnx + +# Use onnxsim optimize onnx model +onnxsim res2net50.onnx res2net50_opt.onnx + +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` +### FP16 + +```bash +# Accuracy +bash scripts/infer_res2net50_fp16_accuracy.sh +# Performance +bash scripts/infer_res2net50_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +----------|-----------|----------|----------|----------|-------- +Res2Net50 | 32 | FP16 | 1641.961 | 78.139 | 93.826 + +## Reference + +Res2Net50: https://github.com/open-mmlab/mmpretrain diff --git a/models/cv/classification/res2net50/igie/build_engine.py b/models/cv/classification/res2net50/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/res2net50/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/res2net50/igie/export.py b/models/cv/classification/res2net50/igie/export.py new file mode 100644 index 00000000..7dc8d9fd --- /dev/null +++ b/models/cv/classification/res2net50/igie/export.py @@ -0,0 +1,78 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import argparse + +import torch +from mmcls.apis import init_model + +class Model(torch.nn.Module): + def __init__(self, config_file, checkpoint_file): + super().__init__() + self.model = init_model(config_file, checkpoint_file, device="cpu") + + def forward(self, x): + feat = self.model.backbone(x) + feat = self.model.neck(feat) + out_head = self.model.head.fc(feat[0]) + return out_head + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--cfg", + type=str, + required=True, + help="model config file.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + config_file = args.cfg + checkpoint_file = args.weight + model = Model(config_file, checkpoint_file).eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == '__main__': + main() + diff --git a/models/cv/classification/res2net50/igie/inference.py b/models/cv/classification/res2net50/igie/inference.py new file mode 100644 index 00000000..1b0c602a --- /dev/null +++ b/models/cv/classification/res2net50/igie/inference.py @@ -0,0 +1,185 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/res2net50/igie/scripts/infer_res2net50_fp16_accuracy.sh b/models/cv/classification/res2net50/igie/scripts/infer_res2net50_fp16_accuracy.sh new file mode 100644 index 00000000..155ec563 --- /dev/null +++ b/models/cv/classification/res2net50/igie/scripts/infer_res2net50_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="res2net50_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path res2net50_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine res2net50_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/res2net50/igie/scripts/infer_res2net50_fp16_performance.sh b/models/cv/classification/res2net50/igie/scripts/infer_res2net50_fp16_performance.sh new file mode 100644 index 00000000..c24472ba --- /dev/null +++ b/models/cv/classification/res2net50/igie/scripts/infer_res2net50_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="res2net50_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path res2net50_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine res2net50_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From bd83375ed056605a329aa4e8229dc487cdad9f45 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Tue, 16 Apr 2024 14:32:08 +0800 Subject: [PATCH 13/19] Add: resnest50 inference script. --- .../classification/resnest50/igie/README.md | 54 +++++ .../resnest50/igie/build_engine.py | 73 +++++++ .../classification/resnest50/igie/export.py | 61 ++++++ .../resnest50/igie/inference.py | 185 ++++++++++++++++++ .../scripts/infer_resnest50_fp16_accuracy.sh | 35 ++++ .../infer_resnest50_fp16_performance.sh | 36 ++++ 6 files changed, 444 insertions(+) create mode 100644 models/cv/classification/resnest50/igie/README.md create mode 100644 models/cv/classification/resnest50/igie/build_engine.py create mode 100644 models/cv/classification/resnest50/igie/export.py create mode 100644 models/cv/classification/resnest50/igie/inference.py create mode 100644 models/cv/classification/resnest50/igie/scripts/infer_resnest50_fp16_accuracy.sh create mode 100644 models/cv/classification/resnest50/igie/scripts/infer_resnest50_fp16_performance.sh diff --git a/models/cv/classification/resnest50/igie/README.md b/models/cv/classification/resnest50/igie/README.md new file mode 100644 index 00000000..5316ab57 --- /dev/null +++ b/models/cv/classification/resnest50/igie/README.md @@ -0,0 +1,54 @@ +# ResNeSt50 + +## Description +ResNeSt50 is a deep convolutional neural network model based on the ResNeSt architecture, specifically designed to enhance performance in visual recognition tasks such as image classification, object detection, instance segmentation, and semantic segmentation. ResNeSt stands for Split-Attention Networks, a modular network architecture that leverages channel-wise attention mechanisms across different network branches to capture cross-feature interactions and learn diverse representations. + +## Setup + +### Install +``` +yum install mesa-libGL +pip3 install onnx +pip3 install tqdm +pip3 install onnxsim +pip3 install git+https://github.com/zhanghang1989/ResNeSt +``` + +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +# export onnx model +python3 export.py --weight resnest50-528c19ca.pth --output resnest50.onnx + +# Use onnxsim optimize onnx model +onnxsim resnest50.onnx resnest50_opt.onnx + +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/imagenet_val/ +``` +### FP16 + +```bash +# Accuracy +bash scripts/infer_resnest50_fp16_accuracy.sh +# Performance +bash scripts/infer_resnest50_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Top-1(%) |Top-5(%) +----------|-----------|----------|----------|----------|-------- +ResNeSt50 | 32 | FP16 | 344.453 | 80.93 | 95.347 + +## Reference + +ResNeSt50: https://github.com/zhanghang1989/ResNeSt diff --git a/models/cv/classification/resnest50/igie/build_engine.py b/models/cv/classification/resnest50/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/classification/resnest50/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/classification/resnest50/igie/export.py b/models/cv/classification/resnest50/igie/export.py new file mode 100644 index 00000000..9514777b --- /dev/null +++ b/models/cv/classification/resnest50/igie/export.py @@ -0,0 +1,61 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import argparse +import torch +from resnest.torch import resnest50 + + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + model = resnest50(pretrained=False) + model.load_state_dict(torch.load(args.weight)) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/models/cv/classification/resnest50/igie/inference.py b/models/cv/classification/resnest50/igie/inference.py new file mode 100644 index 00000000..1b0c602a --- /dev/null +++ b/models/cv/classification/resnest50/igie/inference.py @@ -0,0 +1,185 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from torchvision import transforms + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def get_dataloader(data_path, batch_size, num_workers): + dataset = torchvision.datasets.ImageFolder( + data_path, + transforms.Compose( + [ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.PILToTensor(), + transforms.ConvertImageDtype(torch.float), + transforms.Normalize( + mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225) + ) + ] + ) + ) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=num_workers) + + return dataloader + +def get_topk_accuracy(pred, label): + if isinstance(pred, np.ndarray): + pred = torch.from_numpy(pred) + + if isinstance(label, np.ndarray): + label = torch.from_numpy(label) + + top1_acc = 0 + top5_acc = 0 + for idx in range(len(label)): + label_value = label[idx] + if label_value == torch.topk(pred[idx].float(), 1).indices.data: + top1_acc += 1 + top5_acc += 1 + + elif label_value in torch.topk(pred[idx].float(), 5).indices.data: + top5_acc += 1 + + return top1_acc, top5_acc + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataloader = get_dataloader(args.datasets, batch_size, args.num_workers) + + top1_acc = 0 + top5_acc = 0 + total_num = 0 + + for image, label in tqdm(dataloader): + + # pad the last batch + pad_batch = len(image) != batch_size + + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + # run inference + module.run() + + pred = module.get_output(0).asnumpy() + + if pad_batch: + pred = pred[:origin_size] + + # get batch accuracy + batch_top1_acc, batch_top5_acc = get_topk_accuracy(pred, label) + + top1_acc += batch_top1_acc + top5_acc += batch_top5_acc + total_num += batch_size + + result_stat = {} + result_stat["acc@1"] = round(top1_acc / total_num * 100.0, 3) + result_stat["acc@5"] = round(top5_acc / total_num * 100.0, 3) + + print(f"\n* Top1 acc: {result_stat['acc@1']} %, Top5 acc: {result_stat['acc@5']} %") + +if __name__ == "__main__": + main() diff --git a/models/cv/classification/resnest50/igie/scripts/infer_resnest50_fp16_accuracy.sh b/models/cv/classification/resnest50/igie/scripts/infer_resnest50_fp16_accuracy.sh new file mode 100644 index 00000000..c5a33efe --- /dev/null +++ b/models/cv/classification/resnest50/igie/scripts/infer_resnest50_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnest50_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path resnest50_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine resnest50_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/classification/resnest50/igie/scripts/infer_resnest50_fp16_performance.sh b/models/cv/classification/resnest50/igie/scripts/infer_resnest50_fp16_performance.sh new file mode 100644 index 00000000..82c32e89 --- /dev/null +++ b/models/cv/classification/resnest50/igie/scripts/infer_resnest50_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="resnest50_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path resnest50_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine resnest50_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From 6e8a57173743920ae568e7a2e7419e936737085a Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Wed, 17 Apr 2024 14:13:32 +0800 Subject: [PATCH 14/19] Add: repnet inference script. --- models/cv/trace/repnet/igie/README.md | 58 + models/cv/trace/repnet/igie/build_engine.py | 73 + models/cv/trace/repnet/igie/export.py | 495 + models/cv/trace/repnet/igie/inference.py | 219 + .../cv/trace/repnet/igie/pair_set_vehicle.txt | 10000 ++++++++++++++++ .../scripts/infer_repnet_fp16_accuracy.sh | 35 + .../scripts/infer_repnet_fp16_performance.sh | 36 + 7 files changed, 10916 insertions(+) create mode 100644 models/cv/trace/repnet/igie/README.md create mode 100644 models/cv/trace/repnet/igie/build_engine.py create mode 100644 models/cv/trace/repnet/igie/export.py create mode 100644 models/cv/trace/repnet/igie/inference.py create mode 100644 models/cv/trace/repnet/igie/pair_set_vehicle.txt create mode 100644 models/cv/trace/repnet/igie/scripts/infer_repnet_fp16_accuracy.sh create mode 100644 models/cv/trace/repnet/igie/scripts/infer_repnet_fp16_performance.sh diff --git a/models/cv/trace/repnet/igie/README.md b/models/cv/trace/repnet/igie/README.md new file mode 100644 index 00000000..7c102509 --- /dev/null +++ b/models/cv/trace/repnet/igie/README.md @@ -0,0 +1,58 @@ +# RepNet-VehicleReID + +## Description +The paper "Deep Relative Distance Learning: Tell the Difference Between Similar Vehicles" introduces a model named Deep Relative Distance Learning (DRDL), specifically designed for the problem of vehicle re-identification. DRDL employs a dual-branch deep convolutional network architecture, combined with a coupled clusters loss function and a mixed difference network structure, effectively mapping vehicle images into Euclidean space for similarity measurement. + +## Setup + +### Install +``` +pip3 install onnx +pip3 install tqdm +pip3 install onnxsim +``` + +### Download + +Pretrained model: + +Dataset: to download the VehicleID dataset. + +### Model Conversion +```bash +python3 export.py --weight epoch_14.pth --output repnet.onnx + +# Use onnxsim optimize onnx model +onnxsim repnet.onnx repnet_opt.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/VehicleID/ +``` +### FP16 + +```bash +# Accuracy +bash scripts/infer_deepsort_fp16_accuracy.sh +# Performance +bash scripts/infer_deepsort_fp16_performance.sh +``` + +### INT8 +```bash +# Accuracy +bash scripts/infer_deepsort_int8_accuracy.sh +# Performance +bash scripts/infer_deepsort_int8_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |Acc(%) | +--------|-----------|----------|----------|----------| +RepNet | 32 | FP16 |1373.579 | 99.88 | + +## Reference + +RepNet-MDNet-VehicleReID: https://github.com/CaptainEven/RepNet-MDNet-VehicleReID \ No newline at end of file diff --git a/models/cv/trace/repnet/igie/build_engine.py b/models/cv/trace/repnet/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/trace/repnet/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/trace/repnet/igie/export.py b/models/cv/trace/repnet/igie/export.py new file mode 100644 index 00000000..7c5e1329 --- /dev/null +++ b/models/cv/trace/repnet/igie/export.py @@ -0,0 +1,495 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import argparse +import copy +import math +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.nn import Parameter + +device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') + +class ArcFC(nn.Module): + """ + Implement of large margin arc distance: : + Args: + in_features: size of each input sample + out_features: size of each output_layer sample + s: norm of input feature + m: margin + + cos(theta + m) + """ + + def __init__(self, + in_features, + out_features, + s=30.0, + m=0.50, + easy_margin=False): + """ + ArcMargin + :param in_features: + :param out_features: + :param s: + :param m: + :param easy_margin: + """ + super(ArcFC, self).__init__() + self.in_features = in_features + self.out_features = out_features + + self.s = s + self.m = m + + self.weight = Parameter(torch.FloatTensor(out_features, in_features)) + nn.init.xavier_uniform_(self.weight) + + self.easy_margin = easy_margin + self.cos_m = math.cos(m) + self.sin_m = math.sin(m) + self.th = math.cos(math.pi - m) + self.mm = math.sin(math.pi - m) * m + + def forward(self, input, label): + cosine = F.linear(F.normalize(input, p=2), F.normalize(self.weight, p=2)) + + sine = torch.sqrt(1.0 - torch.pow(cosine, 2)) + + + phi = cosine * self.cos_m - sine * self.sin_m + + if self.easy_margin: + phi = torch.where(cosine > 0, phi, cosine) + else: + phi = torch.where(cosine > self.th, phi, cosine - self.mm) + + one_hot = torch.zeros(cosine.size(), device=device) + one_hot.scatter_(1, label.view(-1, 1).long(), 1) + + output = (one_hot * phi) + ((1.0 - one_hot) * cosine) + output *= self.s + + return output + +class RepNet(torch.nn.Module): + def __init__(self, + out_ids, + out_attribs): + """ + Network definition + :param out_ids: + :param out_attribs: + """ + super(RepNet, self).__init__() + + self.out_ids, self.out_attribs = out_ids, out_attribs + + self.conv1_1 = torch.nn.Conv2d(in_channels=3, + out_channels=64, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv1_2 = torch.nn.ReLU(inplace=True) + self.conv1_3 = torch.nn.Conv2d(in_channels=64, + out_channels=64, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv1_4 = torch.nn.ReLU(inplace=True) + self.conv1_5 = torch.nn.MaxPool2d(kernel_size=2, + stride=2, + padding=0, + dilation=1, + ceil_mode=False) + + self.conv1 = torch.nn.Sequential( + self.conv1_1, + self.conv1_2, + self.conv1_3, + self.conv1_4, + self.conv1_5 + ) + + self.conv2_1 = torch.nn.Conv2d(in_channels=64, + out_channels=128, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv2_2 = torch.nn.ReLU(inplace=True) + self.conv2_3 = torch.nn.Conv2d(in_channels=128, + out_channels=128, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv2_4 = torch.nn.ReLU(inplace=True) + self.conv2_5 = torch.nn.MaxPool2d(kernel_size=2, + stride=2, + padding=0, + dilation=1, + ceil_mode=False) + + self.conv2 = torch.nn.Sequential( + self.conv2_1, + self.conv2_2, + self.conv2_3, + self.conv2_4, + self.conv2_5 + ) + + self.conv3_1 = torch.nn.Conv2d(in_channels=128, + out_channels=256, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv3_2 = torch.nn.ReLU(inplace=True) + self.conv3_3 = torch.nn.Conv2d(in_channels=256, + out_channels=256, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv3_4 = torch.nn.ReLU(inplace=True) + self.conv3_5 = torch.nn.Conv2d(in_channels=256, + out_channels=256, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv3_6 = torch.nn.ReLU(inplace=True) + self.conv3_7 = torch.nn.MaxPool2d(kernel_size=2, + stride=2, + padding=0, + dilation=1, + ceil_mode=False) + + self.conv3 = torch.nn.Sequential( + self.conv3_1, + self.conv3_2, + self.conv3_3, + self.conv3_4, + self.conv3_5, + self.conv3_6, + self.conv3_7 + ) + + self.conv4_1_1 = torch.nn.Conv2d(in_channels=256, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv4_1_2 = torch.nn.ReLU(inplace=True) + self.conv4_1_3 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv4_1_4 = torch.nn.ReLU(inplace=True) + self.conv4_1_5 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv4_1_6 = torch.nn.ReLU(inplace=True) + self.conv4_1_7 = torch.nn.MaxPool2d(kernel_size=2, + stride=2, + padding=0, + dilation=1, + ceil_mode=False) + + self.conv4_1 = torch.nn.Sequential( + self.conv4_1_1, + self.conv4_1_2, + self.conv4_1_3, + self.conv4_1_4, + self.conv4_1_5, + self.conv4_1_6, + self.conv4_1_7 + ) + + self.conv4_2_1 = torch.nn.Conv2d(in_channels=256, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv4_2_2 = torch.nn.ReLU(inplace=True) + self.conv4_2_3 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv4_2_4 = torch.nn.ReLU(inplace=True) + self.conv4_2_5 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv4_2_6 = torch.nn.ReLU(inplace=True) + self.conv4_2_7 = torch.nn.MaxPool2d(kernel_size=2, + stride=2, + padding=0, + dilation=1, + ceil_mode=False) + + self.conv4_2 = torch.nn.Sequential( + self.conv4_2_1, + self.conv4_2_2, + self.conv4_2_3, + self.conv4_2_4, + self.conv4_2_5, + self.conv4_2_6, + self.conv4_2_7 + ) + + self.conv5_1_1 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv5_1_2 = torch.nn.ReLU(inplace=True) + self.conv5_1_3 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv5_1_4 = torch.nn.ReLU(inplace=True) + self.conv5_1_5 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv5_1_6 = torch.nn.ReLU(inplace=True) + self.conv5_1_7 = torch.nn.MaxPool2d(kernel_size=2, + stride=2, + padding=0, + dilation=1, + ceil_mode=False) + + self.conv5_1 = torch.nn.Sequential( + self.conv5_1_1, + self.conv5_1_2, + self.conv5_1_3, + self.conv5_1_4, + self.conv5_1_5, + self.conv5_1_6, + self.conv5_1_7 + ) + + self.conv5_2_1 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv5_2_2 = torch.nn.ReLU(inplace=True) + self.conv5_2_3 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv5_2_4 = torch.nn.ReLU(inplace=True) + self.conv5_2_5 = torch.nn.Conv2d(in_channels=512, + out_channels=512, + kernel_size=(3, 3), + stride=(1, 1), + padding=(1, 1)) + self.conv5_2_6 = torch.nn.ReLU(inplace=True) + self.conv5_2_7 = torch.nn.MaxPool2d(kernel_size=2, + stride=2, + padding=0, + dilation=1, + ceil_mode=False) + + self.conv5_2 = torch.nn.Sequential( + self.conv5_2_1, + self.conv5_2_2, + self.conv5_2_3, + self.conv5_2_4, + self.conv5_2_5, + self.conv5_2_6, + self.conv5_2_7 + ) + + self.FC6_1_1 = torch.nn.Linear(in_features=25088, + out_features=4096, + bias=True) + self.FC6_1_2 = torch.nn.ReLU(inplace=True) + self.FC6_1_3 = torch.nn.Dropout(p=0.5) + self.FC6_1_4 = torch.nn.Linear(in_features=4096, + out_features=4096, + bias=True) + self.FC6_1_5 = torch.nn.ReLU(inplace=True) + self.FC6_1_6 = torch.nn.Dropout(p=0.5) + + self.FC6_1 = torch.nn.Sequential( + self.FC6_1_1, + self.FC6_1_2, + self.FC6_1_3, + self.FC6_1_4, + self.FC6_1_5, + self.FC6_1_6 + ) + + self.FC6_2_1 = copy.deepcopy(self.FC6_1_1) + self.FC6_2_2 = copy.deepcopy(self.FC6_1_2) + self.FC6_2_3 = copy.deepcopy(self.FC6_1_3) + self.FC6_2_4 = copy.deepcopy(self.FC6_1_4) + self.FC6_2_5 = copy.deepcopy(self.FC6_1_5) + self.FC6_2_6 = copy.deepcopy(self.FC6_1_6) + + self.FC6_2 = torch.nn.Sequential( + self.FC6_2_1, + self.FC6_2_2, + self.FC6_2_3, + self.FC6_2_4, + self.FC6_2_5, + self.FC6_2_6 + ) + + self.FC7_1 = torch.nn.Linear(in_features=4096, + out_features=1000, + bias=True) + + self.FC7_2 = torch.nn.Linear(in_features=4096, + out_features=1000, + bias=True) + + self.FC_8 = torch.nn.Linear(in_features=2000, + out_features=1024) + + self.attrib_classifier = torch.nn.Linear(in_features=1000, + out_features=out_attribs) + + self.arc_fc_br2 = ArcFC(in_features=1000, + out_features=out_ids, + s=30.0, + m=0.5, + easy_margin=False) + self.arc_fc_br3 = ArcFC(in_features=1024, + out_features=out_ids, + s=30.0, + m=0.5, + easy_margin=False) + + self.shared_layers = torch.nn.Sequential( + self.conv1, + self.conv2, + self.conv3 + ) + + self.branch_1_feats = torch.nn.Sequential( + self.shared_layers, + self.conv4_1, + self.conv5_1, + ) + + self.branch_1_fc = torch.nn.Sequential( + self.FC6_1, + self.FC7_1 + ) + + self.branch_1 = torch.nn.Sequential( + self.branch_1_feats, + self.branch_1_fc + ) + + self.branch_2_feats = torch.nn.Sequential( + self.shared_layers, + self.conv4_2, + self.conv5_2 + ) + + self.branch_2_fc = torch.nn.Sequential( + self.FC6_2, + self.FC7_2 + ) + + self.branch_2 = torch.nn.Sequential( + self.branch_2_feats, + self.branch_2_fc + ) + + def forward(self, + X, + branch, + label=None): + """ + :param X: + :param branch: + :param label: + :return: + """ + N = X.size(0) + + branch_1 = self.branch_1_feats(X) + branch_2 = self.branch_2_feats(X) + + branch_1 = branch_1.view(N, -1) + branch_2 = branch_2.view(N, -1) + branch_1 = self.branch_1_fc(branch_1) + branch_2 = self.branch_2_fc(branch_2) + + assert branch_1.size() == (N, 1000) and branch_2.size() == (N, 1000) + + fusion_feats = torch.cat((branch_1, branch_2), dim=1) + + assert fusion_feats.size() == (N, 2000) + + X = self.FC_8(fusion_feats) + + assert X.size() == (N, 1024) + + return X + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +if __name__ == "__main__": + args = parse_args() + + model = RepNet(out_ids=10086, out_attribs=257) + + checkpoint = torch.load(args.weight) + model.load_state_dict(checkpoint) + model.eval() + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 224, 224) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") \ No newline at end of file diff --git a/models/cv/trace/repnet/igie/inference.py b/models/cv/trace/repnet/igie/inference.py new file mode 100644 index 00000000..f9cb3a36 --- /dev/null +++ b/models/cv/trace/repnet/igie/inference.py @@ -0,0 +1,219 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from PIL import Image + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--num_workers", + type=int, + default=16, + help="number of workers used in pytorch dataloader.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +class VehicleDatasets(torch.utils.data.Dataset): + def __init__(self, image_list, image_size=(224, 224)): + super().__init__() + self.image_dir_path = image_list + self.images = [] + self.length = 0 + + for img_path in self.image_dir_path: + self.images.append(img_path) + self.length += 1 + + self.transforms = torchvision.transforms.Compose([ + torchvision.transforms.Resize(224), + torchvision.transforms.CenterCrop(224), + torchvision.transforms.ToTensor(), + torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + ]) + + def preprocess(self, image_path): + img = Image.open(image_path) + + if img.mode == 'L' or img.mode == 'I': + img = img.conver("RGB") + + img = self.transforms(img) + + return img + + def __getitem__(self, index): + image = self.preprocess(self.images[index]) + image_name = self.images[index].split('/')[-1].strip() + return image, image_name + + def __len__(self): + return self.length + +def get_dataset(img_dir, pair_set_txt='pair_set_vehicle.txt'): + pairs, imgs_path = [], [] + try: + with open(pair_set_txt, 'r') as fh: + for line in fh.readlines(): + pair = line.strip().split() + + imgs_path.append(os.path.join(img_dir, 'image', pair[0] + '.jpg')) + imgs_path.append(os.path.join(img_dir, 'image', pair[1] + '.jpg')) + + pairs.append(pair) + + except FileNotFoundError: + assert os.path.isfile(pair_set_txt), "File does not exist." + + list(set(imgs_path)).sort() + + dataset = VehicleDatasets(imgs_path) + + return dataset, pairs + +def cosin_metric(x1, x2): + return np.dot(x1, x2) / (np.linalg.norm(x1) * np.linalg.norm(x2)) + +def cal_accuracy(y_score, y_true): + y_score = np.asarray(y_score) + y_true = np.asarray(y_true) + best_acc = 0 + best_th = 0 + + for i in range(len(y_score)): + th = y_score[i] + y_test = (y_score >= th) + acc = np.mean((y_test == y_true).astype(int)) + + if acc > best_acc: + best_acc = acc + best_th = th + + return best_acc, best_th + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # get dataloader + dataset, pairs = get_dataset(args.datasets) + dataloader = torch.utils.data.DataLoader(dataset, batch_size, num_workers=args.num_workers, drop_last=True) + + features_map = {} + + for inputs, path in tqdm(dataloader): + # Pad the last batch + pad_batch = len(inputs) != batch_size + if pad_batch: + origin_size = len(inputs) + inputs = np.resize(inputs, (batch_size, *inputs.shape[1:])) + + module.set_input('input', tvm.nd.array(inputs, device)) + + module.run() + + features = torch.from_numpy(module.get_output(0).asnumpy()) + + if pad_batch: + features = features[:origin_size] + + for index, output in enumerate(features): + features_map[path[index]] = output + + + sims, labels = [], [] + for pair in pairs: + sim = cosin_metric(features_map[pair[0] + '.jpg'], features_map[pair[1] + '.jpg']) + label = int(pair[2]) + sims.append(sim) + labels.append(label) + + acc, th = cal_accuracy(sims, labels) + print('=> best accuracy: %.3f %%, at threshold: %.3f' % (acc * 100.0, th)) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/trace/repnet/igie/pair_set_vehicle.txt b/models/cv/trace/repnet/igie/pair_set_vehicle.txt new file mode 100644 index 00000000..a6a45cde --- /dev/null +++ b/models/cv/trace/repnet/igie/pair_set_vehicle.txt @@ -0,0 +1,10000 @@ +0234105 0077636 0 +0195185 0229204 0 +0039707 0221023 0 +0024287 0185834 0 +0034283 0187487 0 +0269221 0130789 0 +0187865 0187873 1 +0124817 0124813 1 +0198344 0112799 0 +0259252 0228595 0 +0074878 0022247 0 +0130331 0292813 0 +0243115 0314540 0 +0300868 0216484 0 +0155407 0053038 0 +0020127 0087179 0 +0132450 0071587 0 +0026920 0213597 0 +0003793 0003776 1 +0016035 0135018 0 +0254987 0042392 0 +0207955 0048266 0 +0046680 0046686 1 +0016858 0000554 0 +0209295 0219972 0 +0043174 0198415 0 +0110361 0059240 0 +0055455 0055442 1 +0059422 0286462 0 +0119252 0304504 0 +0244123 0244111 1 +0307303 0307304 1 +0007618 0106742 0 +0097443 0063008 0 +0063617 0176007 0 +0266908 0216525 0 +0025769 0242488 0 +0281968 0039648 0 +0177919 0315350 0 +0023839 0023835 1 +0043174 0043144 1 +0040531 0019426 0 +0282471 0282469 1 +0172033 0172027 1 +0063127 0063143 1 +0221023 0280664 0 +0267890 0322971 0 +0243115 0245808 0 +0264771 0216447 0 +0038245 0121033 0 +0280144 0174496 0 +0219804 0111278 0 +0132454 0048266 0 +0304635 0007051 0 +0281217 0281214 1 +0109464 0135857 0 +0102674 0163982 0 +0282078 0150290 0 +0044796 0086944 0 +0200486 0174496 0 +0112814 0130829 0 +0135839 0214583 0 +0218593 0254986 0 +0309139 0309150 1 +0132454 0181315 0 +0174853 0130877 0 +0064859 0181316 0 +0270225 0150285 0 +0040999 0041012 1 +0156327 0156321 1 +0011701 0011698 1 +0060724 0060722 1 +0318455 0318454 1 +0219464 0129192 0 +0284128 0181655 0 +0224423 0057425 0 +0274946 0198811 0 +0121569 0121571 1 +0282078 0019432 0 +0121832 0240477 0 +0147122 0147862 0 +0072183 0201396 0 +0126706 0101672 0 +0108387 0221023 0 +0027818 0082671 0 +0020643 0166083 0 +0300874 0283103 0 +0171104 0042791 0 +0072660 0249297 0 +0231414 0231441 1 +0204464 0238527 0 +0011399 0168973 0 +0073989 0234369 0 +0070203 0150451 0 +0011403 0149709 0 +0022301 0281826 0 +0061504 0110434 0 +0076576 0110441 0 +0027409 0107042 0 +0164980 0260641 0 +0074763 0074774 1 +0201800 0157835 0 +0106742 0300070 0 +0097277 0267734 0 +0090198 0257700 0 +0210889 0123196 0 +0177223 0051748 0 +0266905 0040532 0 +0108671 0212685 0 +0076605 0016010 0 +0166142 0198805 0 +0295148 0295130 1 +0061485 0221017 0 +0219813 0219804 1 +0253507 0236762 0 +0156102 0156096 1 +0108408 0059127 0 +0263750 0110238 0 +0181315 0179604 0 +0275997 0275993 1 +0108678 0041022 0 +0092571 0199281 0 +0286494 0286498 1 +0166142 0053094 0 +0250833 0250814 1 +0174200 0268838 0 +0174017 0026645 0 +0286646 0281217 0 +0085528 0033286 0 +0143350 0190955 0 +0227059 0037141 0 +0264766 0264771 1 +0267734 0181828 0 +0285500 0232313 0 +0311469 0311448 1 +0075274 0182110 0 +0277994 0263750 0 +0235646 0121832 0 +0043144 0084948 0 +0012501 0016716 0 +0250814 0295130 0 +0184356 0310326 0 +0130982 0280186 0 +0230356 0028063 0 +0243135 0306231 0 +0220683 0110361 0 +0229335 0229340 1 +0037019 0235125 0 +0071587 0071583 1 +0284105 0228606 0 +0056263 0174842 0 +0076665 0166142 0 +0257756 0321146 0 +0270250 0300876 0 +0322415 0216530 0 +0310327 0269639 0 +0081317 0222730 0 +0018421 0307258 0 +0080098 0080091 1 +0155415 0232313 0 +0236760 0039707 0 +0063150 0063152 1 +0043174 0176007 0 +0172675 0016735 0 +0067676 0231447 0 +0099977 0028851 0 +0219807 0219804 1 +0092505 0001584 0 +0204464 0204450 1 +0143350 0276576 0 +0214909 0155434 0 +0278136 0190654 0 +0033432 0049530 0 +0144126 0144136 1 +0195185 0044796 0 +0199692 0034043 0 +0163096 0276138 0 +0042791 0027825 0 +0137879 0111510 0 +0235250 0007910 0 +0090169 0217839 0 +0319568 0319565 1 +0242678 0204068 0 +0098382 0125715 0 +0082154 0074166 0 +0248370 0051748 0 +0322401 0322427 1 +0044551 0022247 0 +0107149 0323527 0 +0257764 0269794 0 +0306845 0115260 0 +0016556 0032018 0 +0302451 0273891 0 +0060722 0282078 0 +0101553 0116417 0 +0092556 0202434 0 +0184360 0184356 1 +0086517 0086558 1 +0284102 0151074 0 +0238399 0238405 1 +0015305 0307938 0 +0086233 0005693 0 +0025768 0025089 0 +0016858 0298724 0 +0211320 0151074 0 +0080091 0197385 0 +0188324 0067562 0 +0045657 0322056 0 +0316640 0315215 0 +0063156 0126696 0 +0150675 0130789 0 +0020572 0020608 1 +0063033 0063030 1 +0308559 0044988 0 +0267877 0295707 0 +0034283 0034284 1 +0267832 0119105 0 +0045783 0230356 0 +0280195 0280186 1 +0279527 0279572 1 +0015111 0128934 0 +0059245 0059273 1 +0273891 0273895 1 +0227338 0063641 0 +0102926 0119111 0 +0219469 0097277 0 +0201396 0097436 0 +0115279 0309259 0 +0316641 0319568 0 +0279572 0279527 1 +0108730 0153212 0 +0096587 0096611 1 +0106260 0033300 0 +0267832 0165899 0 +0245099 0050786 0 +0287178 0050786 0 +0085519 0085528 1 +0013442 0013450 1 +0308559 0282466 0 +0119105 0274281 0 +0031134 0286498 0 +0099977 0098633 0 +0235646 0086959 0 +0254568 0018417 0 +0057425 0130801 0 +0195286 0306498 0 +0133787 0102459 0 +0190947 0018780 0 +0283105 0283103 1 +0187481 0181262 0 +0108730 0202986 0 +0016043 0089252 0 +0074878 0067047 0 +0121033 0037108 0 +0060751 0072183 0 +0124813 0124817 1 +0045773 0171690 0 +0297098 0058694 0 +0185838 0293464 0 +0126696 0126706 1 +0156329 0156321 1 +0179604 0016735 0 +0308607 0260570 0 +0092571 0092574 1 +0162790 0137028 0 +0201396 0201422 1 +0219815 0219804 1 +0102447 0276576 0 +0251395 0251394 1 +0182811 0023835 0 +0260633 0260641 1 +0039257 0092560 0 +0200150 0307258 0 +0111278 0062890 0 +0095054 0304519 0 +0150450 0150451 1 +0019428 0158958 0 +0284128 0031142 0 +0026645 0202994 0 +0098397 0214582 0 +0204450 0235463 0 +0033285 0285260 0 +0076605 0174017 0 +0282469 0260751 0 +0124306 0030172 0 +0276138 0153212 0 +0047693 0253707 0 +0295711 0011701 0 +0063641 0124642 0 +0201802 0201800 1 +0154049 0154038 1 +0322052 0251395 0 +0074155 0074147 1 +0007910 0028350 0 +0050966 0299368 0 +0089211 0055442 0 +0025788 0116291 0 +0204455 0182811 0 +0066022 0106540 0 +0022247 0282466 0 +0106541 0087976 0 +0170460 0266908 0 +0178068 0178075 1 +0276144 0159757 0 +0086959 0080098 0 +0049197 0201828 0 +0130825 0300874 0 +0300865 0221999 0 +0119207 0178068 0 +0253500 0201825 0 +0005693 0005697 1 +0087845 0087848 1 +0094808 0115260 0 +0279527 0087697 0 +0087695 0100482 0 +0085528 0114445 0 +0123196 0074766 0 +0110350 0016735 0 +0042791 0042786 1 +0108023 0176026 0 +0108678 0257681 0 +0044988 0227568 0 +0067047 0171006 0 +0022245 0279529 0 +0151853 0283105 0 +0087845 0247408 0 +0217836 0153215 0 +0051748 0051749 1 +0168645 0137419 0 +0171690 0220683 0 +0293464 0236296 0 +0124636 0087179 0 +0119683 0143353 0 +0181653 0248370 0 +0321146 0150237 0 +0022589 0181315 0 +0150285 0217836 0 +0087848 0018421 0 +0276144 0102453 0 +0038223 0276459 0 +0286494 0214469 0 +0176007 0234337 0 +0275997 0295130 0 +0072183 0213597 0 +0253500 0222730 0 +0102453 0166142 0 +0025583 0098633 0 +0295148 0036691 0 +0219464 0048309 0 +0053338 0224423 0 +0309139 0309143 1 +0317565 0299368 0 +0116291 0196944 0 +0020608 0076606 0 +0150675 0049530 0 +0318453 0318455 1 +0110234 0063004 0 +0012629 0012632 1 +0047624 0053037 0 +0306254 0097278 0 +0102680 0249177 0 +0172675 0172690 1 +0309259 0309254 1 +0024298 0126139 0 +0188234 0200605 0 +0199281 0066137 0 +0067050 0097277 0 +0102455 0242488 0 +0176007 0158958 0 +0283105 0022245 0 +0020126 0207307 0 +0284166 0033131 0 +0235125 0235128 1 +0238527 0046680 0 +0181825 0181828 1 +0310236 0310234 1 +0265882 0039250 0 +0186407 0281828 0 +0102930 0102926 1 +0200487 0098621 0 +0216484 0214913 0 +0235243 0235250 1 +0030172 0018779 0 +0268256 0268255 1 +0295516 0176667 0 +0102926 0078491 0 +0230704 0037141 0 +0304236 0304245 1 +0260751 0155407 0 +0242671 0007637 0 +0315349 0315348 1 +0088697 0121033 0 +0048266 0245807 0 +0310236 0185652 0 +0235126 0227050 0 +0276448 0137877 0 +0041022 0118540 0 +0044230 0094807 0 +0199696 0177780 0 +0048266 0015307 0 +0063156 0063150 1 +0063617 0063641 1 +0109476 0107042 0 +0284105 0076576 0 +0126108 0166145 0 +0326020 0264766 0 +0086558 0086548 1 +0105726 0105734 1 +0234369 0234373 1 +0074878 0059422 0 +0005693 0074899 0 +0134959 0135023 1 +0283055 0078497 0 +0281968 0174853 0 +0304240 0219376 0 +0295675 0295711 1 +0326937 0140618 0 +0101685 0257172 0 +0214465 0219815 0 +0268256 0190654 0 +0097278 0108730 0 +0055001 0024287 0 +0055200 0203596 0 +0215797 0040531 0 +0257764 0166083 0 +0181276 0043144 0 +0107359 0107375 1 +0013442 0016867 0 +0114449 0151313 0 +0135023 0134959 1 +0092588 0003793 0 +0257700 0005123 0 +0170206 0170219 1 +0082671 0051749 0 +0153267 0153275 1 +0130877 0240487 0 +0154122 0154128 1 +0238400 0058694 0 +0168969 0168127 0 +0302840 0110456 0 +0119349 0119347 1 +0016043 0119207 0 +0158958 0130877 0 +0249270 0130873 0 +0236514 0016710 0 +0159757 0159759 1 +0043174 0260563 0 +0301736 0301732 1 +0186013 0186022 1 +0249323 0249322 1 +0278136 0041400 0 +0186022 0162130 0 +0227050 0102447 0 +0307304 0217836 0 +0124862 0121028 0 +0035631 0326020 0 +0201825 0149696 0 +0174200 0220683 0 +0076576 0293615 0 +0089583 0201800 0 +0099973 0318455 0 +0231933 0215795 0 +0219958 0188333 0 +0235463 0195186 0 +0185060 0008086 0 +0182811 0182819 1 +0015111 0055200 0 +0031134 0149779 0 +0085528 0085519 1 +0205783 0216418 0 +0031134 0107161 0 +0107149 0220378 0 +0153212 0052795 0 +0156086 0201396 0 +0322401 0322415 1 +0127309 0127305 1 +0102444 0102447 1 +0279527 0078497 0 +0024298 0044796 0 +0219376 0114953 0 +0177931 0176756 0 +0059240 0290467 0 +0012492 0045656 0 +0016710 0024287 0 +0085519 0304635 0 +0134144 0300077 0 +0048267 0048269 1 +0234105 0011399 0 +0044796 0228606 0 +0164983 0238399 0 +0156321 0109476 0 +0217707 0217706 1 +0311470 0322970 0 +0123194 0150237 0 +0034284 0007654 0 +0279409 0205783 0 +0141901 0232313 0 +0307258 0274944 0 +0094807 0221017 0 +0209298 0028343 0 +0101109 0023610 0 +0204450 0022245 0 +0163089 0163088 1 +0318449 0270244 0 +0155398 0239194 0 +0059127 0063921 0 +0032004 0207955 0 +0012492 0003793 0 +0049197 0049179 1 +0309744 0309746 1 +0156098 0273895 0 +0185058 0077636 0 +0130978 0130982 1 +0107375 0048267 0 +0310327 0176683 0 +0050786 0256608 0 +0038833 0119207 0 +0289128 0227050 0 +0066137 0284102 0 +0234105 0234106 1 +0019432 0019426 1 +0184819 0019432 0 +0026928 0026924 1 +0011731 0011701 1 +0280664 0135839 0 +0022245 0227568 0 +0064855 0178277 0 +0000231 0000233 1 +0003302 0199281 0 +0044146 0234320 0 +0199284 0276448 0 +0238532 0121571 0 +0095049 0326937 0 +0294840 0307937 0 +0119252 0210888 0 +0162790 0025786 0 +0269794 0299367 0 +0126108 0156477 0 +0181316 0287181 0 +0150290 0294684 0 +0130825 0024097 0 +0253500 0276576 0 +0104645 0104581 1 +0324802 0324803 1 +0302840 0059123 0 +0235265 0235250 1 +0143353 0137027 0 +0102459 0188561 0 +0297116 0211322 0 +0162130 0301736 0 +0015181 0074155 0 +0204455 0204450 1 +0078491 0218590 0 +0204068 0294655 0 +0047693 0047705 1 +0102459 0183624 0 +0178075 0102460 0 +0214913 0214895 1 +0170055 0170050 1 +0119104 0119111 1 +0318454 0318449 1 +0235626 0177225 0 +0101553 0307258 0 +0204065 0187867 0 +0024287 0014995 0 +0059123 0059127 1 +0307937 0181828 0 +0216447 0110361 0 +0304521 0121028 0 +0166145 0136667 0 +0119202 0214469 0 +0000569 0249323 0 +0181316 0198344 0 +0045242 0066022 0 +0203592 0267877 0 +0184822 0092560 0 +0170050 0198415 0 +0119683 0020643 0 +0112814 0088471 0 +0097896 0236760 0 +0137168 0058690 0 +0071969 0198337 0 +0300865 0025073 0 +0260641 0092588 0 +0282279 0132451 0 +0154128 0154122 1 +0235266 0235308 1 +0034038 0229204 0 +0114449 0137879 0 +0235629 0130331 0 +0051749 0051748 1 +0044161 0044146 1 +0066137 0047624 0 +0136675 0214583 0 +0090198 0090169 1 +0204065 0275996 0 +0289128 0048205 0 +0210888 0124636 0 +0110465 0110234 0 +0242671 0119252 0 +0235629 0075417 0 +0131250 0044795 0 +0155434 0036691 0 +0056263 0279402 0 +0092560 0092556 1 +0156943 0156954 1 +0155398 0155396 1 +0007754 0204870 0 +0013744 0307263 0 +0073992 0022589 0 +0029052 0025109 0 +0033429 0056263 0 +0121571 0121569 1 +0300865 0013744 0 +0014997 0307258 0 +0073932 0073929 1 +0274282 0043144 0 +0102663 0174855 0 +0047705 0134144 0 +0086959 0086944 1 +0177919 0088697 0 +0249323 0271837 0 +0200555 0053037 0 +0283055 0119239 0 +0217696 0164369 0 +0063008 0268263 0 +0121028 0042786 0 +0319170 0001584 0 +0005123 0207307 0 +0165312 0266905 0 +0107369 0181316 0 +0030054 0291519 0 +0323527 0202434 0 +0040531 0040532 1 +0244111 0107042 0 +0297098 0085519 0 +0151313 0151314 1 +0147865 0147862 1 +0307258 0051749 0 +0310236 0310226 1 +0026920 0026924 1 +0154128 0154106 1 +0066131 0066137 1 +0202441 0049597 0 +0119347 0119349 1 +0137419 0063127 0 +0190652 0033303 0 +0164372 0082154 0 +0087848 0095054 0 +0202986 0183626 0 +0016867 0007654 0 +0007051 0263746 0 +0299367 0042392 0 +0053339 0150285 0 +0029174 0059127 0 +0311469 0072151 0 +0235651 0235646 1 +0080091 0200999 0 +0154038 0154049 1 +0029187 0232313 0 +0217674 0203596 0 +0047626 0286499 0 +0112799 0112814 1 +0154128 0000574 0 +0016867 0168987 0 +0044988 0055455 0 +0039257 0207955 0 +0171113 0204389 0 +0171689 0129192 0 +0200564 0234320 0 +0042786 0306375 0 +0326937 0326933 1 +0005697 0105734 0 +0187470 0053640 0 +0218593 0137515 0 +0177770 0240477 0 +0083933 0137512 0 +0051263 0217839 0 +0282078 0181276 0 +0242692 0020127 0 +0076591 0301736 0 +0064284 0071587 0 +0256608 0002379 0 +0304643 0304638 1 +0315206 0130801 0 +0260945 0260933 1 +0227338 0227360 1 +0114478 0317555 0 +0033429 0108398 0 +0154122 0294844 0 +0235646 0022592 0 +0239194 0263779 0 +0306823 0306845 1 +0066026 0294359 0 +0135018 0236507 0 +0153212 0028064 0 +0276091 0155434 0 +0077624 0087697 0 +0149786 0050786 0 +0268838 0126139 0 +0281968 0308555 0 +0200564 0200605 1 +0075000 0013442 0 +0216525 0195664 0 +0170457 0027409 0 +0003303 0097443 0 +0178068 0227859 0 +0045595 0130331 0 +0054987 0171113 0 +0120059 0071959 0 +0094808 0094945 0 +0306845 0322425 0 +0094808 0286466 0 +0214494 0214507 1 +0026928 0026920 1 +0279400 0220683 0 +0200150 0102674 0 +0304517 0185639 0 +0107375 0292823 0 +0185058 0216486 0 +0110312 0110318 1 +0124862 0081318 0 +0212700 0011399 0 +0121028 0121033 1 +0104592 0063923 0 +0110434 0074774 0 +0108030 0235125 0 +0063030 0227860 0 +0196947 0004538 0 +0283103 0283105 1 +0069008 0168630 0 +0212685 0195664 0 +0238428 0238440 1 +0285258 0118456 0 +0085528 0186013 0 +0235465 0234105 0 +0159759 0159757 1 +0222730 0031134 0 +0322085 0124862 0 +0219804 0219813 1 +0137515 0137485 1 +0025783 0055001 0 +0195279 0118456 0 +0245807 0037108 0 +0257700 0214494 0 +0082152 0001584 0 +0201828 0187867 0 +0116291 0116258 1 +0218593 0279568 0 +0155398 0155407 1 +0317555 0064855 0 +0065769 0177225 0 +0097887 0036691 0 +0053416 0053443 1 +0026928 0020572 0 +0172033 0005123 0 +0202986 0224423 0 +0319565 0104584 0 +0206782 0206776 1 +0251395 0164983 0 +0156321 0000574 0 +0118536 0107149 0 +0315348 0055442 0 +0176763 0082667 0 +0181316 0322425 0 +0179428 0190652 0 +0078708 0073989 0 +0213594 0024098 0 +0016556 0168630 0 +0044988 0265247 0 +0110434 0050781 0 +0177580 0188333 0 +0043421 0178068 0 +0166142 0086536 0 +0059750 0154106 0 +0038833 0032018 0 +0186409 0186407 1 +0073992 0032004 0 +0249297 0275993 0 +0156327 0086944 0 +0286466 0065775 0 +0300876 0300874 1 +0322085 0086383 0 +0159757 0186409 0 +0216447 0216423 1 +0110238 0050781 0 +0063921 0304643 0 +0074899 0110456 0 +0176763 0176756 1 +0182471 0199284 0 +0223582 0118540 0 +0063152 0063166 1 +0043174 0085519 0 +0072183 0053443 0 +0179604 0294844 0 +0096611 0096632 1 +0306477 0286498 0 +0050965 0299367 0 +0047687 0196944 0 +0249309 0157632 0 +0072647 0266908 0 +0087845 0087902 1 +0109506 0157835 0 +0080495 0075411 0 +0188333 0150678 0 +0234106 0308555 0 +0304519 0147862 0 +0151074 0069008 0 +0119202 0086536 0 +0317565 0185639 0 +0101114 0293613 0 +0273895 0028063 0 +0326933 0110434 0 +0019426 0202986 0 +0110434 0110441 1 +0174842 0174853 1 +0323466 0323487 1 +0271837 0178277 0 +0262658 0053037 0 +0047626 0047624 1 +0108387 0209295 0 +0231934 0220378 0 +0235461 0235465 1 +0185834 0260945 0 +0099973 0099977 1 +0245808 0324802 0 +0074774 0074766 1 +0135018 0134973 1 +0029174 0036686 0 +0250814 0272503 0 +0093111 0093113 1 +0244111 0265268 0 +0187487 0187481 1 +0210889 0064277 0 +0059750 0059754 1 +0170460 0047705 0 +0066022 0295516 0 +0086558 0074166 0 +0122320 0072647 0 +0098397 0065775 0 +0108023 0126099 0 +0309254 0033286 0 +0130331 0100476 0 +0094923 0094945 1 +0244132 0234106 0 +0206780 0020640 0 +0196944 0195185 0 +0054979 0264771 0 +0273895 0165899 0 +0051748 0315215 0 +0050965 0119111 0 +0121832 0121842 1 +0323464 0202994 0 +0234337 0234320 1 +0000569 0099603 0 +0119239 0257764 0 +0227338 0135018 0 +0042392 0200487 0 +0190652 0304628 0 +0150242 0292837 0 +0119104 0047624 0 +0045656 0076665 0 +0070203 0008086 0 +0185838 0196166 0 +0049179 0066231 0 +0058690 0111278 0 +0034038 0034043 1 +0177225 0086408 0 +0172027 0051748 0 +0037141 0156477 0 +0234337 0123196 0 +0234373 0099973 0 +0020572 0089252 0 +0130801 0260570 0 +0272511 0166145 0 +0256608 0130873 0 +0211967 0028859 0 +0159757 0326937 0 +0282282 0282279 1 +0236296 0155398 0 +0011403 0011399 1 +0104645 0104584 1 +0315215 0230706 0 +0294669 0001572 0 +0007051 0050966 0 +0180814 0294684 0 +0188418 0190654 0 +0223582 0089252 0 +0157340 0026645 0 +0249309 0249297 1 +0147862 0256608 0 +0162790 0162786 1 +0282078 0102663 0 +0063143 0063156 1 +0177225 0127305 0 +0042969 0065769 0 +0181262 0067573 0 +0319928 0318454 0 +0101109 0101114 1 +0316641 0016867 0 +0242671 0108678 0 +0012632 0235646 0 +0110234 0309744 0 +0041400 0041397 1 +0216530 0280139 0 +0176667 0096611 0 +0219972 0030054 0 +0188234 0163096 0 +0102447 0102444 1 +0235266 0235250 1 +0029174 0029187 1 +0109506 0008086 0 +0089583 0101672 0 +0260933 0127309 0 +0168973 0119239 0 +0049530 0030054 0 +0187487 0187470 1 +0054979 0265268 0 +0186407 0235308 0 +0171104 0171113 1 +0184864 0322425 0 +0238405 0238399 1 +0030163 0265247 0 +0207307 0314537 0 +0111510 0041012 0 +0220378 0069008 0 +0214582 0029052 0 +0236507 0181276 0 +0235629 0099977 0 +0052796 0137419 0 +0188231 0086233 0 +0119346 0119347 1 +0291519 0130785 0 +0022247 0078708 0 +0059418 0011717 0 +0263759 0168128 0 +0309259 0164983 0 +0168645 0188413 0 +0107049 0107042 1 +0317567 0323527 0 +0219808 0219813 1 +0065775 0000233 0 +0007622 0007637 1 +0298727 0260751 0 +0121033 0076606 0 +0214909 0214913 1 +0099620 0073992 0 +0028343 0279409 0 +0020871 0224413 0 +0238413 0200998 0 +0318455 0309746 0 +0045242 0120059 0 +0286466 0242692 0 +0325536 0293464 0 +0229204 0264771 0 +0320161 0039707 0 +0128913 0063921 0 +0104645 0229204 0 +0228606 0086944 0 +0126706 0130829 0 +0048309 0283050 0 +0076605 0073929 0 +0307258 0307263 1 +0097277 0097278 1 +0224423 0267877 0 +0195279 0195286 1 +0071959 0089212 0 +0223582 0223589 1 +0044576 0137437 0 +0188324 0015181 0 +0016035 0015983 1 +0229206 0087501 0 +0235651 0022490 0 +0116258 0276459 0 +0081317 0051748 0 +0026647 0307937 0 +0126706 0059127 0 +0156321 0156329 1 +0124642 0321146 0 +0177580 0062892 0 +0269791 0269794 1 +0020867 0044161 0 +0088471 0149779 0 +0230706 0227050 0 +0082154 0082152 1 +0215795 0116417 0 +0278136 0278139 1 +0184360 0273895 0 +0032004 0031996 1 +0278000 0320159 0 +0194940 0026645 0 +0170050 0168984 0 +0102447 0221023 0 +0108408 0013450 0 +0236296 0236295 1 +0174020 0279527 0 +0128910 0285258 0 +0280664 0020474 0 +0257756 0071969 0 +0198337 0076576 0 +0044551 0299368 0 +0204876 0294359 0 +0238405 0238400 1 +0049197 0028063 0 +0282469 0300419 0 +0018780 0018779 1 +0107161 0107149 1 +0041400 0250814 0 +0141916 0141907 1 +0257172 0257178 1 +0116270 0250814 0 +0063921 0093295 0 +0007637 0025775 0 +0149696 0275996 0 +0121033 0195664 0 +0308559 0000569 0 +0326018 0000569 0 +0153275 0135857 0 +0130977 0294684 0 +0304245 0200140 0 +0244109 0244111 1 +0214583 0210384 0 +0227673 0267960 0 +0061485 0211322 0 +0071969 0104584 0 +0311462 0311470 1 +0045245 0023839 0 +0131250 0131263 1 +0243135 0254570 0 +0153267 0016556 0 +0207955 0267960 0 +0119239 0227360 0 +0195661 0000233 0 +0309746 0025584 0 +0110456 0245808 0 +0282469 0219469 0 +0022592 0144136 0 +0061095 0066231 0 +0007051 0007061 1 +0044230 0044229 1 +0002379 0177586 0 +0086959 0320159 0 +0278136 0016858 0 +0007622 0102455 0 +0071969 0212688 0 +0132454 0132450 1 +0017033 0011684 0 +0276570 0050966 0 +0097278 0144136 0 +0035631 0035624 1 +0025109 0235461 0 +0066215 0229206 0 +0236575 0098315 0 +0082152 0082154 1 +0098303 0276140 0 +0267877 0267890 1 +0026636 0317565 0 +0236296 0260563 0 +0049197 0187674 0 +0098311 0238527 0 +0061097 0053664 0 +0165312 0269655 0 +0292813 0292838 1 +0169041 0169043 1 +0119105 0119104 1 +0126139 0041400 0 +0137301 0245808 0 +0257681 0300868 0 +0309746 0307258 0 +0069009 0177919 0 +0156943 0228606 0 +0000559 0300874 0 +0306231 0085519 0 +0134974 0134973 1 +0099977 0122316 0 +0110318 0014997 0 +0241755 0241743 1 +0242692 0212685 0 +0219808 0008083 0 +0200150 0081317 0 +0061504 0163089 0 +0060755 0238413 0 +0275988 0275977 1 +0320161 0210889 0 +0007061 0007051 1 +0027409 0276091 0 +0323535 0034283 0 +0309558 0086260 0 +0075271 0239516 0 +0090185 0137879 0 +0016735 0016716 1 +0250814 0250833 1 +0242671 0242670 1 +0310327 0254570 0 +0308559 0034043 0 +0087697 0170200 0 +0212700 0106731 0 +0047693 0047691 1 +0182110 0227678 0 +0039214 0039215 1 +0219362 0054987 0 +0043421 0130873 0 +0231441 0124813 0 +0107042 0033285 0 +0206780 0179607 0 +0126133 0110297 0 +0063152 0063156 1 +0299367 0156321 0 +0314540 0314537 1 +0029174 0247408 0 +0286646 0166145 0 +0048311 0320161 0 +0013747 0045103 0 +0060724 0002393 0 +0115279 0102663 0 +0236528 0236532 1 +0322970 0049846 0 +0033432 0257756 0 +0216530 0326933 0 +0134144 0151314 0 +0017052 0060755 0 +0094923 0294685 0 +0071583 0039707 0 +0201800 0119202 0 +0053416 0043144 0 +0213597 0213594 1 +0133785 0156954 0 +0201396 0273895 0 +0286667 0214582 0 +0218593 0259252 0 +0188324 0011403 0 +0249297 0280195 0 +0094923 0124817 0 +0124817 0107161 0 +0013747 0250814 0 +0235461 0235463 1 +0106540 0106541 1 +0127309 0219469 0 +0217696 0166142 0 +0268256 0083935 0 +0320161 0063008 0 +0102453 0150242 0 +0007051 0008083 0 +0035624 0199284 0 +0121028 0110234 0 +0067573 0309254 0 +0017052 0017033 1 +0003793 0108398 0 +0126706 0231447 0 +0053664 0322425 0 +0317555 0184819 0 +0281965 0281968 1 +0130877 0201396 0 +0168987 0304504 0 +0150678 0098409 0 +0279402 0058694 0 +0203596 0286494 0 +0216423 0269794 0 +0320161 0178075 0 +0004535 0004530 1 +0016858 0003302 0 +0092556 0092560 1 +0168645 0119252 0 +0198259 0042980 0 +0109506 0202986 0 +0315206 0130877 0 +0118455 0060543 0 +0270225 0270247 1 +0206780 0136675 0 +0157835 0210384 0 +0260563 0001572 0 +0295513 0076591 0 +0095049 0132454 0 +0105726 0120063 0 +0231934 0195286 0 +0023610 0023608 1 +0159759 0294684 0 +0195664 0214582 0 +0156954 0207309 0 +0093295 0109506 0 +0099973 0269655 0 +0176683 0260945 0 +0214271 0005693 0 +0029060 0156954 0 +0022592 0022589 1 +0182819 0033285 0 +0048204 0097896 0 +0310226 0216530 0 +0168987 0039648 0 +0023835 0023839 1 +0168124 0143793 0 +0066137 0207955 0 +0101114 0026924 0 +0074155 0326937 0 +0012501 0101538 0 +0027422 0027409 1 +0306839 0306845 1 +0201800 0201802 1 +0039214 0039221 1 +0170456 0214494 0 +0045595 0007933 0 +0106273 0106260 1 +0253507 0114449 0 +0137877 0076655 0 +0137167 0045773 0 +0162129 0008086 0 +0040999 0102447 0 +0086517 0306823 0 +0295130 0257172 0 +0200998 0316641 0 +0000233 0117863 0 +0294684 0098311 0 +0141901 0141907 1 +0081581 0232317 0 +0023839 0295675 0 +0119202 0055202 0 +0274946 0044229 0 +0050786 0098224 0 +0204389 0111511 0 +0203596 0007637 0 +0025584 0149779 0 +0015307 0020963 0 +0102930 0102933 1 +0198272 0326020 0 +0026647 0026649 1 +0000554 0036686 0 +0188561 0157340 0 +0008083 0304236 0 +0165312 0119683 0 +0235646 0235651 1 +0300874 0300876 1 +0198033 0107049 0 +0110365 0168987 0 +0032004 0044795 0 +0060543 0264766 0 +0147126 0273895 0 +0094945 0131250 0 +0253500 0253507 1 +0060755 0060751 1 +0282466 0279409 0 +0290478 0028859 0 +0185058 0219958 0 +0058684 0058690 1 +0072647 0059240 0 +0276089 0199696 0 +0137419 0065470 0 +0294655 0196947 0 +0244111 0244132 1 +0250833 0295130 0 +0217674 0155407 0 +0182473 0075003 0 +0211969 0025089 0 +0109464 0026645 0 +0022245 0263779 0 +0304521 0195186 0 +0196169 0202994 0 +0214507 0286499 0 +0230706 0087364 0 +0043174 0273606 0 +0114949 0101553 0 +0011399 0011403 1 +0111278 0289130 0 +0230706 0119682 0 +0254986 0097896 0 +0049179 0309061 0 +0011701 0119202 0 +0041012 0213594 0 +0231441 0231414 1 +0098397 0098382 1 +0211320 0211322 1 +0045245 0156477 0 +0236532 0236528 1 +0271837 0190654 0 +0294669 0126108 0 +0184360 0051263 0 +0075000 0098213 0 +0211969 0211967 1 +0214276 0283050 0 +0292838 0292813 1 +0124817 0078708 0 +0325529 0318455 0 +0274944 0119202 0 +0162129 0162130 1 +0244111 0286462 0 +0012632 0012627 1 +0043410 0306498 0 +0162786 0302455 0 +0324803 0094945 0 +0286667 0302451 0 +0230356 0171015 0 +0042368 0214276 0 +0168124 0168127 1 +0276089 0322427 0 +0043410 0043421 1 +0272503 0272511 1 +0298724 0055200 0 +0063008 0063004 1 +0222002 0078721 0 +0154038 0182811 0 +0150675 0265884 0 +0101538 0234105 0 +0143793 0007910 0 +0092574 0283055 0 +0033286 0048130 0 +0097892 0011399 0 +0098315 0098303 1 +0118540 0054980 0 +0176763 0267836 0 +0122316 0195281 0 +0119252 0119239 1 +0223589 0127309 0 +0235463 0235465 1 +0082671 0318454 0 +0286498 0286494 1 +0198805 0291512 0 +0007618 0007654 1 +0108678 0234373 0 +0041388 0297116 0 +0230350 0097443 0 +0108671 0108678 1 +0047693 0053094 0 +0177225 0177223 1 +0023839 0132451 0 +0087697 0031142 0 +0272511 0003302 0 +0045595 0150237 0 +0242678 0242692 1 +0231933 0231934 1 +0132454 0256608 0 +0117882 0117863 1 +0026649 0005141 0 +0110238 0098317 0 +0044161 0117863 0 +0130873 0300070 0 +0214469 0287178 0 +0110365 0110332 1 +0101672 0198421 0 +0114445 0235243 0 +0059273 0059248 1 +0279409 0279402 1 +0000231 0092571 0 +0137877 0026649 0 +0281965 0101685 0 +0232317 0034284 0 +0033300 0306477 0 +0304628 0097436 0 +0202986 0202970 1 +0119105 0227568 0 +0214465 0238400 0 +0177931 0181276 0 +0304521 0171104 0 +0217674 0268838 0 +0172677 0172690 1 +0060722 0216447 0 +0119202 0011701 0 +0023841 0023835 1 +0297098 0096587 0 +0231441 0231427 1 +0047626 0269794 0 +0290478 0244109 0 +0158958 0018417 0 +0106742 0118536 0 +0011717 0162130 0 +0286498 0306197 0 +0311462 0185838 0 +0166142 0174020 0 +0297098 0177223 0 +0075274 0075271 1 +0008257 0026647 0 +0016010 0110465 0 +0011698 0011701 1 +0163089 0163096 1 +0066137 0121569 0 +0054980 0132454 0 +0144136 0022301 0 +0102455 0071587 0 +0300817 0300818 1 +0002393 0002379 1 +0057425 0057463 1 +0061095 0181653 0 +0016556 0016551 1 +0013717 0013710 1 +0081318 0081317 1 +0157835 0018780 0 +0310327 0325536 0 +0067676 0311462 0 +0130982 0130978 1 +0143793 0098303 0 +0101685 0216530 0 +0235243 0077636 0 +0286466 0203592 0 +0219362 0286667 0 +0110234 0101114 0 +0013710 0057462 0 +0020867 0020871 1 +0264766 0137419 0 +0314540 0314536 1 +0028859 0028851 1 +0306241 0306227 1 +0325536 0240477 0 +0043144 0207953 0 +0049179 0204068 0 +0109476 0109464 1 +0219972 0048267 0 +0072151 0045103 0 +0045096 0253675 0 +0180814 0166083 0 +0110434 0172033 0 +0207309 0045595 0 +0204065 0011731 0 +0147122 0099975 0 +0065470 0051263 0 +0319928 0080495 0 +0056257 0230350 0 +0082152 0033303 0 +0094945 0031142 0 +0123194 0080495 0 +0102674 0102663 1 +0304643 0201422 0 +0318453 0242671 0 +0097892 0101211 0 +0004538 0004536 1 +0007654 0007637 1 +0124636 0066532 0 +0267734 0286499 0 +0182109 0304236 0 +0294669 0294655 1 +0274281 0172033 0 +0263759 0234320 0 +0121571 0061097 0 +0130873 0267741 0 +0087179 0220378 0 +0282079 0214271 0 +0234373 0234369 1 +0015297 0015305 1 +0301736 0089212 0 +0064862 0165314 0 +0196166 0066137 0 +0063030 0187674 0 +0295711 0016858 0 +0200486 0177223 0 +0055442 0043144 0 +0072660 0147520 0 +0264766 0203596 0 +0326018 0082667 0 +0058684 0058694 1 +0065470 0093113 0 +0227673 0078721 0 +0005141 0112814 0 +0293465 0267734 0 +0216486 0108408 0 +0276138 0207955 0 +0111278 0116432 0 +0043421 0090198 0 +0302455 0174017 0 +0117865 0117863 1 +0311448 0293464 0 +0254568 0304635 0 +0055001 0202986 0 +0323480 0176763 0 +0285504 0163982 0 +0267890 0020582 0 +0281968 0015297 0 +0066022 0066026 1 +0184864 0223582 0 +0281828 0081317 0 +0159757 0176026 0 +0187671 0187674 1 +0083935 0053094 0 +0235461 0322970 0 +0116261 0116291 1 +0028350 0150451 0 +0033136 0087900 0 +0135839 0265268 0 +0066547 0121832 0 +0269791 0269796 1 +0257700 0108387 0 +0170050 0168969 0 +0281828 0281826 1 +0048130 0286498 0 +0282289 0051263 0 +0171689 0198344 0 +0210888 0121832 0 +0020643 0020640 1 +0092560 0220376 0 +0257700 0031134 0 +0121569 0268827 0 +0134973 0134974 1 +0292838 0043410 0 +0039257 0045783 0 +0300428 0053094 0 +0023835 0033429 0 +0235651 0201802 0 +0064277 0024098 0 +0214469 0055455 0 +0276091 0108731 0 +0185652 0038833 0 +0317567 0242488 0 +0234337 0234106 0 +0268256 0181655 0 +0107359 0107369 1 +0044551 0044576 1 +0274281 0274282 1 +0195185 0140618 0 +0284128 0101672 0 +0286462 0310236 0 +0097443 0141907 0 +0200150 0201422 0 +0048130 0063617 0 +0174509 0027825 0 +0302455 0047693 0 +0169041 0074899 0 +0045595 0250833 0 +0045656 0260570 0 +0171689 0171690 1 +0121571 0087695 0 +0098213 0026645 0 +0063033 0227678 0 +0150242 0084948 0 +0249194 0287181 0 +0133893 0064277 0 +0190654 0176026 0 +0039221 0039215 1 +0039257 0280186 0 +0024287 0199696 0 +0308607 0260945 0 +0116417 0201396 0 +0283103 0219464 0 +0171104 0130785 0 +0045245 0048205 0 +0074166 0030172 0 +0077624 0257178 0 +0284188 0242671 0 +0217662 0302840 0 +0204389 0204380 1 +0137027 0137028 1 +0279527 0265268 0 +0075411 0089211 0 +0060542 0060543 1 +0195281 0195286 1 +0317552 0062890 0 +0284102 0284128 1 +0126139 0126133 1 +0166145 0017033 0 +0055202 0210384 0 +0178068 0318454 0 +0307304 0307303 1 +0089583 0235465 0 +0220683 0005141 0 +0012632 0080508 0 +0280186 0260633 0 +0072647 0072660 1 +0315206 0299368 0 +0114449 0114445 1 +0066365 0236528 0 +0012492 0190652 0 +0274946 0269655 0 +0063127 0063152 1 +0166083 0257178 0 +0263759 0114445 0 +0302455 0150237 0 +0048269 0075000 0 +0002393 0238532 0 +0049847 0283105 0 +0007754 0196166 0 +0002379 0082671 0 +0134959 0135018 1 +0311470 0311462 1 +0319568 0265253 0 +0124642 0170460 0 +0168645 0128910 0 +0015305 0015307 1 +0309139 0227860 0 +0322056 0292837 0 +0151313 0183626 0 +0242692 0049846 0 +0111278 0049846 0 +0155398 0265237 0 +0086233 0092526 0 +0016025 0236299 0 +0088697 0170055 0 +0322427 0300419 0 +0081317 0309150 0 +0220694 0318449 0 +0176007 0168984 0 +0324803 0326020 0 +0177585 0177586 1 +0059249 0059240 1 +0110297 0110289 1 +0294359 0137168 0 +0056257 0056263 1 +0204464 0049179 0 +0121571 0247402 0 +0227360 0227338 1 +0182110 0200487 0 +0129307 0130825 0 +0020572 0020582 1 +0263779 0295148 0 +0072660 0236760 0 +0168987 0124813 0 +0076665 0317565 0 +0188561 0120063 0 +0260751 0125758 0 +0080508 0080495 1 +0219813 0219815 1 +0031142 0031134 1 +0247408 0141901 0 +0170050 0202986 0 +0099975 0099977 1 +0287178 0245094 0 +0101208 0037108 0 +0322415 0322425 1 +0047691 0227673 0 +0047691 0047687 1 +0206782 0087501 0 +0279529 0279572 1 +0300818 0053664 0 +0300817 0081581 0 +0053640 0121033 0 +0061485 0182819 0 +0086259 0041022 0 +0217662 0137877 0 +0168984 0260945 0 +0199696 0279568 0 +0260757 0115260 0 +0147937 0147933 1 +0016052 0016043 1 +0086203 0128910 0 +0129296 0286462 0 +0016710 0016735 1 +0020958 0020963 1 +0308559 0269655 0 +0137297 0162790 0 +0147126 0137167 0 +0204068 0241743 0 +0087186 0050786 0 +0015181 0074766 0 +0155409 0155396 1 +0076655 0174509 0 +0154106 0044146 0 +0207307 0281214 0 +0242488 0242473 1 +0245808 0182819 0 +0140616 0300865 0 +0119238 0231934 0 +0076606 0084944 0 +0214494 0323535 0 +0285500 0178276 0 +0293615 0179604 0 +0143353 0143347 1 +0276459 0143353 0 +0094808 0094807 1 +0188562 0024287 0 +0044230 0119239 0 +0134959 0219469 0 +0207309 0207307 1 +0176756 0039648 0 +0126133 0077636 0 +0275977 0023608 0 +0059419 0109476 0 +0056263 0056257 1 +0000574 0150290 0 +0051263 0235243 0 +0126099 0206780 0 +0119104 0121571 0 +0144136 0201396 0 +0276570 0283054 0 +0076591 0007622 0 +0004535 0102930 0 +0293799 0293791 1 +0059123 0186013 0 +0325529 0087695 0 +0075419 0264771 0 +0097443 0073929 0 +0262658 0232313 0 +0265256 0265247 1 +0119683 0276144 0 +0074878 0217696 0 +0204870 0056263 0 +0282289 0126099 0 +0179428 0137301 0 +0037108 0267959 0 +0273621 0093289 0 +0300876 0214271 0 +0087190 0033432 0 +0097887 0073992 0 +0140618 0267832 0 +0121842 0121832 1 +0182109 0182110 1 +0319930 0270250 0 +0076665 0119105 0 +0214582 0214583 1 +0040532 0196947 0 +0120063 0133893 0 +0110238 0197380 0 +0061485 0061504 1 +0236762 0306823 0 +0322427 0322415 1 +0045001 0299367 0 +0257756 0323535 0 +0294684 0102930 0 +0213597 0044795 0 +0200999 0201828 0 +0020640 0042980 0 +0016735 0055001 0 +0172033 0242678 0 +0043421 0267959 0 +0318455 0073929 0 +0171015 0236514 0 +0276570 0114445 0 +0196166 0102680 0 +0264766 0116432 0 +0280686 0214271 0 +0045001 0174855 0 +0039257 0039250 1 +0061095 0216418 0 +0156484 0033429 0 +0318449 0318454 1 +0003793 0302850 0 +0195279 0207307 0 +0075426 0075411 1 +0059127 0057462 0 +0049197 0204065 0 +0267960 0267959 1 +0135857 0066369 0 +0177223 0130789 0 +0114449 0222740 0 +0309744 0102447 0 +0023608 0119346 0 +0015122 0015111 1 +0308555 0198272 0 +0147520 0083933 0 +0060722 0060724 1 +0285260 0282469 0 +0087978 0267959 0 +0072647 0007622 0 +0105737 0202970 0 +0097278 0097277 1 +0190652 0031996 0 +0290467 0101672 0 +0099620 0195279 0 +0207953 0011399 0 +0120063 0059422 0 +0185838 0176026 0 +0062892 0062890 1 +0032004 0032018 1 +0016867 0115279 0 +0234373 0039250 0 +0279400 0041388 0 +0227059 0046686 0 +0062890 0294359 0 +0008264 0306363 0 +0107375 0101553 0 +0319170 0051251 0 +0291519 0273606 0 +0248370 0200999 0 +0276144 0025786 0 +0063617 0043174 0 +0000574 0000559 1 +0177919 0195661 0 +0025583 0267890 0 +0286667 0149786 0 +0130799 0130801 1 +0311469 0265247 0 +0311448 0311469 1 +0222002 0198811 0 +0198811 0319928 0 +0020640 0231441 0 +0020867 0156321 0 +0310326 0107042 0 +0147937 0154047 0 +0020958 0086944 0 +0265882 0073992 0 +0262658 0137512 0 +0281968 0157632 0 +0053094 0063033 0 +0081581 0248370 0 +0106267 0106273 1 +0098303 0174853 0 +0181655 0188413 0 +0078497 0058694 0 +0309143 0309139 1 +0314536 0314537 1 +0032004 0195185 0 +0065769 0007618 0 +0011399 0147933 0 +0200605 0023835 0 +0162130 0168128 0 +0143794 0149779 0 +0276576 0049597 0 +0007602 0007622 1 +0074147 0185058 0 +0188324 0286498 0 +0089252 0089242 1 +0168645 0203596 0 +0129296 0221017 0 +0008264 0061485 0 +0281214 0050965 0 +0188333 0217674 0 +0293799 0124300 0 +0007051 0140616 0 +0204389 0229206 0 +0254987 0101109 0 +0151088 0106541 0 +0118536 0118541 1 +0231427 0267734 0 +0234366 0020290 0 +0244123 0244132 1 +0181316 0287179 0 +0154049 0020479 0 +0019426 0019432 1 +0027409 0027422 1 +0066137 0281214 0 +0180817 0180814 1 +0050966 0038245 0 +0228606 0037108 0 +0124636 0321146 0 +0231447 0111278 0 +0045001 0018417 0 +0119682 0181315 0 +0234106 0042980 0 +0129190 0048269 0 +0304517 0070208 0 +0186022 0315348 0 +0119683 0320161 0 +0066231 0066237 1 +0184356 0314540 0 +0135857 0260633 0 +0049597 0134974 0 +0041397 0151088 0 +0219464 0219469 1 +0299368 0266908 0 +0073992 0158964 0 +0174197 0174200 1 +0264771 0227859 0 +0257681 0023839 0 +0170457 0227568 0 +0133893 0201800 0 +0238400 0143794 0 +0126706 0102455 0 +0203596 0046686 0 +0271816 0257764 0 +0308555 0308559 1 +0050781 0050786 1 +0308559 0319174 0 +0200150 0168984 0 +0297098 0297116 1 +0243115 0243135 1 +0050966 0220376 0 +0130873 0282279 0 +0236563 0236575 1 +0265237 0274944 0 +0157340 0295516 0 +0088697 0072660 0 +0180814 0177585 0 +0218590 0218593 1 +0297116 0087501 0 +0309150 0065472 0 +0075000 0044161 0 +0134973 0134959 1 +0117882 0295513 0 +0238413 0238405 1 +0092560 0164372 0 +0278139 0003302 0 +0096587 0264766 0 +0012627 0012632 1 +0143794 0082152 0 +0048205 0162129 0 +0265882 0050965 0 +0199281 0199284 1 +0244132 0170055 0 +0016710 0186409 0 +0201396 0022247 0 +0238527 0159757 0 +0066131 0157828 0 +0007618 0007602 1 +0236528 0302850 0 +0179607 0190955 0 +0082671 0235336 0 +0163096 0107042 0 +0292823 0306363 0 +0159759 0037141 0 +0029052 0035458 0 +0257681 0171113 0 +0087978 0087976 1 +0081318 0289130 0 +0001572 0163982 0 +0121028 0284102 0 +0020643 0026636 0 +0059418 0063030 0 +0216447 0285500 0 +0298724 0298727 1 +0300865 0007051 0 +0102459 0102460 1 +0280126 0280139 1 +0047705 0249309 0 +0222002 0251395 0 +0106273 0227568 0 +0292837 0292813 1 +0321152 0099977 0 +0030048 0137512 0 +0007653 0007622 1 +0249297 0249309 1 +0044229 0308606 0 +0102444 0041397 0 +0137437 0188234 0 +0227678 0221999 0 +0241743 0078708 0 +0169043 0137877 0 +0157340 0157343 1 +0037019 0037016 1 +0065472 0063923 0 +0214465 0207307 0 +0170200 0123194 0 +0182471 0182473 1 +0235461 0293464 0 +0097277 0281826 0 +0126139 0294844 0 +0013747 0028859 0 +0276576 0008264 0 +0119252 0144126 0 +0281214 0092556 0 +0214276 0281965 0 +0089582 0089583 1 +0309744 0033286 0 +0007622 0198344 0 +0058694 0151853 0 +0064284 0059422 0 +0157632 0157629 1 +0236563 0113656 0 +0235308 0235265 1 +0040531 0102674 0 +0282466 0060751 0 +0150451 0310326 0 +0098397 0055001 0 +0310234 0310236 1 +0283042 0283050 1 +0280664 0071587 0 +0058690 0058694 1 +0044161 0319174 0 +0095049 0095054 1 +0115279 0089211 0 +0318449 0210889 0 +0176007 0239194 0 +0319172 0320159 0 +0290467 0055200 0 +0118540 0307258 0 +0260945 0101114 0 +0121033 0111280 0 +0285500 0170200 0 +0101114 0147865 0 +0260757 0221999 0 +0018417 0227360 0 +0294362 0070208 0 +0137301 0100482 0 +0126133 0268233 0 +0020640 0108731 0 +0155407 0155396 1 +0257764 0307937 0 +0310236 0147122 0 +0218590 0051749 0 +0234369 0108731 0 +0300818 0242488 0 +0085528 0039662 0 +0202427 0202434 1 +0213594 0178276 0 +0182110 0182107 1 +0053038 0124863 0 +0267960 0287181 0 +0070203 0070208 1 +0097277 0177931 0 +0276138 0276140 1 +0309061 0044578 0 +0028350 0028343 1 +0234373 0055455 0 +0098382 0033300 0 +0020127 0278136 0 +0276576 0174842 0 +0063033 0075000 0 +0221288 0022301 0 +0236563 0016556 0 +0231447 0231414 1 +0223582 0092574 0 +0090185 0060542 0 +0201802 0262658 0 +0260757 0260751 1 +0074766 0195186 0 +0107375 0107369 1 +0124300 0118455 0 +0176763 0244132 0 +0042392 0315206 0 +0284166 0195185 0 +0241743 0078497 0 +0315215 0315206 1 +0179604 0110441 0 +0126696 0238532 0 +0004530 0260945 0 +0025109 0046680 0 +0098224 0176026 0 +0156954 0156943 1 +0196947 0053338 0 +0109503 0109506 1 +0033131 0186022 0 +0098642 0098621 1 +0187867 0187873 1 +0213594 0019428 0 +0277994 0311462 0 +0272503 0038833 0 +0194958 0024298 0 +0053338 0150237 0 +0132450 0042368 0 +0059259 0059240 1 +0147529 0224423 0 +0089242 0090198 0 +0121842 0162129 0 +0306197 0315215 0 +0276570 0177931 0 +0147862 0238527 0 +0110441 0154122 0 +0130789 0174197 0 +0106742 0130873 0 +0235250 0044576 0 +0019426 0315206 0 +0007910 0217674 0 +0124813 0219464 0 +0268233 0053038 0 +0129307 0266908 0 +0109476 0195186 0 +0110367 0110361 1 +0004535 0153275 0 +0309143 0027825 0 +0217696 0198805 0 +0162129 0294362 0 +0162790 0283105 0 +0019428 0098642 0 +0279527 0072660 0 +0110332 0110350 1 +0047624 0227338 0 +0028064 0063030 0 +0236299 0236296 1 +0273895 0119207 0 +0053640 0053664 1 +0310236 0272511 0 +0134973 0097443 0 +0061485 0234369 0 +0042368 0168969 0 +0087976 0107149 0 +0227360 0221017 0 +0059750 0064855 0 +0178277 0051263 0 +0000231 0055442 0 +0037019 0024097 0 +0043421 0212688 0 +0235265 0235243 1 +0219815 0150237 0 +0235250 0235308 1 +0190652 0190955 0 +0082154 0221017 0 +0092556 0114953 0 +0028343 0284105 0 +0042980 0230706 0 +0050786 0149696 0 +0227338 0321152 0 +0178277 0119346 0 +0227059 0038833 0 +0282279 0098642 0 +0105734 0109476 0 +0037141 0037108 1 +0062892 0306498 0 +0098621 0185834 0 +0269224 0269221 1 +0058684 0185834 0 +0016716 0188561 0 +0204450 0204380 1 +0316641 0316640 1 +0089583 0326018 0 +0049603 0109464 0 +0119104 0315350 0 +0030172 0236760 0 +0190654 0235243 0 +0322401 0322425 1 +0269218 0170219 0 +0089252 0245094 0 +0064284 0216447 0 +0286462 0022491 0 +0044576 0044551 1 +0182110 0071587 0 +0002393 0012629 0 +0127305 0309556 0 +0190654 0024287 0 +0165314 0165312 1 +0294684 0165897 0 +0214904 0171104 0 +0286498 0039221 0 +0098311 0111280 0 +0325529 0221017 0 +0269791 0204068 0 +0244132 0244123 1 +0168645 0168630 1 +0194940 0047624 0 +0044796 0110289 0 +0114478 0114473 1 +0284128 0086233 0 +0228595 0228606 1 +0168630 0057425 0 +0071959 0214465 0 +0325539 0325536 1 +0176756 0143794 0 +0060724 0164372 0 +0108408 0130801 0 +0317555 0235463 0 +0005697 0264771 0 +0220694 0236514 0 +0048133 0020277 0 +0081317 0295516 0 +0213597 0220683 0 +0216486 0184356 0 +0106541 0187674 0 +0170206 0170200 1 +0185838 0236575 0 +0257178 0045242 0 +0309558 0183624 0 +0089242 0045096 0 +0270244 0270247 1 +0310326 0045103 0 +0072660 0172702 0 +0157629 0124862 0 +0007653 0007637 1 +0027825 0274281 0 +0102933 0102926 1 +0011684 0011698 1 +0092556 0115260 0 +0210888 0182811 0 +0236296 0121832 0 +0242671 0276576 0 +0133893 0168645 0 +0267890 0071587 0 +0219807 0219815 1 +0202441 0026920 0 +0176026 0174197 0 +0059123 0051748 0 +0182109 0150450 0 +0093289 0093295 1 +0308606 0194958 0 +0182473 0316640 0 +0134144 0134137 1 +0087900 0045656 0 +0199284 0309061 0 +0230706 0054980 0 +0030048 0121842 0 +0242692 0289128 0 +0033432 0033429 1 +0284128 0075271 0 +0200140 0200150 1 +0162130 0044551 0 +0059245 0059235 1 +0114473 0221296 0 +0297116 0204402 0 +0086548 0086558 1 +0297098 0184360 0 +0026649 0023841 0 +0074899 0022245 0 +0074166 0074155 1 +0112814 0267734 0 +0044796 0093111 0 +0174017 0205785 0 +0027422 0104569 0 +0290478 0063166 0 +0267890 0235629 0 +0188561 0109464 0 +0015178 0273621 0 +0092560 0319565 0 +0276089 0240477 0 +0198344 0282469 0 +0067676 0067680 1 +0309062 0086558 0 +0322970 0055202 0 +0263746 0105726 0 +0319928 0000569 0 +0034038 0119207 0 +0083933 0026649 0 +0323527 0117865 0 +0012501 0172690 0 +0238380 0238400 1 +0119238 0119239 1 +0166145 0166142 1 +0108408 0108398 1 +0204402 0204455 1 +0273621 0214904 0 +0319930 0186407 0 +0067680 0185639 0 +0221288 0188418 0 +0018779 0174853 0 +0306823 0306839 1 +0039221 0048204 0 +0162786 0162790 1 +0228606 0212688 0 +0272503 0197380 0 +0318454 0318455 1 +0190947 0080495 0 +0046686 0280664 0 +0259253 0073929 0 +0020963 0020582 0 +0259252 0259253 1 +0218593 0218590 1 +0199696 0075000 0 +0214895 0019432 0 +0098633 0098621 1 +0086383 0182819 0 +0154047 0154038 1 +0134959 0134973 1 +0290467 0229335 0 +0234320 0239194 0 +0048130 0081581 0 +0214909 0066369 0 +0020126 0293464 0 +0259252 0281217 0 +0086233 0282289 0 +0187481 0032018 0 +0227568 0179607 0 +0141916 0141901 1 +0027818 0027825 1 +0051748 0074147 0 +0235353 0235336 1 +0166083 0269791 0 +0211969 0244109 0 +0044146 0171104 0 +0150242 0204065 0 +0203592 0280664 0 +0086408 0129192 0 +0199696 0216484 0 +0190955 0320159 0 +0099593 0099603 1 +0264766 0011399 0 +0242488 0182473 0 +0174855 0171689 0 +0176756 0063033 0 +0285260 0285258 1 +0049847 0088697 0 +0131250 0120063 0 +0275993 0065470 0 +0221296 0102453 0 +0064859 0319930 0 +0114949 0220694 0 +0304521 0304519 1 +0159757 0204402 0 +0156096 0156086 1 +0096632 0040999 0 +0198337 0215795 0 +0108387 0254987 0 +0199692 0256608 0 +0055001 0306193 0 +0227860 0105726 0 +0086944 0087179 0 +0182109 0198344 0 +0317567 0126139 0 +0229335 0236528 0 +0309744 0126139 0 +0181828 0130873 0 +0295711 0295707 1 +0086383 0188413 0 +0047624 0166094 0 +0248384 0045245 0 +0220376 0309143 0 +0278139 0122320 0 +0075000 0022490 0 +0164983 0294684 0 +0005141 0043410 0 +0045001 0023608 0 +0268255 0268216 1 +0003776 0094923 0 +0286499 0090169 0 +0020871 0121033 0 +0099977 0099975 1 +0076655 0159757 0 +0011717 0011684 1 +0323535 0323527 1 +0133785 0133787 1 +0027409 0044796 0 +0087364 0087355 1 +0099973 0122316 0 +0007061 0271816 0 +0007622 0177775 0 +0037016 0097892 0 +0324803 0324802 1 +0108671 0102455 0 +0270244 0270225 1 +0085519 0066231 0 +0194928 0110332 0 +0063033 0182819 0 +0325539 0174509 0 +0227678 0037108 0 +0037019 0120063 0 +0321146 0186022 0 +0282466 0238380 0 +0137301 0164369 0 +0124306 0322427 0 +0171015 0016867 0 +0250833 0211320 0 +0050781 0177585 0 +0136675 0309143 0 +0163088 0026636 0 +0082152 0286498 0 +0249177 0108671 0 +0235629 0013450 0 +0154122 0003776 0 +0241743 0049847 0 +0110238 0170219 0 +0234366 0234373 1 +0047691 0045001 0 +0003303 0003302 1 +0282469 0282471 1 +0156329 0156327 1 +0126139 0054987 0 +0240487 0063033 0 +0114473 0316640 0 +0206776 0286499 0 +0127305 0127309 1 +0150451 0041400 0 +0039662 0039707 1 +0204065 0053339 0 +0071959 0306193 0 +0075419 0075411 1 +0147126 0147122 1 +0134973 0135023 1 +0216423 0216447 1 +0281214 0281217 1 +0017033 0171015 0 +0172027 0248384 0 +0315349 0112799 0 +0088463 0184356 0 +0168630 0133890 0 +0020608 0264766 0 +0282466 0282469 1 +0311469 0211967 0 +0200150 0008264 0 +0059273 0126139 0 +0207955 0166145 0 +0105737 0157632 0 +0088702 0292838 0 +0201825 0201828 1 +0241743 0241755 1 +0110465 0214583 0 +0098633 0098642 1 +0184360 0106731 0 +0055442 0101685 0 +0235265 0235308 1 +0024287 0024298 1 +0097443 0037016 0 +0124862 0124863 1 +0046686 0279402 0 +0018779 0260641 0 +0102930 0168969 0 +0163981 0163982 1 +0136667 0136675 1 +0230350 0049179 0 +0231414 0026645 0 +0184822 0245099 0 +0124813 0234373 0 +0102444 0227673 0 +0202427 0198337 0 +0163088 0164532 0 +0121028 0024098 0 +0267734 0267741 1 +0059754 0059750 1 +0170457 0262643 0 +0186022 0221023 0 +0174853 0007637 0 +0247402 0178075 0 +0309556 0130331 0 +0066131 0087697 0 +0124636 0059123 0 +0044146 0197380 0 +0115279 0277994 0 +0076606 0008086 0 +0045242 0281965 0 +0155409 0155407 1 +0162130 0162129 1 +0065775 0065769 1 +0024287 0087695 0 +0076591 0232317 0 +0324802 0005693 0 +0039221 0067047 0 +0222740 0198259 0 +0257756 0200150 0 +0169043 0257178 0 +0062890 0192139 0 +0215797 0215795 1 +0236762 0236760 1 +0268838 0007654 0 +0248370 0248384 1 +0185060 0109476 0 +0279529 0279568 1 +0251394 0056263 0 +0078497 0323527 0 +0133890 0133893 1 +0083933 0083935 1 +0293615 0035458 0 +0024298 0166094 0 +0055442 0282078 0 +0033303 0033300 1 +0054987 0304504 0 +0107049 0033432 0 +0048130 0048133 1 +0179607 0039215 0 +0262643 0276091 0 +0150675 0295516 0 +0232317 0176667 0 +0015111 0067680 0 +0077624 0166142 0 +0125758 0279572 0 +0231933 0188407 0 +0231427 0231441 1 +0011731 0011717 1 +0199281 0286667 0 +0214271 0065472 0 +0012501 0027422 0 +0041026 0086558 0 +0047705 0307303 0 +0094807 0048205 0 +0130825 0130829 1 +0007933 0293791 0 +0236514 0049197 0 +0188418 0188413 1 +0155396 0249194 0 +0240487 0141901 0 +0249322 0215795 0 +0204068 0003776 0 +0102447 0057463 0 +0078491 0300817 0 +0150237 0151088 0 +0030163 0168984 0 +0176667 0033429 0 +0022316 0306477 0 +0163982 0293791 0 +0087507 0119202 0 +0257756 0236575 0 +0309254 0232317 0 +0147126 0071959 0 +0265882 0265884 1 +0108731 0178075 0 +0235279 0235243 1 +0098382 0267959 0 +0123196 0182811 0 +0263746 0128934 0 +0154047 0154049 1 +0310236 0182473 0 +0187470 0187487 1 +0269655 0131250 0 +0273895 0017052 0 +0285500 0322056 0 +0195661 0195664 1 +0291519 0266908 0 +0249270 0249309 1 +0304638 0304643 1 +0187674 0134137 0 +0242692 0242678 1 +0030163 0294844 0 +0236299 0319565 0 +0284128 0284105 1 +0088471 0287179 0 +0195185 0198811 0 +0150285 0307303 0 +0033300 0293615 0 +0048205 0026928 0 +0194928 0200487 0 +0000569 0000574 1 +0101672 0101685 1 +0260633 0072660 0 +0132454 0130877 0 +0239193 0170200 0 +0271816 0271837 1 +0098213 0098224 1 +0177774 0060542 0 +0260757 0265237 0 +0170219 0118456 0 +0188561 0034043 0 +0071959 0012632 0 +0007061 0153275 0 +0273891 0126108 0 +0063150 0063166 1 +0211327 0097892 0 +0177580 0177585 1 +0293464 0302455 0 +0078491 0196166 0 +0281965 0025583 0 +0273606 0109506 0 +0027818 0310326 0 +0008264 0153267 0 +0029187 0282471 0 +0076591 0022592 0 +0060543 0107161 0 +0115279 0110289 0 +0147937 0087697 0 +0067050 0089252 0 +0185058 0054987 0 +0204402 0204450 1 +0179607 0136675 0 +0176683 0317565 0 +0272503 0076591 0 +0122316 0143793 0 +0235353 0270225 0 +0110297 0285504 0 +0039662 0188562 0 +0101211 0085519 0 +0044988 0262658 0 +0023608 0023610 1 +0295130 0187867 0 +0004535 0214909 0 +0060724 0188333 0 +0306498 0306477 1 +0130799 0078491 0 +0073989 0043421 0 +0130789 0130785 1 +0033432 0177585 0 +0110350 0110367 1 +0211322 0092556 0 +0044796 0094807 0 +0059273 0059245 1 +0007051 0153215 0 +0265884 0177774 0 +0207955 0053081 0 +0041008 0040999 1 +0285500 0217707 0 +0020958 0088697 0 +0222730 0260757 0 +0110234 0171689 0 +0007622 0007602 1 +0294362 0285500 0 +0137301 0170457 0 +0286498 0147520 0 +0027818 0218593 0 +0075419 0267832 0 +0284166 0284188 1 +0150675 0293615 0 +0190947 0130873 0 +0219362 0285260 0 +0306241 0035624 0 +0002379 0002393 1 +0204870 0204876 1 +0022491 0022490 1 +0265269 0265268 1 +0309259 0119207 0 +0174509 0198421 0 +0174509 0309062 0 +0076591 0113653 0 +0120059 0154128 0 +0120059 0120063 1 +0013442 0046686 0 +0234105 0286499 0 +0211967 0052796 0 +0177223 0151852 0 +0112814 0081587 0 +0286494 0286499 1 +0035624 0034038 0 +0268256 0268233 1 +0284128 0284102 1 +0002379 0253707 0 +0007910 0089252 0 +0260751 0295516 0 +0285260 0033131 0 +0211322 0211327 1 +0190652 0007754 0 +0125758 0294844 0 +0306193 0306197 1 +0025768 0045783 0 +0089211 0212688 0 +0136667 0170050 0 +0075000 0219804 0 +0198421 0268838 0 +0050781 0082667 0 +0254987 0236760 0 +0275988 0275993 1 +0113653 0150450 0 +0260933 0309744 0 +0176756 0306498 0 +0171113 0171104 1 +0238400 0238405 1 +0318455 0318449 1 +0023841 0119111 0 +0245808 0248370 0 +0110365 0217696 0 +0020640 0259253 0 +0267960 0299367 0 +0284166 0109503 0 +0064859 0033429 0 +0245808 0121842 0 +0265268 0112799 0 +0074878 0211327 0 +0070203 0087190 0 +0154106 0216486 0 +0277994 0025775 0 +0082667 0195281 0 +0090198 0090139 1 +0164983 0119105 0 +0053443 0238400 0 +0049597 0257681 0 +0072151 0282079 0 +0294359 0238532 0 +0253675 0018417 0 +0073932 0110465 0 +0108671 0124300 0 +0315215 0078491 0 +0116258 0129296 0 +0234106 0235265 0 +0214909 0214895 1 +0035631 0073929 0 +0141901 0074766 0 +0011731 0298727 0 +0214909 0070208 0 +0130825 0048205 0 +0137028 0295675 0 +0061097 0061095 1 +0016035 0016043 1 +0306227 0306241 1 +0159759 0178068 0 +0174200 0299367 0 +0071959 0110312 0 +0043410 0181653 0 +0125758 0066365 0 +0257172 0181276 0 +0235353 0213594 0 +0315215 0141907 0 +0280686 0221296 0 +0050781 0286498 0 +0309746 0163982 0 +0290478 0227678 0 +0037019 0216423 0 +0114445 0087186 0 +0187487 0130801 0 +0087507 0265884 0 +0078491 0044229 0 +0111278 0218593 0 +0116270 0116291 1 +0101211 0101208 1 +0181315 0086408 0 +0202427 0078491 0 +0317565 0317567 1 +0311470 0248370 0 +0016735 0038833 0 +0282466 0097278 0 +0049529 0174197 0 +0198272 0198259 1 +0254986 0061097 0 +0087697 0062890 0 +0016043 0150242 0 +0239193 0137028 0 +0269796 0269791 1 +0242488 0253675 0 +0123194 0041388 0 +0295675 0249323 0 +0126099 0098642 0 +0241743 0051748 0 +0147937 0198421 0 +0287181 0287179 1 +0087186 0314537 0 +0306231 0066365 0 +0026647 0309556 0 +0267877 0066131 0 +0214507 0214494 1 +0122316 0065775 0 +0276089 0049530 0 +0187487 0239516 0 +0239533 0095049 0 +0284102 0150237 0 +0013710 0245808 0 +0235243 0235308 1 +0239533 0239516 1 +0281214 0005141 0 +0326020 0198272 0 +0143793 0143794 1 +0141907 0087695 0 +0045103 0094923 0 +0015178 0293615 0 +0206780 0034283 0 +0260945 0072660 0 +0190652 0004536 0 +0295711 0295675 1 +0299368 0299367 1 +0159759 0026649 0 +0044578 0242670 0 +0092571 0273895 0 +0217706 0075003 0 +0025584 0265253 0 +0121832 0085528 0 +0294840 0128934 0 +0049603 0124813 0 +0020277 0211322 0 +0244109 0244123 1 +0260757 0106541 0 +0059422 0059419 1 +0278139 0297116 0 +0198805 0198811 1 +0020640 0072647 0 +0183624 0133893 0 +0293465 0268256 0 +0308606 0023610 0 +0217836 0155409 0 +0293615 0016867 0 +0018417 0144136 0 +0182473 0081581 0 +0323464 0323480 1 +0122316 0242678 0 +0320161 0066369 0 +0219464 0039215 0 +0249297 0037016 0 +0280686 0102459 0 +0099977 0177580 0 +0198415 0198421 1 +0059123 0075417 0 +0154122 0154106 1 +0007910 0221023 0 +0072660 0171113 0 +0061504 0257178 0 +0026924 0026920 1 +0166145 0130789 0 +0267734 0108408 0 +0199692 0196944 0 +0008257 0201825 0 +0198259 0198274 1 +0210889 0164372 0 +0045594 0298724 0 +0195661 0089252 0 +0039215 0025788 0 +0050966 0155415 0 +0110297 0087902 0 +0322427 0322425 1 +0020867 0211329 0 +0137877 0086260 0 +0111280 0094807 0 +0130801 0177586 0 +0260570 0322085 0 +0086233 0086383 0 +0041025 0041022 1 +0078497 0078491 1 +0281968 0000231 0 +0086259 0086260 1 +0055202 0008257 0 +0073932 0063166 0 +0000554 0000574 1 +0216418 0265882 0 +0150237 0240477 0 +0262658 0037019 0 +0074899 0074878 1 +0325536 0304521 0 +0311448 0097896 0 +0119252 0119238 1 +0227360 0066022 0 +0067573 0283042 0 +0087978 0094807 0 +0059259 0249177 0 +0078497 0215795 0 +0222740 0041022 0 +0049197 0132450 0 +0322056 0075411 0 +0242473 0177225 0 +0172690 0172677 1 +0275977 0137297 0 +0078721 0066369 0 +0074878 0074899 1 +0102455 0267877 0 +0295130 0309143 0 +0075003 0276140 0 +0080508 0066365 0 +0141907 0141901 1 +0194958 0011403 0 +0286646 0023608 0 +0114953 0210889 0 +0081587 0081581 1 +0134959 0188418 0 +0231427 0230704 0 +0295707 0086383 0 +0008083 0061097 0 +0012627 0012629 1 +0207307 0102663 0 +0111280 0143794 0 +0119346 0119349 1 +0194958 0315215 0 +0239194 0013747 0 +0285260 0101672 0 +0307937 0273895 0 +0185834 0280186 0 +0156954 0238405 0 +0016010 0098397 0 +0156327 0190652 0 +0269221 0269224 1 +0168984 0300817 0 +0042791 0033285 0 +0130982 0254987 0 +0285258 0008086 0 +0118540 0047624 0 +0119238 0230356 0 +0174200 0154106 0 +0298727 0013442 0 +0281826 0216484 0 +0151313 0300419 0 +0016052 0016035 1 +0087976 0136667 0 +0268263 0268256 1 +0273891 0063033 0 +0110456 0311469 0 +0107375 0081318 0 +0156943 0188561 0 +0314537 0314536 1 +0020474 0020572 0 +0168973 0112814 0 +0170206 0082671 0 +0234373 0207955 0 +0014997 0185838 0 +0198259 0265268 0 +0136675 0050966 0 +0147937 0168987 0 +0236295 0029174 0 +0115260 0235646 0 +0033303 0256607 0 +0200605 0200564 1 +0016043 0016025 1 +0015178 0072647 0 +0263773 0048130 0 +0231934 0126108 0 +0013717 0260751 0 +0314536 0314540 1 +0276448 0028064 0 +0110312 0044795 0 +0047687 0083935 0 +0306823 0190947 0 +0123194 0286646 0 +0098382 0098397 1 +0121842 0267836 0 +0285260 0205785 0 +0182107 0221023 0 +0306498 0140618 0 +0108030 0053094 0 +0285504 0202994 0 +0194940 0121569 0 +0201828 0223589 0 +0278139 0278136 1 +0130325 0188562 0 +0130785 0238532 0 +0260751 0260757 1 +0309558 0309556 1 +0104645 0104569 1 +0223589 0223582 1 +0248384 0248370 1 +0229204 0292838 0 +0124642 0155398 0 +0042969 0097277 0 +0118455 0118456 1 +0033432 0130331 0 +0089212 0045783 0 +0272511 0065769 0 +0227678 0286462 0 +0304240 0304245 1 +0096611 0096587 1 +0293799 0029060 0 +0115260 0216423 0 +0037016 0037019 1 +0063033 0071587 0 +0263773 0181315 0 +0198421 0279527 0 +0209298 0239193 0 +0118455 0232313 0 +0025109 0025073 1 +0245808 0214904 0 +0020479 0170460 0 +0198421 0111278 0 +0268255 0260933 0 +0063617 0114449 0 +0308606 0070203 0 +0323487 0219469 0 +0265882 0113653 0 +0166083 0284188 0 +0039707 0092526 0 +0214507 0044229 0 +0109506 0137877 0 +0267836 0267832 1 +0188410 0188418 1 +0047687 0047693 1 +0253507 0304245 0 +0040531 0243115 0 +0060542 0061504 0 +0018779 0087697 0 +0201800 0200998 0 +0033136 0033131 1 +0235308 0110434 0 +0268827 0235353 0 +0104569 0104592 1 +0140616 0200998 0 +0020582 0020572 1 +0239533 0155434 0 +0177780 0177770 1 +0051251 0083935 0 +0307263 0106742 0 +0042791 0168630 0 +0062890 0126696 0 +0126108 0186022 0 +0156130 0260633 0 +0030172 0109506 0 +0045783 0174020 0 +0133788 0133785 1 +0238440 0168124 0 +0176667 0176683 1 +0005697 0005693 1 +0040999 0155407 0 +0121569 0101538 0 +0147937 0159757 0 +0007653 0007654 1 +0070203 0014995 0 +0271837 0271816 1 +0260945 0309558 0 +0083935 0265268 0 +0308559 0150242 0 +0238428 0168973 0 +0074878 0101553 0 +0254570 0210394 0 +0186407 0121842 0 +0170219 0210889 0 +0186409 0200487 0 +0029060 0077636 0 +0101114 0300817 0 +0137879 0188324 0 +0124306 0130789 0 +0202434 0242692 0 +0124300 0187674 0 +0211967 0060751 0 +0257681 0307263 0 +0243135 0093113 0 +0135023 0265269 0 +0276459 0276448 1 +0070203 0093113 0 +0121842 0166145 0 +0089252 0235463 0 +0300419 0251395 0 +0254986 0236528 0 +0322056 0322052 1 +0137515 0137512 1 +0181655 0198811 0 +0300428 0025073 0 +0013717 0222730 0 +0196169 0196166 1 +0077624 0084948 0 +0287179 0287181 1 +0085528 0224423 0 +0060722 0049597 0 +0104569 0104645 1 +0194928 0194940 1 +0053081 0053094 1 +0140616 0174853 0 +0200998 0040532 0 +0319170 0231447 0 +0257756 0257764 1 +0156130 0156102 1 +0060751 0220376 0 +0026924 0200999 0 +0026636 0034283 0 +0170460 0074774 0 +0073989 0073932 0 +0260641 0022301 0 +0297098 0107049 0 +0044146 0196166 0 +0119238 0054979 0 +0038831 0273606 0 +0110365 0229204 0 +0200486 0067573 0 +0314537 0125715 0 +0054979 0171690 0 +0322970 0199692 0 +0144126 0230706 0 +0008086 0304628 0 +0317565 0216486 0 +0202434 0202427 1 +0030048 0030054 1 +0276448 0094945 0 +0102447 0048309 0 +0322052 0322085 1 +0130877 0181315 0 +0267959 0277994 0 +0018417 0150285 0 +0007654 0007618 1 +0025089 0204402 0 +0088463 0063030 0 +0243115 0294840 0 +0074899 0264766 0 +0101109 0223589 0 +0319930 0259253 0 +0184360 0282079 0 +0293791 0125758 0 +0089582 0099603 0 +0102930 0060542 0 +0059769 0059754 1 +0127309 0185058 0 +0177774 0231933 0 +0188561 0188562 1 +0188407 0188413 1 +0269791 0067562 0 +0300817 0182473 0 +0133787 0047626 0 +0266908 0285258 0 +0221999 0322970 0 +0326018 0217707 0 +0119682 0043421 0 +0238430 0150451 0 +0180814 0221017 0 +0198344 0242488 0 +0317567 0048311 0 +0151074 0236514 0 +0265884 0293464 0 +0114473 0027825 0 +0321146 0321152 1 +0323464 0323466 1 +0165899 0235266 0 +0317552 0267836 0 +0169041 0323480 0 +0015122 0235626 0 +0162786 0154049 0 +0030163 0195281 0 +0242678 0016858 0 +0111510 0111511 1 +0137515 0098621 0 +0181825 0196169 0 +0319568 0168127 0 +0144136 0026649 0 +0015181 0045242 0 +0309061 0227338 0 +0002379 0150450 0 +0084944 0084948 1 +0124817 0321146 0 +0063004 0306823 0 +0097436 0323480 0 +0048130 0273895 0 +0182471 0227568 0 +0325529 0048267 0 +0126108 0061504 0 +0133788 0024298 0 +0088463 0227574 0 +0322970 0087978 0 +0056257 0011399 0 +0045656 0257178 0 +0235126 0188407 0 +0041012 0041008 1 +0002379 0304643 0 +0075274 0306845 0 +0214913 0214909 1 +0025583 0260570 0 +0163088 0306241 0 +0286667 0101208 0 +0292813 0292837 1 +0059240 0059259 1 +0027422 0086259 0 +0293799 0176667 0 +0140618 0110238 0 +0198337 0198344 1 +0282279 0048130 0 +0060722 0030172 0 +0319565 0000559 0 +0000233 0201422 0 +0019432 0315350 0 +0016735 0037016 0 +0071583 0137301 0 +0168630 0063641 0 +0322425 0322401 1 +0297116 0029052 0 +0076665 0183624 0 +0292813 0029187 0 +0108398 0108408 1 +0168987 0201825 0 +0083933 0106273 0 +0174853 0174855 1 +0158958 0187867 0 +0041397 0087900 0 +0015307 0117865 0 +0235465 0235461 1 +0098409 0154047 0 +0102674 0024287 0 +0211327 0211322 1 +0063033 0219464 0 +0168128 0168127 1 +0002393 0294685 0 +0048130 0309746 0 +0054979 0143794 0 +0293613 0188234 0 +0242473 0040532 0 +0188413 0228606 0 +0132451 0221017 0 +0270247 0270225 1 +0286494 0004530 0 +0106742 0095049 0 +0061485 0089211 0 +0083935 0254986 0 +0069008 0118536 0 +0124642 0195185 0 +0025769 0025775 1 +0281826 0077636 0 +0240487 0008257 0 +0080508 0129192 0 +0168969 0049603 0 +0037016 0200605 0 +0020277 0317555 0 +0048311 0286498 0 +0088697 0088702 1 +0044146 0044988 0 +0153215 0153212 1 +0300874 0293799 0 +0008257 0033285 0 +0061095 0007757 0 +0172702 0172677 1 +0025584 0003303 0 +0124306 0124300 1 +0204464 0204380 1 +0170219 0097436 0 +0228606 0176756 0 +0306845 0071959 0 +0177223 0113653 0 +0137297 0204068 0 +0108408 0164983 0 +0286494 0043144 0 +0067050 0168645 0 +0075274 0227859 0 +0176026 0027422 0 +0276448 0076606 0 +0221999 0163088 0 +0002379 0063008 0 +0114473 0211320 0 +0087355 0076576 0 +0110365 0110350 1 +0182811 0050781 0 +0066532 0092560 0 +0257700 0122320 0 +0282469 0014995 0 +0285258 0018780 0 +0087848 0087902 1 +0033285 0023610 0 +0265253 0016035 0 +0076605 0249322 0 +0265884 0072183 0 +0183624 0183626 1 +0134150 0134144 1 +0012501 0102444 0 +0066237 0066231 1 +0049603 0217706 0 +0219807 0267890 0 +0005123 0322056 0 +0132451 0132450 1 +0102455 0102680 0 +0171104 0300865 0 +0107369 0107375 1 +0129296 0286646 0 +0205783 0053081 0 +0165314 0057463 0 +0020290 0147520 0 +0171104 0106267 0 +0219464 0086233 0 +0230704 0300419 0 +0047626 0049530 0 +0046686 0046680 1 +0311469 0311470 1 +0181315 0235336 0 +0042392 0039250 0 +0184864 0147933 0 +0086536 0086558 1 +0022491 0064859 0 +0045594 0249177 0 +0285260 0127305 0 +0235465 0204068 0 +0201825 0323535 0 +0235626 0137027 0 +0285504 0285500 1 +0071959 0057462 0 +0004530 0279527 0 +0005697 0257178 0 +0286466 0051749 0 +0306241 0051251 0 +0306477 0107149 0 +0005141 0013717 0 +0157629 0210394 0 +0186407 0081318 0 +0007757 0176763 0 +0307263 0211327 0 +0235126 0163982 0 +0080098 0109503 0 +0147865 0056263 0 +0304638 0162786 0 +0131255 0041012 0 +0087190 0087186 1 +0306477 0306498 1 +0182471 0092588 0 +0267832 0108678 0 +0156327 0274944 0 +0181262 0164369 0 +0022301 0022316 1 +0253675 0182811 0 +0033429 0287181 0 +0124863 0102455 0 +0181655 0176756 0 +0207307 0267877 0 +0187470 0063617 0 +0326937 0319172 0 +0087976 0135839 0 +0124863 0124862 1 +0315215 0195664 0 +0206776 0319930 0 +0281828 0110361 0 +0266908 0025073 0 +0220683 0133890 0 +0078721 0023610 0 +0085528 0198032 0 +0061504 0202970 0 +0235353 0020640 0 +0187671 0198337 0 +0101211 0280664 0 +0038833 0147862 0 +0059249 0059259 1 +0018421 0165314 0 +0110289 0187674 0 +0185058 0133893 0 +0315348 0015181 0 +0162129 0013744 0 +0286466 0219469 0 +0310226 0310236 1 +0026649 0257681 0 +0280139 0205785 0 +0022491 0174842 0 +0126133 0262658 0 +0235126 0235128 1 +0039648 0039707 1 +0206776 0298724 0 +0045594 0069008 0 +0286646 0020643 0 +0077624 0166083 0 +0026920 0026928 1 +0049603 0077624 0 +0211320 0029187 0 +0016867 0016858 1 +0170219 0267832 0 +0186407 0282078 0 +0092590 0092588 1 +0109506 0198805 0 +0282282 0195185 0 +0064862 0064859 1 +0207307 0112814 0 +0034043 0034038 1 +0076605 0216525 0 +0244109 0244132 1 +0097277 0159757 0 +0031134 0162129 0 +0216486 0216484 1 +0061097 0073932 0 +0026645 0081587 0 +0080495 0276576 0 +0249270 0249297 1 +0129307 0129296 1 +0236295 0265882 0 +0212688 0151088 0 +0073929 0073932 1 +0078721 0026928 0 +0067047 0238440 0 +0128934 0198274 0 +0318449 0318455 1 +0214507 0072183 0 +0249297 0072660 0 +0238400 0238413 1 +0040531 0016043 0 +0217662 0149696 0 +0156943 0231441 0 +0102459 0317555 0 +0041022 0201828 0 +0201825 0025786 0 +0234320 0214583 0 +0194960 0075000 0 +0204464 0204455 1 +0323480 0025109 0 +0290467 0063127 0 +0266908 0266905 1 +0227859 0216423 0 +0178277 0187873 0 +0087501 0245807 0 +0231414 0049530 0 +0306231 0238440 0 +0051251 0046686 0 +0150290 0150285 1 +0235279 0235266 1 +0055200 0022245 0 +0308555 0292838 0 +0323487 0323466 1 +0309062 0209295 0 +0195664 0195661 1 +0124642 0016556 0 +0143347 0143353 1 +0221017 0062890 0 +0067050 0278139 0 +0099973 0099975 1 +0264771 0264766 1 +0170457 0170456 1 +0085528 0102674 0 +0058690 0016735 0 +0275996 0087697 0 +0294840 0265268 0 +0130877 0037141 0 +0185058 0282289 0 +0326020 0101553 0 +0278139 0011684 0 +0039221 0264771 0 +0126706 0025775 0 +0044795 0308559 0 +0206782 0236760 0 +0040531 0172702 0 +0238413 0060542 0 +0031996 0045595 0 +0101672 0200486 0 +0022490 0157340 0 +0136675 0136667 1 +0183626 0183624 1 +0084944 0115260 0 +0245808 0102933 0 +0090185 0090169 1 +0188333 0205783 0 +0232317 0232313 1 +0107149 0264771 0 +0176683 0176667 1 +0119111 0194958 0 +0228595 0273891 0 +0215797 0137437 0 +0037108 0023610 0 +0153267 0055001 0 +0285500 0107375 0 +0150237 0204068 0 +0081581 0306227 0 +0197385 0128910 0 +0260933 0125715 0 +0263773 0188410 0 +0137419 0286499 0 +0042368 0095054 0 +0020572 0156321 0 +0292823 0114473 0 +0162129 0106540 0 +0151852 0186013 0 +0124817 0015181 0 +0073929 0306498 0 +0061095 0221017 0 +0300077 0247402 0 +0027825 0147529 0 +0106731 0214583 0 +0060542 0205783 0 +0025584 0019432 0 +0064855 0064862 1 +0274281 0074147 0 +0119683 0324803 0 +0156484 0190947 0 +0265237 0265256 1 +0098317 0098303 1 +0230706 0230704 1 +0090198 0315348 0 +0005693 0056257 0 +0107149 0283050 0 +0245808 0245807 1 +0001572 0320159 0 +0020479 0018417 0 +0279568 0279572 1 +0143347 0319565 0 +0035624 0093289 0 +0210384 0262643 0 +0024287 0097887 0 +0154038 0169043 0 +0157835 0114449 0 +0081318 0188418 0 +0099603 0099593 1 +0156098 0156096 1 +0133893 0086944 0 +0053094 0276138 0 +0101208 0164532 0 +0045001 0040532 0 +0282466 0119207 0 +0080091 0321152 0 +0045656 0197385 0 +0168969 0061095 0 +0171689 0058684 0 +0181825 0284102 0 +0029187 0029174 1 +0263773 0087976 0 +0309061 0309062 1 +0061097 0102459 0 +0284166 0185058 0 +0124813 0067676 0 +0265256 0265237 1 +0050786 0050781 1 +0276140 0276144 1 +0307303 0178277 0 +0016052 0016025 1 +0044578 0293464 0 +0150451 0223582 0 +0043144 0310236 0 +0074774 0063152 0 +0239516 0239193 0 +0238532 0176683 0 +0011399 0054987 0 +0279568 0276448 0 +0085528 0061097 0 +0294685 0071959 0 +0282078 0155415 0 +0294655 0214583 0 +0300070 0293465 0 +0129192 0298724 0 +0098633 0072151 0 +0025584 0025583 1 +0164532 0157828 0 +0196944 0306498 0 +0108387 0108398 1 +0075417 0075411 1 +0104645 0036686 0 +0177780 0177775 1 +0033300 0227568 0 +0135839 0007061 0 +0174842 0214494 0 +0195186 0195185 1 +0280126 0166145 0 +0298724 0118456 0 +0283103 0184822 0 +0188418 0055455 0 +0017049 0030172 0 +0048266 0048267 1 +0000231 0022589 0 +0289130 0267832 0 +0182473 0182471 1 +0008083 0022247 0 +0008086 0267960 0 +0016010 0016052 1 +0247402 0320159 0 +0011399 0125715 0 +0137512 0137515 1 +0213594 0213597 1 +0090169 0090198 1 +0295130 0045656 0 +0306198 0235353 0 +0292837 0292838 1 +0231934 0231933 1 +0131250 0087507 0 +0059123 0048309 0 +0168969 0236295 0 +0124862 0254987 0 +0216486 0034284 0 +0205783 0076606 0 +0174197 0124306 0 +0077624 0077636 1 +0220376 0137515 0 +0262658 0281826 0 +0281826 0281828 1 +0188231 0284105 0 +0060751 0045594 0 +0042392 0310327 0 +0249177 0081317 0 +0219804 0112814 0 +0099975 0099973 1 +0063008 0182811 0 +0285260 0192139 0 +0053094 0267836 0 +0287178 0016556 0 +0235629 0044796 0 +0181262 0181276 1 +0114445 0020474 0 +0110350 0117863 0 +0268263 0107161 0 +0071959 0033303 0 +0325529 0219376 0 +0088702 0088697 1 +0178068 0194940 0 +0272503 0273621 0 +0098382 0011399 0 +0293464 0266908 0 +0086259 0234369 0 +0262643 0322425 0 +0093295 0260641 0 +0204870 0078491 0 +0292823 0292838 1 +0182110 0176007 0 +0097278 0016867 0 +0302850 0153267 0 +0015181 0168127 0 +0184360 0020871 0 +0247402 0306197 0 +0325529 0325536 1 +0000569 0000559 1 +0323480 0323487 1 +0086259 0110367 0 +0019428 0019432 1 +0268838 0295130 0 +0325536 0325529 1 +0254570 0244132 0 +0315349 0019432 0 +0062892 0063617 0 +0144126 0151088 0 +0181276 0192139 0 +0223582 0169043 0 +0019432 0088471 0 +0118536 0137877 0 +0235308 0283103 0 +0172702 0172675 1 +0016052 0022301 0 +0200564 0200555 1 +0307258 0135023 0 +0236532 0050966 0 +0043174 0202970 0 +0098224 0131255 0 +0279409 0279400 1 +0041388 0249323 0 +0137301 0017033 0 +0319565 0129192 0 +0304245 0304236 1 +0111280 0281965 0 +0276570 0157629 0 +0033131 0283105 0 +0126099 0269791 0 +0273621 0046686 0 +0304519 0304517 1 +0266908 0132454 0 +0028064 0028063 1 +0004538 0087978 0 +0240486 0240487 1 +0066237 0066215 1 +0309062 0273891 0 +0043421 0151314 0 +0024298 0217707 0 +0100476 0102926 0 +0197385 0197380 1 +0162790 0107369 0 +0060722 0077636 0 +0207955 0207953 1 +0130825 0214271 0 +0000231 0211329 0 +0238400 0055442 0 +0045001 0214583 0 +0267959 0267960 1 +0291519 0291512 1 +0102459 0111510 0 +0231447 0182473 0 +0207307 0018421 0 +0126108 0306227 0 +0003776 0049529 0 +0236575 0323535 0 +0025775 0244123 0 +0025091 0025073 1 +0220694 0220683 1 +0053094 0224413 0 +0307258 0309556 0 +0124863 0238400 0 +0227050 0116432 0 +0174020 0073932 0 +0251394 0251395 1 +0020867 0170055 0 +0235651 0147520 0 +0242671 0156943 0 +0114949 0285258 0 +0210394 0194940 0 +0033432 0020958 0 +0128910 0128934 1 +0137515 0219464 0 +0086944 0265269 0 +0050786 0121028 0 +0130331 0260945 0 +0168984 0248370 0 +0165312 0165314 1 +0281214 0055001 0 +0187865 0308607 0 +0271837 0164536 0 +0025783 0128913 0 +0124306 0040532 0 +0016710 0032018 0 +0227059 0227050 1 +0238532 0238527 1 +0309744 0180817 0 +0163089 0016858 0 +0274944 0171113 0 +0245807 0155398 0 +0109503 0110434 0 +0307938 0137297 0 +0025584 0286466 0 +0035458 0088471 0 +0047626 0219958 0 +0119104 0119105 1 +0267741 0022316 0 +0093111 0143793 0 +0133787 0133785 1 +0220683 0220694 1 +0073989 0249323 0 +0306363 0097278 0 +0118540 0118536 1 +0159759 0011717 0 +0076605 0089583 0 +0207955 0077624 0 +0198805 0147862 0 +0204068 0097278 0 +0259253 0156102 0 +0149709 0147937 0 +0176756 0293615 0 +0048205 0121571 0 +0066131 0061504 0 +0051748 0055202 0 +0257756 0099620 0 +0149709 0125715 0 +0066022 0109464 0 +0309746 0309744 1 +0219807 0219808 1 +0231441 0162130 0 +0179607 0153275 0 +0275977 0275993 1 +0116417 0276570 0 +0025788 0177586 0 +0214276 0150237 0 +0188407 0112814 0 +0011701 0034283 0 +0056263 0171104 0 +0016710 0234366 0 +0147865 0086944 0 +0326933 0051251 0 +0149786 0306231 0 +0174842 0254570 0 +0310236 0048267 0 +0174855 0276091 0 +0293799 0071583 0 +0023835 0309746 0 +0137168 0026647 0 +0135023 0286646 0 +0178075 0319565 0 +0066532 0170206 0 +0219815 0260945 0 +0033432 0092505 0 +0130801 0297098 0 +0273621 0273606 1 +0104592 0104581 1 +0308555 0053038 0 +0003776 0155434 0 +0181653 0181655 1 +0260641 0076655 0 +0297098 0157629 0 +0089583 0024287 0 +0235250 0025769 0 +0147933 0032004 0 +0236295 0236296 1 +0130873 0042392 0 +0150678 0080098 0 +0107369 0019428 0 +0049529 0015178 0 +0043144 0043174 1 +0219376 0065769 0 +0087697 0087695 1 +0198033 0130331 0 +0304635 0109506 0 +0314536 0204870 0 +0109506 0012492 0 +0082154 0057425 0 +0178075 0123194 0 +0076576 0214271 0 +0033131 0013744 0 +0185838 0234369 0 +0110332 0125758 0 +0181655 0015305 0 +0171689 0319565 0 +0214271 0063127 0 +0087900 0087845 1 +0111510 0090139 0 +0315350 0314536 0 +0214276 0308559 0 +0294669 0020867 0 +0119347 0119346 1 +0181828 0166094 0 +0095049 0176667 0 +0188418 0124863 0 +0259253 0291512 0 +0042368 0007602 0 +0024287 0260933 0 +0016710 0016716 1 +0094945 0094923 1 +0053037 0286462 0 +0077624 0135023 0 +0089212 0174200 0 +0053094 0051251 0 +0101553 0156943 0 +0030048 0314540 0 +0133893 0162786 0 +0100476 0071959 0 +0034038 0156086 0 +0308559 0216418 0 +0040531 0310234 0 +0075426 0278136 0 +0245807 0151853 0 +0155434 0236299 0 +0306197 0109503 0 +0199281 0019432 0 +0052796 0063030 0 +0119682 0293791 0 +0038245 0186013 0 +0176763 0278000 0 +0063030 0063033 1 +0112799 0322401 0 +0306477 0016052 0 +0049846 0106267 0 +0235279 0235265 1 +0279400 0279409 1 +0053339 0314537 0 +0105737 0119347 0 +0179604 0075274 0 +0044146 0048311 0 +0110456 0066532 0 +0227574 0306197 0 +0168969 0320159 0 +0282471 0282466 1 +0039257 0099593 0 +0015181 0276459 0 +0073932 0114949 0 +0040531 0292838 0 +0015307 0185058 0 +0235626 0025786 0 +0024098 0018780 0 +0235646 0048266 0 +0116291 0025089 0 +0090185 0315206 0 +0176756 0101211 0 +0217707 0217696 1 +0267959 0092556 0 +0089252 0063617 0 +0265247 0265253 1 +0295707 0295711 1 +0066215 0066231 1 +0028851 0151853 0 +0063008 0045245 0 +0282469 0230706 0 +0026636 0094945 0 +0269794 0238532 0 +0037016 0053664 0 +0316640 0306231 0 +0059245 0059255 1 +0150285 0035624 0 +0202986 0202994 1 +0087902 0087848 1 +0092574 0092556 0 +0216486 0210384 0 +0087190 0309143 0 +0028859 0267959 0 +0011717 0012627 0 +0150678 0206782 0 +0254568 0098633 0 +0196947 0196944 1 +0265882 0179607 0 +0244111 0117863 0 +0018421 0264766 0 +0227338 0067573 0 +0108030 0154049 0 +0043410 0108730 0 +0102455 0102453 1 +0128913 0128934 1 +0174197 0070203 0 +0235651 0227678 0 +0187674 0031134 0 +0133893 0133890 1 +0309062 0024298 0 +0163981 0254986 0 +0093111 0076605 0 +0276144 0276138 1 +0319170 0319174 1 +0188410 0135023 0 +0018417 0109464 0 +0294685 0294684 1 +0177931 0127309 0 +0007754 0076605 0 +0284166 0047705 0 +0116291 0081587 0 +0047687 0047691 1 +0188231 0188234 1 +0253507 0064284 0 +0067680 0067676 1 +0306198 0181825 0 +0131250 0131255 1 +0306845 0178277 0 +0094808 0174200 0 +0242488 0207953 0 +0045245 0184360 0 +0157835 0108671 0 +0306498 0149786 0 +0172677 0302850 0 +0256608 0081318 0 +0147933 0281965 0 +0242464 0280186 0 +0299367 0097892 0 +0051251 0051263 1 +0065769 0276570 0 +0188410 0188413 1 +0107049 0109506 0 +0287178 0287179 1 +0284188 0214469 0 +0176763 0066369 0 +0040532 0169041 0 +0273895 0106541 0 +0065775 0057463 0 +0306363 0028851 0 +0171015 0171006 1 +0280664 0280686 1 +0214904 0214913 1 +0026647 0039707 0 +0020277 0020290 1 +0011731 0033131 0 +0053339 0008264 0 +0045657 0109503 0 +0299368 0125758 0 +0028343 0194928 0 +0200487 0306845 0 +0177770 0177774 1 +0061097 0168973 0 +0206780 0206776 1 +0201828 0322056 0 +0129296 0216484 0 +0232317 0235646 0 +0106260 0239516 0 +0137437 0074899 0 +0269794 0222730 0 +0273895 0186022 0 +0228595 0119347 0 +0150678 0239516 0 +0204402 0204464 1 +0181316 0181315 1 +0090198 0045096 0 +0017033 0017049 1 +0049847 0306197 0 +0093289 0201828 0 +0059248 0059245 1 +0057462 0063921 0 +0174496 0184864 0 +0276448 0026647 0 +0017032 0017049 1 +0133890 0110297 0 +0087501 0050965 0 +0171113 0156477 0 +0319928 0171690 0 +0281217 0280686 0 +0213597 0061095 0 +0022316 0016716 0 +0205785 0028350 0 +0309062 0309061 1 +0097277 0317567 0 +0231447 0231441 1 +0170456 0170457 1 +0137301 0236760 0 +0123196 0236760 0 +0061485 0078497 0 +0269796 0042791 0 +0239194 0188231 0 +0067676 0157835 0 +0136667 0157828 0 +0242671 0050966 0 +0087507 0028851 0 +0201396 0029052 0 +0323487 0323464 1 +0048204 0076605 0 +0304519 0202986 0 +0216418 0216447 1 +0219376 0301732 0 +0111280 0223582 0 +0223582 0235465 0 +0099977 0099973 1 +0071587 0111511 0 +0269639 0269655 1 +0302451 0176667 0 +0176756 0047626 0 +0110441 0034283 0 +0013450 0013442 1 +0063143 0239194 0 +0267832 0122316 0 +0124813 0013450 0 +0078721 0053094 0 +0095054 0095049 1 +0198337 0018780 0 +0294359 0294362 1 +0016735 0016710 1 +0094945 0106541 0 +0149779 0219808 0 +0268827 0268838 1 +0235336 0235353 1 +0063921 0177223 0 +0284128 0282079 0 +0242488 0232313 0 +0223589 0227574 0 +0038223 0231934 0 +0072183 0072151 1 +0023839 0023841 1 +0267890 0267877 1 +0087179 0281965 0 +0198337 0260641 0 +0127305 0112814 0 +0016025 0051251 0 +0124642 0124636 1 +0081587 0110297 0 +0244109 0171104 0 +0283055 0109476 0 +0039214 0022247 0 +0015305 0304628 0 +0130825 0229206 0 +0092560 0262658 0 +0000559 0000569 1 +0134150 0102926 0 +0039662 0015122 0 +0280664 0281828 0 +0310326 0310327 1 +0119104 0119238 0 +0206776 0239533 0 +0267960 0234106 0 +0086203 0102453 0 +0216484 0216486 1 +0059127 0102447 0 +0124817 0066369 0 +0315350 0315349 1 +0274946 0274944 1 +0060724 0216486 0 +0048309 0012501 0 +0055200 0055202 1 +0254568 0254570 1 +0147520 0130799 0 +0025091 0291512 0 +0187865 0097278 0 +0047691 0043421 0 +0185058 0188333 0 +0155434 0155415 1 +0219807 0201802 0 +0324802 0137485 0 +0186409 0308555 0 +0163096 0163089 1 +0163096 0163088 1 +0277994 0199692 0 +0036686 0309061 0 +0322052 0108678 0 +0087179 0087190 1 +0130977 0130978 1 +0235463 0177919 0 +0168645 0031996 0 +0055001 0187481 0 +0315350 0235336 0 +0049846 0276448 0 +0257700 0257681 1 +0256608 0150242 0 +0051748 0020608 0 +0098317 0098311 1 +0018417 0018421 1 +0106540 0164530 0 +0300865 0063617 0 +0129190 0194928 0 +0085528 0020608 0 +0306254 0028064 0 +0017049 0293791 0 +0129190 0084944 0 +0087501 0087507 1 +0280126 0267877 0 +0111511 0074166 0 +0322052 0297098 0 +0304635 0119682 0 +0072183 0002379 0 +0035469 0035458 1 +0317555 0126139 0 +0195286 0300818 0 +0284102 0284105 1 +0222730 0242670 0 +0137167 0126706 0 +0093295 0172690 0 +0046680 0003303 0 +0020290 0126706 0 +0285260 0087978 0 +0202986 0150451 0 +0205785 0205783 1 +0102453 0190955 0 +0126696 0285504 0 +0282079 0046686 0 +0101538 0096611 0 +0155415 0155434 1 +0260757 0110456 0 +0172690 0172702 1 +0122320 0218593 0 +0059123 0228606 0 +0059769 0059750 1 +0060755 0171038 0 +0239516 0239533 1 +0053664 0260751 0 +0265237 0275988 0 +0184867 0291519 0 +0168124 0168128 1 +0003793 0014995 0 +0200150 0046686 0 +0022316 0196944 0 +0307303 0242678 0 +0201422 0201396 1 +0220378 0087186 0 +0171038 0171015 1 +0295711 0063921 0 +0106260 0059418 0 +0082671 0082667 1 +0171038 0171006 1 +0149696 0102663 0 +0311470 0311448 1 +0326020 0188561 0 +0286462 0185834 0 +0164530 0127305 0 +0308607 0204876 0 +0041026 0041025 1 +0156098 0156102 1 +0217662 0011731 0 +0257681 0257700 1 +0204402 0204389 1 +0180814 0044795 0 +0273895 0182110 0 +0194958 0292837 0 +0178276 0202427 0 +0102459 0238405 0 +0089583 0231934 0 +0172677 0306477 0 +0219362 0023841 0 +0073932 0020277 0 +0201422 0289128 0 +0269218 0102444 0 +0260570 0119349 0 +0229206 0275993 0 +0076591 0181828 0 +0121028 0282078 0 +0307263 0048266 0 +0147865 0029174 0 +0075417 0090198 0 +0300077 0300070 1 +0200487 0069009 0 +0048311 0259252 0 +0045594 0195661 0 +0044796 0044795 1 +0249177 0124636 0 +0322415 0322401 1 +0022589 0125715 0 +0162130 0046686 0 +0074878 0156086 0 +0110238 0037141 0 +0307937 0172690 0 +0270244 0007051 0 +0267877 0034284 0 +0047624 0028064 0 +0308607 0153267 0 +0013710 0230706 0 +0302840 0285260 0 +0253507 0292823 0 +0239516 0154106 0 +0260570 0077636 0 +0016025 0016052 1 +0072660 0102453 0 +0211322 0289128 0 +0044796 0265237 0 +0049847 0287179 0 +0137027 0026636 0 +0234337 0020871 0 +0171690 0094945 0 +0238440 0198415 0 +0016556 0295707 0 +0176026 0176007 1 +0008264 0306197 0 +0299367 0299368 1 +0227678 0326018 0 +0158958 0242488 0 +0235629 0151313 0 +0257764 0034284 0 +0292813 0307263 0 +0202970 0124863 0 +0318453 0276576 0 +0007051 0033429 0 +0075271 0075426 0 +0063152 0063150 1 +0110434 0107161 0 +0200999 0156484 0 +0000574 0000554 1 +0004536 0087695 0 +0163096 0049197 0 +0256608 0178075 0 +0007754 0008257 0 +0040532 0150242 0 +0064855 0297098 0 +0324803 0133787 0 +0219813 0247402 0 +0053640 0033136 0 +0309558 0119202 0 +0024098 0174017 0 +0322052 0322056 1 +0113656 0113653 1 +0086517 0045783 0 +0093111 0110441 0 +0306477 0046686 0 +0270225 0270244 1 +0002379 0294684 0 +0199692 0130877 0 +0281214 0185639 0 +0322971 0198421 0 +0317555 0317552 1 +0035624 0007051 0 +0242464 0065775 0 +0319172 0097892 0 +0098315 0157629 0 +0242678 0044146 0 +0299368 0306375 0 +0110289 0321146 0 +0177931 0074147 0 +0004530 0074774 0 +0165899 0202970 0 +0187865 0231447 0 +0007654 0007653 1 +0210394 0210384 1 +0272511 0281214 0 +0093113 0026647 0 +0179419 0179428 1 +0144136 0033429 0 +0028350 0071959 0 +0022490 0039215 0 +0185060 0154106 0 +0240486 0046686 0 +0000554 0000559 1 +0129192 0214494 0 +0183624 0279402 0 +0323466 0262643 0 +0307304 0227859 0 +0029052 0124636 0 +0098213 0302451 0 +0106260 0205783 0 +0170200 0033286 0 +0273621 0063166 0 +0170055 0219808 0 +0168645 0032004 0 +0110367 0326018 0 +0168124 0279402 0 +0128913 0128910 1 +0174855 0174853 1 +0185834 0185060 0 +0025783 0281965 0 +0013442 0215797 0 +0214465 0087186 0 +0044161 0085528 0 +0084948 0294840 0 +0269224 0269218 1 +0319568 0263746 0 +0007754 0020572 0 +0283055 0283050 1 +0151088 0182110 0 +0048204 0033136 0 +0083933 0220694 0 +0129296 0066547 0 +0097436 0141907 0 +0130978 0075003 0 +0211329 0211327 1 +0147862 0147865 1 +0181828 0067680 0 +0188234 0104645 0 +0137168 0029060 0 +0025584 0326937 0 +0066215 0066237 1 +0293791 0018421 0 +0293799 0199281 0 +0318449 0064277 0 +0235626 0119104 0 +0119239 0137879 0 +0184864 0023835 0 +0215795 0279402 0 +0087186 0087190 1 +0323535 0100482 0 +0063008 0062890 0 +0102459 0153275 0 +0023610 0238527 0 +0280186 0177774 0 +0055455 0232317 0 +0306498 0229206 0 +0050965 0151853 0 +0265256 0299368 0 +0170460 0185838 0 +0099620 0218593 0 +0071969 0039221 0 +0141907 0074166 0 +0076655 0007618 0 +0309061 0055200 0 +0165314 0132451 0 +0221017 0201396 0 +0130325 0257756 0 +0213597 0003302 0 +0195279 0061485 0 +0066365 0031142 0 +0082152 0061504 0 +0134959 0121569 0 +0286462 0124636 0 +0325536 0130785 0 +0110238 0256608 0 +0306241 0306231 1 +0090185 0090139 1 +0083933 0168984 0 +0121832 0271816 0 +0043421 0182819 0 +0236760 0153267 0 +0060755 0104584 0 +0109503 0289130 0 +0071587 0078721 0 +0149779 0308555 0 +0084944 0126706 0 +0293613 0293615 1 +0110365 0121569 0 +0013710 0120059 0 +0168127 0168128 1 +0257172 0147520 0 +0134144 0306823 0 +0185639 0102447 0 +0325529 0007653 0 +0174496 0219469 0 +0054987 0055001 1 +0116258 0137167 0 +0119105 0157828 0 +0325536 0125715 0 +0012492 0323464 0 +0322085 0322052 1 +0249270 0072647 0 +0087697 0071583 0 +0059255 0059235 1 +0249322 0020126 0 +0102926 0130799 0 +0093289 0214465 0 +0137485 0137515 1 +0235243 0235266 1 +0022247 0130331 0 +0110434 0015297 0 +0086408 0086383 1 +0157343 0239516 0 +0154122 0075003 0 +0041025 0041026 1 +0300428 0309061 0 +0083933 0048311 0 +0128934 0128913 1 +0326933 0218593 0 +0119207 0098409 0 +0093295 0132450 0 +0163982 0153275 0 +0174853 0174017 0 +0286466 0286462 1 +0269218 0174496 0 +0163982 0295675 0 +0304635 0048205 0 +0106273 0274282 0 +0325539 0219813 0 +0164532 0023839 0 +0275993 0044230 0 +0124862 0020871 0 +0291512 0304236 0 +0097443 0097436 1 +0293464 0033286 0 +0213597 0204402 0 +0176756 0176763 1 +0092588 0219807 0 +0045657 0280195 0 +0265884 0265882 1 +0205785 0324803 0 +0069008 0076606 0 +0086260 0319928 0 +0110297 0204402 0 +0282279 0136667 0 +0214913 0214904 1 +0176683 0194928 0 +0219376 0126706 0 +0045001 0245808 0 +0235250 0235266 1 +0174509 0065769 0 +0013710 0253675 0 +0124306 0200486 0 +0223589 0188561 0 +0003793 0235651 0 +0204380 0204450 1 +0012629 0326933 0 +0204068 0204065 1 +0028343 0228595 0 +0268263 0268255 1 +0307304 0166083 0 +0201828 0050786 0 +0003303 0061097 0 +0124813 0213594 0 +0008083 0056263 0 +0287179 0182819 0 +0249322 0249323 1 +0276459 0166083 0 +0049603 0319565 0 +0124817 0026645 0 +0198421 0291512 0 +0063923 0039221 0 +0319174 0048205 0 +0232317 0181316 0 +0270250 0270244 1 +0232313 0166142 0 +0064284 0064277 1 +0192139 0064862 0 +0049530 0163088 0 +0219362 0219376 1 +0280144 0130877 0 +0099603 0099620 1 +0059248 0147937 0 +0223582 0092571 0 +0049603 0049597 1 +0304635 0183626 0 +0099620 0099593 1 +0183624 0087186 0 +0155407 0155409 1 +0074766 0253500 0 +0197380 0316640 0 +0136667 0097277 0 +0122316 0147126 0 +0074878 0326020 0 +0098311 0098317 1 +0285258 0137167 0 +0073929 0307304 0 +0016052 0015983 1 +0238413 0238380 1 +0049197 0109506 0 +0211329 0039662 0 +0014997 0055001 0 +0052795 0108730 0 +0198337 0048130 0 +0286499 0164536 0 +0279572 0267890 0 +0044796 0164536 0 +0217839 0177770 0 +0294362 0136675 0 +0128910 0213597 0 +0285500 0285504 1 +0304517 0324803 0 +0055455 0214494 0 +0111510 0250814 0 +0280186 0214582 0 +0265882 0041388 0 +0311462 0019426 0 +0038245 0038223 1 +0144126 0151853 0 +0128910 0101553 0 +0187671 0204876 0 +0042368 0081318 0 +0157835 0053443 0 +0125758 0110456 0 +0207309 0309254 0 +0053081 0005141 0 +0317565 0227678 0 +0000559 0164980 0 +0227574 0099975 0 +0188413 0057463 0 +0185639 0184864 0 +0214271 0317565 0 +0097896 0097887 1 +0027422 0143794 0 +0293613 0318455 0 +0295130 0135857 0 +0087695 0045657 0 +0144136 0033286 0 +0013744 0008086 0 +0176026 0025584 0 +0204870 0219464 0 +0181655 0181653 1 +0204455 0177780 0 +0081317 0081318 1 +0114953 0114949 1 +0234373 0030054 0 +0007618 0007653 1 +0181655 0309556 0 +0034283 0307263 0 +0064277 0300818 0 +0087902 0188413 0 +0280144 0182473 0 +0150451 0022247 0 +0130873 0219958 0 +0304638 0177585 0 +0287179 0057462 0 +0293791 0293799 1 +0171104 0154049 0 +0265884 0102930 0 +0236507 0003302 0 +0290478 0200486 0 +0083935 0269794 0 +0042786 0140616 0 +0004530 0004538 1 +0086259 0260757 0 +0155409 0170456 0 +0256607 0308606 0 +0088463 0088471 1 +0049846 0053416 0 +0299367 0240477 0 +0172677 0092574 0 +0221296 0221288 1 +0214507 0182471 0 +0319172 0319174 1 +0109476 0074155 0 +0115260 0290467 0 +0102930 0294684 0 +0231427 0231414 1 +0206776 0206780 1 +0150290 0282289 0 +0107369 0029052 0 +0234320 0181653 0 +0082671 0013747 0 +0094808 0212700 0 +0287179 0287178 1 +0107359 0013710 0 +0174853 0087976 0 +0264766 0293613 0 +0101109 0033429 0 +0067676 0102444 0 +0242678 0076576 0 +0318453 0318449 1 +0241755 0304638 0 +0194958 0194960 1 +0130829 0134973 0 +0018780 0072647 0 +0137027 0048269 0 +0269221 0081587 0 +0198033 0066369 0 +0090139 0090169 1 +0033285 0230350 0 +0235126 0235125 1 +0066365 0156484 0 +0278139 0257681 0 +0110365 0110361 1 +0294685 0087978 0 +0260933 0235463 0 +0007602 0124863 0 +0028859 0164536 0 +0286667 0219958 0 +0127309 0111280 0 +0022245 0117865 0 +0227059 0260563 0 +0014997 0001572 0 +0235461 0283103 0 +0109476 0192139 0 +0276448 0184864 0 +0147122 0055001 0 +0029060 0227860 0 +0240487 0240477 1 +0205783 0141916 0 +0282289 0317552 0 +0029187 0061485 0 +0049846 0078491 0 +0185060 0039662 0 +0192132 0192139 1 +0124642 0217674 0 +0300868 0300865 1 +0002379 0008257 0 +0219972 0219958 1 +0278136 0137167 0 +0242678 0153212 0 +0156102 0317555 0 +0119682 0178068 0 +0013450 0281965 0 +0053443 0293465 0 +0304240 0198421 0 +0187487 0121571 0 +0073929 0151853 0 +0066131 0020871 0 +0114473 0124642 0 +0177775 0177780 1 +0059418 0059422 1 +0037108 0044988 0 +0268838 0156098 0 +0108398 0074774 0 +0137485 0047624 0 +0242464 0242473 1 +0137028 0137027 1 +0149709 0149696 1 +0154106 0238527 0 +0102455 0285260 0 +0287181 0287178 1 +0092574 0156943 0 +0219464 0116417 0 +0013442 0219958 0 +0095054 0236299 0 +0016035 0287179 0 +0280664 0075003 0 +0067680 0063008 0 +0207953 0257764 0 +0049597 0012627 0 +0245099 0284188 0 +0108730 0070208 0 +0086260 0086259 1 +0028350 0051263 0 +0017049 0017052 1 +0256608 0186407 0 +0157629 0290467 0 +0280664 0169043 0 +0059248 0059259 1 +0026920 0200564 0 +0170457 0326018 0 +0316640 0263773 0 +0204876 0204870 1 +0239193 0253507 0 +0235651 0081581 0 +0002393 0137028 0 +0242488 0297116 0 +0319565 0174509 0 +0005141 0195186 0 +0025775 0025768 1 +0177774 0090198 0 +0022247 0209298 0 +0200998 0200999 1 +0311448 0311470 1 +0207309 0165897 0 +0198274 0198272 1 +0015178 0292838 0 +0232317 0251395 0 +0026649 0239533 0 +0235626 0281214 0 +0270250 0066237 0 +0106540 0031142 0 +0216423 0137028 0 +0081318 0020871 0 +0318453 0177223 0 +0194960 0309746 0 +0008264 0201825 0 +0060722 0227338 0 +0166083 0257756 0 +0183626 0227673 0 +0174853 0227338 0 +0022490 0116291 0 +0018780 0156477 0 +0276140 0030048 0 +0281828 0041388 0 +0137879 0022592 0 +0129307 0126133 0 +0076606 0074763 0 +0221017 0151088 0 +0230356 0230350 1 +0300428 0121028 0 +0310327 0310326 1 +0201422 0024298 0 +0203592 0045657 0 +0287178 0287181 1 +0168645 0268838 0 +0235336 0227673 0 +0166094 0110312 0 +0047693 0104592 0 +0190947 0134137 0 +0267836 0235461 0 +0076606 0076605 1 +0287181 0092574 0 +0314540 0039250 0 +0064277 0300817 0 +0061095 0239194 0 +0150678 0124863 0 +0265237 0179428 0 +0025583 0242671 0 +0042392 0171689 0 +0003303 0326018 0 +0302451 0063152 0 +0102453 0087976 0 +0162786 0086558 0 +0239193 0098401 0 +0235243 0235265 1 +0192132 0053640 0 +0011684 0011717 1 +0067680 0016010 0 +0048266 0048269 1 +0133893 0089583 0 +0141901 0174496 0 +0150237 0039648 0 +0031142 0124636 0 +0214271 0214276 1 +0294655 0048266 0 +0106267 0325536 0 +0290478 0283105 0 +0126108 0137167 0 +0110234 0201825 0 +0060722 0157835 0 +0186409 0085519 0 +0235461 0066369 0 +0219469 0119349 0 +0164369 0304245 0 +0110361 0110365 1 +0238440 0262658 0 +0050965 0050966 1 +0183626 0154122 0 +0101114 0249297 0 +0260563 0216447 0 +0260933 0113656 0 +0041388 0041400 1 +0087845 0294362 0 +0213594 0016043 0 +0214271 0110238 0 +0047626 0174853 0 +0110312 0131250 0 +0088463 0198415 0 +0306839 0177770 0 +0019426 0045242 0 +0280139 0156329 0 +0114478 0119252 0 +0119105 0119111 1 +0293799 0130982 0 +0231447 0279409 0 +0263750 0263759 1 +0219469 0300876 0 +0007757 0042786 0 +0059249 0059255 1 +0236296 0172690 0 +0065470 0200140 0 +0074147 0159759 0 +0124862 0320159 0 +0028064 0284102 0 +0201825 0060542 0 +0155409 0040531 0 +0267877 0029174 0 +0053339 0013747 0 +0321146 0284188 0 +0100476 0077624 0 +0055001 0130785 0 +0221288 0308607 0 +0265237 0265247 1 +0227678 0227673 1 +0088471 0315349 0 +0033285 0274281 0 +0085528 0218590 0 +0119347 0008264 0 +0036691 0283105 0 +0086408 0284105 0 +0144126 0087978 0 +0099620 0099603 1 +0168969 0194940 0 +0294359 0087364 0 +0307304 0043174 0 +0089252 0284166 0 +0171690 0214465 0 +0087848 0087900 1 +0087186 0162130 0 +0319172 0181825 0 +0119207 0185058 0 +0162786 0041022 0 +0185060 0235336 0 +0309062 0110312 0 +0285260 0300817 0 +0027422 0087186 0 +0170456 0198811 0 +0083935 0295675 0 +0066215 0210888 0 +0223582 0063008 0 +0276140 0102447 0 +0119207 0119202 1 +0029174 0007910 0 +0168128 0054979 0 +0102460 0137167 0 +0268263 0268233 1 +0042969 0260751 0 +0106742 0016735 0 +0156329 0027825 0 +0197380 0197385 1 +0190652 0267877 0 +0259253 0101211 0 +0052796 0052795 1 +0118536 0286499 0 +0116291 0087179 0 +0269639 0017033 0 +0268216 0116291 0 +0163096 0257681 0 +0022301 0007618 0 +0254570 0002379 0 +0236762 0166094 0 +0130978 0097278 0 +0251395 0059754 0 +0188413 0188407 1 +0158958 0243115 0 +0101109 0059769 0 +0020958 0265247 0 +0107149 0086408 0 +0232313 0200486 0 +0200605 0200555 1 +0119202 0274281 0 +0170200 0170219 1 +0199281 0067573 0 +0001572 0001584 1 +0044576 0041008 0 +0216525 0007618 0 +0249177 0096587 0 +0190654 0067680 0 +0170206 0216530 0 +0055455 0195185 0 +0072151 0072183 1 +0268263 0268216 1 +0162130 0060722 0 +0005141 0005123 1 +0072660 0072647 1 +0105726 0111278 0 +0265882 0114449 0 +0268233 0268263 1 +0102930 0277994 0 +0236507 0057463 0 +0005141 0239194 0 +0178068 0150451 0 +0039648 0039662 1 +0064284 0027409 0 +0057463 0020290 0 +0281968 0028064 0 +0276091 0110465 0 +0094945 0242671 0 +0154106 0274282 0 +0063127 0063150 1 +0304638 0306198 0 +0156086 0156102 1 +0185058 0150290 0 +0236575 0126108 0 +0306845 0018779 0 +0048205 0177931 0 +0007910 0007933 1 +0141901 0300868 0 +0185834 0020963 0 +0321146 0106742 0 +0177225 0203596 0 +0007757 0110234 0 +0053640 0294684 0 +0165897 0282078 0 +0181315 0245099 0 +0317565 0132451 0 +0322425 0169041 0 +0242670 0260563 0 +0144136 0078708 0 +0323527 0170050 0 +0235465 0319170 0 +0073989 0073992 1 +0153212 0212688 0 +0104581 0273891 0 +0087900 0087902 1 +0060751 0086408 0 +0116432 0155434 0 +0066532 0147865 0 +0304521 0048267 0 +0242678 0200999 0 +0196944 0196947 1 +0274281 0198344 0 +0126696 0235128 0 +0094945 0049179 0 +0174020 0129192 0 +0174197 0129192 0 +0011731 0231933 0 +0294840 0294844 1 +0035631 0129296 0 +0110238 0181825 0 +0270250 0286646 0 +0216525 0024097 0 +0256607 0159759 0 +0251394 0140616 0 +0302840 0238527 0 +0059127 0203592 0 +0198344 0297098 0 +0051749 0020127 0 +0063152 0061504 0 +0177774 0177780 1 +0016858 0052795 0 +0030163 0029060 0 +0165312 0035469 0 +0134959 0198805 0 +0042791 0308607 0 +0086959 0098213 0 +0285258 0285260 1 +0260945 0011403 0 +0174197 0037108 0 +0293791 0102455 0 +0047691 0047693 1 +0093295 0156484 0 +0249309 0249270 1 +0135018 0092505 0 +0178075 0238440 0 +0087355 0156484 0 +0020963 0109476 0 +0295130 0295148 1 +0235125 0280186 0 +0176763 0234105 0 +0214583 0118455 0 +0137419 0156329 0 +0199696 0314536 0 +0045657 0276089 0 +0242692 0213597 0 +0044795 0044796 1 +0050966 0242671 0 +0217662 0217674 1 +0231427 0231447 1 +0319172 0013710 0 +0059123 0102455 0 +0238430 0238428 1 +0088697 0004538 0 +0052795 0052796 1 +0104581 0039214 0 +0126108 0126099 1 +0116270 0116258 1 +0171113 0064855 0 +0192132 0249270 0 +0302455 0073932 0 +0157632 0092505 0 +0030048 0008083 0 +0075003 0086548 0 +0179607 0042791 0 +0283105 0026645 0 +0180817 0072647 0 +0307303 0308607 0 +0111510 0089583 0 +0030054 0005123 0 +0055442 0196947 0 +0171038 0157835 0 +0200605 0074899 0 +0133893 0307258 0 +0110456 0168128 0 +0214895 0214904 1 +0093289 0089583 0 +0179428 0242678 0 +0159757 0170457 0 +0221017 0280144 0 +0129192 0217674 0 +0087186 0200605 0 +0088463 0187867 0 +0033285 0180817 0 +0089252 0125715 0 +0020572 0011403 0 +0177770 0177780 1 +0263746 0276091 0 +0063150 0267959 0 +0025768 0045103 0 +0231441 0231447 1 +0196947 0280186 0 +0003793 0284102 0 +0147126 0136675 0 +0030163 0030172 1 +0185058 0185060 1 +0184819 0290478 0 +0276089 0102663 0 +0295513 0136675 0 +0172027 0172033 1 +0227678 0228595 0 +0194940 0049179 0 +0114953 0075417 0 +0065769 0304638 0 +0231934 0315348 0 +0254986 0066547 0 +0048311 0198274 0 +0249309 0170055 0 +0156943 0104581 0 +0147937 0201396 0 +0032004 0007910 0 +0154128 0183624 0 +0286667 0309061 0 +0307304 0267832 0 +0309259 0200150 0 +0041022 0041026 1 +0294684 0186407 0 +0130877 0229335 0 +0020608 0020572 1 +0110434 0184867 0 +0067047 0308607 0 +0081581 0235651 0 +0265882 0200486 0 +0088702 0243135 0 +0257764 0257756 1 +0005697 0295148 0 +0149786 0044230 0 +0184360 0124636 0 +0300070 0300077 1 +0155398 0155409 1 +0023610 0309558 0 +0182811 0107042 0 +0214904 0214909 1 +0169043 0169041 1 +0016716 0016735 1 +0149696 0086383 0 +0219815 0179607 0 +0137879 0098213 0 +0124306 0280186 0 +0282079 0282078 1 +0098311 0098303 1 +0307263 0022247 0 +0025768 0054987 0 +0320159 0320161 1 +0187867 0187865 1 +0072151 0073989 0 +0319170 0130331 0 +0293464 0168969 0 +0024098 0024097 1 +0201396 0150675 0 +0088702 0117865 0 +0053416 0025584 0 +0093111 0180817 0 +0094923 0084948 0 +0307304 0028063 0 +0100476 0192139 0 +0067573 0048311 0 +0087697 0164369 0 +0170206 0309062 0 +0102930 0230356 0 +0179428 0185838 0 +0110434 0277994 0 +0083935 0083933 1 +0206780 0206782 1 +0013442 0285258 0 +0015178 0137301 0 +0293465 0038831 0 +0127305 0302850 0 +0020640 0020643 1 +0096587 0141907 0 +0045245 0170457 0 +0039250 0283105 0 +0102447 0168987 0 +0156484 0156477 1 +0011717 0294840 0 +0186013 0076591 0 +0282079 0098224 0 +0241755 0059127 0 +0051263 0242692 0 +0212700 0217674 0 +0129190 0320159 0 +0043410 0245099 0 +0005141 0284102 0 +0260633 0125758 0 +0198033 0151853 0 +0270250 0201828 0 +0105734 0105737 1 +0164983 0164980 1 +0110234 0293799 0 +0048269 0187865 0 +0150285 0245808 0 +0087364 0274946 0 +0132450 0102663 0 +0105726 0156954 0 +0039707 0269224 0 +0137515 0111511 0 +0231934 0244123 0 +0158958 0120063 0 +0186407 0151088 0 +0116417 0182811 0 +0276144 0039707 0 +0182819 0254986 0 +0088697 0279527 0 +0059249 0059245 1 +0003303 0221999 0 +0017033 0267734 0 +0306231 0306227 1 +0095049 0045096 0 +0268216 0268255 1 +0199281 0174020 0 +0075419 0186013 0 +0301732 0301736 1 +0098397 0214895 0 +0102674 0102680 1 +0080098 0055001 0 +0090169 0273895 0 +0089583 0326020 0 +0107149 0151314 0 +0267960 0178276 0 +0028350 0100482 0 +0282079 0137877 0 +0262643 0262658 1 +0230704 0265884 0 +0235265 0044576 0 +0108023 0196944 0 +0060722 0283042 0 +0276091 0059750 0 +0124636 0124642 1 +0074899 0150678 0 +0098317 0071959 0 +0050781 0238532 0 +0072183 0052795 0 +0202994 0202986 1 +0094807 0257178 0 +0229206 0043144 0 +0198344 0198337 1 +0030163 0159757 0 +0076576 0147933 0 +0292823 0059245 0 +0013717 0122320 0 +0244109 0113653 0 +0181316 0025109 0 +0321152 0298727 0 +0222730 0222740 1 +0045783 0078721 0 +0272503 0267890 0 +0030172 0276138 0 +0078497 0116291 0 +0043174 0020582 0 +0276448 0235629 0 +0176756 0137028 0 +0017049 0017033 1 +0300865 0236528 0 +0065769 0080091 0 +0299367 0192139 0 +0085528 0306845 0 +0280186 0045783 0 +0184867 0251394 0 +0276576 0217839 0 +0221023 0241743 0 +0037108 0192139 0 +0137485 0137512 1 +0316641 0026647 0 +0176007 0176026 1 +0219808 0219815 1 +0087900 0087848 1 +0206776 0206782 1 +0210889 0210888 1 +0249309 0133785 0 +0075274 0249270 0 +0092588 0053416 0 +0200998 0149709 0 +0271837 0238430 0 +0086259 0242473 0 +0300428 0300419 1 +0126139 0300077 0 +0057463 0243135 0 +0170457 0214469 0 +0054979 0195281 0 +0282078 0129190 0 +0057463 0294844 0 +0137512 0295711 0 +0043410 0124862 0 +0241743 0306198 0 +0220376 0044229 0 +0200487 0200486 1 +0200999 0294359 0 +0020958 0027818 0 +0227673 0227678 1 +0121842 0024097 0 +0118540 0008083 0 +0004536 0050781 0 +0184819 0300865 0 +0137879 0220376 0 +0278000 0277994 1 +0143350 0143353 1 +0265269 0316640 0 +0035624 0035631 1 +0282279 0282282 1 +0222730 0163096 0 +0018780 0232317 0 +0236563 0022247 0 +0156102 0156086 1 +0045595 0260563 0 +0304638 0008083 0 +0020474 0286466 0 +0322425 0269796 0 +0198337 0049603 0 +0200487 0194958 0 +0244111 0244109 1 +0238440 0238428 1 +0008264 0316641 0 +0104584 0104645 1 +0143350 0124862 0 +0265882 0182819 0 +0231933 0053338 0 +0168969 0035624 0 +0309746 0306839 0 +0244109 0276091 0 +0104581 0104584 1 +0273606 0016556 0 +0150678 0174842 0 +0283054 0283055 1 +0311470 0155396 0 +0147862 0050781 0 +0200486 0200487 1 +0048267 0188234 0 +0041026 0124306 0 +0020958 0002379 0 +0242670 0045103 0 +0176007 0294669 0 +0126099 0106267 0 +0300070 0176763 0 +0131250 0273621 0 +0196169 0073929 0 +0311470 0174509 0 +0045656 0108678 0 +0020640 0181315 0 +0113653 0102460 0 +0099603 0078708 0 +0028851 0187674 0 +0044146 0044161 1 +0107161 0109476 0 +0092560 0030054 0 +0204068 0108387 0 +0235128 0279568 0 +0024287 0166094 0 +0024097 0281214 0 +0045594 0232313 0 +0282078 0044230 0 +0059248 0059235 1 +0022245 0022247 1 +0081581 0087179 0 +0108030 0076591 0 +0218590 0053338 0 +0001584 0001572 1 +0074763 0087186 0 +0227059 0016716 0 +0075000 0063641 0 +0074899 0153215 0 +0119346 0265269 0 +0007654 0007622 1 +0181828 0302850 0 +0024298 0168127 0 +0309143 0309150 1 +0067050 0067047 1 +0269796 0117882 0 +0135839 0033286 0 +0319930 0319928 1 +0319172 0319170 1 +0082671 0086408 0 +0235463 0235461 1 +0234366 0234369 1 +0108387 0108408 1 +0102926 0257756 0 +0187671 0028350 0 +0150675 0294844 0 +0230356 0087697 0 +0304517 0304519 1 +0054980 0054979 1 +0317567 0317565 1 +0236528 0229204 0 +0042786 0324802 0 +0051748 0220694 0 +0273895 0045783 0 +0137419 0171038 0 +0297116 0119202 0 +0174855 0204870 0 +0257681 0060722 0 +0188333 0020290 0 +0062890 0107161 0 +0087848 0087845 1 +0272503 0066137 0 +0293464 0166083 0 +0076655 0076665 1 +0177919 0177931 1 +0306363 0157828 0 +0020963 0020958 1 +0254568 0174197 0 +0037016 0202994 0 +0216423 0216418 1 +0168127 0168124 1 +0324803 0111511 0 +0134144 0219972 0 +0106273 0110441 0 +0101208 0063921 0 +0025768 0214469 0 +0088697 0204065 0 +0285500 0119682 0 +0135023 0135018 1 +0324803 0049846 0 +0051748 0020127 0 +0269655 0269639 1 +0304519 0310326 0 +0066022 0108678 0 +0319174 0249194 0 +0045595 0174855 0 +0268827 0234106 0 +0231414 0231447 1 +0263773 0147126 0 +0087848 0182107 0 +0307303 0126108 0 +0097278 0274946 0 +0071587 0135839 0 +0048133 0150678 0 +0156477 0236507 0 +0147126 0086517 0 +0027818 0147126 0 +0256607 0319565 0 +0240486 0089211 0 +0170055 0165897 0 +0129190 0057463 0 +0244123 0016716 0 +0087190 0274946 0 +0172027 0093113 0 +0041388 0294362 0 +0186409 0297098 0 +0232317 0045096 0 +0098621 0098642 1 +0200150 0218590 0 +0202427 0028064 0 +0107149 0107161 1 +0304635 0304628 1 +0172677 0229335 0 +0275988 0114445 0 +0177775 0190654 0 +0093295 0238428 0 +0058694 0058684 1 +0302451 0075271 0 +0144126 0066369 0 +0087364 0276459 0 +0300818 0300817 1 +0033300 0257764 0 +0156321 0033285 0 +0196944 0043421 0 +0032018 0031996 1 +0236760 0055001 0 +0022490 0269794 0 +0045242 0007653 0 +0309254 0086383 0 +0045245 0013717 0 +0171690 0283103 0 +0201828 0086259 0 +0188324 0075003 0 +0108671 0063030 0 +0047624 0147933 0 +0206776 0222740 0 +0043144 0182819 0 +0039257 0200150 0 +0050966 0050965 1 +0122316 0211969 0 +0133893 0188324 0 +0314537 0314540 1 +0044146 0235128 0 +0034283 0135857 0 +0245099 0129296 0 +0219804 0219815 1 +0151074 0133890 0 +0270250 0214895 0 +0066215 0118536 0 +0048130 0000574 0 +0014995 0012501 0 +0219807 0219813 1 +0170200 0000554 0 +0127309 0227059 0 +0150675 0238380 0 +0119239 0309259 0 +0096632 0300818 0 +0234320 0086408 0 +0320161 0320159 1 +0216447 0147862 0 +0017052 0000233 0 +0281217 0074774 0 +0177770 0278000 0 +0034043 0268838 0 +0089212 0293799 0 +0092505 0282079 0 +0108730 0108731 1 +0294669 0300817 0 +0289128 0275993 0 +0004530 0166083 0 +0144126 0300876 0 +0274281 0178075 0 +0216423 0306363 0 +0292823 0292837 1 +0309259 0050781 0 +0326933 0326937 1 +0269794 0269791 1 +0279527 0279568 1 +0235243 0235279 1 +0290467 0290478 1 +0269794 0110456 0 +0022589 0097436 0 +0026928 0308559 0 +0200605 0011399 0 +0126139 0221023 0 +0045657 0245099 0 +0024287 0267877 0 +0157835 0144136 0 +0077624 0110234 0 +0098397 0166142 0 +0092560 0007602 0 +0219362 0130801 0 +0159757 0236507 0 +0324803 0285260 0 +0050966 0235626 0 +0029174 0315215 0 +0020867 0168645 0 +0245807 0102460 0 +0284102 0098642 0 +0106541 0300865 0 +0121569 0267741 0 +0063641 0254987 0 +0020290 0053443 0 +0192139 0277994 0 +0078708 0078721 1 +0038223 0053094 0 +0260641 0271816 0 +0220694 0150242 0 +0201396 0013442 0 +0007051 0130801 0 +0126099 0126108 1 +0071969 0216530 0 +0306823 0216447 0 +0040531 0257178 0 +0089211 0235463 0 +0297098 0066547 0 +0300070 0044796 0 +0200486 0098311 0 +0263773 0282279 0 +0222002 0137301 0 +0181276 0257700 0 +0190654 0219362 0 +0102444 0089211 0 +0251395 0306845 0 +0150285 0040531 0 +0030172 0030163 1 +0016052 0195664 0 +0092590 0267959 0 +0023610 0198344 0 +0041400 0077636 0 +0129192 0254986 0 +0038245 0322427 0 +0065472 0077624 0 +0049597 0049603 1 +0070203 0052796 0 +0254568 0326937 0 +0107042 0107049 1 +0219469 0219464 1 +0268255 0265253 0 +0179604 0179607 1 +0130982 0130977 1 +0236507 0269791 0 +0322056 0322085 1 +0294655 0049530 0 +0310327 0041025 0 +0136675 0088471 0 +0066547 0269224 0 +0089582 0295707 0 +0137027 0177770 0 +0295711 0268255 0 +0071959 0211967 0 +0234337 0269639 0 +0155396 0027409 0 +0216525 0130977 0 +0030172 0024287 0 +0212685 0212700 1 +0049846 0024098 0 +0239193 0202427 0 +0200487 0242692 0 +0151314 0180817 0 +0107369 0308606 0 +0187481 0187470 1 +0105726 0092574 0 +0300817 0222730 0 +0163088 0163096 1 +0294359 0234106 0 +0217706 0306193 0 +0295130 0227360 0 +0020871 0045595 0 +0109476 0274281 0 +0188324 0119252 0 +0176007 0042980 0 +0111278 0074763 0 +0238428 0147122 0 +0198805 0115260 0 +0012627 0236514 0 +0121569 0116291 0 +0016551 0234337 0 +0049179 0049197 1 +0120059 0113653 0 +0020871 0217839 0 +0118541 0118536 1 +0227050 0137515 0 +0220694 0242670 0 +0267741 0267734 1 +0038223 0099603 0 +0045001 0235646 0 +0059418 0315349 0 +0048133 0147862 0 +0052796 0028859 0 +0129190 0129192 1 +0049530 0276448 0 +0228606 0228595 1 +0025091 0020582 0 +0209295 0044795 0 +0026649 0067050 0 +0040532 0198811 0 +0265269 0181276 0 +0178075 0198415 0 +0121028 0155434 0 +0125715 0306375 0 +0184864 0005123 0 +0076665 0076655 1 +0206780 0184864 0 +0187487 0176026 0 +0085528 0097436 0 +0126099 0016735 0 +0104645 0104592 1 +0048311 0037016 0 +0130785 0090198 0 +0281826 0055200 0 +0074155 0108730 0 +0045245 0041025 0 +0311448 0171015 0 +0000569 0265269 0 +0019428 0066369 0 +0188410 0188407 1 +0326937 0151853 0 +0158958 0007910 0 +0106541 0063008 0 +0309744 0216423 0 +0170460 0184356 0 +0323535 0025768 0 +0172033 0130978 0 +0188562 0198032 0 +0309558 0067680 0 +0063033 0048205 0 +0057425 0066231 0 +0317555 0001572 0 +0056263 0163096 0 +0071583 0089211 0 +0070208 0070203 1 +0059750 0204068 0 +0235243 0119346 0 +0136675 0168645 0 +0249297 0249270 1 +0092574 0092571 1 +0162129 0040532 0 +0045595 0037019 0 +0286494 0042786 0 +0176667 0274946 0 +0314536 0047626 0 +0012632 0012629 1 +0219958 0219972 1 +0087976 0017052 0 +0092588 0092590 1 +0283050 0283054 1 +0170219 0166142 0 +0249309 0066137 0 +0200999 0015983 0 +0286466 0256607 0 +0028350 0269218 0 +0190955 0204068 0 +0179419 0151313 0 +0247402 0273891 0 +0211329 0300865 0 +0216423 0156477 0 +0114445 0260570 0 +0119239 0119238 1 +0020640 0124306 0 +0055202 0055200 1 +0044229 0280186 0 +0048269 0300419 0 +0035631 0198033 0 +0257172 0149709 0 +0230704 0026645 0 +0015297 0185639 0 +0325539 0099975 0 +0306477 0126133 0 +0304245 0234369 0 +0042368 0249194 0 +0207955 0055442 0 +0168984 0147520 0 +0065472 0065470 1 +0187671 0149786 0 +0147529 0166142 0 +0067047 0314540 0 +0219464 0284166 0 +0114445 0114449 1 +0020127 0216484 0 +0269796 0269794 1 +0053094 0273895 0 +0097443 0000574 0 +0039250 0039257 1 +0221017 0300876 0 +0177775 0177774 1 +0134973 0221296 0 +0049847 0049846 1 +0016858 0156102 0 +0144136 0018779 0 +0033429 0181825 0 +0217707 0182471 0 +0265256 0168124 0 +0299368 0105737 0 +0143794 0143793 1 +0187867 0284188 0 +0055202 0044146 0 +0302455 0302451 1 +0121571 0005123 0 +0323487 0247402 0 +0157632 0204876 0 +0220378 0220376 1 +0088471 0072660 0 +0011701 0137877 0 +0185058 0325539 0 +0064862 0020608 0 +0171038 0114949 0 +0119104 0066365 0 +0306254 0108398 0 +0276138 0276144 1 +0259252 0280186 0 +0219815 0249322 0 +0063004 0020126 0 +0109503 0043421 0 +0203592 0263746 0 +0013747 0121033 0 +0227568 0227574 1 +0057463 0057425 1 +0236528 0130785 0 +0170219 0102447 0 +0075271 0043410 0 +0162130 0234105 0 +0041012 0040999 1 +0207307 0007622 0 +0273891 0033131 0 +0200998 0126706 0 +0273606 0273621 1 +0004535 0238380 0 +0224423 0066022 0 +0295130 0267734 0 +0230706 0267960 0 +0001572 0299368 0 +0020582 0020608 1 +0242671 0066022 0 +0027422 0092571 0 +0105737 0105726 1 +0044161 0274281 0 +0186409 0282282 0 +0075417 0038833 0 +0147933 0147937 1 +0107375 0227338 0 +0112799 0036691 0 +0211327 0031996 0 +0169043 0013710 0 +0048130 0048204 0 +0242692 0299368 0 +0307258 0029052 0 +0130801 0038223 0 +0136667 0165314 0 +0092574 0253707 0 +0273891 0326937 0 +0168127 0172675 0 +0125758 0177580 0 +0322971 0110318 0 +0204389 0204402 1 +0060755 0245094 0 +0300428 0001584 0 +0310236 0143353 0 +0089583 0089582 1 +0011701 0011717 1 +0256607 0256608 1 +0242678 0196947 0 +0101672 0081317 0 +0234369 0234366 1 +0040532 0040531 1 +0137512 0137485 1 +0188407 0188418 1 +0101685 0108731 0 +0102444 0280186 0 +0322970 0217662 0 +0092505 0137485 0 +0153275 0212688 0 +0311448 0311462 1 +0198337 0008083 0 +0294684 0185639 0 +0071583 0020582 0 +0282466 0061485 0 +0143794 0126696 0 +0279409 0209295 0 +0043421 0235463 0 +0059240 0186407 0 +0086259 0121832 0 +0130785 0238527 0 +0269655 0080495 0 +0259253 0259252 1 +0036686 0110441 0 +0281968 0281965 1 +0126108 0323480 0 +0049603 0184864 0 +0166142 0190955 0 +0275997 0275977 1 +0309746 0244111 0 +0060722 0307938 0 +0110350 0110361 1 +0127305 0048133 0 +0129190 0260641 0 +0017052 0017032 1 +0039662 0044229 0 +0291519 0015307 0 +0301736 0177770 0 +0227859 0212700 0 +0198421 0071587 0 +0286667 0306241 0 +0092571 0259252 0 +0310236 0282079 0 +0263779 0235126 0 +0073992 0309556 0 +0062890 0062892 1 +0236299 0236295 1 +0187487 0118456 0 +0019428 0214904 0 +0155396 0155409 1 +0276576 0007757 0 +0101538 0101553 1 +0059419 0059422 1 +0023841 0130829 0 +0162786 0109506 0 +0059418 0066131 0 +0177225 0004535 0 +0263746 0073932 0 +0276576 0168973 0 +0306198 0306197 1 +0045595 0045594 1 +0306839 0306823 1 +0179428 0179419 1 +0186407 0051263 0 +0315206 0315215 1 +0306498 0016867 0 +0094945 0177585 0 +0294844 0308559 0 +0041025 0104581 0 +0082152 0302455 0 +0219815 0163088 0 +0022316 0276576 0 +0220683 0200564 0 +0106273 0094923 0 +0190654 0056257 0 +0048204 0048205 1 +0150290 0249177 0 +0238440 0116261 0 +0049197 0126108 0 +0304638 0174496 0 +0198344 0210384 0 +0269224 0050965 0 +0294840 0298727 0 +0097887 0097896 1 +0114473 0114478 1 +0129190 0293791 0 +0130799 0176756 0 +0075419 0075417 1 +0235465 0235463 1 +0202970 0179607 0 +0039215 0039221 1 +0075274 0087900 0 +0081317 0087697 0 +0190955 0190652 0 +0106742 0106731 1 +0099973 0259252 0 +0190652 0177774 0 +0294362 0227059 0 +0099593 0182110 0 +0059419 0110234 0 +0221296 0074766 0 +0322401 0143794 0 +0320161 0101553 0 +0156477 0198421 0 +0074766 0074763 1 +0306197 0179419 0 +0069009 0196944 0 +0300876 0221023 0 +0182471 0098311 0 +0012629 0273895 0 +0042392 0166094 0 +0276091 0035624 0 +0254570 0098642 0 +0034284 0188234 0 +0280126 0280144 1 +0007754 0007757 1 +0322056 0045656 0 +0027422 0276576 0 +0253707 0253675 1 +0143347 0121842 0 +0060543 0059259 0 +0086203 0271837 0 +0110456 0110465 1 +0041400 0220694 0 +0170460 0170456 1 +0129296 0042392 0 +0018421 0018417 1 +0306254 0306231 1 +0282466 0282471 1 +0060755 0236575 0 +0176026 0119105 0 +0188333 0196169 0 +0130829 0130825 1 +0306845 0306823 1 +0188333 0181316 0 +0000569 0000554 1 +0130982 0011399 0 +0302451 0245094 0 +0156086 0214465 0 +0164983 0019432 0 +0286494 0034043 0 +0119238 0087695 0 +0286462 0294844 0 +0078708 0020643 0 +0183624 0322415 0 +0300419 0020474 0 +0118536 0231933 0 +0106731 0106742 1 +0304638 0033429 0 +0282289 0282282 1 +0149786 0290467 0 +0153212 0224423 0 +0057463 0098397 0 +0000554 0000569 1 +0104592 0104584 1 +0036686 0211327 0 +0287179 0130877 0 +0099603 0309150 0 +0323480 0049529 0 +0020867 0166142 0 +0069008 0024287 0 +0259253 0101685 0 +0134973 0121569 0 +0110367 0110365 1 +0168984 0168987 1 +0320159 0286466 0 +0102444 0234105 0 +0227360 0030163 0 +0247408 0064277 0 +0180814 0180817 1 +0181655 0156484 0 +0020867 0026647 0 +0066022 0269218 0 +0038833 0309558 0 +0132451 0132454 1 +0076591 0174509 0 +0120063 0306198 0 +0062892 0294685 0 +0117882 0129190 0 +0049603 0095054 0 +0087848 0286494 0 +0214494 0001584 0 +0214895 0214909 1 +0202427 0300077 0 +0126108 0121842 0 +0008083 0041026 0 +0045595 0181315 0 +0315215 0269639 0 +0119239 0119252 1 +0155434 0216447 0 +0198805 0038833 0 +0198032 0049529 0 +0238428 0238430 1 +0118540 0219958 0 +0087902 0227050 0 +0163982 0163981 1 +0004535 0165314 0 +0194928 0156484 0 +0090198 0188324 0 +0027818 0020963 0 +0070203 0311448 0 +0317555 0040532 0 +0102930 0317552 0 +0026649 0026647 1 +0153212 0170460 0 +0217706 0306231 0 +0283055 0016556 0 +0248370 0217836 0 +0227568 0123196 0 +0224423 0081581 0 +0044988 0045001 1 +0238532 0086233 0 +0022592 0202441 0 +0295711 0195281 0 +0026636 0026645 1 +0008086 0269639 0 +0260570 0082667 0 +0059127 0033285 0 +0087697 0135839 0 +0107375 0107359 1 +0293465 0293464 1 +0022247 0286466 0 +0025775 0025769 1 +0307938 0043410 0 +0020582 0008086 0 +0185652 0231933 0 +0218590 0044796 0 +0185639 0147862 0 +0201800 0218593 0 +0048205 0048204 1 +0076655 0280664 0 +0087364 0137877 0 +0204389 0242671 0 +0280139 0280126 1 +0045242 0282079 0 +0163982 0124642 0 +0017052 0017049 1 +0323480 0323466 1 +0257681 0302840 0 +0174853 0217839 0 +0034043 0124863 0 +0170055 0262658 0 +0205785 0039215 0 +0087695 0275988 0 +0274282 0181316 0 +0249322 0098303 0 +0085528 0297116 0 +0214271 0227678 0 +0162130 0213594 0 +0033136 0212685 0 +0137879 0028064 0 +0121832 0038831 0 +0024098 0119682 0 +0300419 0300428 1 +0015181 0015178 1 +0087507 0065472 0 +0219958 0269794 0 +0041022 0050965 0 +0144136 0256608 0 +0039221 0046680 0 +0265237 0020479 0 +0125715 0236295 0 +0304638 0309061 0 +0165312 0045245 0 +0294669 0147937 0 +0282289 0282279 1 +0168630 0249194 0 +0273895 0273891 1 +0182811 0075274 0 +0180814 0074155 0 +0317552 0317555 1 +0029060 0029052 1 +0171015 0171038 1 +0222002 0090185 0 +0324803 0121842 0 +0317552 0227360 0 +0143350 0025089 0 +0176763 0055001 0 +0274281 0154038 0 +0131250 0060542 0 +0212700 0212688 1 +0063008 0236296 0 +0074166 0221023 0 +0015122 0098401 0 +0013717 0309062 0 +0028343 0025109 0 +0227050 0015111 0 +0061504 0147520 0 +0204068 0157632 0 +0179419 0102447 0 +0308606 0308607 1 +0300868 0020474 0 +0055001 0054987 1 +0004536 0004535 1 +0102680 0102663 1 +0134144 0124642 0 +0319930 0230706 0 +0137512 0223582 0 +0217707 0186013 0 +0221017 0063921 0 +0162130 0112814 0 +0217839 0120063 0 +0017052 0236295 0 +0126108 0064277 0 +0024298 0007051 0 +0064284 0074878 0 +0108678 0108671 1 +0264766 0181316 0 +0098303 0292813 0 +0239533 0151853 0 +0147122 0147126 1 +0319565 0116432 0 +0264766 0154049 0 +0007618 0055001 0 +0171689 0198259 0 +0259252 0020963 0 +0280686 0280664 1 +0214465 0281217 0 +0268838 0268827 1 +0259253 0158964 0 +0235646 0235336 0 +0019426 0019428 1 +0171113 0212685 0 +0011403 0049197 0 +0304628 0206776 0 +0177225 0062890 0 +0283050 0283055 1 +0101553 0097892 0 +0295130 0054979 0 +0235128 0112799 0 +0300874 0073992 0 +0224423 0033300 0 +0203596 0281965 0 +0034038 0172702 0 +0282282 0106731 0 +0220376 0200605 0 +0054979 0242464 0 +0098621 0302455 0 +0011698 0011684 1 +0214271 0101208 0 +0182471 0269639 0 +0147937 0130877 0 +0190652 0042969 0 +0270247 0267832 0 +0124300 0124306 1 +0269791 0098621 0 +0238380 0214271 0 +0020126 0060542 0 +0011684 0274282 0 +0059255 0059240 1 +0076591 0129192 0 +0143350 0022592 0 +0157828 0238440 0 +0137028 0194940 0 +0059419 0200487 0 +0323535 0315215 0 +0181315 0181316 1 +0086233 0185639 0 +0093111 0195186 0 +0110441 0087364 0 +0270250 0087902 0 +0042392 0063143 0 +0273895 0290467 0 +0125715 0020126 0 +0131263 0131250 1 +0039214 0053081 0 +0056263 0067047 0 +0064862 0111511 0 +0318455 0181315 0 +0135857 0036691 0 +0033136 0048309 0 +0137437 0087848 0 +0071587 0014997 0 +0119238 0024097 0 +0186409 0188231 0 +0122320 0174855 0 +0185838 0185834 1 +0074147 0074155 1 +0227338 0154122 0 +0034284 0034283 1 +0067047 0067050 1 +0306839 0201422 0 +0033285 0020572 0 +0120063 0080091 0 +0053037 0113656 0 +0294685 0304245 0 +0196947 0280139 0 +0056257 0078497 0 +0087978 0176007 0 +0319930 0286462 0 +0181655 0293465 0 +0267836 0264766 0 +0185060 0214909 0 +0180817 0154049 0 +0107375 0211327 0 +0029060 0098303 0 +0071583 0048269 0 +0003793 0249297 0 +0122316 0016867 0 +0015178 0137515 0 +0048269 0025584 0 +0031142 0053339 0 +0271816 0165897 0 +0116291 0125758 0 +0036691 0036686 1 +0151088 0300865 0 +0284166 0293613 0 +0281968 0176007 0 +0123194 0177931 0 +0074766 0074774 1 +0231414 0012492 0 +0260933 0227059 0 +0211322 0141916 0 +0066532 0030054 0 +0186022 0186013 1 +0199284 0221296 0 +0282471 0212700 0 +0268233 0268256 1 +0301732 0306498 0 +0026649 0254986 0 +0090139 0227568 0 +0137515 0125715 0 +0041400 0041388 1 +0087902 0034038 0 +0149779 0149786 1 +0242473 0105726 0 +0063921 0300070 0 +0027825 0165899 0 +0126099 0055455 0 +0325539 0325529 1 +0295707 0022316 0 +0078708 0063033 0 +0202986 0219972 0 +0092590 0309558 0 +0020582 0214465 0 +0273891 0166142 0 +0165899 0089212 0 +0164980 0101114 0 +0235125 0235126 1 +0102444 0119682 0 +0137879 0131255 0 +0289130 0289128 1 +0045773 0020867 0 +0050965 0024097 0 +0286667 0286646 1 +0315215 0053081 0 +0302850 0214507 0 +0171015 0128934 0 +0141916 0108731 0 +0039215 0194958 0 +0098311 0087501 0 +0018417 0008264 0 +0265247 0178068 0 +0059750 0267734 0 +0102930 0300876 0 +0107369 0108030 0 +0235265 0235279 1 +0026647 0005141 0 +0215795 0184360 0 +0126706 0188231 0 +0106541 0188561 0 +0204380 0214895 0 +0238413 0238400 1 +0238430 0238440 1 +0119105 0266905 0 +0016551 0326937 0 +0187873 0187865 1 +0075417 0075426 1 +0263750 0263746 1 +0162786 0053416 0 +0135018 0135023 1 +0035624 0007602 0 +0294684 0230704 0 +0269224 0063030 0 +0059235 0059259 1 +0220694 0137879 0 +0194958 0007618 0 +0220376 0220378 1 +0045096 0240487 0 +0149786 0128934 0 +0044795 0036686 0 +0157828 0133787 0 +0253507 0042980 0 +0137879 0137877 1 +0121033 0178075 0 +0087355 0025091 0 +0113656 0102444 0 +0306375 0320159 0 +0319928 0319930 1 +0055442 0098315 0 +0117865 0206780 0 +0143794 0323527 0 +0149696 0149709 1 +0097887 0097892 1 +0074166 0174020 0 +0174200 0116432 0 +0059248 0059249 1 +0265269 0033136 0 +0274944 0000231 0 +0066547 0066532 1 +0082667 0309259 0 +0271816 0144126 0 +0101211 0029060 0 +0092574 0302840 0 +0184819 0184822 1 +0050786 0106260 0 +0197385 0280664 0 +0235279 0235308 1 +0007757 0007754 1 +0282279 0174842 0 +0069008 0045783 0 +0108671 0041022 0 +0298724 0149709 0 +0066237 0008257 0 +0042791 0278136 0 +0286498 0163982 0 +0304521 0081581 0 +0257172 0248384 0 +0201828 0201825 1 +0050965 0326020 0 +0045783 0242670 0 +0151313 0304517 0 +0013710 0045657 0 +0294655 0294669 1 +0022245 0286494 0 +0025583 0249194 0 +0048205 0028859 0 +0092505 0127309 0 +0319174 0200486 0 +0298727 0019428 0 +0274944 0214913 0 +0048311 0048309 1 +0201802 0170050 0 +0268255 0268256 1 +0076576 0076591 1 +0039221 0263773 0 +0137167 0063617 0 +0019426 0097436 0 +0066369 0253500 0 +0089242 0028851 0 +0119683 0119682 1 +0102933 0102930 1 +0198415 0093113 0 +0039250 0048204 0 +0244111 0087186 0 +0275997 0034038 0 +0003793 0195664 0 +0178277 0015181 0 +0061095 0061097 1 +0177931 0177919 1 +0058690 0058684 1 +0104584 0039707 0 +0306375 0012501 0 +0020126 0066365 0 +0150285 0163981 0 +0109464 0028063 0 +0198344 0315348 0 +0177774 0177770 1 +0130785 0092588 0 +0107042 0013747 0 +0181262 0259253 0 +0033286 0106273 0 +0323535 0042368 0 +0219815 0169041 0 +0053640 0106731 0 +0218590 0194960 0 +0067680 0222740 0 +0176007 0230704 0 +0204065 0211967 0 +0143350 0143347 1 +0101109 0281217 0 +0072647 0239516 0 +0080098 0081318 0 +0181262 0030172 0 +0239533 0082154 0 +0135857 0042791 0 +0275993 0275996 1 +0127305 0013442 0 +0002379 0300874 0 +0168645 0130873 0 +0194960 0194958 1 +0049603 0118456 0 +0310327 0097278 0 +0098397 0143353 0 +0276576 0087976 0 +0098397 0121832 0 +0306193 0306198 1 +0078491 0078497 1 +0235353 0223582 0 +0322427 0087695 0 +0221296 0123196 0 +0020474 0037141 0 +0004530 0004535 1 +0214583 0033300 0 +0017032 0199692 0 +0053338 0188231 0 +0198337 0045594 0 +0298727 0186022 0 +0260757 0108030 0 +0322415 0294684 0 +0273891 0165314 0 +0214494 0080508 0 +0297098 0117882 0 +0059235 0059240 1 +0074155 0074166 1 +0273895 0286494 0 +0070203 0082154 0 +0064277 0064284 1 +0117863 0090198 0 +0195186 0285258 0 +0024298 0024287 1 +0227673 0324803 0 +0235125 0157835 0 +0098382 0008257 0 +0270225 0309254 0 +0318453 0318454 1 +0309254 0030054 0 +0283054 0254987 0 +0212688 0212700 1 +0198337 0104581 0 +0126706 0306198 0 +0137168 0269639 0 +0222730 0245808 0 +0235651 0052795 0 +0249297 0282282 0 +0036686 0036691 1 +0295148 0056257 0 +0055455 0282289 0 +0227678 0094808 0 +0081587 0105737 0 +0285260 0038223 0 +0018779 0018780 1 +0165899 0011399 0 +0005141 0137485 0 +0093111 0107359 0 +0263773 0030048 0 +0309062 0304628 0 +0107149 0170457 0 +0319170 0200564 0 +0137301 0137297 1 +0108023 0108030 1 +0190652 0190654 1 +0309746 0143793 0 +0025073 0025091 1 +0300428 0025584 0 +0113656 0066137 0 +0022301 0130325 0 +0302455 0075271 0 +0020277 0300428 0 +0125715 0150678 0 +0063923 0063921 1 +0055202 0089211 0 +0213594 0220378 0 +0033432 0088463 0 +0151853 0202970 0 +0119349 0119346 1 +0129190 0281826 0 +0150242 0286646 0 +0067047 0174020 0 +0150451 0013744 0 +0061485 0253507 0 +0035631 0051251 0 +0295711 0198415 0 +0055442 0273891 0 +0269655 0195279 0 +0020290 0023610 0 +0022247 0022245 1 +0309062 0003303 0 +0045656 0326933 0 +0166094 0198272 0 +0201800 0101208 0 +0242678 0198337 0 +0027825 0086203 0 +0224413 0276091 0 +0102455 0265268 0 +0101208 0101211 1 +0242678 0304240 0 +0123196 0198274 0 +0012632 0199692 0 +0211320 0092560 0 +0092574 0004538 0 +0066131 0229204 0 +0168645 0055202 0 +0072183 0111278 0 +0184867 0323480 0 +0011399 0102674 0 +0081318 0008264 0 +0275977 0275996 1 +0268263 0015297 0 +0260757 0147862 0 +0133785 0082667 0 +0086260 0282279 0 +0200605 0149786 0 +0306845 0075411 0 +0183624 0086383 0 +0242473 0309259 0 +0060724 0219804 0 +0276576 0265269 0 +0053640 0067047 0 +0200140 0157343 0 +0132450 0216530 0 +0076655 0200999 0 +0196944 0131250 0 +0198033 0026636 0 +0275977 0072151 0 +0292837 0231934 0 +0293464 0156954 0 +0147937 0075419 0 +0200150 0200140 1 +0200564 0116417 0 +0166083 0168973 0 +0022247 0184356 0 +0157835 0197385 0 +0087848 0067047 0 +0007637 0007618 1 +0106260 0106273 1 +0133788 0249323 0 +0076591 0045656 0 +0269218 0269221 1 +0216423 0198274 0 +0084944 0259252 0 +0231414 0201422 0 +0076606 0314537 0 +0158964 0207307 0 +0157828 0157835 1 +0282289 0235125 0 +0234320 0130801 0 +0198811 0094807 0 +0300419 0284166 0 +0202434 0202441 1 +0063004 0299368 0 +0234106 0267877 0 +0186407 0170055 0 +0045657 0229206 0 +0156086 0304519 0 +0248384 0267836 0 +0181315 0072151 0 +0038223 0038245 1 +0302451 0214507 0 +0204876 0098213 0 +0075426 0214583 0 +0185060 0185058 1 +0309744 0105726 0 +0253707 0108678 0 +0302840 0230706 0 +0037108 0037141 1 +0242473 0242464 1 +0066365 0022316 0 +0300419 0028064 0 +0098397 0183624 0 +0064855 0219362 0 +0321152 0321146 1 +0094923 0069009 0 +0300077 0088697 0 +0102455 0087355 0 +0059259 0042791 0 +0108731 0108730 1 +0156329 0180814 0 +0319174 0319172 1 +0035624 0025073 0 +0275996 0134137 0 +0309746 0177770 0 +0202986 0188324 0 +0001572 0119682 0 +0020277 0291519 0 +0166145 0049529 0 +0134137 0086558 0 +0310326 0172690 0 +0025768 0200150 0 +0128910 0279409 0 +0003303 0135018 0 +0257178 0242464 0 +0300818 0235463 0 +0235651 0063641 0 +0055200 0126706 0 +0156321 0156327 1 +0016716 0008257 0 +0054980 0024298 0 +0080091 0269794 0 +0053339 0028343 0 +0165897 0179419 0 +0119346 0133893 0 +0150237 0182471 0 +0000233 0183624 0 +0023841 0023839 1 +0231414 0231427 1 +0044146 0015122 0 +0109464 0110312 0 +0166094 0065775 0 +0243135 0320159 0 +0007754 0090198 0 +0222730 0041388 0 +0266905 0140616 0 +0084944 0137168 0 +0065470 0065472 1 +0147520 0234320 0 +0151313 0220683 0 +0309556 0198337 0 +0023610 0087355 0 +0158958 0022589 0 +0130978 0130977 1 +0210394 0300077 0 +0165312 0020582 0 +0235126 0063152 0 +0026645 0077636 0 +0231933 0180814 0 +0150450 0049847 0 +0034283 0043410 0 +0325539 0164369 0 +0248370 0008086 0 +0195185 0195186 1 +0012632 0264766 0 +0244111 0020963 0 +0198415 0061485 0 +0092526 0092505 1 +0230356 0198259 0 +0170050 0170055 1 +0053338 0306477 0 +0242671 0216530 0 +0187873 0187867 1 +0086383 0107161 0 +0128934 0168128 0 +0106260 0106267 1 +0201800 0215795 0 +0227574 0181315 0 +0124642 0308555 0 +0294362 0283105 0 +0219808 0249323 0 +0174853 0048267 0 +0253500 0075000 0 +0087179 0087186 1 +0207953 0110238 0 +0124642 0229340 0 +0211327 0286466 0 +0117865 0124642 0 +0150285 0253707 0 +0048266 0187470 0 +0016551 0202994 0 +0086959 0257756 0 +0096611 0200140 0 +0286499 0286494 1 +0195186 0020958 0 +0187865 0072647 0 +0025109 0230704 0 +0037141 0092590 0 +0116291 0080508 0 +0100476 0100482 1 +0074774 0151313 0 +0186013 0048130 0 +0066365 0102455 0 +0101211 0064855 0 +0092571 0033285 0 +0324803 0147862 0 +0132451 0075271 0 +0004535 0004536 1 +0083933 0302451 0 +0081587 0087179 0 +0049529 0049530 1 +0150450 0177225 0 +0130331 0195186 0 +0283054 0041397 0 +0158964 0050786 0 +0119238 0087697 0 +0317567 0177580 0 +0236295 0126706 0 +0217839 0024098 0 +0093289 0300865 0 +0042368 0319928 0 +0203596 0041388 0 +0257756 0016556 0 +0102680 0119347 0 +0089242 0310234 0 +0147122 0253675 0 +0230350 0267832 0 +0083935 0235128 0 +0057462 0057425 1 +0025768 0025775 1 +0059422 0147122 0 +0318454 0318453 1 +0219469 0022491 0 +0015307 0078708 0 +0293613 0062890 0 +0300070 0201825 0 +0074878 0201422 0 +0260933 0260945 1 +0104584 0104592 1 +0248384 0137168 0 +0239194 0239193 1 +0202434 0075003 0 +0240477 0020127 0 +0114949 0114953 1 +0033285 0110332 0 +0309556 0309558 1 +0025783 0171113 0 +0276091 0126706 0 +0011717 0048130 0 +0235626 0293465 0 +0304236 0304240 1 +0087900 0108678 0 +0221288 0216423 0 +0005141 0323527 0 +0110365 0245099 0 +0177780 0177774 1 +0056257 0322970 0 +0048205 0107359 0 +0155407 0155398 1 +0238405 0045103 0 +0066369 0066365 1 +0259252 0253507 0 +0059240 0117882 0 +0221017 0221023 1 +0033131 0033136 1 +0064859 0064862 1 +0041026 0323527 0 +0182107 0182109 1 +0266905 0324802 0 +0190955 0174197 0 +0249309 0022247 0 +0263759 0042969 0 +0309744 0159759 0 +0279568 0279527 1 +0044578 0044576 1 +0135023 0134973 1 +0157343 0157340 1 +0062890 0259253 0 +0315206 0044230 0 +0053339 0187671 0 +0211329 0141907 0 +0277994 0018421 0 +0211329 0177770 0 +0094923 0038831 0 +0242671 0063030 0 +0172027 0123196 0 +0289128 0289130 1 +0220378 0201802 0 +0170219 0170206 1 +0263759 0124306 0 +0154106 0137027 0 +0073929 0048205 0 +0181655 0093295 0 +0048311 0066547 0 +0063030 0149709 0 +0053094 0299368 0 +0049197 0269218 0 +0004530 0004536 1 +0283105 0295675 0 +0059235 0097896 0 +0038831 0038833 1 +0051748 0110441 0 +0302455 0078721 0 +0086260 0315350 0 +0194928 0111510 0 +0276091 0166142 0 +0282282 0282289 1 +0108398 0186409 0 +0156329 0222730 0 +0130331 0130325 1 +0320161 0274944 0 +0066547 0044796 0 +0207953 0217836 0 +0292813 0022247 0 +0022592 0041400 0 +0326018 0049597 0 +0053037 0156943 0 +0025089 0238380 0 +0040531 0013442 0 +0244132 0244109 1 +0326933 0156086 0 +0030163 0224423 0 +0130801 0196947 0 +0201828 0137168 0 +0026928 0245094 0 +0111510 0133787 0 +0224413 0306823 0 +0077636 0236563 0 +0137168 0267877 0 +0316641 0054979 0 +0260933 0326933 0 +0035631 0317552 0 +0216530 0290467 0 +0273895 0276448 0 +0132454 0132451 1 +0049529 0053081 0 +0169041 0101538 0 +0275977 0275988 1 +0004538 0004535 1 +0196166 0005697 0 +0051263 0101672 0 +0001584 0236507 0 +0293799 0245808 0 +0101208 0170200 0 +0268233 0180817 0 +0293791 0204389 0 +0101114 0101538 0 +0018417 0075271 0 +0137028 0242692 0 +0092590 0224423 0 +0267832 0064859 0 +0074899 0044576 0 +0279402 0279409 1 +0202994 0202970 1 +0184819 0048311 0 +0201396 0020582 0 +0060542 0135839 0 +0280144 0280126 1 +0306823 0174020 0 +0007933 0002379 0 +0057425 0020290 0 +0133787 0133890 0 +0069009 0069008 1 +0134973 0276089 0 +0089212 0110238 0 +0205783 0063923 0 +0044229 0024098 0 +0273621 0097887 0 +0034284 0201802 0 +0024287 0118455 0 +0187865 0187867 1 +0124300 0097443 0 +0294655 0020290 0 +0220683 0190947 0 +0157340 0157835 0 +0211320 0168973 0 +0053339 0053338 1 +0301732 0283105 0 +0274946 0008083 0 +0211320 0081317 0 +0110234 0110238 1 +0304236 0269639 0 +0319170 0110456 0 +0025783 0063641 0 +0020640 0176667 0 +0159757 0087697 0 +0156098 0137419 0 +0238440 0213594 0 +0188562 0086548 0 +0150451 0182107 0 +0124636 0000569 0 +0118456 0188561 0 +0280126 0151853 0 +0210394 0277994 0 +0081317 0044551 0 +0067050 0227860 0 +0176763 0047687 0 +0149786 0149779 1 +0071969 0238399 0 +0048309 0276140 0 +0011403 0109476 0 +0045773 0045783 1 +0110456 0317565 0 +0011731 0075417 0 +0185639 0176683 0 +0122320 0122316 1 +0168984 0073989 0 +0083933 0216447 0 +0066237 0050781 0 +0276091 0276089 1 +0096632 0096611 1 +0289130 0025089 0 +0000559 0000554 1 +0304519 0304521 1 +0129192 0137419 0 +0135839 0135857 1 +0269639 0265882 0 +0071959 0071969 1 +0082667 0070203 0 +0300070 0026647 0 +0089242 0089252 1 +0101672 0234369 0 +0053094 0053081 1 +0176007 0008086 0 +0286466 0135023 0 +0015111 0041400 0 +0033286 0033285 1 +0101109 0044578 0 +0000574 0048267 0 +0081581 0081587 1 +0214583 0109464 0 +0042786 0245094 0 +0274282 0149709 0 +0092588 0073932 0 +0214582 0119238 0 +0172027 0242692 0 +0147529 0046686 0 +0022247 0236514 0 +0044161 0053664 0 +0323487 0164369 0 +0207309 0081581 0 +0281214 0203592 0 +0170457 0277994 0 +0143793 0052796 0 +0194928 0147937 0 +0004530 0017052 0 +0211329 0254570 0 +0088463 0235126 0 +0251395 0011399 0 +0284188 0124306 0 +0220376 0040531 0 +0078721 0078708 1 +0069008 0069009 1 +0149786 0087848 0 +0157835 0124300 0 +0245099 0245094 1 +0190947 0190955 1 +0174496 0315206 0 +0022592 0205785 0 +0113653 0195286 0 +0065472 0214276 0 +0216447 0216418 1 +0257756 0181315 0 +0222002 0221999 1 +0293464 0181653 0 +0104569 0080091 0 +0130785 0130789 1 +0007654 0035458 0 +0124862 0059754 0 +0294655 0188407 0 +0123196 0116258 0 +0187674 0198805 0 +0125758 0147529 0 +0306839 0028064 0 +0290478 0306231 0 +0116432 0217839 0 +0045245 0229335 0 +0249323 0130978 0 +0016735 0326018 0 +0063004 0267890 0 +0035458 0249177 0 +0239193 0234366 0 +0055200 0195661 0 +0276459 0049847 0 +0003776 0242473 0 +0297116 0320159 0 +0049197 0048204 0 +0042980 0265882 0 +0188333 0076605 0 +0198032 0071969 0 +0098224 0147862 0 +0054979 0054980 1 +0151314 0094807 0 +0059769 0279572 0 +0287179 0112799 0 +0018421 0102930 0 +0039707 0297098 0 +0131255 0131250 1 +0182811 0321152 0 +0239193 0195279 0 +0234106 0284128 0 +0199284 0199281 1 +0094807 0094808 1 +0123196 0013747 0 +0000559 0000574 1 +0147933 0242678 0 +0242692 0268827 0 +0164536 0164532 1 +0196947 0168124 0 +0098633 0032018 0 +0224413 0224423 1 +0308606 0147933 0 +0286498 0286499 1 +0016858 0016867 1 +0118540 0306193 0 +0321152 0281217 0 +0230704 0294840 0 +0011698 0136675 0 +0276448 0304638 0 +0044578 0044551 1 +0047624 0307303 0 +0235353 0301732 0 +0119682 0043410 0 +0119683 0007051 0 +0209295 0209298 1 +0059750 0165312 0 +0020290 0115260 0 +0130977 0130982 1 +0294840 0102447 0 +0164369 0098213 0 +0134974 0304245 0 +0320159 0163088 0 +0269655 0105734 0 +0058694 0058690 1 +0005693 0234337 0 +0131263 0300865 0 +0137437 0041008 0 +0164536 0253675 0 +0157632 0235128 0 +0229206 0229204 1 +0323527 0324802 0 +0238413 0128913 0 +0141901 0018780 0 +0249322 0256608 0 +0087186 0129307 0 +0030172 0041026 0 +0206782 0280186 0 +0304504 0304521 1 +0214494 0114445 0 +0026636 0302455 0 +0217706 0321152 0 +0094807 0074147 0 +0200487 0092560 0 +0232317 0026649 0 +0179607 0179604 1 +0275997 0275988 1 +0282469 0282466 1 +0222740 0315206 0 +0245094 0170456 0 +0214895 0316641 0 +0293613 0117865 0 +0012501 0089583 0 +0013450 0307258 0 +0253707 0013744 0 +0025788 0025786 1 +0020963 0072647 0 +0293613 0227568 0 +0025583 0227678 0 +0086383 0257172 0 +0219362 0234106 0 +0011717 0204464 0 +0048205 0307263 0 +0059419 0059418 1 +0219813 0219807 1 +0185838 0053339 0 +0134959 0137301 0 +0042969 0042980 1 +0031134 0075411 0 +0093111 0326020 0 +0158964 0176763 0 +0278136 0224423 0 +0012501 0162129 0 +0213594 0235336 0 +0171113 0170206 0 +0070203 0015122 0 +0195286 0195281 1 +0204380 0188324 0 +0025109 0242464 0 +0257764 0023841 0 +0025788 0123194 0 +0272503 0316641 0 +0020479 0060724 0 +0205783 0324803 0 +0087355 0045245 0 +0106273 0125715 0 +0306363 0078491 0 +0110318 0205783 0 +0060722 0319928 0 +0216525 0119252 0 +0129192 0129190 1 +0198344 0321146 0 +0124642 0214469 0 +0195281 0195279 1 +0082671 0063923 0 +0293791 0044551 0 +0063152 0315215 0 +0230350 0220694 0 +0037108 0276570 0 +0026924 0133890 0 +0279529 0279527 1 +0274944 0075274 0 +0005123 0005141 1 +0075274 0150451 0 +0025089 0025091 1 +0234337 0196947 0 +0227568 0265256 0 +0097277 0118456 0 +0306227 0306231 1 +0073932 0254570 0 +0216418 0109464 0 +0077636 0234366 0 +0192139 0039662 0 +0029052 0234369 0 +0219469 0102455 0 +0157828 0320159 0 +0279409 0187481 0 +0117882 0117865 1 +0033432 0144126 0 +0201802 0108030 0 +0265268 0227050 0 +0025768 0025769 1 +0060724 0267877 0 +0003303 0294655 0 +0238399 0238380 1 +0311469 0111278 0 +0281968 0219972 0 +0092556 0056263 0 +0217706 0217696 1 +0134144 0306375 0 +0018779 0187873 0 +0047693 0198033 0 +0110332 0227860 0 +0196944 0022490 0 +0085528 0028063 0 +0270250 0282469 0 +0051263 0132451 0 +0109476 0027409 0 +0066137 0066131 1 +0134137 0134150 1 +0015122 0007754 0 +0011403 0108387 0 +0235336 0181828 0 +0100476 0156327 0 +0123196 0018421 0 +0022245 0210889 0 +0044988 0174017 0 +0022589 0164532 0 +0007602 0304504 0 +0137297 0007637 0 +0045783 0275997 0 +0275993 0275988 1 +0227673 0037016 0 +0130331 0125758 0 +0137419 0137437 1 +0057425 0008257 0 +0236575 0065769 0 +0076606 0220683 0 +0241755 0034283 0 +0037019 0307938 0 +0098224 0098213 1 +0097892 0083933 0 +0304635 0097887 0 +0090139 0090185 1 +0198032 0198033 1 +0098642 0098633 1 +0221023 0221017 1 +0234320 0231933 0 +0024097 0131255 0 +0150678 0202970 0 +0131263 0131255 1 +0026649 0130873 0 +0157632 0071959 0 +0028064 0181828 0 +0094808 0023608 0 +0249177 0218590 0 +0268256 0274944 0 +0087848 0119683 0 +0304245 0304240 1 +0289130 0263773 0 +0011698 0011731 1 +0257700 0110238 0 +0045657 0116291 0 +0065775 0020871 0 +0285504 0064862 0 +0033303 0101208 0 +0061097 0081581 0 +0089212 0027818 0 +0302451 0236528 0 +0089212 0078721 0 +0165899 0267960 0 +0309556 0048133 0 +0221288 0182109 0 +0267741 0236507 0 +0244109 0076605 0 +0306498 0035631 0 +0028851 0028859 1 +0029187 0291519 0 +0154038 0287179 0 +0131250 0177586 0 +0165899 0287181 0 +0309746 0014995 0 +0263779 0102444 0 +0024097 0019426 0 +0123196 0123194 1 +0094923 0321146 0 +0222730 0041012 0 +0190947 0097887 0 +0086944 0295130 0 +0121832 0126706 0 +0016010 0074878 0 +0254986 0217662 0 +0066547 0030048 0 +0254570 0254568 1 +0007618 0221023 0 +0283103 0235465 0 +0257700 0153267 0 +0016551 0202986 0 +0038833 0038831 1 +0155415 0110365 0 +0310327 0073989 0 +0286646 0063030 0 +0108678 0170219 0 +0018780 0063030 0 +0150237 0150242 1 +0324802 0181315 0 +0204870 0101538 0 +0304643 0188333 0 +0118540 0315206 0 +0315349 0074766 0 +0136675 0094807 0 +0230356 0195185 0 +0013747 0013744 1 +0015181 0174853 0 +0101109 0023841 0 +0218593 0236514 0 +0020963 0066365 0 +0300428 0318449 0 +0115260 0156329 0 +0302840 0302850 1 +0038245 0117882 0 +0216525 0310226 0 +0234106 0234105 1 +0118540 0230350 0 +0005123 0097436 0 +0110332 0235353 0 +0234106 0198033 0 +0304635 0224423 0 +0281968 0045773 0 +0012627 0126696 0 +0222002 0269218 0 +0204068 0084948 0 +0020608 0015307 0 +0291519 0235646 0 +0289130 0324803 0 +0003303 0007933 0 +0063152 0063143 1 +0065769 0022491 0 +0137301 0171038 0 +0086548 0086536 1 +0064855 0064859 1 +0020963 0065470 0 +0034043 0218593 0 +0267734 0147862 0 +0025584 0056257 0 +0254987 0254986 1 +0211969 0182471 0 +0253707 0056257 0 +0042392 0174842 0 +0109476 0012629 0 +0318453 0247408 0 +0028851 0286498 0 +0306375 0092556 0 +0134137 0051251 0 +0111278 0071587 0 +0029187 0224423 0 +0086536 0270250 0 +0267734 0155398 0 +0223589 0196944 0 +0310226 0088463 0 +0020640 0185058 0 +0270250 0270225 1 +0295516 0239533 0 +0029052 0029060 1 +0294655 0098401 0 +0099620 0222002 0 +0211329 0055455 0 +0065769 0118456 0 +0286462 0109476 0 +0119238 0135857 0 +0187487 0134959 0 +0074155 0301736 0 +0117863 0117882 1 +0080508 0076605 0 +0088471 0072151 0 +0041012 0028859 0 +0164980 0273621 0 +0238527 0315215 0 +0213594 0034043 0 +0164530 0110332 0 +0219972 0015122 0 +0273895 0198415 0 +0249309 0096632 0 +0200564 0295148 0 +0092574 0236760 0 +0267960 0024097 0 +0110441 0182107 0 +0235265 0282279 0 +0259252 0322056 0 +0039648 0049597 0 +0295516 0295513 1 +0157835 0041012 0 +0108731 0129307 0 +0270247 0270250 1 +0067573 0013710 0 +0253675 0234337 0 +0048267 0281828 0 +0045594 0127309 0 +0092590 0267960 0 +0300874 0236528 0 +0253500 0083933 0 +0162130 0286499 0 +0074166 0074147 1 +0197380 0151074 0 +0108408 0292838 0 +0165897 0126099 0 +0280664 0272503 0 +0143353 0143350 1 +0000233 0126099 0 +0053664 0207309 0 +0235308 0235250 1 +0011684 0322415 0 +0116432 0280186 0 +0300077 0276091 0 +0206780 0242670 0 +0207307 0023835 0 +0257681 0047687 0 +0094923 0239516 0 +0180817 0153212 0 +0137297 0020126 0 +0147937 0048133 0 +0037108 0190955 0 +0092571 0259253 0 +0178276 0125758 0 +0151853 0151852 1 +0042786 0042791 1 +0326933 0158958 0 +0110297 0022316 0 +0114449 0267734 0 +0304504 0304517 1 +0014995 0014997 1 +0325536 0325539 1 +0294684 0294685 1 +0080098 0121028 0 +0172033 0039221 0 +0242678 0254986 0 +0130873 0130877 1 +0149696 0130799 0 +0027409 0076591 0 +0020643 0314537 0 +0281828 0235243 0 +0215795 0033286 0 +0121832 0166083 0 +0082667 0306197 0 +0186407 0186409 1 +0197380 0235279 0 +0060722 0020867 0 +0304643 0280186 0 +0056257 0093111 0 +0306241 0111510 0 +0050781 0199692 0 +0044988 0282469 0 +0053338 0141916 0 +0182811 0176026 0 +0007051 0065769 0 +0290478 0290467 1 +0102926 0238405 0 +0196166 0304628 0 +0174020 0174017 1 +0039214 0234373 0 +0150675 0272503 0 +0014997 0014995 1 +0249323 0114473 0 +0239193 0041397 0 +0020643 0170055 0 +0116432 0116417 1 +0055442 0187873 0 +0257700 0285258 0 +0267741 0268827 0 +0119202 0053038 0 +0212688 0092574 0 +0063923 0140618 0 +0309150 0309139 1 +0317565 0282466 0 +0077624 0017032 0 +0273891 0105726 0 +0265268 0174496 0 +0300865 0130789 0 +0061095 0101211 0 +0102674 0213597 0 +0101685 0293799 0 +0123194 0123196 1 +0300419 0134144 0 +0049179 0294362 0 +0026645 0026636 1 +0165899 0165897 1 +0110465 0061095 0 +0054987 0102444 0 +0263773 0263779 1 +0114445 0150450 0 +0212700 0097443 0 +0005141 0042969 0 +0192132 0279402 0 +0119683 0236575 0 +0322970 0300817 0 +0201802 0124636 0 +0039250 0235250 0 +0154106 0154128 1 +0107149 0104584 0 +0248370 0235651 0 +0020640 0304643 0 +0137437 0102455 0 +0049529 0294684 0 +0309254 0144136 0 +0131255 0107049 0 +0063030 0085519 0 +0000231 0127305 0 +0042786 0153212 0 +0099620 0317552 0 +0054979 0053094 0 +0045001 0044988 1 +0137515 0318455 0 +0212700 0212685 1 +0198032 0183626 0 +0092588 0174017 0 +0286646 0286667 1 +0235308 0235266 1 +0028350 0033286 0 +0044796 0118456 0 +0106273 0106267 1 +0082154 0052796 0 +0280144 0280139 1 +0157828 0317552 0 +0157629 0207309 0 +0082152 0048133 0 +0067680 0292837 0 +0063923 0311470 0 +0190947 0162129 0 +0098642 0124863 0 +0116291 0317555 0 +0216418 0300419 0 +0232313 0054980 0 +0196166 0314537 0 +0071959 0001572 0 +0186013 0065769 0 +0239193 0239194 1 +0304519 0135023 0 +0309259 0141907 0 +0096587 0267890 0 +0263759 0048130 0 +0274281 0045594 0 +0172033 0301736 0 +0214583 0080495 0 +0073989 0024098 0 +0074155 0164980 0 +0276089 0276091 1 +0185652 0321152 0 +0026647 0127305 0 +0106742 0163089 0 +0026645 0227859 0 +0086233 0257172 0 +0150450 0223582 0 +0046680 0030054 0 +0066547 0106541 0 +0104581 0099977 0 +0247408 0013442 0 +0065769 0188333 0 +0176756 0199696 0 +0055442 0055455 1 +0040999 0041008 1 +0060751 0060755 1 +0202441 0202434 1 +0300817 0089242 0 +0283103 0317552 0 +0130829 0093111 0 +0055455 0049846 0 +0110350 0110365 1 +0242692 0297116 0 +0219815 0219807 1 +0143350 0005123 0 +0253707 0119104 0 +0177780 0077624 0 +0188562 0234369 0 +0210384 0210394 1 +0263759 0263750 1 +0256608 0256607 1 +0134973 0135018 1 +0157629 0049847 0 +0270244 0270250 1 +0310234 0102926 0 +0090169 0090139 1 +0063921 0063923 1 +0059422 0044576 0 +0307303 0029187 0 +0309061 0123196 0 +0227059 0093289 0 +0087186 0016716 0 +0045657 0177919 0 +0113656 0073932 0 +0102663 0102674 1 +0025583 0162129 0 +0026647 0227860 0 +0224423 0224413 1 +0051748 0088463 0 +0130325 0141907 0 +0082667 0064284 0 +0176007 0207309 0 +0020867 0291512 0 +0245094 0309254 0 +0121832 0281214 0 +0086536 0024298 0 +0269794 0130325 0 +0177586 0177580 1 +0318449 0318453 1 +0072151 0298724 0 +0119252 0003302 0 +0306227 0306254 1 +0074763 0012632 0 +0279572 0048266 0 +0262658 0262643 1 +0199696 0273606 0 +0097892 0045656 0 +0270225 0115260 0 +0211969 0108731 0 +0086233 0075411 0 +0137167 0133788 0 +0222740 0306823 0 +0172675 0172702 1 +0066026 0057462 0 +0020963 0020290 0 +0187470 0069009 0 +0016043 0016716 0 +0087976 0042791 0 +0200999 0064284 0 +0119111 0064862 0 +0130785 0257172 0 +0066365 0153275 0 +0099593 0099620 1 +0048133 0048130 1 +0284102 0057425 0 +0150450 0059248 0 +0284188 0284166 1 +0188418 0045783 0 +0295148 0247402 0 +0172033 0222740 0 +0294840 0054980 0 +0236296 0287179 0 +0143347 0020867 0 +0211329 0253507 0 +0038223 0059754 0 +0143794 0114445 0 +0151088 0151074 1 +0013450 0168645 0 +0322971 0168128 0 +0188561 0107042 0 +0214465 0113653 0 +0086259 0227860 0 +0284188 0025091 0 +0033131 0085528 0 +0236507 0282078 0 +0306823 0059249 0 +0235128 0294362 0 +0030048 0188324 0 +0026636 0045245 0 +0055200 0309558 0 +0264766 0299368 0 +0000231 0110361 0 +0048267 0030048 0 +0098315 0098317 1 +0188561 0235308 0 +0267836 0063152 0 +0023835 0192132 0 +0141901 0040531 0 +0200486 0016556 0 +0204450 0204402 1 +0084948 0156321 0 +0274282 0286466 0 +0307303 0292813 0 +0185639 0185652 1 +0172675 0172677 1 +0206776 0090185 0 +0238428 0063156 0 +0110441 0110434 1 +0199281 0216423 0 +0174842 0174855 1 +0108030 0124813 0 +0067676 0317565 0 +0078721 0124862 0 +0049197 0129296 0 +0110318 0110312 1 +0217674 0164369 0 +0198274 0198259 1 +0203592 0297116 0 +0147865 0109476 0 +0005141 0171689 0 +0214583 0207309 0 +0188234 0222730 0 +0150675 0182473 0 +0106260 0008264 0 +0202970 0202986 1 +0156954 0319928 0 +0028350 0227574 0 +0102680 0119104 0 +0102680 0102674 1 +0194958 0198032 0 +0011684 0011701 1 +0235465 0217707 0 +0144136 0230706 0 +0286466 0003793 0 +0133890 0033429 0 +0102663 0102680 1 +0156943 0293799 0 +0110238 0099973 0 +0060751 0063004 0 +0035458 0035469 1 +0124636 0074899 0 +0058690 0093111 0 +0215797 0045594 0 +0042791 0056263 0 +0027422 0295675 0 +0275996 0275988 1 +0294840 0086260 0 +0045783 0269639 0 +0187671 0177774 0 +0110312 0112799 0 +0106541 0106540 1 +0281968 0185060 0 +0300077 0196166 0 +0222740 0222730 1 +0033432 0279568 0 +0227574 0110465 0 +0279529 0242464 0 +0129192 0227059 0 +0155415 0260757 0 +0253500 0044578 0 +0028063 0028064 1 +0198337 0093295 0 +0044161 0035624 0 +0099973 0088702 0 +0257681 0205783 0 +0043410 0074766 0 +0071583 0282079 0 +0020474 0022301 0 +0024287 0198805 0 +0220694 0273606 0 +0276459 0306197 0 +0116417 0015307 0 +0110297 0150451 0 +0318455 0318453 1 +0065472 0102663 0 +0015305 0015297 1 +0300817 0017032 0 +0269224 0067573 0 +0087976 0016556 0 +0253500 0073989 0 +0149709 0005697 0 +0260945 0015122 0 +0057462 0045245 0 +0179604 0106260 0 +0049197 0269796 0 +0238400 0033300 0 +0085519 0198421 0 +0204389 0204450 1 +0050965 0290478 0 +0241743 0176683 0 +0216486 0219362 0 +0093295 0093289 1 +0058690 0023835 0 +0087507 0102663 0 +0311470 0311469 1 +0190955 0080091 0 +0184864 0221999 0 +0311469 0311462 1 +0116291 0116261 1 +0230706 0198811 0 +0268838 0110234 0 +0184864 0214494 0 +0115279 0115260 1 +0184867 0184864 1 +0164369 0164372 1 +0163982 0030048 0 +0150678 0165314 0 +0219813 0090139 0 +0304521 0238532 0 +0150451 0150450 1 +0325536 0291512 0 +0260933 0181315 0 +0188418 0188410 1 +0116270 0098409 0 +0137437 0137419 1 +0182473 0194928 0 +0205783 0015297 0 +0166094 0133787 0 +0116432 0076605 0 +0003776 0003793 1 +0022316 0315348 0 +0025783 0130799 0 +0238527 0156943 0 +0183626 0028350 0 +0102453 0102455 1 +0292813 0048311 0 +0197385 0209295 0 +0120063 0120059 1 +0202441 0177931 0 +0198344 0015111 0 +0219813 0178075 0 +0150285 0309061 0 +0100476 0207955 0 +0286462 0048130 0 +0115279 0012492 0 +0282466 0129307 0 +0177770 0177775 1 +0220694 0153275 0 +0051263 0289128 0 +0031142 0228606 0 +0044146 0207953 0 +0300865 0195661 0 +0001572 0063617 0 +0235353 0151853 0 +0182819 0101538 0 +0073932 0190947 0 +0298727 0298724 1 +0257700 0020126 0 +0276138 0250833 0 +0323480 0063166 0 +0130977 0119349 0 +0034283 0304638 0 +0269221 0269218 1 +0219815 0219808 1 +0317555 0284166 0 +0110332 0154122 0 +0265247 0221999 0 +0096587 0053038 0 +0309558 0235250 0 +0170456 0179604 0 +0098213 0059769 0 +0087501 0020867 0 +0198421 0253507 0 +0101211 0137301 0 +0293464 0054987 0 +0275988 0209298 0 +0177780 0195185 0 +0282282 0154049 0 +0262658 0130825 0 +0164532 0164536 1 +0126108 0085528 0 +0155396 0155398 1 +0168973 0166142 0 +0164372 0164369 1 +0030163 0257681 0 +0247408 0197385 0 +0294362 0281214 0 +0302850 0185060 0 +0204876 0087902 0 +0177580 0177586 1 +0219804 0151074 0 +0066369 0174509 0 +0099977 0168987 0 +0064859 0071583 0 +0204389 0204464 1 +0283105 0157828 0 +0027818 0044551 0 +0156096 0064284 0 +0316640 0319930 0 +0057462 0319928 0 +0000233 0188407 0 +0170219 0300876 0 +0020608 0020958 0 +0137437 0150242 0 +0155434 0081587 0 +0300874 0223582 0 +0195286 0195279 1 +0045001 0016551 0 +0000231 0171113 0 +0005697 0326937 0 +0025089 0025073 1 +0065769 0219807 0 +0220683 0018780 0 +0028343 0280686 0 +0177774 0129307 0 +0271837 0323527 0 +0098317 0069008 0 +0174020 0053640 0 +0198337 0033285 0 +0044795 0061097 0 +0181825 0042392 0 +0042392 0042368 1 +0231441 0265269 0 +0267832 0124306 0 +0275996 0275977 1 +0063156 0201802 0 +0156130 0293613 0 +0322415 0249309 0 +0025786 0025783 1 +0130785 0087695 0 +0158958 0158964 1 +0276576 0276570 1 +0020572 0201825 0 +0230350 0230356 1 +0236507 0236514 1 +0281965 0180814 0 +0032004 0140616 0 +0304643 0054979 0 +0293464 0293465 1 +0020871 0306254 0 +0020479 0020474 1 +0130873 0216486 0 +0300817 0298724 0 +0087507 0087501 1 +0279572 0279568 1 +0136667 0200486 0 +0179428 0034038 0 +0022247 0024098 0 +0098401 0116261 0 +0131263 0207307 0 +0102453 0198415 0 +0224413 0090198 0 +0153215 0150285 0 +0302455 0001584 0 +0039707 0039662 1 +0098317 0011717 0 +0304521 0304504 1 +0265247 0126099 0 +0309062 0087848 0 +0181276 0156954 0 +0244111 0162130 0 +0182109 0130873 0 +0185652 0144136 0 +0197380 0055202 0 +0276570 0141916 0 +0130325 0130331 1 +0092526 0293613 0 +0033303 0062890 0 +0326020 0326018 1 +0249322 0033303 0 +0267741 0028859 0 +0203592 0203596 1 +0059273 0204389 0 +0150678 0176007 0 +0266905 0266908 1 +0039707 0039648 1 +0249194 0222002 0 +0259253 0164980 0 +0046680 0156477 0 +0235465 0269791 0 +0039221 0039214 1 +0038831 0227050 0 +0073989 0199696 0 +0030172 0013744 0 +0185834 0185838 1 +0133890 0308555 0 +0150451 0299367 0 +0002393 0221288 0 +0232317 0158958 0 +0093111 0024287 0 +0198032 0033136 0 +0015181 0110312 0 +0281217 0294844 0 +0281828 0094808 0 +0185834 0044576 0 +0168969 0130789 0 +0319565 0281826 0 +0025775 0316640 0 +0216530 0216525 1 +0150290 0098397 0 +0063143 0063127 1 +0292813 0326933 0 +0076665 0265882 0 +0176763 0323535 0 +0114949 0273895 0 +0171006 0171015 1 +0020871 0028343 0 +0176667 0297098 0 +0204380 0204455 1 +0053640 0130799 0 +0302840 0214465 0 +0023839 0196944 0 +0119202 0119207 1 +0162129 0147865 0 +0170200 0170206 1 +0108408 0269655 0 +0253507 0253500 1 +0195279 0195281 1 +0205783 0232317 0 +0044229 0029060 0 +0053037 0055200 0 +0162130 0046680 0 +0086517 0306197 0 +0221023 0124306 0 +0074147 0074166 1 +0090185 0090198 1 +0048204 0043174 0 +0157629 0157632 1 +0116258 0116261 1 +0031134 0031142 1 +0117863 0117865 1 +0242464 0141901 0 +0094945 0194928 0 +0219815 0219813 1 +0016867 0157835 0 +0059127 0143794 0 +0074774 0274944 0 +0194940 0194928 1 +0053037 0053038 1 +0062890 0267836 0 +0273606 0082154 0 +0188234 0015178 0 +0063004 0063008 1 +0241743 0181316 0 +0041008 0041012 1 +0102460 0087507 0 +0005123 0238527 0 +0268255 0268263 1 +0150678 0248384 0 +0020277 0116291 0 +0026645 0157828 0 +0048309 0048311 1 +0111511 0074766 0 +0172033 0098633 0 +0004535 0077624 0 +0214469 0214465 1 +0206776 0308606 0 +0210384 0041400 0 +0047691 0047705 1 +0236295 0236299 1 +0045783 0045773 1 +0023610 0144126 0 +0211322 0114449 0 +0066215 0221999 0 +0300874 0047624 0 +0102680 0169043 0 +0172677 0172702 1 +0291519 0022316 0 +0130799 0092571 0 +0194940 0166142 0 +0082667 0082671 1 +0231933 0025786 0 +0221999 0222002 1 +0003303 0254986 0 +0211329 0211320 1 +0211329 0080091 0 +0190654 0135857 0 +0220376 0195664 0 +0026928 0015178 0 +0174200 0321152 0 +0059750 0053038 0 +0238527 0238532 1 +0238405 0238413 1 +0044578 0022592 0 +0210889 0294685 0 +0033285 0033286 1 +0244111 0187481 0 +0230704 0230706 1 +0293791 0323535 0 +0098621 0098633 1 +0039257 0086548 0 +0055200 0124636 0 +0147126 0184356 0 +0273891 0204065 0 +0195664 0286667 0 +0289130 0178277 0 +0117882 0136667 0 +0018421 0285260 0 +0126706 0055455 0 +0092574 0055455 0 +0017052 0249322 0 +0063166 0143353 0 +0071969 0071959 1 +0012629 0012627 1 +0204389 0170460 0 +0066137 0239516 0 +0060543 0187674 0 +0319930 0254986 0 +0055200 0181825 0 +0067050 0087364 0 +0085519 0064862 0 +0232313 0232317 1 +0317552 0029052 0 +0322415 0286646 0 +0204870 0180814 0 +0039221 0118455 0 +0235626 0235629 1 +0066215 0086259 0 +0277994 0278000 1 +0126133 0126139 1 +0309150 0309558 0 +0319928 0171104 0 +0080508 0054979 0 +0164536 0164530 1 +0140616 0140618 1 +0196947 0295148 0 +0172027 0265884 0 +0062890 0087355 0 +0025769 0282471 0 +0286667 0286499 0 +0315206 0179607 0 +0015178 0015181 1 +0147122 0066365 0 +0087902 0087900 1 +0309556 0199281 0 +0062890 0100476 0 +0282079 0054979 0 +0283054 0283042 1 +0267960 0308606 0 +0311470 0300818 0 +0140618 0140616 1 +0074147 0269655 0 +0276448 0033285 0 +0285260 0249322 0 +0075426 0075417 1 +0061504 0074878 0 +0027818 0076591 0 +0227859 0285260 0 +0118541 0242671 0 +0065472 0118455 0 +0102663 0162130 0 +0309556 0023835 0 +0098317 0310326 0 +0002379 0089582 0 +0184864 0038833 0 +0051749 0231933 0 +0055442 0268216 0 +0232317 0045245 0 +0281968 0039707 0 +0257681 0087507 0 +0166094 0196166 0 +0295130 0295516 0 +0041022 0041025 1 +0282079 0168973 0 +0029052 0201422 0 +0054979 0085519 0 +0086233 0086203 1 +0042368 0042392 1 +0062890 0190955 0 +0268256 0270247 0 +0284105 0284102 1 +0294844 0127305 0 +0280195 0156130 0 +0059754 0037141 0 +0154049 0154047 1 +0157343 0162130 0 +0102459 0140618 0 +0092590 0101553 0 +0181828 0181825 1 +0217662 0086959 0 +0074147 0108730 0 +0186409 0060755 0 +0041400 0204464 0 +0179604 0181262 0 +0306231 0306241 1 +0078497 0286462 0 +0221296 0126099 0 +0235128 0235125 1 +0109503 0053640 0 +0230350 0143350 0 +0156130 0156096 1 +0240486 0240477 1 +0156102 0156098 1 +0188234 0003302 0 +0001584 0205783 0 +0171113 0304245 0 +0119111 0232317 0 +0076591 0076576 1 +0058684 0254987 0 +0194958 0285260 0 +0017032 0017033 1 +0202441 0280186 0 +0035624 0099593 0 +0000574 0000569 1 +0063641 0063617 1 +0315348 0202986 0 +0002379 0183624 0 +0292823 0292813 1 +0089252 0072151 0 +0036686 0306498 0 +0045096 0050786 0 +0044988 0031142 0 +0201825 0243135 0 +0110361 0110367 1 +0170460 0204068 0 +0024097 0121842 0 +0219972 0170206 0 +0253507 0300070 0 +0242488 0059127 0 +0112814 0112799 1 +0007618 0007622 1 +0022589 0007061 0 +0098621 0084948 0 +0231933 0151314 0 +0116291 0001584 0 +0219376 0129307 0 +0281217 0267836 0 +0153215 0001572 0 +0235250 0309254 0 +0094807 0307304 0 +0043410 0112814 0 +0101685 0129190 0 +0062890 0290467 0 +0271837 0135857 0 +0114949 0005693 0 +0035458 0057463 0 +0137437 0102674 0 +0147122 0087190 0 +0309556 0238413 0 +0097436 0097443 1 +0302455 0291512 0 +0267959 0182819 0 +0283105 0185652 0 +0202994 0120059 0 +0234369 0070203 0 +0235463 0140618 0 +0087190 0087179 1 +0007910 0292823 0 +0174855 0306375 0 +0170460 0124862 0 +0060724 0268263 0 +0236514 0126696 0 +0147933 0061485 0 +0282469 0033131 0 +0248370 0067047 0 +0137168 0137167 1 +0065470 0060751 0 +0003302 0003303 1 +0070208 0140618 0 +0105726 0105737 1 +0137437 0227859 0 +0019428 0325539 0 +0260751 0219464 0 +0045656 0168984 0 +0016551 0044795 0 +0020867 0282469 0 +0190955 0190947 1 +0273891 0276576 0 +0192139 0099620 0 +0015181 0044161 0 +0133788 0133787 1 +0125758 0020958 0 +0268216 0268256 1 +0059255 0059273 1 +0214276 0100476 0 +0023835 0023841 1 +0121569 0195279 0 +0228595 0194928 0 +0198337 0220683 0 +0182109 0182107 1 +0153275 0153267 1 +0200999 0073992 0 +0174853 0163981 0 +0030163 0124863 0 +0045594 0042368 0 +0174017 0124817 0 +0304245 0056263 0 +0128934 0086260 0 +0065472 0096611 0 +0076606 0284102 0 +0177585 0177580 1 +0283103 0185058 0 +0163088 0163089 1 +0201802 0064284 0 +0088702 0015178 0 +0232313 0126133 0 +0300876 0192132 0 +0174509 0174496 1 +0276138 0045001 0 +0275988 0308555 0 +0181262 0151313 0 +0007933 0007910 1 +0014995 0300874 0 +0172702 0172690 1 +0059259 0319565 0 +0234320 0234337 1 +0201396 0092560 0 +0022245 0119346 0 +0110234 0035624 0 +0047626 0102459 0 +0323466 0144136 0 +0075426 0102444 0 +0227678 0214469 0 +0136667 0076591 0 +0227859 0055442 0 +0224413 0020290 0 +0069008 0080508 0 +0270247 0119207 0 +0051749 0282078 0 +0179419 0205785 0 +0204464 0204389 1 +0101672 0214895 0 +0293464 0276089 0 +0051251 0087845 0 +0177586 0098213 0 +0143347 0028350 0 +0178276 0178277 1 +0257178 0257172 1 +0265882 0015983 0 +0125715 0066131 0 +0102459 0041008 0 +0034038 0185639 0 +0278136 0121028 0 +0302850 0045242 0 +0276459 0039257 0 +0230356 0236563 0 +0025786 0025788 1 +0130325 0116261 0 +0060751 0295130 0 +0110289 0029187 0 +0310234 0310226 1 +0280186 0108731 0 +0158964 0096587 0 +0174509 0087978 0 +0323480 0323464 1 +0282471 0022245 0 +0042392 0114478 0 +0153212 0153215 1 +0276091 0174496 0 +0153215 0019432 0 +0304635 0276091 0 +0295130 0299367 0 +0170050 0285260 0 +0282279 0282289 1 +0278136 0072183 0 +0025091 0025109 1 +0214465 0214469 1 +0070203 0039214 0 +0020479 0301736 0 +0137167 0244123 0 +0240486 0281968 0 +0322971 0077636 0 +0279527 0086536 0 +0007622 0172033 0 +0039215 0308606 0 +0263779 0263773 1 +0065472 0190654 0 +0147126 0235465 0 +0326937 0011399 0 +0200998 0156954 0 +0326018 0326020 1 +0086558 0202970 0 +0219376 0304504 0 +0307938 0307263 0 +0267734 0067676 0 +0321146 0201825 0 +0020643 0297116 0 +0202441 0086517 0 +0294362 0133890 0 +0230356 0257700 0 +0067680 0157629 0 +0199696 0199692 1 +0028851 0179607 0 +0102455 0282282 0 +0235265 0220378 0 +0323527 0157828 0 +0022316 0095049 0 +0117882 0231934 0 +0200140 0127309 0 +0124863 0276089 0 +0176683 0293615 0 +0121033 0121028 1 +0111510 0124306 0 +0064862 0064855 1 +0311462 0311448 1 +0100482 0249309 0 +0279527 0204068 0 +0047691 0022301 0 +0196166 0137485 0 +0031996 0032004 1 +0057425 0280186 0 +0184864 0251395 0 +0124863 0129192 0 +0236296 0236299 1 +0304517 0304504 1 +0084944 0286494 0 +0171104 0199281 0 +0315350 0315348 1 +0203596 0236507 0 +0251395 0075411 0 +0075003 0075000 1 +0323527 0231427 0 +0061485 0324803 0 +0102460 0102459 1 +0022490 0022491 1 +0023610 0235266 0 +0307263 0307258 1 +0238405 0238380 1 +0171104 0054980 0 +0280186 0280195 1 +0031134 0198415 0 +0059259 0143353 0 +0210888 0285260 0 +0045656 0045657 1 +0292837 0080098 0 +0247402 0247408 1 +0020608 0102455 0 +0102663 0214494 0 +0024298 0317567 0 +0135857 0239194 0 +0195279 0025583 0 +0306823 0317555 0 +0093289 0182819 0 +0033300 0033285 0 +0171104 0118541 0 +0321152 0218590 0 +0297116 0297098 1 +0286646 0102444 0 +0124813 0024287 0 +0033131 0143353 0 +0099593 0177580 0 +0293464 0023608 0 +0168987 0213594 0 +0013717 0144136 0 +0236528 0129190 0 +0319174 0319170 1 +0276089 0028350 0 +0227859 0229204 0 +0098397 0110312 0 +0058684 0053640 0 +0174496 0174509 1 +0045096 0045103 1 +0135018 0134974 1 +0075426 0075419 1 +0116258 0116291 1 +0118541 0020608 0 +0156477 0156484 1 +0249322 0170200 0 +0158964 0110297 0 +0186022 0174197 0 +0121033 0037141 0 +0214276 0214271 1 +0187674 0168987 0 +0273891 0306845 0 +0216418 0216423 1 +0045657 0111278 0 +0260641 0137877 0 +0086203 0086233 1 +0238440 0008264 0 +0004536 0004538 1 +0096632 0149779 0 +0182107 0276144 0 +0012501 0132451 0 +0149709 0018779 0 +0194928 0049197 0 +0155407 0056263 0 +0053038 0194940 0 +0141901 0032004 0 +0132450 0132454 1 +0276570 0066369 0 +0087695 0087697 1 +0008264 0008257 1 +0093113 0104581 0 +0219464 0157340 0 +0203596 0187487 0 +0022490 0067050 0 +0101211 0048311 0 +0082667 0251395 0 +0155434 0147865 0 +0263779 0284128 0 +0165314 0004536 0 +0113653 0113656 1 +0008257 0314537 0 +0276089 0276576 0 +0242670 0119682 0 +0067562 0067573 1 +0133890 0037019 0 +0260757 0282469 0 +0059123 0003303 0 +0043144 0256608 0 +0038831 0306375 0 +0004536 0248370 0 +0292813 0292823 1 +0086548 0307258 0 +0184867 0228595 0 +0185652 0235353 0 +0323527 0323535 1 +0002379 0150285 0 +0125758 0125715 1 +0190652 0247408 0 +0111511 0111510 1 +0151313 0241755 0 +0066231 0284102 0 +0165897 0165899 1 +0045242 0045245 1 +0027825 0027818 1 +0316641 0177919 0 +0306197 0306193 1 +0086959 0219376 0 +0326937 0186013 0 +0285500 0107149 0 +0151853 0249194 0 +0065470 0249297 0 +0132450 0097443 0 +0063156 0132451 0 +0147529 0244123 0 +0186407 0284166 0 +0293465 0043144 0 +0265269 0124636 0 +0099977 0089212 0 +0307303 0072151 0 +0170219 0170200 1 +0133787 0058684 0 +0205783 0051748 0 +0034283 0026645 0 +0059235 0059273 1 +0276576 0024098 0 +0047624 0047626 1 +0217662 0022301 0 +0188234 0282282 0 +0242671 0092571 0 +0110238 0015297 0 +0067676 0066365 0 +0137028 0033303 0 +0177223 0147862 0 +0020608 0207307 0 +0163089 0076591 0 +0141916 0293615 0 +0157343 0293791 0 +0099973 0034283 0 +0109464 0053443 0 +0045657 0055001 0 +0044146 0065470 0 +0156327 0023839 0 +0102930 0097277 0 +0281968 0201802 0 +0280126 0128934 0 +0253675 0254986 0 +0087190 0020963 0 +0114953 0210384 0 +0101114 0101109 1 +0082671 0114953 0 +0042392 0076606 0 +0058694 0170460 0 +0025073 0230356 0 +0102933 0132451 0 +0119105 0188410 0 +0157835 0045783 0 +0095049 0081318 0 +0000233 0262658 0 +0177225 0177774 0 +0265268 0272503 0 +0015307 0015297 1 +0050786 0198811 0 +0018780 0177931 0 +0213594 0184864 0 +0023610 0265256 0 +0141907 0141916 1 +0177931 0236296 0 +0075003 0185834 0 +0059127 0059123 1 +0265237 0265253 1 +0147126 0275997 0 +0125758 0086536 0 +0023608 0024098 0 +0045656 0107049 0 +0110297 0170219 0 +0049179 0166094 0 +0064862 0080098 0 +0285260 0107369 0 +0263759 0263746 1 +0087364 0137485 0 +0098315 0007618 0 +0227574 0024097 0 +0262643 0137297 0 +0168987 0168984 1 +0086383 0086408 1 +0102926 0102933 1 +0022301 0015307 0 +0176026 0110361 0 +0018779 0194940 0 +0284102 0030172 0 +0015983 0016052 1 +0151088 0116258 0 +0126108 0022247 0 +0083935 0257700 0 +0309062 0314540 0 +0322971 0322970 1 +0150678 0150675 1 +0219376 0219362 1 +0073992 0073989 1 +0034283 0025768 0 +0317567 0294359 0 +0214583 0309558 0 +0144126 0126706 0 +0307938 0307937 1 +0056263 0270225 0 +0280686 0181653 0 +0020290 0096611 0 +0098397 0059127 0 +0301732 0049530 0 +0066137 0076665 0 +0022316 0022301 1 +0283055 0283054 1 +0086259 0026649 0 +0242473 0143794 0 +0182107 0182110 1 +0260563 0260570 1 +0250833 0186013 0 +0315350 0195286 0 +0039215 0197380 0 +0231414 0086548 0 +0053038 0053037 1 +0322085 0121033 0 +0235461 0170050 0 +0168630 0128934 0 +0082152 0110465 0 +0184867 0253707 0 +0204389 0257681 0 +0221017 0093111 0 +0279529 0295707 0 +0119682 0098401 0 +0066231 0066215 1 +0086548 0248384 0 +0019432 0019428 1 +0116417 0053338 0 +0129190 0188324 0 +0192139 0011403 0 +0172690 0023839 0 +0039707 0129190 0 +0063617 0102455 0 +0317565 0224423 0 +0033432 0044578 0 +0199284 0098382 0 +0304638 0235125 0 +0032018 0032004 1 +0204455 0204380 1 +0243135 0007933 0 +0221023 0320161 0 +0269655 0286646 0 +0184360 0065769 0 +0020608 0020582 1 +0260570 0260563 1 +0285258 0020277 0 +0108398 0108387 1 +0192139 0217674 0 +0178075 0178068 1 +0071583 0072647 0 +0302455 0275997 0 +0306197 0306198 1 +0177931 0055455 0 +0181315 0019428 0 +0278139 0060543 0 +0157629 0066137 0 +0153267 0151314 0 +0098303 0098315 1 +0188413 0188418 1 +0039257 0204065 0 +0084944 0089212 0 +0234369 0171690 0 +0087902 0087845 1 +0195186 0150237 0 +0033429 0033432 1 +0080091 0260570 0 +0204065 0204068 1 +0122316 0122320 1 +0067050 0278136 0 +0317565 0204870 0 +0133890 0309150 0 +0216525 0216530 1 +0059235 0179604 0 +0116261 0116258 1 +0267877 0219958 0 +0122316 0147862 0 +0075419 0075426 1 +0188333 0249309 0 +0197380 0013747 0 +0292813 0274282 0 +0309061 0310234 0 +0150678 0176756 0 +0221288 0198033 0 +0194928 0028343 0 +0150242 0059123 0 +0260641 0260633 1 +0016716 0016710 1 +0217839 0217836 1 +0109506 0109503 1 +0154122 0309558 0 +0211329 0319930 0 +0168127 0211327 0 +0020608 0292837 0 +0188410 0292837 0 +0282466 0005123 0 +0247408 0058684 0 +0102680 0183626 0 +0172677 0172675 1 +0053339 0077636 0 +0171689 0101211 0 +0066532 0249177 0 +0128934 0080495 0 +0110238 0254987 0 +0037108 0174197 0 +0050786 0052796 0 +0016858 0245099 0 +0198033 0087978 0 +0253675 0053081 0 +0034283 0309150 0 +0289128 0046686 0 +0086383 0310326 0 +0088697 0030172 0 +0185639 0076605 0 +0074878 0050781 0 +0048309 0077624 0 +0199692 0319565 0 +0097892 0097896 1 +0022589 0022592 1 +0094945 0001572 0 +0174197 0177585 0 +0063641 0157340 0 +0174853 0174842 1 +0030054 0263746 0 +0164980 0199692 0 +0234373 0234366 1 +0124817 0039221 0 +0088702 0060722 0 +0013710 0013717 1 +0310234 0290478 0 +0130877 0130873 1 +0295711 0059418 0 +0315350 0098213 0 +0107161 0037019 0 +0049603 0236299 0 +0053338 0034043 0 +0066369 0047705 0 +0308559 0188562 0 +0200140 0182811 0 +0214276 0214465 0 +0174017 0232313 0 +0067573 0053640 0 +0045657 0069009 0 +0207307 0207309 1 +0117882 0097892 0 +0017032 0121571 0 +0130873 0064862 0 +0195185 0219362 0 +0049179 0033286 0 +0197380 0086260 0 +0050965 0290467 0 +0098315 0111510 0 +0280686 0028063 0 +0116261 0116270 1 +0235626 0174200 0 +0059418 0059419 1 +0039215 0224423 0 +0007653 0007618 1 +0202970 0045245 0 +0041397 0041388 1 +0137297 0119683 0 +0041397 0074899 0 +0158964 0071583 0 +0061504 0211969 0 +0043410 0039221 0 +0279572 0279529 1 +0239533 0050786 0 +0150285 0047626 0 +0166142 0322970 0 +0263750 0059754 0 +0270225 0110312 0 +0071583 0071587 1 +0077636 0177223 0 +0110238 0110234 1 +0106541 0071583 0 +0185834 0137027 0 +0149786 0108671 0 +0217696 0210384 0 +0170460 0102680 0 +0268216 0219958 0 +0137877 0038223 0 +0306498 0119239 0 +0284102 0086959 0 +0137437 0293613 0 +0284188 0106540 0 +0020290 0013442 0 +0235626 0276140 0 +0130785 0195664 0 +0236295 0118536 0 +0219958 0086203 0 +0053338 0249323 0 +0087976 0273606 0 +0061504 0166094 0 +0066369 0171113 0 +0158958 0086558 0 +0020290 0020277 1 +0236514 0236507 1 +0156484 0293464 0 +0066026 0066022 1 +0149709 0182107 0 +0198033 0271816 0 +0187867 0045773 0 +0048311 0008086 0 +0204464 0204402 1 +0184864 0184867 1 +0066532 0057462 0 +0098317 0098315 1 +0114473 0309259 0 +0085528 0020126 0 +0286462 0067676 0 +0035469 0042791 0 +0024298 0260757 0 +0244111 0244123 1 +0066026 0153275 0 +0227568 0168984 0 +0124813 0318454 0 +0200150 0176667 0 +0088471 0020958 0 +0147937 0241743 0 +0250833 0066365 0 +0048311 0188561 0 +0230350 0059419 0 +0182819 0095049 0 +0041397 0200140 0 +0087845 0114478 0 +0016735 0101553 0 +0236760 0236762 1 +0219972 0102447 0 +0236760 0198337 0 +0279568 0033429 0 +0041026 0041022 1 +0294684 0293791 0 +0185652 0185639 1 +0121028 0149709 0 +0153275 0045242 0 +0086383 0045103 0 +0218590 0135018 0 +0215795 0170050 0 +0236762 0300865 0 +0015983 0016010 1 +0107369 0107359 1 +0039707 0102453 0 +0114949 0227859 0 +0315348 0315350 1 +0172033 0304240 0 +0112814 0013710 0 +0007754 0197380 0 +0116258 0087976 0 +0156327 0156329 1 +0220376 0196947 0 +0169041 0049197 0 +0304504 0304519 1 +0086408 0019426 0 +0157828 0302850 0 +0280139 0094807 0 +0093113 0192132 0 +0260570 0322401 0 +0130799 0192132 0 +0326018 0231933 0 +0051263 0198337 0 +0039250 0264766 0 +0059259 0059235 1 +0111280 0111278 1 +0154038 0316640 0 +0256607 0176667 0 +0168128 0168124 1 +0182471 0033303 0 +0022589 0168645 0 +0004535 0004538 1 +0134150 0134137 1 +0236295 0102926 0 +0184360 0238532 0 +0268827 0186407 0 +0268233 0268216 1 +0045001 0012501 0 +0097278 0231441 0 +0243115 0122316 0 +0137877 0107042 0 +0025583 0025584 1 +0307937 0307938 1 +0163982 0195185 0 +0174017 0005123 0 +0291512 0092574 0 +0221288 0221296 1 +0075003 0164532 0 +0047691 0090169 0 +0269221 0242464 0 +0005123 0036691 0 +0235250 0257764 0 +0084944 0022247 0 +0280186 0098382 0 +0025783 0025786 1 +0200555 0200605 1 +0260757 0317555 0 +0206782 0286466 0 +0220683 0187671 0 +0315348 0315349 1 +0118540 0118541 1 +0057425 0057462 1 +0026636 0306375 0 +0007051 0239194 0 +0130982 0042791 0 +0300865 0236532 0 +0295130 0151852 0 +0026636 0242464 0 +0196166 0196169 1 +0212685 0023608 0 +0286498 0129192 0 +0151853 0244132 0 +0322085 0322056 1 +0256608 0290467 0 +0054979 0088697 0 +0069009 0014997 0 +0064284 0321152 0 +0050965 0260633 0 +0267832 0123196 0 +0120063 0281217 0 +0007622 0007654 1 +0300874 0153267 0 +0005123 0032004 0 +0242670 0324803 0 +0093113 0008086 0 +0012632 0147933 0 +0236507 0043174 0 +0134974 0214507 0 +0023835 0257172 0 +0204389 0157340 0 +0143347 0143350 1 +0317552 0262658 0 +0044230 0028350 0 +0322401 0292813 0 +0260563 0229335 0 +0279568 0235125 0 +0211329 0073932 0 +0098315 0098311 1 +0102460 0287179 0 +0045242 0245094 0 +0063923 0219808 0 +0188324 0227059 0 +0260933 0022589 0 +0022245 0067050 0 +0118456 0118455 1 +0224413 0198344 0 +0080098 0290467 0 +0174855 0174842 1 +0199284 0008086 0 +0235266 0235265 1 +0125758 0217707 0 +0028064 0254570 0 +0086517 0086548 1 +0326933 0080098 0 +0093111 0195661 0 +0053339 0051251 0 +0025783 0025788 1 +0007051 0235128 0 +0242488 0242464 1 +0194960 0317552 0 +0088463 0257764 0 +0231427 0130873 0 +0067573 0118536 0 +0078708 0171113 0 +0200605 0037141 0 +0066215 0088463 0 +0263779 0271837 0 +0221999 0029052 0 +0129307 0168645 0 +0213597 0026920 0 +0013442 0074147 0 +0227050 0227059 1 +0214583 0321152 0 +0238440 0238430 1 +0087186 0087179 1 +0249177 0249194 1 +0229340 0217706 0 +0038223 0045242 0 +0040531 0200998 0 +0112814 0035631 0 +0007653 0269796 0 +0041400 0250833 0 +0137485 0070203 0 +0290478 0190652 0 +0308559 0168973 0 +0081587 0094923 0 +0155409 0200605 0 +0083933 0321146 0 +0017052 0008264 0 +0257756 0111511 0 +0106541 0107042 0 +0024298 0092588 0 +0164532 0164530 1 +0214583 0214582 1 +0300428 0268256 0 +0070208 0049597 0 +0289128 0260633 0 +0137027 0280195 0 +0117865 0117882 1 +0110465 0094945 0 +0201802 0324803 0 +0177775 0300428 0 +0204380 0137027 0 +0110289 0004535 0 +0184822 0184819 1 +0157340 0198811 0 +0049846 0190947 0 +0283050 0283042 1 +0154106 0059123 0 +0085519 0235646 0 +0229340 0300865 0 +0309150 0309143 1 +0012627 0082154 0 +0025769 0036691 0 +0158964 0265253 0 +0030163 0279409 0 +0066022 0235126 0 +0119346 0239193 0 +0143350 0210384 0 +0025769 0025768 1 +0076606 0304236 0 +0164530 0164536 1 +0306197 0236507 0 +0060751 0282282 0 +0011717 0011731 1 +0063008 0126108 0 +0110289 0110297 1 +0045657 0045656 1 +0244123 0326933 0 +0128913 0078497 0 +0283055 0168630 0 +0214469 0211327 0 +0044551 0044578 1 +0072647 0092560 0 +0080495 0016867 0 +0188418 0188407 1 +0166083 0166094 1 +0092588 0005693 0 +0176756 0020963 0 +0302455 0082667 0 +0045594 0077624 0 +0102459 0157632 0 +0158958 0204065 0 +0318453 0285258 0 +0206782 0206780 1 +0025089 0176026 0 +0300419 0201828 0 +0214582 0214465 0 +0276459 0151853 0 +0256608 0106540 0 +0286462 0286466 1 +0177223 0177225 1 +0080508 0273891 0 +0186409 0251394 0 +0059754 0059769 1 +0263746 0263759 1 +0013747 0110441 0 +0060722 0309143 0 +0020474 0280139 0 +0165897 0001572 0 +0267959 0018779 0 +0287179 0104584 0 +0049179 0219464 0 +0294844 0294840 1 +0295513 0295516 1 +0028859 0038245 0 +0094945 0039250 0 +0097892 0136675 0 +0217674 0274281 0 +0311470 0286462 0 +0044229 0044230 1 +0106267 0229340 0 +0319170 0319172 1 +0300817 0055202 0 +0122320 0092526 0 +0075271 0186409 0 +0249323 0181655 0 +0293791 0082154 0 +0164980 0164983 1 +0245808 0286466 0 +0013710 0286499 0 +0266905 0087507 0 +0217836 0155415 0 +0110332 0043144 0 +0012492 0130978 0 +0265269 0099593 0 +0022316 0286646 0 +0202970 0130873 0 +0205785 0240487 0 +0271837 0219362 0 +0053443 0199692 0 +0198272 0198274 1 +0020640 0250814 0 +0042392 0126133 0 +0030163 0147933 0 +0000233 0000231 1 +0069009 0207955 0 +0219464 0078497 0 +0092571 0306498 0 +0076665 0214469 0 +0210384 0276091 0 +0042791 0307303 0 +0306823 0180817 0 +0159759 0098224 0 +0176763 0206776 0 +0127305 0311448 0 +0144136 0176026 0 +0060543 0119349 0 +0029187 0295675 0 +0126133 0018421 0 +0084948 0012501 0 +0109464 0109476 1 +0022301 0188413 0 +0294669 0090198 0 +0098409 0098401 1 +0306363 0306375 1 +0047624 0030172 0 +0153212 0170457 0 +0126139 0050966 0 +0170456 0014997 0 +0118541 0118540 1 +0128934 0128910 1 +0037019 0221999 0 +0108731 0281217 0 +0177775 0177770 1 +0319928 0293464 0 +0198033 0063641 0 +0007910 0188324 0 +0183624 0257764 0 +0135839 0087902 0 +0274944 0274946 1 +0137877 0137879 1 +0266908 0140616 0 +0089242 0234373 0 +0213597 0126706 0 +0074763 0074766 1 +0097436 0274282 0 +0104592 0227859 0 +0294685 0283054 0 +0308607 0308606 1 +0306198 0306193 1 +0067676 0304240 0 +0309556 0060542 0 +0003303 0086959 0 +0244123 0100482 0 +0120059 0081581 0 +0280186 0031142 0 +0230706 0034283 0 +0174842 0020582 0 +0238405 0048266 0 +0202986 0269791 0 +0300865 0024098 0 +0185639 0113653 0 +0059418 0111278 0 +0016025 0015983 1 +0153275 0097436 0 +0031134 0186409 0 +0198259 0198272 1 +0204402 0204380 1 +0271816 0322052 0 +0291519 0034038 0 +0011731 0011698 1 +0043174 0157343 0 +0086383 0022592 0 +0119239 0102680 0 +0052796 0060755 0 +0062890 0108030 0 +0273895 0241755 0 +0260633 0263773 0 +0187671 0243135 0 +0286667 0135018 0 +0129190 0257756 0 +0060543 0031134 0 +0236296 0210394 0 +0016551 0016556 1 +0214909 0214904 1 +0306227 0130785 0 +0282471 0124306 0 +0098401 0098409 1 +0047624 0116270 0 +0044551 0147122 0 +0272511 0272503 1 +0298727 0177586 0 +0108408 0108387 1 +0037019 0020572 0 +0300817 0037141 0 +0196169 0195664 0 +0158964 0158958 1 +0218593 0101685 0 +0108030 0102453 0 +0324803 0130978 0 +0270250 0270247 1 +0005693 0276089 0 +0170200 0088702 0 +0016043 0304643 0 +0013744 0013747 1 +0086233 0217706 0 +0184356 0184360 1 +0322970 0162129 0 +0028851 0128910 0 +0063127 0131250 0 +0220683 0048311 0 +0310327 0323527 0 +0188333 0188324 1 +0037019 0306498 0 +0090139 0206782 0 +0059273 0050781 0 +0059248 0059255 1 +0238527 0061095 0 +0156321 0076605 0 +0276091 0147865 0 +0072660 0281965 0 +0059127 0180814 0 +0086944 0086959 1 +0179428 0187873 0 +0151313 0013442 0 +0131255 0131263 1 +0188324 0188333 1 +0008083 0008086 1 +0236760 0110238 0 +0118541 0063004 0 +0204450 0204455 1 +0276091 0049603 0 +0045595 0028859 0 +0280195 0284102 0 +0045103 0045096 1 +0321152 0294844 0 +0147122 0200998 0 +0076665 0111280 0 +0174020 0063008 0 +0043421 0200487 0 +0307263 0326933 0 +0126099 0026647 0 +0166145 0169043 0 +0077624 0235243 0 +0217839 0013450 0 +0088471 0088463 1 +0047693 0087976 0 +0086203 0265268 0 +0317555 0034043 0 +0295675 0295707 1 +0108408 0088463 0 +0265884 0203596 0 +0198805 0236575 0 +0049603 0164536 0 +0044146 0101685 0 +0151314 0151313 1 +0045103 0107359 0 +0157828 0066026 0 +0049846 0049847 1 +0062890 0084944 0 +0096587 0106273 0 +0188413 0214909 0 +0183624 0018780 0 +0304638 0284105 0 +0200564 0042980 0 +0320161 0098382 0 +0269639 0221288 0 +0248370 0301732 0 +0151852 0201828 0 +0110318 0097896 0 +0236532 0309062 0 +0081587 0102447 0 +0033432 0018780 0 +0325536 0198337 0 +0269218 0269224 1 +0198811 0198805 1 +0163089 0168984 0 +0322427 0322401 1 +0015307 0015305 1 +0089582 0028859 0 +0157343 0260933 0 +0275988 0275997 1 +0281217 0223582 0 +0097443 0230706 0 +0206782 0019426 0 +0222730 0214465 0 +0047705 0127309 0 +0300865 0135857 0 +0165899 0321152 0 +0217836 0217839 1 +0157828 0174855 0 +0136667 0043144 0 +0235646 0229204 0 +0012492 0012501 1 +0047693 0047687 1 +0075000 0075003 1 +0106541 0031142 0 +0325539 0269218 0 +0324803 0130877 0 +0300817 0087848 0 +0097443 0168124 0 +0292823 0179607 0 +0137301 0292837 0 +0082152 0016556 0 +0108408 0206776 0 +0196944 0307937 0 +0039662 0221288 0 +0245807 0245808 1 +0315349 0315350 1 +0302850 0026649 0 +0293613 0245807 0 +0273621 0272511 0 +0217674 0247408 0 +0117863 0217674 0 +0168973 0168969 1 +0322970 0322971 1 +0106267 0110234 0 +0121033 0098213 0 +0020474 0020479 1 +0107161 0150237 0 +0187470 0187481 1 +0322970 0013747 0 +0198337 0034283 0 +0047626 0127309 0 +0135839 0000554 0 +0116270 0116261 1 +0323487 0236507 0 +0098213 0264771 0 +0026647 0281214 0 +0143350 0011403 0 +0023610 0273621 0 +0043174 0179419 0 +0041008 0129307 0 +0294669 0269655 0 +0132450 0132451 1 +0133893 0129307 0 +0043144 0110312 0 +0212685 0131255 0 +0025583 0319172 0 +0176007 0098311 0 +0187481 0235265 0 +0195661 0078708 0 +0263746 0263750 1 +0072647 0052796 0 +0157343 0118541 0 +0203596 0147937 0 +0102459 0162129 0 +0107042 0084948 0 +0286462 0097436 0 +0265247 0265256 1 +0132454 0188234 0 +0087978 0257764 0 +0326933 0187481 0 +0074147 0242670 0 +0008086 0008083 1 +0236507 0066369 0 +0099977 0074155 0 +0137437 0140618 0 +0293464 0245808 0 +0200998 0101208 0 +0304628 0229204 0 +0075271 0075274 1 +0249322 0184822 0 +0015178 0242670 0 +0096587 0096632 1 +0020479 0181315 0 +0157828 0168969 0 +0162129 0153215 0 +0279529 0308559 0 +0151074 0220376 0 +0309062 0198344 0 +0283105 0118541 0 +0168127 0239193 0 +0162129 0115260 0 +0034038 0061097 0 +0048267 0014997 0 +0244132 0244111 1 +0100476 0157835 0 +0217839 0097277 0 +0147937 0075271 0 +0080495 0221296 0 +0271816 0176007 0 +0203596 0018417 0 +0200999 0200998 1 +0293615 0293613 1 +0110238 0025091 0 +0015307 0315348 0 +0059248 0053081 0 +0294655 0100482 0 +0047705 0047693 1 +0051263 0051251 1 +0315206 0028063 0 +0217706 0002379 0 +0110350 0066022 0 +0236299 0033131 0 +0306477 0277994 0 +0078491 0055001 0 +0001584 0168969 0 +0137297 0137301 1 +0248370 0073992 0 +0016010 0015983 1 +0121571 0136667 0 +0141901 0028063 0 +0150285 0086260 0 +0282079 0273621 0 +0295707 0295675 1 +0045001 0099977 0 +0267890 0048267 0 +0326018 0163096 0 +0230704 0040532 0 +0240487 0248370 0 +0231441 0086203 0 +0020871 0297116 0 +0036686 0013710 0 +0115279 0257172 0 +0309254 0309259 1 +0236575 0236563 1 +0087179 0114473 0 +0304628 0043410 0 +0020127 0020126 1 +0219813 0121033 0 +0020126 0045595 0 +0293465 0309150 0 +0195661 0182473 0 +0256608 0156943 0 +0102447 0260945 0 +0218593 0143794 0 +0098303 0098311 1 +0307937 0108023 0 +0308559 0308555 1 +0185058 0280686 0 +0060543 0249270 0 +0041008 0281968 0 +0300874 0309556 0 +0057462 0057463 1 +0324803 0069009 0 +0197385 0271837 0 +0267890 0304245 0 +0130982 0048311 0 +0048266 0143793 0 +0214895 0174017 0 +0280139 0280144 1 +0043421 0043410 1 +0102926 0121842 0 +0104581 0104645 1 +0235629 0154106 0 +0135018 0134959 1 +0212688 0309254 0 +0134137 0134144 1 +0087190 0295130 0 +0257178 0087364 0 +0201825 0100482 0 +0063030 0311448 0 +0300818 0230356 0 +0015983 0016025 1 +0300077 0147529 0 +0183626 0110234 0 +0188562 0048204 0 +0080508 0060751 0 +0023610 0078491 0 +0116258 0073929 0 +0055442 0012632 0 +0302455 0229204 0 +0065472 0194958 0 +0013717 0045657 0 +0039215 0037108 0 +0004535 0013710 0 +0265253 0265256 1 +0187481 0221288 0 +0199692 0199696 1 +0137515 0291512 0 +0015983 0125758 0 +0254987 0198415 0 +0086259 0236760 0 +0231934 0045096 0 +0133787 0133788 1 +0317555 0241743 0 +0179604 0088697 0 +0187487 0239533 0 +0090139 0090198 1 +0106273 0098303 0 +0257172 0119683 0 +0035624 0250814 0 +0241755 0264766 0 +0310226 0310234 1 +0124642 0317555 0 +0076665 0110312 0 +0188407 0188410 1 +0200555 0200564 1 +0266908 0151088 0 +0124862 0089212 0 +0048269 0048266 1 +0104581 0104592 1 +0086558 0086536 1 +0170206 0290467 0 +0295513 0045773 0 +0044795 0309061 0 +0242473 0248370 0 +0187481 0187487 1 +0151852 0151853 1 +0093111 0077624 0 +0156954 0294655 0 +0192132 0074899 0 +0229340 0229335 1 +0168645 0174842 0 +0306241 0223589 0 +0043174 0044576 0 +0236563 0137877 0 +0042969 0217706 0 +0119238 0119252 1 +0147520 0100482 0 +0092588 0267877 0 +0060542 0153275 0 +0071969 0238413 0 +0294362 0294359 1 +0284105 0257764 0 +0283055 0283042 1 +0308607 0309259 0 +0140618 0119347 0 +0074763 0087501 0 +0102926 0033303 0 +0042392 0188231 0 +0053081 0227673 0 +0322085 0268838 0 +0076591 0150451 0 +0150242 0150237 1 +0112799 0087976 0 +0322056 0016035 0 +0072183 0326937 0 +0087507 0012492 0 +0013710 0016716 0 +0110456 0063156 0 +0308607 0157828 0 +0039215 0039214 1 +0205783 0205785 1 +0277994 0020871 0 +0088697 0086959 0 +0309139 0210394 0 +0055442 0022490 0 +0182819 0182811 1 +0077636 0077624 1 +0049530 0049529 1 +0013450 0023839 0 +0156130 0156098 1 +0227678 0157343 0 +0119682 0119683 1 +0260933 0197380 0 +0170206 0046686 0 +0121832 0224423 0 +0249323 0130801 0 +0265247 0265237 1 +0300070 0016867 0 +0187867 0278000 0 +0323464 0323487 1 +0089252 0232317 0 +0098633 0095049 0 +0016735 0238532 0 +0297116 0046686 0 +0067676 0235308 0 +0030054 0115260 0 +0066131 0319170 0 +0309061 0101553 0 +0195281 0200605 0 +0124863 0074763 0 +0217836 0063004 0 +0182110 0182109 1 +0137167 0137168 1 +0041397 0306839 0 +0056263 0309556 0 +0151074 0282466 0 +0033432 0306254 0 +0011717 0011698 1 +0086203 0184360 0 +0157629 0220378 0 +0126139 0267960 0 +0151852 0276089 0 +0260570 0039648 0 +0104581 0104569 1 +0066369 0182811 0 +0045103 0130978 0 +0298724 0003303 0 +0119347 0019428 0 +0166094 0166083 1 +0149779 0022589 0 +0151074 0151088 1 +0004535 0172033 0 +0162130 0027409 0 +0018417 0235336 0 +0276570 0285500 0 +0306363 0053443 0 +0238532 0045001 0 +0198805 0043410 0 +0095054 0025769 0 +0039662 0039648 1 +0025788 0025783 1 +0107042 0153212 0 +0075000 0249177 0 +0269655 0270244 0 +0178276 0053416 0 +0156086 0156098 1 +0266905 0097278 0 +0098303 0217836 0 +0038223 0107375 0 +0130331 0304521 0 +0098397 0269794 0 +0280126 0011717 0 +0008257 0026649 0 +0323487 0013450 0 +0260933 0048269 0 +0230356 0150675 0 +0306254 0306227 1 +0322415 0168984 0 +0155396 0166083 0 +0101685 0101672 1 +0136667 0086408 0 +0215797 0300818 0 +0097892 0063923 0 +0008086 0115260 0 +0228595 0031142 0 +0257700 0041025 0 +0227059 0280195 0 +0033136 0038831 0 +0162130 0107369 0 +0093295 0163096 0 +0217696 0217707 1 +0025769 0253507 0 +0238399 0238413 1 +0102926 0102930 1 +0190654 0190652 1 +0235626 0214276 0 +0273895 0316641 0 +0147520 0147529 1 +0185060 0294844 0 +0029052 0292837 0 +0322425 0195661 0 +0286499 0249177 0 +0227860 0227859 1 +0276570 0276576 1 +0098303 0214469 0 +0153267 0177931 0 +0044988 0114445 0 +0168645 0259253 0 +0188562 0188561 1 +0163982 0081581 0 +0116417 0245807 0 +0271837 0217662 0 +0000233 0241755 0 +0143794 0121832 0 +0155396 0155407 1 +0087355 0087364 1 +0029060 0076606 0 +0070203 0023835 0 +0016052 0016010 1 +0234366 0150450 0 +0008257 0008264 1 +0073989 0262643 0 +0092571 0319930 0 +0217674 0217662 1 +0157340 0102447 0 +0007618 0007637 1 +0034038 0187487 0 +0059422 0059418 1 +0304240 0030048 0 +0214271 0038223 0 +0280686 0033303 0 +0156329 0306231 0 +0195664 0116261 0 +0028859 0129192 0 +0127309 0109506 0 +0245099 0319568 0 +0316640 0316641 1 +0087978 0078708 0 +0283042 0279402 0 +0172690 0071969 0 +0020871 0020867 1 +0220694 0110367 0 +0270247 0270244 1 +0020479 0071969 0 +0307937 0238440 0 +0271816 0076655 0 +0162130 0229340 0 +0144136 0144126 1 +0168127 0321146 0 +0016025 0016035 1 +0018421 0099973 0 +0282466 0007051 0 +0072183 0014997 0 +0115260 0231933 0 +0043410 0318449 0 +0235463 0181825 0 +0026636 0123196 0 +0242464 0242488 1 +0257700 0277994 0 +0216423 0042786 0 +0196169 0060543 0 +0204876 0253507 0 +0227574 0227568 1 +0283050 0199281 0 +0265256 0265253 1 +0092574 0140616 0 +0133785 0133788 1 +0110465 0110456 1 +0089212 0198033 0 +0099975 0319172 0 +0111511 0065472 0 +0197380 0011684 0 +0092526 0022245 0 +0115279 0124306 0 +0121571 0200486 0 +0057425 0315348 0 +0094923 0054980 0 +0163982 0015305 0 +0234320 0027818 0 +0064859 0064855 1 +0265253 0265237 1 +0203596 0203592 1 +0150450 0300070 0 +0295516 0007754 0 +0099977 0210889 0 +0153275 0201802 0 +0168973 0094808 0 +0025109 0025089 1 +0319172 0088702 0 +0114949 0050965 0 +0299367 0168973 0 +0185058 0207953 0 +0179428 0114449 0 +0198033 0198032 1 +0066532 0276140 0 +0177586 0304635 0 +0306477 0257756 0 +0095054 0049603 0 +0130982 0204380 0 +0249194 0249177 1 +0202970 0202994 1 +0115279 0256607 0 +0247408 0247402 1 +0027825 0268216 0 +0028859 0034283 0 +0107049 0112814 0 +0251394 0073929 0 +0092571 0015178 0 +0269224 0171015 0 +0065769 0114473 0 +0125758 0053037 0 +0066022 0171104 0 +0110441 0304240 0 +0198344 0234320 0 +0115260 0115279 1 +0235461 0172675 0 +0124300 0168128 0 +0211327 0211320 1 +0204380 0204464 1 +0061504 0061485 1 +0066369 0053339 0 +0132450 0092588 0 +0268263 0124813 0 +0154106 0154122 1 +0071969 0279400 0 +0306845 0220694 0 +0216418 0000231 0 +0217662 0166094 0 +0053443 0053416 1 +0211329 0211322 1 +0300876 0244109 0 +0026636 0227568 0 +0164536 0143793 0 +0100476 0157828 0 +0201422 0307258 0 +0269794 0269796 1 +0238532 0140618 0 +0249322 0049529 0 +0045245 0045242 1 +0096632 0096587 1 +0164983 0179604 0 +0197385 0309254 0 +0066137 0013744 0 +0202441 0202427 1 +0078721 0130325 0 +0140618 0087355 0 +0048269 0267960 0 +0108030 0108023 1 +0198415 0022589 0 +0130331 0076606 0 +0130801 0309254 0 +0017032 0214582 0 +0114949 0172677 0 +0170050 0048266 0 +0134150 0107369 0 +0129296 0129307 1 +0174509 0130799 0 +0022491 0096611 0 +0309061 0247408 0 +0126696 0000554 0 +0074774 0156943 0 +0236532 0025788 0 +0235336 0074899 0 +0060543 0307258 0 +0214904 0214895 1 +0130801 0130799 1 +0170050 0285500 0 +0214465 0179428 0 +0086203 0301732 0 +0321146 0304635 0 +0158958 0205783 0 +0195661 0260945 0 +0168645 0187865 0 +0249323 0075426 0 +0013710 0243115 0 +0285504 0076605 0 +0081587 0227338 0 +0322971 0088702 0 +0217696 0217706 1 +0276091 0028343 0 +0235126 0300419 0 +0114449 0093111 0 +0304245 0022247 0 +0207307 0051748 0 +0230704 0130982 0 +0260751 0150237 0 +0098311 0098315 1 +0044988 0038831 0 +0078497 0072183 0 +0074774 0074763 1 +0267832 0267836 1 +0231934 0201800 0 +0281965 0129192 0 +0012501 0012492 1 +0210384 0101553 0 +0216423 0204065 0 +0188234 0188231 1 +0063641 0169043 0 +0020126 0028064 0 +0015305 0136667 0 +0045096 0299368 0 +0020474 0109476 0 +0060724 0236760 0 +0320159 0217836 0 +0011701 0011731 1 +0013717 0245094 0 +0293613 0007051 0 +0158958 0265253 0 +0194928 0300419 0 +0220378 0277994 0 +0137512 0156102 0 +0227059 0200605 0 +0213594 0066131 0 +0207309 0306477 0 +0112814 0089212 0 +0243135 0066547 0 +0188413 0188410 1 +0164530 0164532 1 +0030048 0276570 0 +0076665 0048133 0 +0254986 0254987 1 +0262658 0260570 0 +0075411 0075417 1 +0016010 0016043 1 +0017049 0017032 1 +0186013 0097436 0 +0150237 0130799 0 +0066215 0181653 0 +0249270 0065769 0 +0101538 0245807 0 +0092505 0270250 0 +0271837 0165314 0 +0306231 0306254 1 +0207953 0207955 1 +0227859 0227860 1 +0236575 0319172 0 +0211322 0211329 1 +0164530 0185060 0 +0231447 0013710 0 +0074878 0055200 0 +0045773 0168645 0 +0111511 0200555 0 +0212685 0170456 0 +0278136 0044578 0 +0129307 0207953 0 +0177586 0177585 1 +0264766 0108387 0 +0030054 0000574 0 +0110312 0033429 0 +0053038 0228606 0 +0086536 0086548 1 +0174853 0254568 0 +0151314 0272511 0 +0121832 0102447 0 +0063150 0063127 1 +0126706 0126696 1 +0277994 0020608 0 +0314540 0227338 0 +0222730 0300876 0 +0273621 0121033 0 +0300865 0082667 0 +0132454 0263746 0 +0063923 0245807 0 +0075411 0075419 1 +0322056 0007653 0 +0092574 0198033 0 +0282079 0260933 0 +0223589 0168984 0 +0236575 0143353 0 +0048311 0210888 0 +0005141 0061097 0 +0102453 0266908 0 +0307303 0137512 0 +0020582 0110297 0 +0166142 0319930 0 +0086408 0104645 0 +0166142 0166145 1 +0317565 0119105 0 +0108023 0306845 0 +0135857 0135839 1 +0306231 0297116 0 +0190947 0290467 0 +0218593 0092590 0 +0279400 0279402 1 +0022245 0082671 0 +0302451 0302455 1 +0219362 0294669 0 +0016710 0154049 0 +0049179 0066137 0 +0235651 0240486 0 +0250814 0207955 0 +0099603 0135023 0 +0292837 0292823 1 +0297116 0063923 0 +0164369 0273621 0 +0219958 0168645 0 +0231427 0159759 0 +0043410 0039707 0 +0260945 0081317 0 +0235463 0215795 0 +0166094 0269218 0 +0066547 0119683 0 +0214507 0269224 0 +0147937 0276140 0 +0179428 0076606 0 +0306254 0149696 0 +0211967 0211969 1 +0236760 0205783 0 +0101685 0063641 0 +0212688 0212685 1 +0141916 0022491 0 +0150675 0150678 1 +0119202 0111280 0 +0053338 0053339 1 +0025584 0025091 0 +0309150 0113653 0 +0040531 0174509 0 +0060542 0031142 0 +0126133 0249194 0 +0311448 0116432 0 +0157835 0119207 0 +0238532 0147937 0 +0015297 0015307 1 +0166142 0188410 0 +0111280 0179607 0 +0114473 0130331 0 +0234105 0154038 0 +0265882 0053443 0 +0020479 0119207 0 +0025584 0304519 0 +0137168 0217707 0 +0075417 0075419 1 +0043144 0171038 0 +0097896 0097892 1 +0219813 0318455 0 +0022589 0168630 0 +0291519 0300876 0 +0137437 0149786 0 +0031134 0089583 0 +0154038 0154047 1 +0141901 0141916 1 +0184360 0219464 0 +0202986 0035458 0 +0278136 0309744 0 +0129192 0251394 0 +0290478 0049597 0 +0114449 0263759 0 +0020127 0045103 0 +0302850 0276091 0 +0198421 0198415 1 +0151313 0053094 0 +0171690 0171689 1 +0291512 0236514 0 +0119111 0119105 1 +0033300 0033303 1 +0168630 0061485 0 +0245094 0245099 1 +0309254 0078497 0 +0292823 0126696 0 +0281965 0101672 0 +0017032 0004530 0 +0109503 0183624 0 +0050786 0134959 0 +0190654 0070203 0 +0114449 0269794 0 +0029187 0270225 0 +0274282 0274281 1 +0040531 0284166 0 +0213594 0026636 0 +0265253 0236514 0 +0235128 0235126 1 +0283055 0099975 0 +0014995 0156477 0 +0292838 0292837 1 +0111278 0111280 1 +0150237 0293613 0 +0293465 0008264 0 +0211320 0211329 1 +0020126 0087695 0 +0007061 0188231 0 +0107369 0118536 0 +0011698 0011717 1 +0029060 0001584 0 +0044576 0044578 1 +0053664 0053640 1 +0004538 0004530 1 +0029174 0185639 0 +0063166 0262658 0 +0076576 0195185 0 +0137027 0181262 0 +0119252 0274282 0 +0186409 0209298 0 +0199281 0184822 0 +0285500 0176683 0 +0087978 0059240 0 +0024097 0024098 1 +0230356 0007910 0 +0041388 0041397 1 +0066237 0001584 0 +0132451 0265256 0 +0143794 0075419 0 +0168969 0168973 1 +0052795 0025775 0 +0215795 0086383 0 +0135857 0326933 0 +0280664 0143793 0 +0242670 0242671 1 +0035469 0235651 0 +0240487 0240486 1 +0300865 0300868 1 +0076591 0287178 0 +0188561 0144136 0 +0114949 0174020 0 +0098224 0236532 0 +0302455 0300077 0 +0265268 0265269 1 +0187671 0234337 0 +0044229 0125715 0 +0015181 0270244 0 +0048309 0254987 0 +0245808 0286462 0 +0020608 0272511 0 +0292813 0015305 0 +0156943 0234320 0 +0156098 0098213 0 +0187481 0060755 0 +0017033 0017032 1 +0171689 0194928 0 +0034283 0047626 0 +0060751 0236532 0 +0130825 0023610 0 +0154128 0045001 0 +0100482 0100476 1 +0020126 0020127 1 +0087976 0087978 1 +0024097 0180814 0 +0181276 0181262 1 +0016043 0124813 0 +0170457 0170460 1 +0211967 0188333 0 +0025783 0130789 0 +0130325 0143793 0 +0155409 0155398 1 +0086959 0200140 0 +0300874 0015297 0 +0039257 0095049 0 +0094808 0171038 0 +0084948 0084944 1 +0107161 0039215 0 +0207953 0045096 0 +0045103 0097436 0 +0184360 0074878 0 +0065769 0065775 1 +0219808 0219807 1 +0120063 0130789 0 +0284105 0284128 1 +0025073 0086944 0 +0069008 0018421 0 +0125715 0125758 1 +0074147 0210394 0 +0157835 0157828 1 +0168630 0168645 1 +0236514 0121832 0 +0076605 0076606 1 +0178276 0110234 0 +0275996 0275997 1 +0275997 0275996 1 +0094923 0244132 0 +0199696 0045656 0 +0093113 0093111 1 +0304638 0022247 0 +0054980 0124863 0 +0257756 0043174 0 +0102933 0126108 0 +0304628 0304635 1 +0211322 0211320 1 +0215795 0215797 1 +0074147 0040532 0 +0249194 0184864 0 +0235125 0074878 0 +0264766 0315206 0 +0027422 0236762 0 +0083935 0309259 0 +0323487 0323480 1 +0156130 0024098 0 +0322425 0322415 1 +0024098 0143353 0 +0045783 0062890 0 +0147933 0077636 0 +0200564 0245094 0 +0029060 0207953 0 +0234105 0100482 0 +0235266 0235279 1 +0280186 0181262 0 +0149779 0089242 0 +0293613 0269639 0 +0013744 0229335 0 +0060543 0060542 1 +0269791 0300818 0 +0048269 0048267 1 +0289130 0218593 0 +0039257 0213597 0 +0074763 0234369 0 +0213594 0031996 0 +0019428 0019426 1 +0201396 0300874 0 +0150285 0150290 1 +0156096 0156130 1 +0150237 0102674 0 +0045594 0214904 0 +0271816 0022491 0 +0031134 0102455 0 +0000559 0044795 0 +0111510 0260570 0 +0063921 0295148 0 +0080495 0080508 1 +0304635 0306845 0 +0170460 0170457 1 +0300865 0214582 0 +0066365 0030172 0 +0036691 0178277 0 +0110434 0000554 0 +0066365 0111278 0 +0097277 0276138 0 +0101109 0267741 0 +0235461 0198421 0 +0104569 0104581 1 +0177223 0169043 0 +0087978 0302850 0 +0086383 0033300 0 +0265253 0265247 1 +0092588 0150237 0 +0322085 0053339 0 +0057462 0182110 0 +0047705 0047687 1 +0106267 0106260 1 +0089211 0089212 1 +0086259 0093111 0 +0156329 0057425 0 +0061504 0028350 0 +0129307 0284128 0 +0306845 0074878 0 +0048267 0048266 1 +0307263 0159759 0 +0030054 0030048 1 +0232313 0029187 0 +0234105 0064859 0 +0238428 0049847 0 +0274282 0220694 0 +0127305 0155415 0 +0200487 0257172 0 +0096611 0127305 0 +0326020 0236514 0 +0294844 0051263 0 +0221296 0011698 0 +0024098 0186022 0 +0055455 0007637 0 +0275993 0101114 0 +0043410 0306823 0 +0202434 0022247 0 +0130331 0127305 0 +0151314 0154049 0 +0081587 0141907 0 +0184864 0279568 0 +0257681 0087900 0 +0227568 0271837 0 +0085519 0235308 0 +0089212 0089211 1 +0326937 0118456 0 +0101553 0101538 1 +0045096 0107042 0 +0017033 0017052 1 +0007653 0110318 0 +0134144 0134150 1 +0024298 0045594 0 +0170456 0170460 1 +0042980 0042969 1 +0249322 0092574 0 +0141916 0216486 0 +0239193 0063150 0 +0088463 0023608 0 +0214469 0072660 0 +0053037 0046680 0 +0228595 0124813 0 +0007910 0119252 0 +0210888 0210889 1 +0031996 0032018 1 +0321152 0326020 0 +0256608 0164983 0 +0181315 0205785 0 +0163096 0304643 0 +0109476 0137515 0 +0176667 0171690 0 +0176007 0156130 0 +0038245 0089583 0 +0118536 0118540 1 +0304504 0000569 0 +0015111 0015122 1 +0291512 0291519 1 +0184864 0282471 0 +0056263 0297116 0 +0116258 0116270 1 +0276576 0265882 0 +0067562 0268838 0 +0088697 0202441 0 +0273621 0309143 0 +0071969 0184356 0 +0109506 0248370 0 +0007654 0205783 0 +0069008 0005697 0 +0257681 0199281 0 +0080495 0188561 0 +0200999 0147122 0 +0200998 0158958 0 +0066532 0066547 1 +0269791 0260570 0 +0012629 0041397 0 +0097436 0292838 0 +0059422 0033286 0 +0075274 0292823 0 +0199692 0012501 0 +0188324 0307258 0 +0325529 0325539 1 +0035469 0020474 0 +0262658 0118455 0 +0281826 0210888 0 +0231447 0231427 1 +0248384 0082154 0 +0279402 0279400 1 +0300428 0290478 0 +0066215 0244109 0 +0028064 0089252 0 +0242473 0242488 1 +0274281 0276144 0 +0276576 0029187 0 +0326933 0216530 0 +0162129 0073932 0 +0235461 0211322 0 +0187674 0187671 1 +0110332 0110365 1 +0129190 0012629 0 +0209298 0209295 1 +0276448 0276459 1 +0199696 0052796 0 +0147529 0147520 1 +0110441 0041008 0 +0156484 0092505 0 +0017032 0131250 0 +0026636 0230706 0 +0041397 0041400 1 +0073929 0106742 0 +0283105 0085519 0 +0217706 0217707 1 +0294844 0251394 0 +0055200 0159759 0 +0051748 0320161 0 +0116432 0304236 0 +0136675 0315348 0 +0236514 0188234 0 +0200486 0070203 0 +0282078 0282079 1 +0098409 0177780 0 +0028343 0028350 1 +0013747 0228595 0 +0114473 0066365 0 +0300868 0247408 0 +0073929 0238428 0 +0244123 0217662 0 +0098311 0178068 0 +0092505 0092526 1 +0254568 0045103 0 +0013744 0044230 0 +0239533 0253707 0 +0042980 0033136 0 +0282469 0250814 0 +0125758 0051251 0 +0093113 0013744 0 +0192139 0282471 0 +0137028 0317555 0 +0286499 0286498 1 +0015307 0044988 0 +0054979 0149786 0 +0240477 0240487 1 +0263746 0015181 0 +0026645 0263750 0 +0235629 0235626 1 +0283042 0283054 1 +0049530 0310327 0 +0243135 0243115 1 +0232317 0053664 0 +0291519 0275997 0 +0064859 0214895 0 +0219813 0106731 0 +0092505 0240486 0 +0105734 0105726 1 +0209298 0250833 0 +0235265 0270225 0 +0156477 0039250 0 +0151074 0174853 0 +0035458 0023839 0 +0307937 0319928 0 +0134974 0135018 1 +0306375 0306363 1 +0171006 0171038 1 +0293464 0165312 0 +0080091 0080098 1 +0076576 0147520 0 +0097277 0235128 0 +0319565 0319568 1 +0184864 0102444 0 +0248384 0274281 0 +0119111 0119104 1 +0067573 0067562 1 +0234105 0122320 0 +0229204 0229206 1 +0266908 0030172 0 +0110312 0220694 0 +0126139 0056257 0 +0308606 0097278 0 +0089583 0300817 0 +0141901 0268827 0 +0045242 0060722 0 +0276140 0276138 1 +0178277 0178276 1 +0090139 0195279 0 +0048269 0056263 0 +0276138 0231441 0 +0300070 0181655 0 +0253675 0253707 1 +0117882 0245807 0 +0306254 0306241 1 +0104569 0104584 1 +0082152 0251395 0 +0200998 0105734 0 +0045242 0025775 0 +0043144 0047624 0 +0234369 0112814 0 +0035624 0188410 0 +0276459 0151852 0 +0063923 0040999 0 +0207307 0309558 0 +0294655 0130829 0 +0113653 0302451 0 +0066365 0066369 1 +0304240 0304236 1 +0239533 0300817 0 +0029060 0282471 0 +0147529 0207955 0 +0053443 0070208 0 +0087190 0150290 0 +0240477 0240486 1 +0105737 0105734 1 +0164983 0063143 0 +0120059 0155396 0 +0181315 0124306 0 +0194958 0110456 0 +0027409 0064859 0 +0045594 0045595 1 +0300818 0267890 0 +0183624 0092526 0 +0174200 0174197 1 +0304628 0166094 0 +0306845 0306839 1 +0177919 0165899 0 +0007654 0253500 0 +0279572 0049529 0 +0178075 0065472 0 +0156954 0232313 0 +0000554 0144126 0 +0302850 0302840 1 +0174017 0174020 1 +0271837 0122320 0 +0176667 0018421 0 +0319172 0282471 0 +0181825 0020127 0 +0196947 0118455 0 +0081318 0275988 0 +0029174 0147937 0 +0179604 0111510 0 +0026636 0072183 0 +0075271 0039214 0 +0116417 0116432 1 +0306363 0229335 0 +0035458 0082152 0 +0212685 0212688 1 +0170457 0149696 0 +0285500 0013744 0 +0130799 0044576 0 +0202427 0202441 1 +0202986 0060724 0 +0082152 0236532 0 +0198415 0044578 0 +0038833 0012501 0 +0269791 0164372 0 +0055442 0165897 0 +0192139 0192132 1 +0249322 0279529 0 +0086959 0182473 0 +0106731 0228606 0 +0060755 0076665 0 +0172690 0108730 0 +0185058 0064855 0 +0245807 0180817 0 +0099973 0074766 0 +0162129 0265253 0 +0236507 0130789 0 +0038831 0168630 0 +0286646 0037019 0 +0084944 0029174 0 +0235250 0235279 1 +0222730 0114445 0 +0131255 0053094 0 +0306363 0081581 0 +0126133 0144126 0 +0097892 0097887 1 +0097278 0076655 0 +0153215 0057463 0 diff --git a/models/cv/trace/repnet/igie/scripts/infer_repnet_fp16_accuracy.sh b/models/cv/trace/repnet/igie/scripts/infer_repnet_fp16_accuracy.sh new file mode 100644 index 00000000..b8305122 --- /dev/null +++ b/models/cv/trace/repnet/igie/scripts/infer_repnet_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="repnet_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path repnet_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine repnet_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/trace/repnet/igie/scripts/infer_repnet_fp16_performance.sh b/models/cv/trace/repnet/igie/scripts/infer_repnet_fp16_performance.sh new file mode 100644 index 00000000..ae0fb0bb --- /dev/null +++ b/models/cv/trace/repnet/igie/scripts/infer_repnet_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="repnet_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,224,224 \ + --precision fp16 \ + --engine_path repnet_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine repnet_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From 4290e3124d4a8660185b77f9a6cf41559b543cd9 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Wed, 17 Apr 2024 16:00:59 +0800 Subject: [PATCH 15/19] Add: centernet inference script. --- models/cv/detection/centernet/README.md | 51 +++ ...ernet_r18-dcnv2_8xb16-crop512-140e_coco.py | 151 +++++++ .../centernet/base/coco_detection.py | 75 ++++ .../centernet/base/default_runtime.py | 39 ++ .../detection/centernet/base/schedule_1x.py | 43 ++ models/cv/detection/centernet/build_engine.py | 73 ++++ .../centernet_r18_8xb16-crop512-140e_coco.py | 369 ++++++++++++++++++ .../cv/detection/centernet/deploy_default.py | 41 ++ models/cv/detection/centernet/export.py | 74 ++++ models/cv/detection/centernet/inference.py | 149 +++++++ .../scripts/infer_centernet_fp16_accuracy.sh | 35 ++ .../infer_centernet_fp16_performance.sh | 36 ++ 12 files changed, 1136 insertions(+) create mode 100644 models/cv/detection/centernet/README.md create mode 100644 models/cv/detection/centernet/base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py create mode 100644 models/cv/detection/centernet/base/coco_detection.py create mode 100644 models/cv/detection/centernet/base/default_runtime.py create mode 100644 models/cv/detection/centernet/base/schedule_1x.py create mode 100644 models/cv/detection/centernet/build_engine.py create mode 100644 models/cv/detection/centernet/centernet_r18_8xb16-crop512-140e_coco.py create mode 100644 models/cv/detection/centernet/deploy_default.py create mode 100644 models/cv/detection/centernet/export.py create mode 100644 models/cv/detection/centernet/inference.py create mode 100644 models/cv/detection/centernet/scripts/infer_centernet_fp16_accuracy.sh create mode 100644 models/cv/detection/centernet/scripts/infer_centernet_fp16_performance.sh diff --git a/models/cv/detection/centernet/README.md b/models/cv/detection/centernet/README.md new file mode 100644 index 00000000..96622a01 --- /dev/null +++ b/models/cv/detection/centernet/README.md @@ -0,0 +1,51 @@ +# RetinaNet + +## Description +RetinaNet, an innovative object detector, challenges the conventional trade-off between speed and accuracy in the realm of computer vision. Traditionally, two-stage detectors, exemplified by R-CNN, achieve high accuracy by applying a classifier to a limited set of candidate object locations. In contrast, one-stage detectors, like RetinaNet, operate over a dense sampling of possible object locations, aiming for simplicity and speed. + +## Setup + +### Install +```bash +yum install mesa-libGL +pip3 install onnx +pip3 install tqdm +pip3 install mmdet +pip3 install mmdeploy +pip3 install mmengine +``` + +### Download + +Pretrained model: < https://download.openmmlab.com/mmdetection/v2.0/centernet/centernet_resnet18_140e_coco/centernet_resnet18_140e_coco_20210705_093630-bb5b3bf7.pth> + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +# export onnx model +python3 export.py --weight centernet_resnet18_140e_coco_20210705_093630-bb5b3bf7.pth --cfg centernet_r18_8xb16-crop512-140e_coco.py --output centernet.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/coco/ +``` +### FP16 + +```bash +# Accuracy +bash scripts/infer_centernet_fp16_accuracy.sh +# Performance +bash scripts/infer_centernet_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |IOU@0.5 |IOU@0.5:0.95 | +----------|-----------|----------|----------|----------|---------------| +Centernet | 32 | FP16 | 799.70 | 0.423 | 0.258 | + +## Reference + +mmdetection: https://github.com/open-mmlab/mmdetection.git diff --git a/models/cv/detection/centernet/base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py b/models/cv/detection/centernet/base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py new file mode 100644 index 00000000..894e4b4f --- /dev/null +++ b/models/cv/detection/centernet/base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py @@ -0,0 +1,151 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +_base_ = [ + 'coco_detection.py', + 'schedule_1x.py', 'default_runtime.py', +] + +dataset_type = 'CocoDataset' +data_root = 'data/coco/' + +# model settings +model = dict( + type='CenterNet', + data_preprocessor=dict( + type='DetDataPreprocessor', + mean=[123.675, 116.28, 103.53], + std=[58.395, 57.12, 57.375], + bgr_to_rgb=True), + backbone=dict( + type='ResNet', + depth=18, + norm_eval=False, + norm_cfg=dict(type='BN'), + init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')), + neck=dict( + type='CTResNetNeck', + in_channels=512, + num_deconv_filters=(256, 128, 64), + num_deconv_kernels=(4, 4, 4), + use_dcn=True), + bbox_head=dict( + type='CenterNetHead', + num_classes=80, + in_channels=64, + feat_channels=64, + loss_center_heatmap=dict(type='GaussianFocalLoss', loss_weight=1.0), + loss_wh=dict(type='L1Loss', loss_weight=0.1), + loss_offset=dict(type='L1Loss', loss_weight=1.0)), + train_cfg=None, + test_cfg=dict(topk=100, local_maximum_kernel=3, max_per_img=100)) + +train_pipeline = [ + dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}), + dict(type='LoadAnnotations', with_bbox=True), + dict( + type='PhotoMetricDistortion', + brightness_delta=32, + contrast_range=(0.5, 1.5), + saturation_range=(0.5, 1.5), + hue_delta=18), + dict( + type='RandomCenterCropPad', + # The cropped images are padded into squares during training, + # but may be less than crop_size. + crop_size=(512, 512), + ratios=(0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3), + mean=[0, 0, 0], + std=[1, 1, 1], + to_rgb=True, + test_pad_mode=None), + # Make sure the output is always crop_size. + dict(type='Resize', scale=(512, 512), keep_ratio=True), + dict(type='RandomFlip', prob=0.5), + dict(type='PackDetInputs') +] +test_pipeline = [ + dict( + type='LoadImageFromFile', + backend_args={{_base_.backend_args}}, + to_float32=True), + # don't need Resize + dict( + type='RandomCenterCropPad', + ratios=None, + border=None, + mean=[0, 0, 0], + std=[1, 1, 1], + to_rgb=True, + test_mode=True, + test_pad_mode=['logical_or', 31], + test_pad_add_pix=1), + dict(type='LoadAnnotations', with_bbox=True), + dict( + type='PackDetInputs', + meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'border')) +] + +# Use RepeatDataset to speed up training +train_dataloader = dict( + batch_size=16, + num_workers=4, + persistent_workers=True, + sampler=dict(type='DefaultSampler', shuffle=True), + dataset=dict( + _delete_=True, + type='RepeatDataset', + times=5, + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file='annotations/instances_train2017.json', + data_prefix=dict(img='train2017/'), + filter_cfg=dict(filter_empty_gt=True, min_size=32), + pipeline=train_pipeline, + backend_args={{_base_.backend_args}}, + ))) + +val_dataloader = dict(dataset=dict(pipeline=test_pipeline)) +test_dataloader = val_dataloader + +# optimizer +# Based on the default settings of modern detectors, the SGD effect is better +# than the Adam in the source code, so we use SGD default settings and +# if you use adam+lr5e-4, the map is 29.1. +optim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2)) + +max_epochs = 28 +# learning policy +# Based on the default settings of modern detectors, we added warmup settings. +param_scheduler = [ + dict( + type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, + end=1000), + dict( + type='MultiStepLR', + begin=0, + end=max_epochs, + by_epoch=True, + milestones=[18, 24], # the real step is [18*5, 24*5] + gamma=0.1) +] +train_cfg = dict(max_epochs=max_epochs) # the real epoch is 28*5=140 + +# NOTE: `auto_scale_lr` is for automatically scaling LR, +# USER SHOULD NOT CHANGE ITS VALUES. +# base_batch_size = (8 GPUs) x (16 samples per GPU) +auto_scale_lr = dict(base_batch_size=128) diff --git a/models/cv/detection/centernet/base/coco_detection.py b/models/cv/detection/centernet/base/coco_detection.py new file mode 100644 index 00000000..f58fe67b --- /dev/null +++ b/models/cv/detection/centernet/base/coco_detection.py @@ -0,0 +1,75 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# dataset settings +dataset_type = 'CocoDataset' +data_root = 'data/coco/' + +backend_args = None + +train_pipeline = [ + dict(type='LoadImageFromFile', backend_args=backend_args), + dict(type='LoadAnnotations', with_bbox=True), + dict(type='Resize', scale=(1333, 800), keep_ratio=True), + dict(type='RandomFlip', prob=0.5), + dict(type='PackDetInputs') +] +test_pipeline = [ + dict(type='LoadImageFromFile', backend_args=backend_args), + dict(type='Resize', scale=(1333, 800), keep_ratio=True), + # If you don't have a gt annotation, delete the pipeline + dict(type='LoadAnnotations', with_bbox=True), + dict( + type='PackDetInputs', + meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', + 'scale_factor')) +] +train_dataloader = dict( + batch_size=2, + num_workers=2, + persistent_workers=True, + sampler=dict(type='DefaultSampler', shuffle=True), + batch_sampler=dict(type='AspectRatioBatchSampler'), + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file='annotations/instances_train2017.json', + data_prefix=dict(img='train2017/'), + filter_cfg=dict(filter_empty_gt=True, min_size=32), + pipeline=train_pipeline, + backend_args=backend_args)) +val_dataloader = dict( + batch_size=1, + num_workers=2, + persistent_workers=True, + drop_last=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file='annotations/instances_val2017.json', + data_prefix=dict(img='val2017/'), + test_mode=True, + pipeline=test_pipeline, + backend_args=backend_args)) +test_dataloader = val_dataloader + +val_evaluator = dict( + type='CocoMetric', + ann_file=data_root + 'annotations/instances_val2017.json', + metric='bbox', + format_only=False, + backend_args=backend_args) +test_evaluator = val_evaluator \ No newline at end of file diff --git a/models/cv/detection/centernet/base/default_runtime.py b/models/cv/detection/centernet/base/default_runtime.py new file mode 100644 index 00000000..609d8037 --- /dev/null +++ b/models/cv/detection/centernet/base/default_runtime.py @@ -0,0 +1,39 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +default_scope = 'mmdet' + +default_hooks = dict( + timer=dict(type='IterTimerHook'), + logger=dict(type='LoggerHook', interval=50), + param_scheduler=dict(type='ParamSchedulerHook'), + checkpoint=dict(type='CheckpointHook', interval=1), + sampler_seed=dict(type='DistSamplerSeedHook'), + visualization=dict(type='DetVisualizationHook')) + +env_cfg = dict( + cudnn_benchmark=False, + mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0), + dist_cfg=dict(backend='nccl'), +) + +vis_backends = [dict(type='LocalVisBackend')] +visualizer = dict( + type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer') +log_processor = dict(type='LogProcessor', window_size=50, by_epoch=True) + +log_level = 'INFO' +load_from = None +resume = False diff --git a/models/cv/detection/centernet/base/schedule_1x.py b/models/cv/detection/centernet/base/schedule_1x.py new file mode 100644 index 00000000..9b16d80c --- /dev/null +++ b/models/cv/detection/centernet/base/schedule_1x.py @@ -0,0 +1,43 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# training schedule for 1x +train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_interval=1) +val_cfg = dict(type='ValLoop') +test_cfg = dict(type='TestLoop') + +# learning rate +param_scheduler = [ + dict( + type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500), + dict( + type='MultiStepLR', + begin=0, + end=12, + by_epoch=True, + milestones=[8, 11], + gamma=0.1) +] + +# optimizer +optim_wrapper = dict( + type='OptimWrapper', + optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)) + +# Default setting for scaling LR automatically +# - `enable` means enable scaling LR automatically +# or not by default. +# - `base_batch_size` = (8 GPUs) x (2 samples per GPU). +auto_scale_lr = dict(enable=False, base_batch_size=16) diff --git a/models/cv/detection/centernet/build_engine.py b/models/cv/detection/centernet/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/detection/centernet/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/detection/centernet/centernet_r18_8xb16-crop512-140e_coco.py b/models/cv/detection/centernet/centernet_r18_8xb16-crop512-140e_coco.py new file mode 100644 index 00000000..81a21421 --- /dev/null +++ b/models/cv/detection/centernet/centernet_r18_8xb16-crop512-140e_coco.py @@ -0,0 +1,369 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +auto_scale_lr = dict(base_batch_size=128, enable=False) +backend_args = None +data_root = 'data/coco/' +dataset_type = 'CocoDataset' +default_hooks = dict( + checkpoint=dict(interval=1, type='CheckpointHook'), + logger=dict(interval=50, type='LoggerHook'), + param_scheduler=dict(type='ParamSchedulerHook'), + sampler_seed=dict(type='DistSamplerSeedHook'), + timer=dict(type='IterTimerHook'), + visualization=dict(type='DetVisualizationHook')) +default_scope = 'mmdet' +env_cfg = dict( + cudnn_benchmark=False, + dist_cfg=dict(backend='nccl'), + mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0)) +load_from = None +log_level = 'INFO' +log_processor = dict(by_epoch=True, type='LogProcessor', window_size=50) +max_epochs = 28 +model = dict( + backbone=dict( + depth=18, + init_cfg=dict(checkpoint='torchvision://resnet18', type='Pretrained'), + norm_cfg=dict(type='BN'), + norm_eval=False, + type='ResNet'), + bbox_head=dict( + feat_channels=64, + in_channels=64, + loss_center_heatmap=dict(loss_weight=1.0, type='GaussianFocalLoss'), + loss_offset=dict(loss_weight=1.0, type='L1Loss'), + loss_wh=dict(loss_weight=0.1, type='L1Loss'), + num_classes=80, + type='CenterNetHead'), + data_preprocessor=dict( + bgr_to_rgb=True, + mean=[ + 123.675, + 116.28, + 103.53, + ], + std=[ + 58.395, + 57.12, + 57.375, + ], + type='DetDataPreprocessor'), + neck=dict( + in_channels=512, + num_deconv_filters=( + 256, + 128, + 64, + ), + num_deconv_kernels=( + 4, + 4, + 4, + ), + type='CTResNetNeck', + use_dcn=False), + test_cfg=dict(local_maximum_kernel=3, max_per_img=100, topk=100), + train_cfg=None, + type='CenterNet') +optim_wrapper = dict( + clip_grad=dict(max_norm=35, norm_type=2), + optimizer=dict(lr=0.02, momentum=0.9, type='SGD', weight_decay=0.0001), + type='OptimWrapper') +param_scheduler = [ + dict( + begin=0, by_epoch=False, end=1000, start_factor=0.001, + type='LinearLR'), + dict( + begin=0, + by_epoch=True, + end=28, + gamma=0.1, + milestones=[ + 18, + 24, + ], + type='MultiStepLR'), +] +resume = False +test_cfg = dict(type='TestLoop') +test_dataloader = dict( + batch_size=32, + dataset=dict( + ann_file='annotations/instances_val2017.json', + backend_args=None, + data_prefix=dict(img='images/val2017/'), + data_root='/home/peng.yang/Datasets/coco', + pipeline=[ + dict(backend_args=None, to_float32=True, type='LoadImageFromFile'), + dict( + border=None, + mean=[ + 0, + 0, + 0, + ], + ratios=None, + std=[ + 1, + 1, + 1, + ], + test_mode=True, + test_pad_add_pix=1, + test_pad_mode=[ + 'logical_or', + 31, + ], + to_rgb=True, + type='RandomCenterCropPad'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'border', + ), + type='PackDetInputs'), + ], + test_mode=True, + type='CocoDataset'), + drop_last=False, + num_workers=2, + persistent_workers=True, + sampler=dict(shuffle=False, type='DefaultSampler')) +test_evaluator = dict( + ann_file='/home/peng.yang/Datasets/coco/annotations/instances_val2017.json', + backend_args=None, + format_only=False, + metric='bbox', + type='CocoMetric') +test_pipeline = [ + dict(backend_args=None, to_float32=True, type='LoadImageFromFile'), + dict( + border=None, + mean=[ + 0, + 0, + 0, + ], + ratios=None, + std=[ + 1, + 1, + 1, + ], + test_mode=True, + test_pad_add_pix=1, + test_pad_mode=[ + 'logical_or', + 31, + ], + to_rgb=True, + type='RandomCenterCropPad'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'border', + ), + type='PackDetInputs'), +] +train_cfg = dict(max_epochs=28, type='EpochBasedTrainLoop', val_interval=1) +train_dataloader = dict( + batch_sampler=dict(type='AspectRatioBatchSampler'), + batch_size=16, + dataset=dict( + dataset=dict( + ann_file='annotations/instances_train2017.json', + backend_args=None, + data_prefix=dict(img='train2017/'), + data_root='data/coco/', + filter_cfg=dict(filter_empty_gt=True, min_size=32), + pipeline=[ + dict(backend_args=None, type='LoadImageFromFile'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + brightness_delta=32, + contrast_range=( + 0.5, + 1.5, + ), + hue_delta=18, + saturation_range=( + 0.5, + 1.5, + ), + type='PhotoMetricDistortion'), + dict( + crop_size=( + 512, + 512, + ), + mean=[ + 0, + 0, + 0, + ], + ratios=( + 0.6, + 0.7, + 0.8, + 0.9, + 1.0, + 1.1, + 1.2, + 1.3, + ), + std=[ + 1, + 1, + 1, + ], + test_pad_mode=None, + to_rgb=True, + type='RandomCenterCropPad'), + dict(keep_ratio=True, scale=( + 512, + 512, + ), type='Resize'), + dict(prob=0.5, type='RandomFlip'), + dict(type='PackDetInputs'), + ], + type='CocoDataset'), + times=5, + type='RepeatDataset'), + num_workers=4, + persistent_workers=True, + sampler=dict(shuffle=True, type='DefaultSampler')) +train_pipeline = [ + dict(backend_args=None, type='LoadImageFromFile'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + brightness_delta=32, + contrast_range=( + 0.5, + 1.5, + ), + hue_delta=18, + saturation_range=( + 0.5, + 1.5, + ), + type='PhotoMetricDistortion'), + dict( + crop_size=( + 512, + 512, + ), + mean=[ + 0, + 0, + 0, + ], + ratios=( + 0.6, + 0.7, + 0.8, + 0.9, + 1.0, + 1.1, + 1.2, + 1.3, + ), + std=[ + 1, + 1, + 1, + ], + test_pad_mode=None, + to_rgb=True, + type='RandomCenterCropPad'), + dict(keep_ratio=True, scale=( + 512, + 512, + ), type='Resize'), + dict(prob=0.5, type='RandomFlip'), + dict(type='PackDetInputs'), +] +val_cfg = dict(type='ValLoop') +val_dataloader = dict( + batch_size=1, + dataset=dict( + ann_file='annotations/instances_val2017.json', + backend_args=None, + data_prefix=dict(img='val2017/'), + data_root='data/coco/', + pipeline=[ + dict(backend_args=None, to_float32=True, type='LoadImageFromFile'), + dict( + border=None, + mean=[ + 0, + 0, + 0, + ], + ratios=None, + std=[ + 1, + 1, + 1, + ], + test_mode=True, + test_pad_add_pix=1, + test_pad_mode=[ + 'logical_or', + 31, + ], + to_rgb=True, + type='RandomCenterCropPad'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'border', + ), + type='PackDetInputs'), + ], + test_mode=True, + type='CocoDataset'), + drop_last=False, + num_workers=2, + persistent_workers=True, + sampler=dict(shuffle=False, type='DefaultSampler')) +val_evaluator = dict( + ann_file='data/coco/annotations/instances_val2017.json', + backend_args=None, + format_only=False, + metric='bbox', + type='CocoMetric') +vis_backends = [ + dict(type='LocalVisBackend'), +] +visualizer = dict( + name='visualizer', + type='DetLocalVisualizer', + vis_backends=[ + dict(type='LocalVisBackend'), + ]) +work_dir = './' diff --git a/models/cv/detection/centernet/deploy_default.py b/models/cv/detection/centernet/deploy_default.py new file mode 100644 index 00000000..b8d8e43d --- /dev/null +++ b/models/cv/detection/centernet/deploy_default.py @@ -0,0 +1,41 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +onnx_config = dict( + type='onnx', + export_params=True, + keep_initializers_as_inputs=False, + opset_version=11, + save_file='end2end.onnx', + input_names=['input'], + output_names=['output'], + input_shape=None, + optimize=True) + +codebase_config = dict( + type='mmdet', + task='ObjectDetection', + model_type='end2end', + post_processing=dict( + score_threshold=0.05, + confidence_threshold=0.005, + iou_threshold=0.5, + max_output_boxes_per_class=200, + pre_top_k=5000, + keep_top_k=100, + background_label_id=-1, + )) + +backend_config = dict(type='onnxruntime') \ No newline at end of file diff --git a/models/cv/detection/centernet/export.py b/models/cv/detection/centernet/export.py new file mode 100644 index 00000000..25672ab2 --- /dev/null +++ b/models/cv/detection/centernet/export.py @@ -0,0 +1,74 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import argparse + +import torch +from mmdeploy.utils import load_config +from mmdeploy.apis import build_task_processor + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--cfg", + type=str, + required=True, + help="model config file.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + deploy_cfg = 'deploy_default.py' + model_cfg = args.cfg + model_checkpoint = args.weight + + deploy_cfg, model_cfg = load_config(deploy_cfg, model_cfg) + + task_processor = build_task_processor(model_cfg, deploy_cfg, device='cpu') + + model = task_processor.build_pytorch_model(model_checkpoint) + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 672, 672) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == '__main__': + main() + diff --git a/models/cv/detection/centernet/inference.py b/models/cv/detection/centernet/inference.py new file mode 100644 index 00000000..d3417486 --- /dev/null +++ b/models/cv/detection/centernet/inference.py @@ -0,0 +1,149 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from mmdet.registry import RUNNERS +from mmengine.config import Config + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # Runner config + cfg = Config.fromfile("centernet_r18_8xb16-crop512-140e_coco.py") + cfg.work_dir = "./" + + cfg['test_dataloader']['batch_size'] = batch_size + cfg['test_dataloader']['dataset']['data_root'] = args.datasets + cfg['test_dataloader']['dataset']['data_prefix']['img'] = 'images/val2017/' + cfg['test_evaluator']['ann_file'] = os.path.join(args.datasets, 'annotations/instances_val2017.json') + + runner = RUNNERS.build(cfg) + + for input_data in tqdm(runner.test_dataloader): + + input_data = runner.model.data_preprocessor(input_data, False) + image = input_data['inputs'].cpu() + + pad_batch = len(image) != batch_size + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + module.run() + + outputs = [] + + for i in range(module.get_num_outputs()): + output = module.get_output(i).asnumpy() + + if pad_batch: + output = output[:origin_size] + + output = torch.from_numpy(output) + outputs.append(output) + + batch_img_metas = [ + data_samples.metainfo for data_samples in input_data['data_samples'] + ] + + results_list = runner.model.bbox_head.predict_by_feat([outputs[0]], [outputs[1]], [outputs[2]], batch_img_metas=batch_img_metas, rescale=True) + + batch_data_samples = runner.model.add_pred_to_datasample(input_data['data_samples'], results_list) + + runner.test_evaluator.process(data_samples=batch_data_samples, data_batch=input_data) + + metrics = runner.test_evaluator.evaluate(len(runner.test_dataloader.dataset)) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/detection/centernet/scripts/infer_centernet_fp16_accuracy.sh b/models/cv/detection/centernet/scripts/infer_centernet_fp16_accuracy.sh new file mode 100644 index 00000000..4cbf2e21 --- /dev/null +++ b/models/cv/detection/centernet/scripts/infer_centernet_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="centernet.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,672,672 \ + --precision fp16 \ + --engine_path centernet_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine centernet_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/detection/centernet/scripts/infer_centernet_fp16_performance.sh b/models/cv/detection/centernet/scripts/infer_centernet_fp16_performance.sh new file mode 100644 index 00000000..1d3c6955 --- /dev/null +++ b/models/cv/detection/centernet/scripts/infer_centernet_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="centernet.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,672,672 \ + --precision fp16 \ + --engine_path centernet_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine centernet_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From 5971104500ae8849cac4e659897e8d263548ae6b Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Wed, 17 Apr 2024 16:07:02 +0800 Subject: [PATCH 16/19] Update: centernet path. --- models/cv/detection/centernet/{ => igie}/README.md | 0 .../base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py | 0 models/cv/detection/centernet/{ => igie}/base/coco_detection.py | 0 models/cv/detection/centernet/{ => igie}/base/default_runtime.py | 0 models/cv/detection/centernet/{ => igie}/base/schedule_1x.py | 0 models/cv/detection/centernet/{ => igie}/build_engine.py | 0 .../centernet/{ => igie}/centernet_r18_8xb16-crop512-140e_coco.py | 0 models/cv/detection/centernet/{ => igie}/deploy_default.py | 0 models/cv/detection/centernet/{ => igie}/export.py | 0 models/cv/detection/centernet/{ => igie}/inference.py | 0 .../centernet/{ => igie}/scripts/infer_centernet_fp16_accuracy.sh | 0 .../{ => igie}/scripts/infer_centernet_fp16_performance.sh | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename models/cv/detection/centernet/{ => igie}/README.md (100%) rename models/cv/detection/centernet/{ => igie}/base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py (100%) rename models/cv/detection/centernet/{ => igie}/base/coco_detection.py (100%) rename models/cv/detection/centernet/{ => igie}/base/default_runtime.py (100%) rename models/cv/detection/centernet/{ => igie}/base/schedule_1x.py (100%) rename models/cv/detection/centernet/{ => igie}/build_engine.py (100%) rename models/cv/detection/centernet/{ => igie}/centernet_r18_8xb16-crop512-140e_coco.py (100%) rename models/cv/detection/centernet/{ => igie}/deploy_default.py (100%) rename models/cv/detection/centernet/{ => igie}/export.py (100%) rename models/cv/detection/centernet/{ => igie}/inference.py (100%) rename models/cv/detection/centernet/{ => igie}/scripts/infer_centernet_fp16_accuracy.sh (100%) rename models/cv/detection/centernet/{ => igie}/scripts/infer_centernet_fp16_performance.sh (100%) diff --git a/models/cv/detection/centernet/README.md b/models/cv/detection/centernet/igie/README.md similarity index 100% rename from models/cv/detection/centernet/README.md rename to models/cv/detection/centernet/igie/README.md diff --git a/models/cv/detection/centernet/base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py b/models/cv/detection/centernet/igie/base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py similarity index 100% rename from models/cv/detection/centernet/base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py rename to models/cv/detection/centernet/igie/base/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py diff --git a/models/cv/detection/centernet/base/coco_detection.py b/models/cv/detection/centernet/igie/base/coco_detection.py similarity index 100% rename from models/cv/detection/centernet/base/coco_detection.py rename to models/cv/detection/centernet/igie/base/coco_detection.py diff --git a/models/cv/detection/centernet/base/default_runtime.py b/models/cv/detection/centernet/igie/base/default_runtime.py similarity index 100% rename from models/cv/detection/centernet/base/default_runtime.py rename to models/cv/detection/centernet/igie/base/default_runtime.py diff --git a/models/cv/detection/centernet/base/schedule_1x.py b/models/cv/detection/centernet/igie/base/schedule_1x.py similarity index 100% rename from models/cv/detection/centernet/base/schedule_1x.py rename to models/cv/detection/centernet/igie/base/schedule_1x.py diff --git a/models/cv/detection/centernet/build_engine.py b/models/cv/detection/centernet/igie/build_engine.py similarity index 100% rename from models/cv/detection/centernet/build_engine.py rename to models/cv/detection/centernet/igie/build_engine.py diff --git a/models/cv/detection/centernet/centernet_r18_8xb16-crop512-140e_coco.py b/models/cv/detection/centernet/igie/centernet_r18_8xb16-crop512-140e_coco.py similarity index 100% rename from models/cv/detection/centernet/centernet_r18_8xb16-crop512-140e_coco.py rename to models/cv/detection/centernet/igie/centernet_r18_8xb16-crop512-140e_coco.py diff --git a/models/cv/detection/centernet/deploy_default.py b/models/cv/detection/centernet/igie/deploy_default.py similarity index 100% rename from models/cv/detection/centernet/deploy_default.py rename to models/cv/detection/centernet/igie/deploy_default.py diff --git a/models/cv/detection/centernet/export.py b/models/cv/detection/centernet/igie/export.py similarity index 100% rename from models/cv/detection/centernet/export.py rename to models/cv/detection/centernet/igie/export.py diff --git a/models/cv/detection/centernet/inference.py b/models/cv/detection/centernet/igie/inference.py similarity index 100% rename from models/cv/detection/centernet/inference.py rename to models/cv/detection/centernet/igie/inference.py diff --git a/models/cv/detection/centernet/scripts/infer_centernet_fp16_accuracy.sh b/models/cv/detection/centernet/igie/scripts/infer_centernet_fp16_accuracy.sh similarity index 100% rename from models/cv/detection/centernet/scripts/infer_centernet_fp16_accuracy.sh rename to models/cv/detection/centernet/igie/scripts/infer_centernet_fp16_accuracy.sh diff --git a/models/cv/detection/centernet/scripts/infer_centernet_fp16_performance.sh b/models/cv/detection/centernet/igie/scripts/infer_centernet_fp16_performance.sh similarity index 100% rename from models/cv/detection/centernet/scripts/infer_centernet_fp16_performance.sh rename to models/cv/detection/centernet/igie/scripts/infer_centernet_fp16_performance.sh -- Gitee From 93e056a92d6eaf4e6078581a81b33c6dacf3b99b Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Thu, 18 Apr 2024 10:09:00 +0800 Subject: [PATCH 17/19] Add: hrnet inference script & update centernet README. --- models/cv/detection/centernet/igie/README.md | 8 +- models/cv/detection/hrnet/igie/README.md | 55 ++++ .../hrnet/igie/base/coco_detection.py | 75 +++++ .../hrnet/igie/base/default_runtime.py | 39 +++ .../fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py | 58 ++++ .../fcos_r50-caffe_fpn_gn-head_1x_coco.py | 90 ++++++ ...fcos_r50-caffe_fpn_gn-head_4xb4-1x_coco.py | 19 ++ .../detection/hrnet/igie/base/schedule_1x.py | 43 +++ .../cv/detection/hrnet/igie/build_engine.py | 73 +++++ .../cv/detection/hrnet/igie/deploy_default.py | 41 +++ models/cv/detection/hrnet/igie/export.py | 74 +++++ .../fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py | 286 ++++++++++++++++++ models/cv/detection/hrnet/igie/inference.py | 157 ++++++++++ .../igie/scripts/infer_hrnet_fp16_accuracy.sh | 35 +++ .../scripts/infer_hrnet_fp16_performance.sh | 36 +++ 15 files changed, 1085 insertions(+), 4 deletions(-) create mode 100644 models/cv/detection/hrnet/igie/README.md create mode 100644 models/cv/detection/hrnet/igie/base/coco_detection.py create mode 100644 models/cv/detection/hrnet/igie/base/default_runtime.py create mode 100644 models/cv/detection/hrnet/igie/base/fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py create mode 100644 models/cv/detection/hrnet/igie/base/fcos_r50-caffe_fpn_gn-head_1x_coco.py create mode 100644 models/cv/detection/hrnet/igie/base/fcos_r50-caffe_fpn_gn-head_4xb4-1x_coco.py create mode 100644 models/cv/detection/hrnet/igie/base/schedule_1x.py create mode 100644 models/cv/detection/hrnet/igie/build_engine.py create mode 100644 models/cv/detection/hrnet/igie/deploy_default.py create mode 100644 models/cv/detection/hrnet/igie/export.py create mode 100644 models/cv/detection/hrnet/igie/fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py create mode 100644 models/cv/detection/hrnet/igie/inference.py create mode 100644 models/cv/detection/hrnet/igie/scripts/infer_hrnet_fp16_accuracy.sh create mode 100644 models/cv/detection/hrnet/igie/scripts/infer_hrnet_fp16_performance.sh diff --git a/models/cv/detection/centernet/igie/README.md b/models/cv/detection/centernet/igie/README.md index 96622a01..b246b523 100644 --- a/models/cv/detection/centernet/igie/README.md +++ b/models/cv/detection/centernet/igie/README.md @@ -1,7 +1,7 @@ -# RetinaNet +# CenterNet ## Description -RetinaNet, an innovative object detector, challenges the conventional trade-off between speed and accuracy in the realm of computer vision. Traditionally, two-stage detectors, exemplified by R-CNN, achieve high accuracy by applying a classifier to a limited set of candidate object locations. In contrast, one-stage detectors, like RetinaNet, operate over a dense sampling of possible object locations, aiming for simplicity and speed. +CenterNet is an efficient object detection model that simplifies the traditional object detection process by representing targets as the center points of their bounding boxes and using keypoint estimation techniques to locate these points. This model not only excels in speed, achieving real-time detection while maintaining high accuracy, but also exhibits good versatility, easily extending to tasks such as 3D object detection and human pose estimation. CenterNet's network architecture employs various optimized fully convolutional networks and combines effective loss functions, making the model training and inference process more efficient. ## Setup @@ -17,7 +17,7 @@ pip3 install mmengine ### Download -Pretrained model: < https://download.openmmlab.com/mmdetection/v2.0/centernet/centernet_resnet18_140e_coco/centernet_resnet18_140e_coco_20210705_093630-bb5b3bf7.pth> +Pretrained model: Dataset: to download the validation dataset. @@ -44,7 +44,7 @@ bash scripts/infer_centernet_fp16_performance.sh Model |BatchSize |Precision |FPS |IOU@0.5 |IOU@0.5:0.95 | ----------|-----------|----------|----------|----------|---------------| -Centernet | 32 | FP16 | 799.70 | 0.423 | 0.258 | +CenterNet | 32 | FP16 | 799.70 | 0.423 | 0.258 | ## Reference diff --git a/models/cv/detection/hrnet/igie/README.md b/models/cv/detection/hrnet/igie/README.md new file mode 100644 index 00000000..43d07f54 --- /dev/null +++ b/models/cv/detection/hrnet/igie/README.md @@ -0,0 +1,55 @@ +# HRNet + +## Description +HRNet is an advanced deep learning architecture for human pose estimation, characterized by its maintenance of high-resolution representations throughout the entire network process, thereby avoiding the low-to-high resolution recovery step typical of traditional models. The network features parallel multi-resolution subnetworks and enriches feature representation through repeated multi-scale fusion, which enhances the accuracy of keypoint detection. Additionally, HRNet offers computational efficiency and has demonstrated superior performance over previous methods on several standard datasets. + +## Setup + +### Install +```bash +yum install mesa-libGL +pip3 install onnx +pip3 install tqdm +pip3 install onnxsim +pip3 install mmdet +pip3 install mmdeploy +pip3 install mmengine +``` + +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +# export onnx model +python3 export.py --weight fcos_hrnetv2p_w18_gn-head_4x4_1x_coco_20201212_100710-4ad151de.pth --cfg fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py --output hrnet.onnx + +# Use onnxsim optimize onnx model +onnxsim hrnet.onnx hrnet_opt.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/coco/ +``` +### FP16 + +```bash +# Accuracy +bash scripts/infer_hrnet_fp16_accuracy.sh +# Performance +bash scripts/infer_hrnet_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |IOU@0.5 |IOU@0.5:0.95 | +-------|-----------|----------|----------|----------|---------------| +HRNet | 32 | FP16 | 64.282 | 0.491 | 0.326 | + +## Reference + +mmdetection: https://github.com/open-mmlab/mmdetection.git diff --git a/models/cv/detection/hrnet/igie/base/coco_detection.py b/models/cv/detection/hrnet/igie/base/coco_detection.py new file mode 100644 index 00000000..f58fe67b --- /dev/null +++ b/models/cv/detection/hrnet/igie/base/coco_detection.py @@ -0,0 +1,75 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# dataset settings +dataset_type = 'CocoDataset' +data_root = 'data/coco/' + +backend_args = None + +train_pipeline = [ + dict(type='LoadImageFromFile', backend_args=backend_args), + dict(type='LoadAnnotations', with_bbox=True), + dict(type='Resize', scale=(1333, 800), keep_ratio=True), + dict(type='RandomFlip', prob=0.5), + dict(type='PackDetInputs') +] +test_pipeline = [ + dict(type='LoadImageFromFile', backend_args=backend_args), + dict(type='Resize', scale=(1333, 800), keep_ratio=True), + # If you don't have a gt annotation, delete the pipeline + dict(type='LoadAnnotations', with_bbox=True), + dict( + type='PackDetInputs', + meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', + 'scale_factor')) +] +train_dataloader = dict( + batch_size=2, + num_workers=2, + persistent_workers=True, + sampler=dict(type='DefaultSampler', shuffle=True), + batch_sampler=dict(type='AspectRatioBatchSampler'), + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file='annotations/instances_train2017.json', + data_prefix=dict(img='train2017/'), + filter_cfg=dict(filter_empty_gt=True, min_size=32), + pipeline=train_pipeline, + backend_args=backend_args)) +val_dataloader = dict( + batch_size=1, + num_workers=2, + persistent_workers=True, + drop_last=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file='annotations/instances_val2017.json', + data_prefix=dict(img='val2017/'), + test_mode=True, + pipeline=test_pipeline, + backend_args=backend_args)) +test_dataloader = val_dataloader + +val_evaluator = dict( + type='CocoMetric', + ann_file=data_root + 'annotations/instances_val2017.json', + metric='bbox', + format_only=False, + backend_args=backend_args) +test_evaluator = val_evaluator \ No newline at end of file diff --git a/models/cv/detection/hrnet/igie/base/default_runtime.py b/models/cv/detection/hrnet/igie/base/default_runtime.py new file mode 100644 index 00000000..609d8037 --- /dev/null +++ b/models/cv/detection/hrnet/igie/base/default_runtime.py @@ -0,0 +1,39 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +default_scope = 'mmdet' + +default_hooks = dict( + timer=dict(type='IterTimerHook'), + logger=dict(type='LoggerHook', interval=50), + param_scheduler=dict(type='ParamSchedulerHook'), + checkpoint=dict(type='CheckpointHook', interval=1), + sampler_seed=dict(type='DistSamplerSeedHook'), + visualization=dict(type='DetVisualizationHook')) + +env_cfg = dict( + cudnn_benchmark=False, + mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0), + dist_cfg=dict(backend='nccl'), +) + +vis_backends = [dict(type='LocalVisBackend')] +visualizer = dict( + type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer') +log_processor = dict(type='LogProcessor', window_size=50, by_epoch=True) + +log_level = 'INFO' +load_from = None +resume = False diff --git a/models/cv/detection/hrnet/igie/base/fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py b/models/cv/detection/hrnet/igie/base/fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py new file mode 100644 index 00000000..17b1a876 --- /dev/null +++ b/models/cv/detection/hrnet/igie/base/fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py @@ -0,0 +1,58 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +_base_ = 'fcos_r50-caffe_fpn_gn-head_4xb4-1x_coco.py' +model = dict( + data_preprocessor=dict( + mean=[103.53, 116.28, 123.675], + std=[57.375, 57.12, 58.395], + bgr_to_rgb=False), + backbone=dict( + _delete_=True, + type='HRNet', + extra=dict( + stage1=dict( + num_modules=1, + num_branches=1, + block='BOTTLENECK', + num_blocks=(4, ), + num_channels=(64, )), + stage2=dict( + num_modules=1, + num_branches=2, + block='BASIC', + num_blocks=(4, 4), + num_channels=(32, 64)), + stage3=dict( + num_modules=4, + num_branches=3, + block='BASIC', + num_blocks=(4, 4, 4), + num_channels=(32, 64, 128)), + stage4=dict( + num_modules=3, + num_branches=4, + block='BASIC', + num_blocks=(4, 4, 4, 4), + num_channels=(32, 64, 128, 256))), + init_cfg=dict( + type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w32')), + neck=dict( + _delete_=True, + type='HRFPN', + in_channels=[32, 64, 128, 256], + out_channels=256, + stride=2, + num_outs=5)) diff --git a/models/cv/detection/hrnet/igie/base/fcos_r50-caffe_fpn_gn-head_1x_coco.py b/models/cv/detection/hrnet/igie/base/fcos_r50-caffe_fpn_gn-head_1x_coco.py new file mode 100644 index 00000000..9e04ad5f --- /dev/null +++ b/models/cv/detection/hrnet/igie/base/fcos_r50-caffe_fpn_gn-head_1x_coco.py @@ -0,0 +1,90 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +_base_ = [ + 'coco_detection.py', + 'schedule_1x.py', 'default_runtime.py' +] + +# model settings +model = dict( + type='FCOS', + data_preprocessor=dict( + type='DetDataPreprocessor', + mean=[102.9801, 115.9465, 122.7717], + std=[1.0, 1.0, 1.0], + bgr_to_rgb=False, + pad_size_divisor=32), + backbone=dict( + type='ResNet', + depth=50, + num_stages=4, + out_indices=(0, 1, 2, 3), + frozen_stages=1, + norm_cfg=dict(type='BN', requires_grad=False), + norm_eval=True, + style='caffe', + init_cfg=dict( + type='Pretrained', + checkpoint='open-mmlab://detectron/resnet50_caffe')), + neck=dict( + type='FPN', + in_channels=[256, 512, 1024, 2048], + out_channels=256, + start_level=1, + add_extra_convs='on_output', # use P5 + num_outs=5, + relu_before_extra_convs=True), + bbox_head=dict( + type='FCOSHead', + num_classes=80, + in_channels=256, + stacked_convs=4, + feat_channels=256, + strides=[8, 16, 32, 64, 128], + loss_cls=dict( + type='FocalLoss', + use_sigmoid=True, + gamma=2.0, + alpha=0.25, + loss_weight=1.0), + loss_bbox=dict(type='IoULoss', loss_weight=1.0), + loss_centerness=dict( + type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)), + # testing settings + test_cfg=dict( + nms_pre=1000, + min_bbox_size=0, + score_thr=0.05, + nms=dict(type='nms', iou_threshold=0.5), + max_per_img=100)) + +# learning rate +param_scheduler = [ + dict(type='ConstantLR', factor=1.0 / 3, by_epoch=False, begin=0, end=500), + dict( + type='MultiStepLR', + begin=0, + end=12, + by_epoch=True, + milestones=[8, 11], + gamma=0.1) +] + +# optimizer +optim_wrapper = dict( + optimizer=dict(lr=0.01), + paramwise_cfg=dict(bias_lr_mult=2., bias_decay_mult=0.), + clip_grad=dict(max_norm=35, norm_type=2)) diff --git a/models/cv/detection/hrnet/igie/base/fcos_r50-caffe_fpn_gn-head_4xb4-1x_coco.py b/models/cv/detection/hrnet/igie/base/fcos_r50-caffe_fpn_gn-head_4xb4-1x_coco.py new file mode 100644 index 00000000..3f9af61a --- /dev/null +++ b/models/cv/detection/hrnet/igie/base/fcos_r50-caffe_fpn_gn-head_4xb4-1x_coco.py @@ -0,0 +1,19 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +_base_ = 'fcos_r50-caffe_fpn_gn-head_1x_coco.py' + +# dataset settings +train_dataloader = dict(batch_size=4, num_workers=4) diff --git a/models/cv/detection/hrnet/igie/base/schedule_1x.py b/models/cv/detection/hrnet/igie/base/schedule_1x.py new file mode 100644 index 00000000..9b16d80c --- /dev/null +++ b/models/cv/detection/hrnet/igie/base/schedule_1x.py @@ -0,0 +1,43 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# training schedule for 1x +train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_interval=1) +val_cfg = dict(type='ValLoop') +test_cfg = dict(type='TestLoop') + +# learning rate +param_scheduler = [ + dict( + type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500), + dict( + type='MultiStepLR', + begin=0, + end=12, + by_epoch=True, + milestones=[8, 11], + gamma=0.1) +] + +# optimizer +optim_wrapper = dict( + type='OptimWrapper', + optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)) + +# Default setting for scaling LR automatically +# - `enable` means enable scaling LR automatically +# or not by default. +# - `base_batch_size` = (8 GPUs) x (2 samples per GPU). +auto_scale_lr = dict(enable=False, base_batch_size=16) diff --git a/models/cv/detection/hrnet/igie/build_engine.py b/models/cv/detection/hrnet/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/detection/hrnet/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/detection/hrnet/igie/deploy_default.py b/models/cv/detection/hrnet/igie/deploy_default.py new file mode 100644 index 00000000..b8d8e43d --- /dev/null +++ b/models/cv/detection/hrnet/igie/deploy_default.py @@ -0,0 +1,41 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +onnx_config = dict( + type='onnx', + export_params=True, + keep_initializers_as_inputs=False, + opset_version=11, + save_file='end2end.onnx', + input_names=['input'], + output_names=['output'], + input_shape=None, + optimize=True) + +codebase_config = dict( + type='mmdet', + task='ObjectDetection', + model_type='end2end', + post_processing=dict( + score_threshold=0.05, + confidence_threshold=0.005, + iou_threshold=0.5, + max_output_boxes_per_class=200, + pre_top_k=5000, + keep_top_k=100, + background_label_id=-1, + )) + +backend_config = dict(type='onnxruntime') \ No newline at end of file diff --git a/models/cv/detection/hrnet/igie/export.py b/models/cv/detection/hrnet/igie/export.py new file mode 100644 index 00000000..bceaba78 --- /dev/null +++ b/models/cv/detection/hrnet/igie/export.py @@ -0,0 +1,74 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import argparse + +import torch +from mmdeploy.utils import load_config +from mmdeploy.apis import build_task_processor + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--cfg", + type=str, + required=True, + help="model config file.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + deploy_cfg = 'deploy_default.py' + model_cfg = args.cfg + model_checkpoint = args.weight + + deploy_cfg, model_cfg = load_config(deploy_cfg, model_cfg) + + task_processor = build_task_processor(model_cfg, deploy_cfg, device='cpu') + + model = task_processor.build_pytorch_model(model_checkpoint) + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 800, 800) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == '__main__': + main() + diff --git a/models/cv/detection/hrnet/igie/fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py b/models/cv/detection/hrnet/igie/fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py new file mode 100644 index 00000000..14128f25 --- /dev/null +++ b/models/cv/detection/hrnet/igie/fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py @@ -0,0 +1,286 @@ +auto_scale_lr = dict(base_batch_size=16, enable=False) +backend_args = None +data_root = 'data/coco/' +dataset_type = 'CocoDataset' +default_hooks = dict( + checkpoint=dict(interval=1, type='CheckpointHook'), + logger=dict(interval=50, type='LoggerHook'), + param_scheduler=dict(type='ParamSchedulerHook'), + sampler_seed=dict(type='DistSamplerSeedHook'), + timer=dict(type='IterTimerHook'), + visualization=dict(type='DetVisualizationHook')) +default_scope = 'mmdet' +env_cfg = dict( + cudnn_benchmark=False, + dist_cfg=dict(backend='nccl'), + mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0)) +load_from = None +log_level = 'INFO' +log_processor = dict(by_epoch=True, type='LogProcessor', window_size=50) +model = dict( + backbone=dict( + extra=dict( + stage1=dict( + block='BOTTLENECK', + num_blocks=(4, ), + num_branches=1, + num_channels=(64, ), + num_modules=1), + stage2=dict( + block='BASIC', + num_blocks=( + 4, + 4, + ), + num_branches=2, + num_channels=( + 18, + 36, + ), + num_modules=1), + stage3=dict( + block='BASIC', + num_blocks=( + 4, + 4, + 4, + ), + num_branches=3, + num_channels=( + 18, + 36, + 72, + ), + num_modules=4), + stage4=dict( + block='BASIC', + num_blocks=( + 4, + 4, + 4, + 4, + ), + num_branches=4, + num_channels=( + 18, + 36, + 72, + 144, + ), + num_modules=3)), + init_cfg=dict( + checkpoint='open-mmlab://msra/hrnetv2_w18', type='Pretrained'), + type='HRNet'), + bbox_head=dict( + feat_channels=256, + in_channels=256, + loss_bbox=dict(loss_weight=1.0, type='IoULoss'), + loss_centerness=dict( + loss_weight=1.0, type='CrossEntropyLoss', use_sigmoid=True), + loss_cls=dict( + alpha=0.25, + gamma=2.0, + loss_weight=1.0, + type='FocalLoss', + use_sigmoid=True), + num_classes=80, + stacked_convs=4, + strides=[ + 8, + 16, + 32, + 64, + 128, + ], + type='FCOSHead'), + data_preprocessor=dict( + bgr_to_rgb=False, + mean=[ + 103.53, + 116.28, + 123.675, + ], + pad_size_divisor=32, + std=[ + 57.375, + 57.12, + 58.395, + ], + type='DetDataPreprocessor'), + neck=dict( + in_channels=[ + 18, + 36, + 72, + 144, + ], + num_outs=5, + out_channels=256, + stride=2, + type='HRFPN'), + test_cfg=dict( + max_per_img=100, + min_bbox_size=0, + nms=dict(iou_threshold=0.5, type='nms'), + nms_pre=1000, + score_thr=0.05), + type='FCOS') +optim_wrapper = dict( + clip_grad=dict(max_norm=35, norm_type=2), + optimizer=dict(lr=0.01, momentum=0.9, type='SGD', weight_decay=0.0001), + paramwise_cfg=dict(bias_decay_mult=0.0, bias_lr_mult=2.0), + type='OptimWrapper') +param_scheduler = [ + dict( + begin=0, + by_epoch=False, + end=500, + factor=0.3333333333333333, + type='ConstantLR'), + dict( + begin=0, + by_epoch=True, + end=12, + gamma=0.1, + milestones=[ + 8, + 11, + ], + type='MultiStepLR'), +] +resume = False +test_cfg = dict(type='TestLoop') +test_dataloader = dict( + batch_size=32, + dataset=dict( + ann_file='annotations/instances_val2017.json', + backend_args=None, + data_prefix=dict(img='images/val2017/'), + data_root='/home/peng.yang/Datasets/coco', + pipeline=[ + dict(backend_args=None, type='LoadImageFromFile'), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'scale_factor', + ), + type='PackDetInputs'), + ], + test_mode=True, + type='CocoDataset'), + drop_last=False, + num_workers=2, + persistent_workers=True, + sampler=dict(shuffle=False, type='DefaultSampler')) +test_evaluator = dict( + ann_file='/home/peng.yang/Datasets/coco/annotations/instances_val2017.json', + backend_args=None, + format_only=False, + metric='bbox', + type='CocoMetric') +test_pipeline = [ + dict(backend_args=None, type='LoadImageFromFile'), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'scale_factor', + ), + type='PackDetInputs'), +] +train_cfg = dict(max_epochs=12, type='EpochBasedTrainLoop', val_interval=1) +train_dataloader = dict( + batch_sampler=dict(type='AspectRatioBatchSampler'), + batch_size=4, + dataset=dict( + ann_file='annotations/instances_train2017.json', + backend_args=None, + data_prefix=dict(img='train2017/'), + data_root='data/coco/', + filter_cfg=dict(filter_empty_gt=True, min_size=32), + pipeline=[ + dict(backend_args=None, type='LoadImageFromFile'), + dict(type='LoadAnnotations', with_bbox=True), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(prob=0.5, type='RandomFlip'), + dict(type='PackDetInputs'), + ], + type='CocoDataset'), + num_workers=4, + persistent_workers=True, + sampler=dict(shuffle=True, type='DefaultSampler')) +train_pipeline = [ + dict(backend_args=None, type='LoadImageFromFile'), + dict(type='LoadAnnotations', with_bbox=True), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(prob=0.5, type='RandomFlip'), + dict(type='PackDetInputs'), +] +val_cfg = dict(type='ValLoop') +val_dataloader = dict( + batch_size=1, + dataset=dict( + ann_file='annotations/instances_val2017.json', + backend_args=None, + data_prefix=dict(img='val2017/'), + data_root='data/coco/', + pipeline=[ + dict(backend_args=None, type='LoadImageFromFile'), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'scale_factor', + ), + type='PackDetInputs'), + ], + test_mode=True, + type='CocoDataset'), + drop_last=False, + num_workers=2, + persistent_workers=True, + sampler=dict(shuffle=False, type='DefaultSampler')) +val_evaluator = dict( + ann_file='data/coco/annotations/instances_val2017.json', + backend_args=None, + format_only=False, + metric='bbox', + type='CocoMetric') +vis_backends = [ + dict(type='LocalVisBackend'), +] +visualizer = dict( + name='visualizer', + type='DetLocalVisualizer', + vis_backends=[ + dict(type='LocalVisBackend'), + ]) +work_dir = './' diff --git a/models/cv/detection/hrnet/igie/inference.py b/models/cv/detection/hrnet/igie/inference.py new file mode 100644 index 00000000..310eaaef --- /dev/null +++ b/models/cv/detection/hrnet/igie/inference.py @@ -0,0 +1,157 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from mmdet.registry import RUNNERS +from mmengine.config import Config + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # Runner config + cfg = Config.fromfile("fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py") + cfg.work_dir = "./" + + cfg['test_dataloader']['batch_size'] = batch_size + cfg['test_dataloader']['dataset']['data_root'] = args.datasets + cfg['test_dataloader']['dataset']['data_prefix']['img'] = 'images/val2017/' + cfg['test_evaluator']['ann_file'] = os.path.join(args.datasets, 'annotations/instances_val2017.json') + + runner = RUNNERS.build(cfg) + + for input_data in tqdm(runner.test_dataloader): + + input_data = runner.model.data_preprocessor(input_data, False) + image = input_data['inputs'].cpu() + + pad_batch = len(image) != batch_size + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + module.run() + + cls_score = [] + box_reg = [] + score_factors = [] + + for i in range(module.get_num_outputs()): + output = module.get_output(i).asnumpy() + + if pad_batch: + output = output[:origin_size] + + output = torch.from_numpy(output) + + if output.shape[1] == 80: + cls_score.append(output) + elif output.shape[1] == 4: + box_reg.append(output) + else: + score_factors.append(output) + + batch_img_metas = [ + data_samples.metainfo for data_samples in input_data['data_samples'] + ] + + results_list = runner.model.bbox_head.predict_by_feat(cls_score, box_reg, score_factors, batch_img_metas=batch_img_metas, rescale=True) + + batch_data_samples = runner.model.add_pred_to_datasample(input_data['data_samples'], results_list) + + runner.test_evaluator.process(data_samples=batch_data_samples, data_batch=input_data) + + metrics = runner.test_evaluator.evaluate(len(runner.test_dataloader.dataset)) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/detection/hrnet/igie/scripts/infer_hrnet_fp16_accuracy.sh b/models/cv/detection/hrnet/igie/scripts/infer_hrnet_fp16_accuracy.sh new file mode 100644 index 00000000..d613f2e2 --- /dev/null +++ b/models/cv/detection/hrnet/igie/scripts/infer_hrnet_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="hrnet_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,800,800 \ + --precision fp16 \ + --engine_path hrnet_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine hrnet_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/detection/hrnet/igie/scripts/infer_hrnet_fp16_performance.sh b/models/cv/detection/hrnet/igie/scripts/infer_hrnet_fp16_performance.sh new file mode 100644 index 00000000..ec405bb1 --- /dev/null +++ b/models/cv/detection/hrnet/igie/scripts/infer_hrnet_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="hrnet_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,800,800 \ + --precision fp16 \ + --engine_path hrnet_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine hrnet_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From 672fb9d7520087dcbe5cded47a47487ba6b8b9c5 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Tue, 23 Apr 2024 02:06:12 +0000 Subject: [PATCH 18/19] Add: foveabox inference script. --- models/cv/detection/foveabox/igie/README.md | 55 ++++ .../foveabox/igie/base/coco_detection.py | 75 +++++ .../foveabox/igie/base/default_runtime.py | 39 +++ .../foveabox/igie/base/schedule_1x.py | 43 +++ .../detection/foveabox/igie/build_engine.py | 73 +++++ .../detection/foveabox/igie/deploy_default.py | 41 +++ models/cv/detection/foveabox/igie/export.py | 74 +++++ .../igie/fovea_r50_fpn_4xb4-1x_coco.py | 272 ++++++++++++++++++ .../cv/detection/foveabox/igie/inference.py | 154 ++++++++++ .../scripts/infer_foveabox_fp16_accuracy.sh | 35 +++ .../infer_foveabox_fp16_performance.sh | 36 +++ 11 files changed, 897 insertions(+) create mode 100644 models/cv/detection/foveabox/igie/README.md create mode 100644 models/cv/detection/foveabox/igie/base/coco_detection.py create mode 100644 models/cv/detection/foveabox/igie/base/default_runtime.py create mode 100644 models/cv/detection/foveabox/igie/base/schedule_1x.py create mode 100644 models/cv/detection/foveabox/igie/build_engine.py create mode 100644 models/cv/detection/foveabox/igie/deploy_default.py create mode 100644 models/cv/detection/foveabox/igie/export.py create mode 100644 models/cv/detection/foveabox/igie/fovea_r50_fpn_4xb4-1x_coco.py create mode 100644 models/cv/detection/foveabox/igie/inference.py create mode 100644 models/cv/detection/foveabox/igie/scripts/infer_foveabox_fp16_accuracy.sh create mode 100644 models/cv/detection/foveabox/igie/scripts/infer_foveabox_fp16_performance.sh diff --git a/models/cv/detection/foveabox/igie/README.md b/models/cv/detection/foveabox/igie/README.md new file mode 100644 index 00000000..6ff16901 --- /dev/null +++ b/models/cv/detection/foveabox/igie/README.md @@ -0,0 +1,55 @@ +# FoveaBox + +## Description +FoveaBox is an advanced anchor-free object detection framework that enhances accuracy and flexibility by directly predicting the existence and bounding box coordinates of objects. Utilizing a Feature Pyramid Network (FPN), it adeptly handles targets of varying scales, particularly excelling with objects of arbitrary aspect ratios. FoveaBox also demonstrates robustness against image deformations. + +## Setup + +### Install +```bash +yum install mesa-libGL +pip3 install onnx +pip3 install tqdm +pip3 install onnxsim +pip3 install mmdet +pip3 install mmdeploy +pip3 install mmengine +``` + +### Download + +Pretrained model: + +Dataset: to download the validation dataset. + +### Model Conversion +```bash +# export onnx model +python3 export.py --weight fovea_r50_fpn_4x4_1x_coco_20200219-ee4d5303.pth --cfg fovea_r50_fpn_4xb4-1x_coco.py --output foveabox.onnx + +# Use onnxsim optimize onnx model +onnxsim foveabox.onnx foveabox_opt.onnx +``` + +## Inference +```bash +export DATASETS_DIR=/Path/to/coco/ +``` +### FP16 + +```bash +# Accuracy +bash scripts/infer_foveabox_fp16_accuracy.sh +# Performance +bash scripts/infer_foveabox_fp16_performance.sh +``` + +## Results + +Model |BatchSize |Precision |FPS |IOU@0.5 |IOU@0.5:0.95 | +---------|-----------|----------|----------|----------|---------------| +FoveaBox | 32 | FP16 | 192.496 | 0.531 | 0.346 | + +## Reference + +mmdetection: https://github.com/open-mmlab/mmdetection.git diff --git a/models/cv/detection/foveabox/igie/base/coco_detection.py b/models/cv/detection/foveabox/igie/base/coco_detection.py new file mode 100644 index 00000000..f58fe67b --- /dev/null +++ b/models/cv/detection/foveabox/igie/base/coco_detection.py @@ -0,0 +1,75 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# dataset settings +dataset_type = 'CocoDataset' +data_root = 'data/coco/' + +backend_args = None + +train_pipeline = [ + dict(type='LoadImageFromFile', backend_args=backend_args), + dict(type='LoadAnnotations', with_bbox=True), + dict(type='Resize', scale=(1333, 800), keep_ratio=True), + dict(type='RandomFlip', prob=0.5), + dict(type='PackDetInputs') +] +test_pipeline = [ + dict(type='LoadImageFromFile', backend_args=backend_args), + dict(type='Resize', scale=(1333, 800), keep_ratio=True), + # If you don't have a gt annotation, delete the pipeline + dict(type='LoadAnnotations', with_bbox=True), + dict( + type='PackDetInputs', + meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', + 'scale_factor')) +] +train_dataloader = dict( + batch_size=2, + num_workers=2, + persistent_workers=True, + sampler=dict(type='DefaultSampler', shuffle=True), + batch_sampler=dict(type='AspectRatioBatchSampler'), + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file='annotations/instances_train2017.json', + data_prefix=dict(img='train2017/'), + filter_cfg=dict(filter_empty_gt=True, min_size=32), + pipeline=train_pipeline, + backend_args=backend_args)) +val_dataloader = dict( + batch_size=1, + num_workers=2, + persistent_workers=True, + drop_last=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file='annotations/instances_val2017.json', + data_prefix=dict(img='val2017/'), + test_mode=True, + pipeline=test_pipeline, + backend_args=backend_args)) +test_dataloader = val_dataloader + +val_evaluator = dict( + type='CocoMetric', + ann_file=data_root + 'annotations/instances_val2017.json', + metric='bbox', + format_only=False, + backend_args=backend_args) +test_evaluator = val_evaluator \ No newline at end of file diff --git a/models/cv/detection/foveabox/igie/base/default_runtime.py b/models/cv/detection/foveabox/igie/base/default_runtime.py new file mode 100644 index 00000000..609d8037 --- /dev/null +++ b/models/cv/detection/foveabox/igie/base/default_runtime.py @@ -0,0 +1,39 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +default_scope = 'mmdet' + +default_hooks = dict( + timer=dict(type='IterTimerHook'), + logger=dict(type='LoggerHook', interval=50), + param_scheduler=dict(type='ParamSchedulerHook'), + checkpoint=dict(type='CheckpointHook', interval=1), + sampler_seed=dict(type='DistSamplerSeedHook'), + visualization=dict(type='DetVisualizationHook')) + +env_cfg = dict( + cudnn_benchmark=False, + mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0), + dist_cfg=dict(backend='nccl'), +) + +vis_backends = [dict(type='LocalVisBackend')] +visualizer = dict( + type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer') +log_processor = dict(type='LogProcessor', window_size=50, by_epoch=True) + +log_level = 'INFO' +load_from = None +resume = False diff --git a/models/cv/detection/foveabox/igie/base/schedule_1x.py b/models/cv/detection/foveabox/igie/base/schedule_1x.py new file mode 100644 index 00000000..9b16d80c --- /dev/null +++ b/models/cv/detection/foveabox/igie/base/schedule_1x.py @@ -0,0 +1,43 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# training schedule for 1x +train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_interval=1) +val_cfg = dict(type='ValLoop') +test_cfg = dict(type='TestLoop') + +# learning rate +param_scheduler = [ + dict( + type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500), + dict( + type='MultiStepLR', + begin=0, + end=12, + by_epoch=True, + milestones=[8, 11], + gamma=0.1) +] + +# optimizer +optim_wrapper = dict( + type='OptimWrapper', + optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)) + +# Default setting for scaling LR automatically +# - `enable` means enable scaling LR automatically +# or not by default. +# - `base_batch_size` = (8 GPUs) x (2 samples per GPU). +auto_scale_lr = dict(enable=False, base_batch_size=16) diff --git a/models/cv/detection/foveabox/igie/build_engine.py b/models/cv/detection/foveabox/igie/build_engine.py new file mode 100644 index 00000000..d3626ae7 --- /dev/null +++ b/models/cv/detection/foveabox/igie/build_engine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import tvm +import argparse +from tvm import relay +from tvm.relay.import_model import import_model_to_igie + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--model_path", + type=str, + required=True, + help="original model path.") + + parser.add_argument("--engine_path", + type=str, + required=True, + help="igie export engine path.") + + parser.add_argument("--input", + type=str, + required=True, + help=""" + input info of the model, format should be: + input_name:input_shape + eg: --input input:1,3,224,224. + """) + + parser.add_argument("--precision", + type=str, + choices=["fp32", "fp16", "int8"], + required=True, + help="model inference precision.") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + # get input valueinfo + input_name, input_shape = args.input.split(":") + shape = tuple([int(s) for s in input_shape.split(",")]) + input_dict = {input_name: shape} + + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + + mod, params = import_model_to_igie(args.model_path, input_dict, backend="igie") + + # build engine + lib = tvm.relay.build(mod, target=target, params=params, precision=args.precision) + + # export engine + lib.export_library(args.engine_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/detection/foveabox/igie/deploy_default.py b/models/cv/detection/foveabox/igie/deploy_default.py new file mode 100644 index 00000000..b8d8e43d --- /dev/null +++ b/models/cv/detection/foveabox/igie/deploy_default.py @@ -0,0 +1,41 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +onnx_config = dict( + type='onnx', + export_params=True, + keep_initializers_as_inputs=False, + opset_version=11, + save_file='end2end.onnx', + input_names=['input'], + output_names=['output'], + input_shape=None, + optimize=True) + +codebase_config = dict( + type='mmdet', + task='ObjectDetection', + model_type='end2end', + post_processing=dict( + score_threshold=0.05, + confidence_threshold=0.005, + iou_threshold=0.5, + max_output_boxes_per_class=200, + pre_top_k=5000, + keep_top_k=100, + background_label_id=-1, + )) + +backend_config = dict(type='onnxruntime') \ No newline at end of file diff --git a/models/cv/detection/foveabox/igie/export.py b/models/cv/detection/foveabox/igie/export.py new file mode 100644 index 00000000..bceaba78 --- /dev/null +++ b/models/cv/detection/foveabox/igie/export.py @@ -0,0 +1,74 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import argparse + +import torch +from mmdeploy.utils import load_config +from mmdeploy.apis import build_task_processor + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--weight", + type=str, + required=True, + help="pytorch model weight.") + + parser.add_argument("--cfg", + type=str, + required=True, + help="model config file.") + + parser.add_argument("--output", + type=str, + required=True, + help="export onnx model path.") + + args = parser.parse_args() + return args + +def main(): + args = parse_args() + + deploy_cfg = 'deploy_default.py' + model_cfg = args.cfg + model_checkpoint = args.weight + + deploy_cfg, model_cfg = load_config(deploy_cfg, model_cfg) + + task_processor = build_task_processor(model_cfg, deploy_cfg, device='cpu') + + model = task_processor.build_pytorch_model(model_checkpoint) + + input_names = ['input'] + output_names = ['output'] + dynamic_axes = {'input': {0: '-1'}, 'output': {0: '-1'}} + dummy_input = torch.randn(1, 3, 800, 800) + + torch.onnx.export( + model, + dummy_input, + args.output, + input_names = input_names, + dynamic_axes = dynamic_axes, + output_names = output_names, + opset_version=13 + ) + + print("Export onnx model successfully! ") + +if __name__ == '__main__': + main() + diff --git a/models/cv/detection/foveabox/igie/fovea_r50_fpn_4xb4-1x_coco.py b/models/cv/detection/foveabox/igie/fovea_r50_fpn_4xb4-1x_coco.py new file mode 100644 index 00000000..b6e2e4ed --- /dev/null +++ b/models/cv/detection/foveabox/igie/fovea_r50_fpn_4xb4-1x_coco.py @@ -0,0 +1,272 @@ +auto_scale_lr = dict(base_batch_size=16, enable=False) +backend_args = None +data_root = 'data/coco/' +dataset_type = 'CocoDataset' +default_hooks = dict( + checkpoint=dict(interval=1, type='CheckpointHook'), + logger=dict(interval=50, type='LoggerHook'), + param_scheduler=dict(type='ParamSchedulerHook'), + sampler_seed=dict(type='DistSamplerSeedHook'), + timer=dict(type='IterTimerHook'), + visualization=dict(type='DetVisualizationHook')) +default_scope = 'mmdet' +env_cfg = dict( + cudnn_benchmark=False, + dist_cfg=dict(backend='nccl'), + mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0)) +load_from = None +log_level = 'INFO' +log_processor = dict(by_epoch=True, type='LogProcessor', window_size=50) +model = dict( + backbone=dict( + depth=50, + frozen_stages=1, + init_cfg=dict(checkpoint='torchvision://resnet50', type='Pretrained'), + norm_cfg=dict(requires_grad=True, type='BN'), + norm_eval=True, + num_stages=4, + out_indices=( + 0, + 1, + 2, + 3, + ), + style='pytorch', + type='ResNet'), + bbox_head=dict( + base_edge_list=[ + 16, + 32, + 64, + 128, + 256, + ], + feat_channels=256, + in_channels=256, + loss_bbox=dict(beta=0.11, loss_weight=1.0, type='SmoothL1Loss'), + loss_cls=dict( + alpha=0.4, + gamma=1.5, + loss_weight=1.0, + type='FocalLoss', + use_sigmoid=True), + num_classes=80, + scale_ranges=( + ( + 1, + 64, + ), + ( + 32, + 128, + ), + ( + 64, + 256, + ), + ( + 128, + 512, + ), + ( + 256, + 2048, + ), + ), + sigma=0.4, + stacked_convs=4, + strides=[ + 8, + 16, + 32, + 64, + 128, + ], + type='FoveaHead', + with_deform=False), + data_preprocessor=dict( + bgr_to_rgb=True, + mean=[ + 123.675, + 116.28, + 103.53, + ], + pad_size_divisor=32, + std=[ + 58.395, + 57.12, + 57.375, + ], + type='DetDataPreprocessor'), + neck=dict( + add_extra_convs='on_input', + in_channels=[ + 256, + 512, + 1024, + 2048, + ], + num_outs=5, + out_channels=256, + start_level=1, + type='FPN'), + test_cfg=dict( + max_per_img=100, + nms=dict(iou_threshold=0.5, type='nms'), + nms_pre=1000, + score_thr=0.05), + train_cfg=dict(), + type='FOVEA') +optim_wrapper = dict( + optimizer=dict(lr=0.01, momentum=0.9, type='SGD', weight_decay=0.0001), + type='OptimWrapper') +param_scheduler = [ + dict( + begin=0, by_epoch=False, end=500, start_factor=0.001, type='LinearLR'), + dict( + begin=0, + by_epoch=True, + end=12, + gamma=0.1, + milestones=[ + 8, + 11, + ], + type='MultiStepLR'), +] +resume = False +test_cfg = dict(type='TestLoop') +test_dataloader = dict( + batch_size=32, + dataset=dict( + ann_file='annotations/instances_val2017.json', + backend_args=None, + data_prefix=dict(img='images/val2017/'), + data_root='/home/peng.yang/Datasets/coco', + pipeline=[ + dict(backend_args=None, type='LoadImageFromFile'), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'scale_factor', + ), + type='PackDetInputs'), + ], + test_mode=True, + type='CocoDataset'), + drop_last=False, + num_workers=2, + persistent_workers=True, + sampler=dict(shuffle=False, type='DefaultSampler')) +test_evaluator = dict( + ann_file='/home/peng.yang/Datasets/coco/annotations/instances_val2017.json', + backend_args=None, + format_only=False, + metric='bbox', + type='CocoMetric') +test_pipeline = [ + dict(backend_args=None, type='LoadImageFromFile'), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'scale_factor', + ), + type='PackDetInputs'), +] +train_cfg = dict(max_epochs=12, type='EpochBasedTrainLoop', val_interval=1) +train_dataloader = dict( + batch_sampler=dict(type='AspectRatioBatchSampler'), + batch_size=4, + dataset=dict( + ann_file='annotations/instances_train2017.json', + backend_args=None, + data_prefix=dict(img='train2017/'), + data_root='data/coco/', + filter_cfg=dict(filter_empty_gt=True, min_size=32), + pipeline=[ + dict(backend_args=None, type='LoadImageFromFile'), + dict(type='LoadAnnotations', with_bbox=True), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(prob=0.5, type='RandomFlip'), + dict(type='PackDetInputs'), + ], + type='CocoDataset'), + num_workers=4, + persistent_workers=True, + sampler=dict(shuffle=True, type='DefaultSampler')) +train_pipeline = [ + dict(backend_args=None, type='LoadImageFromFile'), + dict(type='LoadAnnotations', with_bbox=True), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(prob=0.5, type='RandomFlip'), + dict(type='PackDetInputs'), +] +val_cfg = dict(type='ValLoop') +val_dataloader = dict( + batch_size=1, + dataset=dict( + ann_file='annotations/instances_val2017.json', + backend_args=None, + data_prefix=dict(img='val2017/'), + data_root='data/coco/', + pipeline=[ + dict(backend_args=None, type='LoadImageFromFile'), + dict(keep_ratio=True, scale=( + 800, + 800, + ), type='Resize'), + dict(type='LoadAnnotations', with_bbox=True), + dict( + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'scale_factor', + ), + type='PackDetInputs'), + ], + test_mode=True, + type='CocoDataset'), + drop_last=False, + num_workers=2, + persistent_workers=True, + sampler=dict(shuffle=False, type='DefaultSampler')) +val_evaluator = dict( + ann_file='data/coco/annotations/instances_val2017.json', + backend_args=None, + format_only=False, + metric='bbox', + type='CocoMetric') +vis_backends = [ + dict(type='LocalVisBackend'), +] +visualizer = dict( + name='visualizer', + type='DetLocalVisualizer', + vis_backends=[ + dict(type='LocalVisBackend'), + ]) +work_dir = './' diff --git a/models/cv/detection/foveabox/igie/inference.py b/models/cv/detection/foveabox/igie/inference.py new file mode 100644 index 00000000..6b31d898 --- /dev/null +++ b/models/cv/detection/foveabox/igie/inference.py @@ -0,0 +1,154 @@ +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import argparse +import tvm +import torch +import torchvision +import numpy as np +from tvm import relay +from tqdm import tqdm +from mmdet.registry import RUNNERS +from mmengine.config import Config + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument("--engine", + type=str, + required=True, + help="igie engine path.") + + parser.add_argument("--batchsize", + type=int, + required=True, + help="inference batch size.") + + parser.add_argument("--datasets", + type=str, + required=True, + help="datasets path.") + + parser.add_argument("--input_name", + type=str, + required=True, + help="input name of the model.") + + parser.add_argument("--warmup", + type=int, + default=3, + help="number of warmup before test.") + + parser.add_argument("--acc_target", + type=float, + default=None, + help="Model inference Accuracy target.") + + parser.add_argument("--fps_target", + type=float, + default=None, + help="Model inference FPS target.") + + parser.add_argument("--perf_only", + type=bool, + default=False, + help="Run performance test only") + + args = parser.parse_args() + + return args + +def main(): + args = parse_args() + + batch_size = args.batchsize + + # create iluvatar target & device + target = tvm.target.iluvatar(model="MR", options="-libs=cudnn,cublas,ixinfer") + device = tvm.device(target.kind.name, 0) + + # load engine + lib = tvm.runtime.load_module(args.engine) + + # create runtime from engine + module = tvm.contrib.graph_executor.GraphModule(lib["default"](device)) + + # just run perf test + if args.perf_only: + ftimer = module.module.time_evaluator("run", device, number=100, repeat=1) + prof_res = np.array(ftimer().results) * 1000 + fps = batch_size * 1000 / np.mean(prof_res) + print(f"\n* Mean inference time: {np.mean(prof_res):.3f} ms, Mean fps: {fps:.3f}") + else: + # warm up + for _ in range(args.warmup): + module.run() + + # Runner config + cfg = Config.fromfile("fovea_r50_fpn_4xb4-1x_coco.py") + cfg.work_dir = "./" + + cfg['test_dataloader']['batch_size'] = batch_size + cfg['test_dataloader']['dataset']['data_root'] = args.datasets + cfg['test_dataloader']['dataset']['data_prefix']['img'] = 'images/val2017/' + cfg['test_evaluator']['ann_file'] = os.path.join(args.datasets, 'annotations/instances_val2017.json') + + runner = RUNNERS.build(cfg) + + for input_data in tqdm(runner.test_dataloader): + + input_data = runner.model.data_preprocessor(input_data, False) + image = input_data['inputs'].cpu() + + pad_batch = len(image) != batch_size + if pad_batch: + origin_size = len(image) + image = np.resize(image, (batch_size, *image.shape[1:])) + + module.set_input(args.input_name, tvm.nd.array(image, device)) + + module.run() + + cls_score = [] + box_reg = [] + + for i in range(module.get_num_outputs()): + output = module.get_output(i).asnumpy() + + if pad_batch: + output = output[:origin_size] + + output = torch.from_numpy(output) + + if output.shape[1] == 80: + cls_score.append(output) + elif output.shape[1] == 4: + box_reg.append(output) + + batch_img_metas = [ + data_samples.metainfo for data_samples in input_data['data_samples'] + ] + + results_list = runner.model.bbox_head.predict_by_feat(cls_score, box_reg, batch_img_metas=batch_img_metas, rescale=True) + + batch_data_samples = runner.model.add_pred_to_datasample(input_data['data_samples'], results_list) + + runner.test_evaluator.process(data_samples=batch_data_samples, data_batch=input_data) + + metrics = runner.test_evaluator.evaluate(len(runner.test_dataloader.dataset)) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models/cv/detection/foveabox/igie/scripts/infer_foveabox_fp16_accuracy.sh b/models/cv/detection/foveabox/igie/scripts/infer_foveabox_fp16_accuracy.sh new file mode 100644 index 00000000..511ac21c --- /dev/null +++ b/models/cv/detection/foveabox/igie/scripts/infer_foveabox_fp16_accuracy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="foveabox_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,800,800 \ + --precision fp16 \ + --engine_path foveabox_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine foveabox_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ No newline at end of file diff --git a/models/cv/detection/foveabox/igie/scripts/infer_foveabox_fp16_performance.sh b/models/cv/detection/foveabox/igie/scripts/infer_foveabox_fp16_performance.sh new file mode 100644 index 00000000..76d692a1 --- /dev/null +++ b/models/cv/detection/foveabox/igie/scripts/infer_foveabox_fp16_performance.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +batchsize=32 +model_path="foveabox_opt.onnx" +datasets_path=${DATASETS_DIR} + +# build engine +python3 build_engine.py \ + --model_path ${model_path} \ + --input input:${batchsize},3,800,800 \ + --precision fp16 \ + --engine_path foveabox_bs_${batchsize}_fp16.so + + +# inference +python3 inference.py \ + --engine foveabox_bs_${batchsize}_fp16.so \ + --batchsize ${batchsize} \ + --input_name input \ + --datasets ${datasets_path} \ + --perf_only True \ No newline at end of file -- Gitee From e6bfbc4f4c8f8a910261d5061e546b2f750040d6 Mon Sep 17 00:00:00 2001 From: YoungPeng Date: Tue, 30 Apr 2024 06:41:07 +0000 Subject: [PATCH 19/19] Update: repnet README. --- models/cv/trace/repnet/igie/README.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/models/cv/trace/repnet/igie/README.md b/models/cv/trace/repnet/igie/README.md index 7c102509..acecfada 100644 --- a/models/cv/trace/repnet/igie/README.md +++ b/models/cv/trace/repnet/igie/README.md @@ -34,17 +34,9 @@ export DATASETS_DIR=/Path/to/VehicleID/ ```bash # Accuracy -bash scripts/infer_deepsort_fp16_accuracy.sh +bash scripts/infer_repnet_fp16_accuracy.sh # Performance -bash scripts/infer_deepsort_fp16_performance.sh -``` - -### INT8 -```bash -# Accuracy -bash scripts/infer_deepsort_int8_accuracy.sh -# Performance -bash scripts/infer_deepsort_int8_performance.sh +bash scripts/infer_repnet_fp16_performance.sh ``` ## Results @@ -55,4 +47,4 @@ RepNet | 32 | FP16 |1373.579 | 99.88 | ## Reference -RepNet-MDNet-VehicleReID: https://github.com/CaptainEven/RepNet-MDNet-VehicleReID \ No newline at end of file +RepNet-MDNet-VehicleReID: https://github.com/CaptainEven/RepNet-MDNet-VehicleReID -- Gitee