From 95ff2f6fb53ff28e3c282892905855fe3771f7ef Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Tue, 21 Apr 2026 17:44:19 +0800 Subject: [PATCH] add ixrt models --- README.md | 9 +- README_en.md | 9 +- .../cv/classification/deit_b/ixrt/README.md | 64 +++ .../classification/deit_b/ixrt/ci/prepare.sh | 31 ++ .../scripts/infer_deit_b_fp16_accuracy.sh | 130 +++++ .../scripts/infer_deit_b_fp16_performance.sh | 130 +++++ .../ixrt_common/config/DEIT_B_CONFIG | 33 ++ .../ixrt_common/config/MOBILENET_V1_CONFIG | 33 ++ .../ixrt_common/config/VIT_CONFIG | 33 ++ .../mobilenet_v1/ixrt/README.md | 74 +++ .../mobilenet_v1/ixrt/ci/prepare.sh | 31 ++ .../infer_mobilenet_v1_fp16_accuracy.sh | 141 +++++ .../infer_mobilenet_v1_fp16_performance.sh | 141 +++++ .../infer_mobilenet_v1_int8_accuracy.sh | 141 +++++ .../infer_mobilenet_v1_int8_performance.sh | 141 +++++ models/cv/classification/vit/ixrt/README.md | 64 +++ .../cv/classification/vit/ixrt/ci/prepare.sh | 31 ++ .../ixrt/scripts/infer_vit_fp16_accuracy.sh | 60 +++ .../scripts/infer_vit_fp16_performance.sh | 62 +++ models/cv/ocr/dbnet/ixrt/README.md | 67 +++ models/cv/ocr/dbnet/ixrt/build_engine.py | 49 ++ models/cv/ocr/dbnet/ixrt/ci/prepare.sh | 22 + models/cv/ocr/dbnet/ixrt/dbnet_inference.py | 106 ++++ .../ixrt/scripts/infer_dbnet_fp16_accuracy.sh | 54 ++ .../scripts/infer_dbnet_fp16_performance.sh | 54 ++ .../ixrt/scripts/infer_dbnet_int8_accuracy.sh | 54 ++ .../scripts/infer_dbnet_int8_performance.sh | 54 ++ models/cv/ocr/dbnet/ixrt/util/__init__.py | 4 + models/cv/ocr/dbnet/ixrt/util/common.py | 66 +++ .../cv/ocr/dbnet/ixrt/util/db_postprocess.py | 249 +++++++++ models/cv/ocr/dbnet/ixrt/util/dbnet_det.py | 235 ++++++++ models/cv/ocr/dbnet/ixrt/util/eval_det_iou.py | 289 ++++++++++ .../ddrnet/ixrt/README.md | 66 +++ .../ddrnet/ixrt/build_engine.py | 68 +++ .../ddrnet/ixrt/ci/prepare.sh | 22 + .../ddrnet/ixrt/deploy.py | 127 +++++ .../ddrnet/ixrt/inference.py | 301 +++++++++++ .../ddrnet/ixrt/quant.py | 70 +++ .../scripts/infer_ddrnet_fp16_accuracy.sh | 120 +++++ .../scripts/infer_ddrnet_fp16_performance.sh | 115 ++++ .../scripts/infer_ddrnet_int8_accuracy.sh | 138 +++++ .../scripts/infer_ddrnet_int8_performance.sh | 133 +++++ .../ddrnet/ixrt/sim_onnx_model.py | 17 + .../ddrnet/ixrt/utils/__init__.py | 14 + .../ddrnet/ixrt/utils/dataset.py | 139 +++++ .../ddrnet/ixrt/utils/metrics.py | 55 ++ .../deepspeech2/ixrt/README.md | 74 +++ .../deepspeech2/ixrt/build_engine.py | 82 +++ .../deepspeech2/ixrt/ci/prepare.sh | 34 ++ .../deepspeech2/ixrt/convert_weights.py | 141 +++++ .../deepspeech2/ixrt/cut_onnx_model.py | 12 + .../deepspeech2/ixrt/data/decoder.pdparams | Bin 0 -> 254370 bytes .../deepspeech2/ixrt/data/demo_002_en.wav | Bin 0 -> 270226 bytes .../deepspeech2/ixrt/data/mean_std.json | 1 + .../deepspeech2/ixrt/data/preprocess.yaml | 25 + .../deepspeech2/ixrt/dataset/__init__.py | 2 + .../deepspeech2/ixrt/dataset/librispeech.py | 70 +++ .../deepspeech2/ixrt/decoder/__init__.py | 2 + .../deepspeech2/ixrt/decoder/align.py | 162 ++++++ .../deepspeech2/ixrt/decoder/ctc.py | 443 +++++++++++++++ .../deepspeech2/ixrt/decoder/ctc_utils.py | 195 +++++++ .../ixrt/decoder/ctcdecoder/__init__.py | 18 + .../decoder/ctcdecoder/decoders_deprecated.py | 235 ++++++++ .../decoder/ctcdecoder/scorer_deprecated.py | 78 +++ .../ixrt/decoder/ctcdecoder/swig_wrapper.py | 146 +++++ .../ixrt/decoder/ctcdecoder/test_decoders.py | 87 +++ .../deepspeech2/ixrt/decoder/loss.py | 166 ++++++ .../ixrt/decoder/utils/text_grid.py | 114 ++++ .../deepspeech2/ixrt/decoder/utils/utility.py | 79 +++ .../deepspeech2/ixrt/inference.py | 183 +++++++ .../deepspeech2/ixrt/inference_demo.py | 179 +++++++ .../deepspeech2/ixrt/load_ixrt_plugin.py | 13 + .../ixrt/modify_model_to_dynamic.py | 28 + .../infer_deepspeech2_fp16_accuracy.sh | 41 ++ .../infer_deepspeech2_fp16_performance.sh | 41 ++ .../deepspeech2/ixrt/transform/__init__.py | 2 + .../deepspeech2/ixrt/transform/cmvn.py | 187 +++++++ .../deepspeech2/ixrt/transform/functional.py | 110 ++++ .../ixrt/transform/spec_augment.py | 193 +++++++ .../deepspeech2/ixrt/transform/spectrogram.py | 505 ++++++++++++++++++ .../ixrt/transform/transformation.py | 156 ++++++ .../deepspeech2/ixrt/utils/__init__.py | 16 + .../deepspeech2/ixrt/utils/error_rate.py | 351 ++++++++++++ .../deepspeech2/ixrt/utils/load_tensorrt.py | 56 ++ tests/model_info.json | 201 +++++++ tests/run_ixrt.py | 18 +- 86 files changed, 8482 insertions(+), 10 deletions(-) create mode 100644 models/cv/classification/deit_b/ixrt/README.md create mode 100644 models/cv/classification/deit_b/ixrt/ci/prepare.sh create mode 100644 models/cv/classification/deit_b/ixrt/scripts/infer_deit_b_fp16_accuracy.sh create mode 100644 models/cv/classification/deit_b/ixrt/scripts/infer_deit_b_fp16_performance.sh create mode 100644 models/cv/classification/ixrt_common/config/DEIT_B_CONFIG create mode 100644 models/cv/classification/ixrt_common/config/MOBILENET_V1_CONFIG create mode 100644 models/cv/classification/ixrt_common/config/VIT_CONFIG create mode 100644 models/cv/classification/mobilenet_v1/ixrt/README.md create mode 100644 models/cv/classification/mobilenet_v1/ixrt/ci/prepare.sh create mode 100644 models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_fp16_accuracy.sh create mode 100644 models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_fp16_performance.sh create mode 100644 models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_int8_accuracy.sh create mode 100644 models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_int8_performance.sh create mode 100644 models/cv/classification/vit/ixrt/README.md create mode 100644 models/cv/classification/vit/ixrt/ci/prepare.sh create mode 100644 models/cv/classification/vit/ixrt/scripts/infer_vit_fp16_accuracy.sh create mode 100644 models/cv/classification/vit/ixrt/scripts/infer_vit_fp16_performance.sh create mode 100644 models/cv/ocr/dbnet/ixrt/README.md create mode 100644 models/cv/ocr/dbnet/ixrt/build_engine.py create mode 100644 models/cv/ocr/dbnet/ixrt/ci/prepare.sh create mode 100644 models/cv/ocr/dbnet/ixrt/dbnet_inference.py create mode 100644 models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_fp16_accuracy.sh create mode 100644 models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_fp16_performance.sh create mode 100644 models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_int8_accuracy.sh create mode 100644 models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_int8_performance.sh create mode 100644 models/cv/ocr/dbnet/ixrt/util/__init__.py create mode 100644 models/cv/ocr/dbnet/ixrt/util/common.py create mode 100644 models/cv/ocr/dbnet/ixrt/util/db_postprocess.py create mode 100644 models/cv/ocr/dbnet/ixrt/util/dbnet_det.py create mode 100644 models/cv/ocr/dbnet/ixrt/util/eval_det_iou.py create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/README.md create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/build_engine.py create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/ci/prepare.sh create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/deploy.py create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/inference.py create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/quant.py create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_fp16_accuracy.sh create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_fp16_performance.sh create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_int8_accuracy.sh create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_int8_performance.sh create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/sim_onnx_model.py create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/utils/__init__.py create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/utils/dataset.py create mode 100644 models/cv/semantic_segmentation/ddrnet/ixrt/utils/metrics.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/README.md create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/build_engine.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/ci/prepare.sh create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/convert_weights.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/cut_onnx_model.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/data/decoder.pdparams create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/data/demo_002_en.wav create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/data/mean_std.json create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/data/preprocess.yaml create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/dataset/__init__.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/dataset/librispeech.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/__init__.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/align.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctc.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctc_utils.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/__init__.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/decoders_deprecated.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/scorer_deprecated.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/swig_wrapper.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/test_decoders.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/loss.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/utils/text_grid.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/decoder/utils/utility.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/inference.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/inference_demo.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/load_ixrt_plugin.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/modify_model_to_dynamic.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/scripts/infer_deepspeech2_fp16_accuracy.sh create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/scripts/infer_deepspeech2_fp16_performance.sh create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/transform/__init__.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/transform/cmvn.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/transform/functional.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/transform/spec_augment.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/transform/spectrogram.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/transform/transformation.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/utils/__init__.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/utils/error_rate.py create mode 100644 models/speech/speech_recognition/deepspeech2/ixrt/utils/load_tensorrt.py diff --git a/README.md b/README.md index 642183cc..8e7d5371 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,7 @@ | CSPResNet50 | FP16 | [✅](models/cv/classification/cspresnet50/igie) | [✅](models/cv/classification/cspresnet50/ixrt) | 4.3.0 | | | INT8 | | [✅](models/cv/classification/cspresnet50/ixrt) | 4.3.0 | | CSPResNeXt50 | FP16 | [✅](models/cv/classification/cspresnext50/igie) | [✅](models/cv/classification/cspresnext50/ixrt) | 4.3.0 | +| DeiT-B | FP16 | | [✅](models/cv/classification/deit_b/ixrt) | 4.4.0 | | DeiT-tiny | FP16 | [✅](models/cv/classification/deit_tiny/igie) | [✅](models/cv/classification/deit_tiny/ixrt) | 4.3.0 | | DenseNet121 | FP16 | [✅](models/cv/classification/densenet121/igie) | [✅](models/cv/classification/densenet121/ixrt) | 4.3.0 | | | INT8 | [✅](models/cv/classification/densenet121/igie) | | 4.4.0 | @@ -152,6 +153,7 @@ | MNASNet0_75 | FP16 | [✅](models/cv/classification/mnasnet0_75/igie) | | 4.3.0 | | MNASNet1_0 | FP16 | [✅](models/cv/classification/mnasnet1_0/igie) | | 4.3.0 | | MNASNet1_3 | FP16 | [✅](models/cv/classification/mnasnet1_3/igie) | | 4.3.0 | +| MobileNetV1 | FP16 | | [✅](models/cv/classification/mobilenet_v1/ixrt) | 4.4.0 | | MobileNetV2 | FP16 | [✅](models/cv/classification/mobilenet_v2/igie) | [✅](models/cv/classification/mobilenet_v2/ixrt) | 4.3.0 | | | INT8 | [✅](models/cv/classification/mobilenet_v2/igie) | [✅](models/cv/classification/mobilenet_v2/ixrt) | 4.3.0 | | MobileNetV3_Large | FP16 | [✅](models/cv/classification/mobilenet_v3_large/igie) | | 4.3.0 | @@ -211,7 +213,7 @@ | | INT8 | [✅](models/cv/classification/vgg16/igie) | | 4.3.0 | | VGG19 | FP16 | [✅](models/cv/classification/vgg19/igie) | | 4.3.0 | | VGG19_BN | FP16 | [✅](models/cv/classification/vgg19_bn/igie) | | 4.3.0 | -| ViT | FP16 | [✅](models/cv/classification/vit/igie) | | 4.3.0 | +| ViT | FP16 | [✅](models/cv/classification/vit/igie) | [✅](models/cv/classification/vit/ixit) | 4.3.0 | | ViT-B-32 | FP16 | [✅](models/cv/classification/vit_b_32/igie) | | 4.4.0 | | ViT-L-14 | FP16 | [✅](models/cv/classification/vit_l_14/igie) | | 4.4.0 | | Wide ResNet50 | FP16 | [✅](models/cv/classification/wide_resnet50/igie) | [✅](models/cv/classification/wide_resnet50/ixrt) | 4.3.0 | @@ -293,9 +295,10 @@ | Model | Prec. | IGIE | ixRT | IXUCA SDK | |---------------|-------|---------------------------------------|---------------------------------------|-----------| +| CRNN | FP16 | | [✅](models/cv/ocr/crnn/ixrt) | 4.4.0 | +| DBNet | FP16 | | [✅](models/cv/ocr/dbnet/ixrt) | 4.4.0 | | Kie_layoutXLM | FP16 | [✅](models/cv/ocr/kie_layoutxlm/igie) | | 4.3.0 | | SVTR | FP16 | [✅](models/cv/ocr/svtr/igie) | | 4.3.0 | -| CRNN | FP16 | | [✅](models/cv/ocr/crnn/ixrt) | 4.4.0 | #### 姿态估计 @@ -316,6 +319,7 @@ | Model | Prec. | IGIE | ixRT | IXUCA SDK | |-------|-------|------------------------------------------------|------------------------------------------------|-----------| +| DDRNet | FP16 | | [✅](models/cv/semantic_segmentation/ddrnet/ixrt) | 4.4.0 | | UNet | FP16 | [✅](models/cv/semantic_segmentation/unet/igie) | [✅](models/cv/semantic_segmentation/unet/ixrt) | 4.3.0 | #### 多目标跟踪 @@ -377,6 +381,7 @@ | Model | Prec. | IGIE | ixRT | IXUCA SDK | |-----------------|-------|-----------------------------------------------------|-----------------------------------------------------------|-----------| | Conformer | FP16 | [✅](models/audio/speech_recognition/conformer/igie) | [✅](models/audio/speech_recognition/conformer/ixrt) | 4.3.0 | +| DeepSpeech2 | FP16 | | [✅](models/speech/speech_recognition/deepspeech2/ixrt) | 4.4.0 | | Transformer ASR | FP16 | | [✅](models/audio/speech_recognition/transformer_asr/ixrt) | 4.2.0 | ### 其他 diff --git a/README_en.md b/README_en.md index af9c0e6e..0b55d2b0 100644 --- a/README_en.md +++ b/README_en.md @@ -111,6 +111,7 @@ inference to be expanded in the future. | CSPResNet50 | FP16 | [✅](models/cv/classification/cspresnet50/igie) | [✅](models/cv/classification/cspresnet50/ixrt) | 4.3.0 | | | INT8 | | [✅](models/cv/classification/cspresnet50/ixrt) | 4.3.0 | | CSPResNeXt50 | FP16 | [✅](models/cv/classification/cspresnext50/igie) | [✅](models/cv/classification/cspresnext50/ixrt) | 4.3.0 | +| DeiT-B | FP16 | | [✅](models/cv/classification/deit_b/ixrt) | 4.4.0 | | DeiT-tiny | FP16 | [✅](models/cv/classification/deit_tiny/igie) | [✅](models/cv/classification/deit_tiny/ixrt) | 4.3.0 | | DenseNet121 | FP16 | [✅](models/cv/classification/densenet121/igie) | [✅](models/cv/classification/densenet121/ixrt) | 4.3.0 | | | INT8 | [✅](models/cv/classification/densenet121/igie) | | 4.4.0 | @@ -144,6 +145,7 @@ inference to be expanded in the future. | MNASNet0_75 | FP16 | [✅](models/cv/classification/mnasnet0_75/igie) | | 4.3.0 | | MNASNet1_0 | FP16 | [✅](models/cv/classification/mnasnet1_0/igie) | | 4.3.0 | | MNASNet1_3 | FP16 | [✅](models/cv/classification/mnasnet1_3/igie) | | 4.3.0 | +| MobileNetV1 | FP16 | | [✅](models/cv/classification/mobilenet_v1/ixrt) | 4.4.0 | | MobileNetV2 | FP16 | [✅](models/cv/classification/mobilenet_v2/igie) | [✅](models/cv/classification/mobilenet_v2/ixrt) | 4.3.0 | | | INT8 | [✅](models/cv/classification/mobilenet_v2/igie) | [✅](models/cv/classification/mobilenet_v2/ixrt) | 4.3.0 | | MobileNetV3_Large | FP16 | [✅](models/cv/classification/mobilenet_v3_large/igie) | | 4.3.0 | @@ -203,7 +205,7 @@ inference to be expanded in the future. | | INT8 | [✅](models/cv/classification/vgg16/igie) | | 4.3.0 | | VGG19 | FP16 | [✅](models/cv/classification/vgg19/igie) | | 4.3.0 | | VGG19_BN | FP16 | [✅](models/cv/classification/vgg19_bn/igie) | | 4.3.0 | -| ViT | FP16 | [✅](models/cv/classification/vit/igie) | | 4.3.0 | +| ViT | FP16 | [✅](models/cv/classification/vit/igie) | [✅](models/cv/classification/vit/ixit) | 4.3.0 | | ViT-B-32 | FP16 | [✅](models/cv/classification/vit_b_32/igie) | | 4.4.0 | | ViT-L-14 | FP16 | [✅](models/cv/classification/vit_l_14/igie) | | 4.4.0 | | Wide ResNet50 | FP16 | [✅](models/cv/classification/wide_resnet50/igie) | [✅](models/cv/classification/wide_resnet50/ixrt) | 4.3.0 | @@ -284,9 +286,10 @@ inference to be expanded in the future. | Model | Prec. | IGIE | ixRT | IXUCA SDK | |---------------|-------|---------------------------------------|---------------------------------------|-----------| +| CRNN | FP16 | | [✅](models/cv/ocr/crnn/ixrt) | 4.4.0 | +| DBNet | FP16 | | [✅](models/cv/ocr/dbnet/ixrt) | 4.4.0 | | Kie_layoutXLM | FP16 | [✅](models/cv/ocr/kie_layoutxlm/igie) | | 4.3.0 | | SVTR | FP16 | [✅](models/cv/ocr/svtr/igie) | | 4.3.0 | -| CRNN | FP16 | | [✅](models/cv/ocr/crnn/ixrt) | 4.4.0 | #### Pose Estimation @@ -307,6 +310,7 @@ inference to be expanded in the future. | Model | Prec. | IGIE | ixRT | IXUCA SDK | |-------|-------|------------------------------------------------|------------------------------------------------|-----------| +| DDRNet | FP16 | | [✅](models/cv/semantic_segmentation/ddrnet/ixrt) | 4.4.0 | | UNet | FP16 | [✅](models/cv/semantic_segmentation/unet/igie) | [✅](models/cv/semantic_segmentation/unet/ixrt) | 4.3.0 | #### Multi-Object Tracking @@ -367,6 +371,7 @@ inference to be expanded in the future. | Model | Prec. | IGIE | ixRT | IXUCA SDK | |-----------------|-------|-----------------------------------------------------|-----------------------------------------------------------|-----------| | Conformer | FP16 | [✅](models/audio/speech_recognition/conformer/igie) | [✅](models/audio/speech_recognition/conformer/ixrt) | 4.3.0 | +| DeepSpeech2 | FP16 | | [✅](models/speech/speech_recognition/deepspeech2/ixrt) | 4.4.0 | | Transformer ASR | FP16 | | [✅](models/audio/speech_recognition/transformer_asr/ixrt) | 4.2.0 | ### Others diff --git a/models/cv/classification/deit_b/ixrt/README.md b/models/cv/classification/deit_b/ixrt/README.md new file mode 100644 index 00000000..bc315f97 --- /dev/null +++ b/models/cv/classification/deit_b/ixrt/README.md @@ -0,0 +1,64 @@ +# DeiT-Base (ixRT) + +## Model Description + +DeiT-Base (Data-efficient Image Transformer Base) is a vision transformer model that uses knowledge distillation to achieve competitive performance with fewer training resources. + +## Supported Environments + +| GPU | [IXUCA SDK](https://gitee.com/deep-spark/deepspark#%E5%A4%A9%E6%95%B0%E6%99%BA%E7%AE%97%E8%BD%AF%E4%BB%B6%E6%A0%88-ixuca) | Release | +| :----: | :----: | :----: | +| MR-V100 | 4.4.0 | 26.06 | + +## Model Preparation + +### Prepare Resources + +Pretrained model: + +Download the [imagenet](https://www.image-net.org/download.php) to download the validation dataset. + +### Install Dependencies + +```bash +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +pip3 install -r ../../ixrt_common/requirements.txt +``` + +### Model Conversion + +```bash +mkdir checkpoints +cd checkpoints +wget http://files.deepspark.org.cn:880/deepspark/data/checkpoints/deit_b.onnx +``` + +## Model Inference + +```bash +export PROJ_DIR=./ +export DATASETS_DIR=/path/to/imagenet_val/ +export CHECKPOINTS_DIR=./checkpoints +export RUN_DIR=../../ixrt_common/ +export CONFIG_DIR=../../ixrt_common/config/DEIT_B_CONFIG +``` + +### FP16 + +```bash +# Test ACC +bash scripts/infer_deit_b_fp16_accuracy.sh +# Test FPS +bash scripts/infer_deit_b_fp16_performance.sh +``` + +## Model Results + +| Model | BatchSize | Precision | FPS | Top-1(%) | Top-5(%) | +| ----------- | --------- | --------- | ------- | -------- | -------- | +| DeiT-Base | 32 | FP16 | 596.381 | 81.7 | 95.6 | diff --git a/models/cv/classification/deit_b/ixrt/ci/prepare.sh b/models/cv/classification/deit_b/ixrt/ci/prepare.sh new file mode 100644 index 00000000..249358b9 --- /dev/null +++ b/models/cv/classification/deit_b/ixrt/ci/prepare.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -x + +ID=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"') +if [[ ${ID} == "ubuntu" ]]; then + apt install -y libgl1-mesa-glx +elif [[ ${ID} == "centos" ]]; then + yum install -y mesa-libGL +else + echo "Not Support Os" +fi + +pip3 install tqdm onnxsim opencv-python==4.6.0.66 + +mkdir -p checkpoints +cp /root/data/checkpoints/deit_b.onnx checkpoints/ \ No newline at end of file diff --git a/models/cv/classification/deit_b/ixrt/scripts/infer_deit_b_fp16_accuracy.sh b/models/cv/classification/deit_b/ixrt/scripts/infer_deit_b_fp16_accuracy.sh new file mode 100644 index 00000000..e8c90b68 --- /dev/null +++ b/models/cv/classification/deit_b/ixrt/scripts/infer_deit_b_fp16_accuracy.sh @@ -0,0 +1,130 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +BSZ=32 +TGT=0.796 +WARM_UP=0 +LOOP_COUNT=-1 +RUN_MODE=ACC +PRECISION=float16 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +source ${CONFIG_DIR} +ORIGINE_MODEL=${CHECKPOINTS_DIR}/${ORIGINE_MODEL} + +echo CHECKPOINTS_DIR : ${CHECKPOINTS_DIR} +echo DATASETS_DIR : ${DATASETS_DIR} +echo RUN_DIR : ${RUN_DIR} +echo CONFIG_DIR : ${CONFIG_DIR} +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} + +step=0 + +# Simplify Model +let step++ +echo; +echo [STEP ${step}] : Simplify Model +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model Skipped, ${SIM_MODEL} has been existed +else + python3 ${RUN_DIR}/simplify_model.py \ + --origin_model $ORIGINE_MODEL \ + --output_model ${SIM_MODEL} || exit 1 + echo " "Generate ${SIM_MODEL} +fi + +# Refine Model +let step++ +echo; +echo [STEP ${step}] : Refine Model +REFINE_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_refine.onnx +if [ -f ${REFINE_MODEL} ];then + echo " "Refine Model Skipped, ${REFINE_MODEL} has been existed +else + python3 ${RUN_DIR}/refine_model.py \ + --onnx_path ${SIM_MODEL} \ + --dst_onnx_path ${REFINE_MODEL} \ + --bsz ${BSZ} \ + --imgsz ${IMGSIZE} || exit 1 +fi + +# Change Batchsize +let step++ +echo; +echo [STEP ${step}] : Change Batchsize +FINAL_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.onnx +if [ -f $FINAL_MODEL ];then + echo " "Change Batchsize Skipped, $FINAL_MODEL has been existed +else + python3 ${RUN_DIR}/modify_batchsize.py \ + --batch_size ${BSZ} \ + --origin_model ${REFINE_MODEL} \ + --output_model ${FINAL_MODEL} || exit 1 + echo " "Generate ${FINAL_MODEL} +fi + +# Build Engine +let step++ +echo; +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ -f $ENGINE_FILE ];then + echo " "Build Engine Skip, $ENGINE_FILE has been existed +else + python3 ${RUN_DIR}/build_engine.py \ + --precision ${PRECISION} \ + --model ${FINAL_MODEL} \ + --engine ${ENGINE_FILE} || exit 1 + echo " "Generate Engine ${ENGINE_FILE} +fi + +# Inference +let step++ +echo; +echo [STEP ${step}] : Inference +python3 ${RUN_DIR}/inference.py \ + --engine_file=${ENGINE_FILE} \ + --datasets_dir=${DATASETS_DIR} \ + --imgsz=${IMGSIZE} \ + --warm_up=${WARM_UP} \ + --loop_count ${LOOP_COUNT} \ + --test_mode ${RUN_MODE} \ + --acc_target ${TGT} \ + --bsz ${BSZ}; check_status + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/classification/deit_b/ixrt/scripts/infer_deit_b_fp16_performance.sh b/models/cv/classification/deit_b/ixrt/scripts/infer_deit_b_fp16_performance.sh new file mode 100644 index 00000000..9d41dafa --- /dev/null +++ b/models/cv/classification/deit_b/ixrt/scripts/infer_deit_b_fp16_performance.sh @@ -0,0 +1,130 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +BSZ=32 +TGT=0.796 +WARM_UP=10 +LOOP_COUNT=20 +RUN_MODE=FPS +PRECISION=float16 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +source ${CONFIG_DIR} +ORIGINE_MODEL=${CHECKPOINTS_DIR}/${ORIGINE_MODEL} + +echo CHECKPOINTS_DIR : ${CHECKPOINTS_DIR} +echo DATASETS_DIR : ${DATASETS_DIR} +echo RUN_DIR : ${RUN_DIR} +echo CONFIG_DIR : ${CONFIG_DIR} +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} + +step=0 + +# Simplify Model +let step++ +echo; +echo [STEP ${step}] : Simplify Model +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model Skipped, ${SIM_MODEL} has been existed +else + python3 ${RUN_DIR}/simplify_model.py \ + --origin_model $ORIGINE_MODEL \ + --output_model ${SIM_MODEL} + echo " "Generate ${SIM_MODEL} +fi + +# Refine Model +let step++ +echo; +echo [STEP ${step}] : Refine Model +REFINE_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_refine.onnx +if [ -f ${REFINE_MODEL} ];then + echo " "Refine Model Skipped, ${REFINE_MODEL} has been existed +else + python3 ${RUN_DIR}/refine_model.py \ + --onnx_path ${SIM_MODEL} \ + --dst_onnx_path ${REFINE_MODEL} \ + --bsz ${BSZ} \ + --imgsz ${IMGSIZE} +fi + +# Change Batchsize +let step++ +echo; +echo [STEP ${step}] : Change Batchsize +FINAL_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.onnx +if [ -f $FINAL_MODEL ];then + echo " "Change Batchsize Skipped, $FINAL_MODEL has been existed +else + python3 ${RUN_DIR}/modify_batchsize.py \ + --batch_size ${BSZ} \ + --origin_model ${REFINE_MODEL} \ + --output_model ${FINAL_MODEL} + echo " "Generate ${FINAL_MODEL} +fi + +# Build Engine +let step++ +echo; +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ -f $ENGINE_FILE ];then + echo " "Build Engine Skip, $ENGINE_FILE has been existed +else + python3 ${RUN_DIR}/build_engine.py \ + --precision ${PRECISION} \ + --model ${FINAL_MODEL} \ + --engine ${ENGINE_FILE} + echo " "Generate Engine ${ENGINE_FILE} +fi + +# Inference +let step++ +echo; +echo [STEP ${step}] : Inference +python3 ${RUN_DIR}/inference.py \ + --engine_file=${ENGINE_FILE} \ + --datasets_dir=${DATASETS_DIR} \ + --imgsz=${IMGSIZE} \ + --warm_up=${WARM_UP} \ + --loop_count ${LOOP_COUNT} \ + --test_mode ${RUN_MODE} \ + --fps_target ${TGT} \ + --bsz ${BSZ}; check_status + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/classification/ixrt_common/config/DEIT_B_CONFIG b/models/cv/classification/ixrt_common/config/DEIT_B_CONFIG new file mode 100644 index 00000000..4170a28b --- /dev/null +++ b/models/cv/classification/ixrt_common/config/DEIT_B_CONFIG @@ -0,0 +1,33 @@ +# 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. +# IMGSIZE : 模型输入hw大小 +# MODEL_NAME : 生成onnx/engine的basename +# ORIGINE_MODEL : 原始onnx文件名称 +IMGSIZE=224 +MODEL_NAME=Deit_base +ORIGINE_MODEL=deit_b.onnx + +# QUANT CONFIG (仅PRECISION为int8时生效) + # QUANT_OBSERVER : 量化策略,可选 [hist_percentile, percentile, minmax, entropy, ema] + # QUANT_BATCHSIZE : 量化时组dataloader的batchsize, 最好和onnx中的batchsize保持一致,有些op可能推导shape错误(比如Reshape) + # QUANT_STEP : 量化步数 + # QUANT_SEED : 随机种子 保证量化结果可复现 + # QUANT_EXIST_ONNX : 如果有其他来源的量化模型则填写 +QUANT_OBSERVER=hist_percentile +QUANT_BATCHSIZE=1 +QUANT_STEP=32 +QUANT_SEED=42 +DISABLE_QUANT_LIST= +QUANT_EXIST_ONNX= \ No newline at end of file diff --git a/models/cv/classification/ixrt_common/config/MOBILENET_V1_CONFIG b/models/cv/classification/ixrt_common/config/MOBILENET_V1_CONFIG new file mode 100644 index 00000000..c49b9b6a --- /dev/null +++ b/models/cv/classification/ixrt_common/config/MOBILENET_V1_CONFIG @@ -0,0 +1,33 @@ +# 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. +# IMGSIZE : 模型输入hw大小 +# MODEL_NAME : 生成onnx/engine的basename +# ORIGINE_MODEL : 原始onnx文件名称 +IMGSIZE=224 +MODEL_NAME=MobileNet_v1 +ORIGINE_MODEL=mobilenet_v1.onnx + +# QUANT CONFIG (仅PRECISION为int8时生效) + # QUANT_OBSERVER : 量化策略,可选 [hist_percentile, percentile, minmax, entropy, ema] + # QUANT_BATCHSIZE : 量化时组dataloader的batchsize, 最好和onnx中的batchsize保持一致,有些op可能推导shape错误(比如Reshape) + # QUANT_STEP : 量化步数 + # QUANT_SEED : 随机种子 保证量化结果可复现 + # QUANT_EXIST_ONNX : 如果有其他来源的量化模型则填写 +QUANT_OBSERVER=percentile +QUANT_BATCHSIZE=1 +QUANT_STEP=32 +QUANT_SEED=42 +DISABLE_QUANT_LIST="fc7 prob" +QUANT_EXIST_ONNX= \ No newline at end of file diff --git a/models/cv/classification/ixrt_common/config/VIT_CONFIG b/models/cv/classification/ixrt_common/config/VIT_CONFIG new file mode 100644 index 00000000..0e46d9e9 --- /dev/null +++ b/models/cv/classification/ixrt_common/config/VIT_CONFIG @@ -0,0 +1,33 @@ +# 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. +# IMGSIZE : 模型输入hw大小 +# MODEL_NAME : 生成onnx/engine的basename +# ORIGINE_MODEL : 原始onnx文件名称 +IMGSIZE=224 +MODEL_NAME=vit_b_16 +ORIGINE_MODEL=vit_b_16_sim.onnx + +# QUANT CONFIG (仅PRECISION为int8时生效) + # QUANT_OBSERVER : 量化策略,可选 [hist_percentile, percentile, minmax, entropy, ema] + # QUANT_BATCHSIZE : 量化时组dataloader的batchsize, 最好和onnx中的batchsize保持一致,有些op可能推导shape错误(比如Reshape) + # QUANT_STEP : 量化步数 + # QUANT_SEED : 随机种子 保证量化结果可复现 + # QUANT_EXIST_ONNX : 如果有其他来源的量化模型则填写 +QUANT_OBSERVER=minmax +QUANT_BATCHSIZE=32 +QUANT_STEP=32 +QUANT_SEED=42 +DISABLE_QUANT_LIST= +QUANT_EXIST_ONNX= \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v1/ixrt/README.md b/models/cv/classification/mobilenet_v1/ixrt/README.md new file mode 100644 index 00000000..6451f4cb --- /dev/null +++ b/models/cv/classification/mobilenet_v1/ixrt/README.md @@ -0,0 +1,74 @@ +# MobileNetV1 (ixRT) + +## Model Description + +MobileNetV1 is a efficient model architecture using depthwise separable convolutions. It is designed to efficiently maximize accuracy while being mindful of the tight resource constraints. + +## Supported Environments + +| GPU | [IXUCA SDK](https://gitee.com/deep-spark/deepspark#%E5%A4%A9%E6%95%B0%E6%99%BA%E7%AE%97%E8%BD%AF%E4%BB%B6%E6%A0%88-ixuca) | Release | +| :----: | :----: | :----: | +| MR-V100 | 4.4.0 | 26.06 | + +## Model Preparation + +### Prepare Resources + +Pretrained model: + +Download the [imagenet](https://www.image-net.org/download.php) to download the validation dataset. + +### Install Dependencies + +```bash +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +pip3 install -r ../../ixrt_common/requirements.txt +``` + +### Model Conversion + +```bash +mkdir checkpoints +cd checkpoints +wget http://files.deepspark.org.cn:880/deepspark/data/checkpoints/mobilenet_v1.onnx +``` + +## Model Inference + +```bash +export PROJ_DIR=./ +export DATASETS_DIR=/path/to/imagenet_val/ +export CHECKPOINTS_DIR=./checkpoints +export RUN_DIR=../../ixrt_common/ +export CONFIG_DIR=../../ixrt_common/config/MOBILENET_V1_CONFIG +``` + +### FP16 + +```bash +# Test ACC +bash scripts/infer_mobilenet_v1_fp16_accuracy.sh +# Test FPS +bash scripts/infer_mobilenet_v1_fp16_performance.sh +``` + +### INT8 + +```bash +# Test ACC +bash scripts/infer_mobilenet_v1_int8_accuracy.sh +# Test FPS +bash scripts/infer_mobilenet_v1_int8_performance.sh +``` + +## Model Results + +| Model | BatchSize | Precision | FPS | Top-1(%) | Top-5(%) | +| ----------- | --------- | --------- | ------- | -------- | -------- | +| MobileNetV1 | 32 | FP16 | 13862.317 | 71.6 | 90.3 | +| MobileNetV1 | 32 | INT8 | 17485.601 | 70.9 | 89.9 | \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v1/ixrt/ci/prepare.sh b/models/cv/classification/mobilenet_v1/ixrt/ci/prepare.sh new file mode 100644 index 00000000..02b34ea0 --- /dev/null +++ b/models/cv/classification/mobilenet_v1/ixrt/ci/prepare.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -x + +ID=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"') +if [[ ${ID} == "ubuntu" ]]; then + apt install -y libgl1-mesa-glx +elif [[ ${ID} == "centos" ]]; then + yum install -y mesa-libGL +else + echo "Not Support Os" +fi + +pip3 install tqdm onnxsim opencv-python==4.6.0.66 + +mkdir -p checkpoints +cp /root/data/checkpoints/mobilenet_v1.onnx checkpoints/ \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_fp16_accuracy.sh b/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_fp16_accuracy.sh new file mode 100644 index 00000000..ffe54388 --- /dev/null +++ b/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_fp16_accuracy.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +BSZ=32 +TGT=-1 +WARM_UP=0 +LOOP_COUNT=-1 +RUN_MODE=ACC +PRECISION=float16 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +source ${CONFIG_DIR} +ORIGINE_MODEL=${CHECKPOINTS_DIR}/${ORIGINE_MODEL} + +echo CHECKPOINTS_DIR : ${CHECKPOINTS_DIR} +echo DATASETS_DIR : ${DATASETS_DIR} +echo RUN_DIR : ${RUN_DIR} +echo CONFIG_DIR : ${CONFIG_DIR} +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} + +step=0 +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx + +# Simplify Model +let step++ +echo; +echo [STEP ${step}] : Simplify Model +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model, ${SIM_MODEL} has been existed +else + python3 ${RUN_DIR}/simplify_model.py \ + --origin_model $ORIGINE_MODEL \ + --output_model ${SIM_MODEL} + echo " "Generate ${SIM_MODEL} +fi + +# Quant Model +if [ $PRECISION == "int8" ];then + let step++ + echo; + echo [STEP ${step}] : Quant Model + if [[ -z ${QUANT_EXIST_ONNX} ]];then + QUANT_EXIST_ONNX=$CHECKPOINTS_DIR/quantized_${MODEL_NAME}.onnx + fi + if [[ -f ${QUANT_EXIST_ONNX} ]];then + SIM_MODEL=${QUANT_EXIST_ONNX} + echo " "Quant Model Skip, ${QUANT_EXIST_ONNX} has been existed + else + python3 ${RUN_DIR}/quant.py \ + --model ${SIM_MODEL} \ + --model_name ${MODEL_NAME} \ + --dataset_dir ${DATASETS_DIR} \ + --observer ${QUANT_OBSERVER} \ + --disable_quant_names ${DISABLE_QUANT_LIST[@]} \ + --save_dir $CHECKPOINTS_DIR \ + --bsz ${QUANT_BATCHSIZE} \ + --step ${QUANT_STEP} \ + --seed ${QUANT_SEED} \ + --imgsz ${IMGSIZE} + SIM_MODEL=${QUANT_EXIST_ONNX} + echo " "Generate ${SIM_MODEL} + fi +fi + +# Change Batchsize +let step++ +echo; +echo [STEP ${step}] : Change Batchsize +FINAL_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_${BSZ}.onnx +if [ -f $FINAL_MODEL ];then + echo " "Change Batchsize Skip, $FINAL_MODEL has been existed +else + python3 ${RUN_DIR}/modify_batchsize.py --batch_size ${BSZ} \ + --origin_model ${SIM_MODEL} --output_model ${FINAL_MODEL} + echo " "Generate ${FINAL_MODEL} +fi + +# Build Engine +let step++ +echo; +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ -f $ENGINE_FILE ];then + echo " "Build Engine Skip, $ENGINE_FILE has been existed +else + python3 ${RUN_DIR}/build_engine.py \ + --precision ${PRECISION} \ + --model ${FINAL_MODEL} \ + --engine ${ENGINE_FILE} + echo " "Generate Engine ${ENGINE_FILE} +fi + +# Inference +let step++ +echo; +echo [STEP ${step}] : Inference +python3 ${RUN_DIR}/inference.py \ + --engine_file=${ENGINE_FILE} \ + --datasets_dir=${DATASETS_DIR} \ + --imgsz=${IMGSIZE} \ + --warm_up=${WARM_UP} \ + --loop_count ${LOOP_COUNT} \ + --test_mode ${RUN_MODE} \ + --acc_target ${TGT} \ + --bsz ${BSZ}; check_status + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_fp16_performance.sh b/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_fp16_performance.sh new file mode 100644 index 00000000..43a0f216 --- /dev/null +++ b/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_fp16_performance.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +BSZ=32 +TGT=-1 +WARM_UP=3 +LOOP_COUNT=20 +RUN_MODE=FPS +PRECISION=float16 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +source ${CONFIG_DIR} +ORIGINE_MODEL=${CHECKPOINTS_DIR}/${ORIGINE_MODEL} + +echo CHECKPOINTS_DIR : ${CHECKPOINTS_DIR} +echo DATASETS_DIR : ${DATASETS_DIR} +echo RUN_DIR : ${RUN_DIR} +echo CONFIG_DIR : ${CONFIG_DIR} +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} + +step=0 +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx + +# Simplify Model +let step++ +echo; +echo [STEP ${step}] : Simplify Model +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model, ${SIM_MODEL} has been existed +else + python3 ${RUN_DIR}/simplify_model.py \ + --origin_model $ORIGINE_MODEL \ + --output_model ${SIM_MODEL} + echo " "Generate ${SIM_MODEL} +fi + +# Quant Model +if [ $PRECISION == "int8" ];then + let step++ + echo; + echo [STEP ${step}] : Quant Model + if [[ -z ${QUANT_EXIST_ONNX} ]];then + QUANT_EXIST_ONNX=$CHECKPOINTS_DIR/quantized_${MODEL_NAME}.onnx + fi + if [[ -f ${QUANT_EXIST_ONNX} ]];then + SIM_MODEL=${QUANT_EXIST_ONNX} + echo " "Quant Model Skip, ${QUANT_EXIST_ONNX} has been existed + else + python3 ${RUN_DIR}/quant.py \ + --model ${SIM_MODEL} \ + --model_name ${MODEL_NAME} \ + --dataset_dir ${DATASETS_DIR} \ + --observer ${QUANT_OBSERVER} \ + --disable_quant_names ${DISABLE_QUANT_LIST[@]} \ + --save_dir $CHECKPOINTS_DIR \ + --bsz ${QUANT_BATCHSIZE} \ + --step ${QUANT_STEP} \ + --seed ${QUANT_SEED} \ + --imgsz ${IMGSIZE} + SIM_MODEL=${QUANT_EXIST_ONNX} + echo " "Generate ${SIM_MODEL} + fi +fi + +# Change Batchsize +let step++ +echo; +echo [STEP ${step}] : Change Batchsize +FINAL_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_${BSZ}.onnx +if [ -f $FINAL_MODEL ];then + echo " "Change Batchsize Skip, $FINAL_MODEL has been existed +else + python3 ${RUN_DIR}/modify_batchsize.py --batch_size ${BSZ} \ + --origin_model ${SIM_MODEL} --output_model ${FINAL_MODEL} + echo " "Generate ${FINAL_MODEL} +fi + +# Build Engine +let step++ +echo; +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ -f $ENGINE_FILE ];then + echo " "Build Engine Skip, $ENGINE_FILE has been existed +else + python3 ${RUN_DIR}/build_engine.py \ + --precision ${PRECISION} \ + --model ${FINAL_MODEL} \ + --engine ${ENGINE_FILE} + echo " "Generate Engine ${ENGINE_FILE} +fi + +# Inference +let step++ +echo; +echo [STEP ${step}] : Inference +python3 ${RUN_DIR}/inference.py \ + --engine_file=${ENGINE_FILE} \ + --datasets_dir=${DATASETS_DIR} \ + --imgsz=${IMGSIZE} \ + --warm_up=${WARM_UP} \ + --loop_count ${LOOP_COUNT} \ + --test_mode ${RUN_MODE} \ + --fps_target ${TGT} \ + --bsz ${BSZ}; check_status + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_int8_accuracy.sh b/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_int8_accuracy.sh new file mode 100644 index 00000000..ab3292a6 --- /dev/null +++ b/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_int8_accuracy.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +BSZ=32 +TGT=-1 +WARM_UP=0 +LOOP_COUNT=-1 +RUN_MODE=ACC +PRECISION=int8 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +source ${CONFIG_DIR} +ORIGINE_MODEL=${CHECKPOINTS_DIR}/${ORIGINE_MODEL} + +echo CHECKPOINTS_DIR : ${CHECKPOINTS_DIR} +echo DATASETS_DIR : ${DATASETS_DIR} +echo RUN_DIR : ${RUN_DIR} +echo CONFIG_DIR : ${CONFIG_DIR} +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} + +step=0 +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx + +# Simplify Model +let step++ +echo; +echo [STEP ${step}] : Simplify Model +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model, ${SIM_MODEL} has been existed +else + python3 ${RUN_DIR}/simplify_model.py \ + --origin_model $ORIGINE_MODEL \ + --output_model ${SIM_MODEL} + echo " "Generate ${SIM_MODEL} +fi + +# Quant Model +if [ $PRECISION == "int8" ];then + let step++ + echo; + echo [STEP ${step}] : Quant Model + if [[ -z ${QUANT_EXIST_ONNX} ]];then + QUANT_EXIST_ONNX=$CHECKPOINTS_DIR/quantized_${MODEL_NAME}.onnx + fi + if [[ -f ${QUANT_EXIST_ONNX} ]];then + SIM_MODEL=${QUANT_EXIST_ONNX} + echo " "Quant Model Skip, ${QUANT_EXIST_ONNX} has been existed + else + python3 ${RUN_DIR}/quant.py \ + --model ${SIM_MODEL} \ + --model_name ${MODEL_NAME} \ + --dataset_dir ${DATASETS_DIR} \ + --observer ${QUANT_OBSERVER} \ + --disable_quant_names ${DISABLE_QUANT_LIST[@]} \ + --save_dir $CHECKPOINTS_DIR \ + --bsz ${QUANT_BATCHSIZE} \ + --step ${QUANT_STEP} \ + --seed ${QUANT_SEED} \ + --imgsz ${IMGSIZE} + SIM_MODEL=${QUANT_EXIST_ONNX} + echo " "Generate ${SIM_MODEL} + fi +fi + +# Change Batchsize +let step++ +echo; +echo [STEP ${step}] : Change Batchsize +FINAL_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_quant_${BSZ}.onnx +if [ -f $FINAL_MODEL ];then + echo " "Change Batchsize Skip, $FINAL_MODEL has been existed +else + python3 ${RUN_DIR}/modify_batchsize.py --batch_size ${BSZ} \ + --origin_model ${SIM_MODEL} --output_model ${FINAL_MODEL} + echo " "Generate ${FINAL_MODEL} +fi + +# Build Engine +let step++ +echo; +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ -f $ENGINE_FILE ];then + echo " "Build Engine Skip, $ENGINE_FILE has been existed +else + python3 ${RUN_DIR}/build_engine.py \ + --precision ${PRECISION} \ + --model ${FINAL_MODEL} \ + --engine ${ENGINE_FILE} + echo " "Generate Engine ${ENGINE_FILE} +fi + +# Inference +let step++ +echo; +echo [STEP ${step}] : Inference +python3 ${RUN_DIR}/inference.py \ + --engine_file=${ENGINE_FILE} \ + --datasets_dir=${DATASETS_DIR} \ + --imgsz=${IMGSIZE} \ + --warm_up=${WARM_UP} \ + --loop_count ${LOOP_COUNT} \ + --test_mode ${RUN_MODE} \ + --acc_target ${TGT} \ + --bsz ${BSZ}; check_status + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_int8_performance.sh b/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_int8_performance.sh new file mode 100644 index 00000000..a8c63eab --- /dev/null +++ b/models/cv/classification/mobilenet_v1/ixrt/scripts/infer_mobilenet_v1_int8_performance.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +BSZ=32 +TGT=-1 +WARM_UP=3 +LOOP_COUNT=20 +RUN_MODE=FPS +PRECISION=int8 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +source ${CONFIG_DIR} +ORIGINE_MODEL=${CHECKPOINTS_DIR}/${ORIGINE_MODEL} + +echo CHECKPOINTS_DIR : ${CHECKPOINTS_DIR} +echo DATASETS_DIR : ${DATASETS_DIR} +echo RUN_DIR : ${RUN_DIR} +echo CONFIG_DIR : ${CONFIG_DIR} +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} + +step=0 +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx + +# Simplify Model +let step++ +echo; +echo [STEP ${step}] : Simplify Model +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model, ${SIM_MODEL} has been existed +else + python3 ${RUN_DIR}/simplify_model.py \ + --origin_model $ORIGINE_MODEL \ + --output_model ${SIM_MODEL} + echo " "Generate ${SIM_MODEL} +fi + +# Quant Model +if [ $PRECISION == "int8" ];then + let step++ + echo; + echo [STEP ${step}] : Quant Model + if [[ -z ${QUANT_EXIST_ONNX} ]];then + QUANT_EXIST_ONNX=$CHECKPOINTS_DIR/quantized_${MODEL_NAME}.onnx + fi + if [[ -f ${QUANT_EXIST_ONNX} ]];then + SIM_MODEL=${QUANT_EXIST_ONNX} + echo " "Quant Model Skip, ${QUANT_EXIST_ONNX} has been existed + else + python3 ${RUN_DIR}/quant.py \ + --model ${SIM_MODEL} \ + --model_name ${MODEL_NAME} \ + --dataset_dir ${DATASETS_DIR} \ + --observer ${QUANT_OBSERVER} \ + --disable_quant_names ${DISABLE_QUANT_LIST[@]} \ + --save_dir $CHECKPOINTS_DIR \ + --bsz ${QUANT_BATCHSIZE} \ + --step ${QUANT_STEP} \ + --seed ${QUANT_SEED} \ + --imgsz ${IMGSIZE} + SIM_MODEL=${QUANT_EXIST_ONNX} + echo " "Generate ${SIM_MODEL} + fi +fi + +# Change Batchsize +let step++ +echo; +echo [STEP ${step}] : Change Batchsize +FINAL_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_quant_${BSZ}.onnx +if [ -f $FINAL_MODEL ];then + echo " "Change Batchsize Skip, $FINAL_MODEL has been existed +else + python3 ${RUN_DIR}/modify_batchsize.py --batch_size ${BSZ} \ + --origin_model ${SIM_MODEL} --output_model ${FINAL_MODEL} + echo " "Generate ${FINAL_MODEL} +fi + +# Build Engine +let step++ +echo; +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ -f $ENGINE_FILE ];then + echo " "Build Engine Skip, $ENGINE_FILE has been existed +else + python3 ${RUN_DIR}/build_engine.py \ + --precision ${PRECISION} \ + --model ${FINAL_MODEL} \ + --engine ${ENGINE_FILE} + echo " "Generate Engine ${ENGINE_FILE} +fi + +# Inference +let step++ +echo; +echo [STEP ${step}] : Inference +python3 ${RUN_DIR}/inference.py \ + --engine_file=${ENGINE_FILE} \ + --datasets_dir=${DATASETS_DIR} \ + --imgsz=${IMGSIZE} \ + --warm_up=${WARM_UP} \ + --loop_count ${LOOP_COUNT} \ + --test_mode ${RUN_MODE} \ + --fps_target ${TGT} \ + --bsz ${BSZ}; check_status + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/classification/vit/ixrt/README.md b/models/cv/classification/vit/ixrt/README.md new file mode 100644 index 00000000..e0e7fb7e --- /dev/null +++ b/models/cv/classification/vit/ixrt/README.md @@ -0,0 +1,64 @@ +# Vision Transformer (ViT) (ixRT) + +## Model Description + +Vision Transformer (ViT) applies a pure transformer to images without any convolution. It divides an image into patches and processes them through transformer layers. + +## Supported Environments + +| GPU | [IXUCA SDK](https://gitee.com/deep-spark/deepspark#%E5%A4%A9%E6%95%B0%E6%99%BA%E7%AE%97%E8%BD%AF%E4%BB%B6%E6%A0%88-ixuca) | Release | +| :----: | :----: | :----: | +| MR-V100 | 4.4.0 | 26.06 | + +## Model Preparation + +### Prepare Resources + +Pretrained model: + +Download the [imagenet](https://www.image-net.org/download.php) to download the validation dataset. + +### Install Dependencies + +```bash +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +pip3 install -r ../../ixrt_common/requirements.txt +``` + +### Model Conversion + +```bash +mkdir checkpoints +cd checkpoints +wget http://files.deepspark.org.cn:880/deepspark/data/checkpoints/vit_b_16_sim.onnx +``` + +## Model Inference + +```bash +export PROJ_DIR=./ +export DATASETS_DIR=/path/to/imagenet_val/ +export CHECKPOINTS_DIR=./checkpoints +export RUN_DIR=../../ixrt_common/ +export CONFIG_DIR=../../ixrt_common/config/VIT_CONFIG +``` + +### FP16 + +```bash +# Test ACC +bash scripts/infer_vit_fp16_accuracy.sh +# Test FPS +bash scripts/infer_vit_fp16_performance.sh +``` + +## Model Results + +| Model | BatchSize | Precision | FPS | Top-1(%) | Top-5(%) | +| ----------- | --------- | --------- | ------- | -------- | -------- | +| ViT-B/16 | 32 | FP16 | 461.038 | 81.1 | 95.3 | \ No newline at end of file diff --git a/models/cv/classification/vit/ixrt/ci/prepare.sh b/models/cv/classification/vit/ixrt/ci/prepare.sh new file mode 100644 index 00000000..c12ce37b --- /dev/null +++ b/models/cv/classification/vit/ixrt/ci/prepare.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -x + +ID=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"') +if [[ ${ID} == "ubuntu" ]]; then + apt install -y libgl1-mesa-glx +elif [[ ${ID} == "centos" ]]; then + yum install -y mesa-libGL +else + echo "Not Support Os" +fi + +pip3 install tqdm onnxsim opencv-python==4.6.0.66 + +mkdir -p checkpoints +cp /root/data/checkpoints/vit_b_16_sim.onnx checkpoints/ \ No newline at end of file diff --git a/models/cv/classification/vit/ixrt/scripts/infer_vit_fp16_accuracy.sh b/models/cv/classification/vit/ixrt/scripts/infer_vit_fp16_accuracy.sh new file mode 100644 index 00000000..1d6c116c --- /dev/null +++ b/models/cv/classification/vit/ixrt/scripts/infer_vit_fp16_accuracy.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# Copyright (c) 2026, 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. +set -euo pipefail + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + echo "fails" + EXIT_STATUS=1 + fi +} +# Run paraments +warm_up=10 +BSZ=32 +TGT=-1 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +datasets_dir=${DATASETS_DIR} +onnx_model=${CHECKPOINTS_DIR}/vit_b_16_sim.onnx +engine_file=${CHECKPOINTS_DIR}/vit_b_16.engine + +echo "Build Fp16 Engine!" +python3 ${RUN_DIR}/build_engine.py \ + --precision float16 \ + --model ${onnx_model} \ + --engine ${engine_file}; check_status + +echo "Fp16 Inference Acc!" +python3 ${RUN_DIR}/inference.py \ + --test_mode ACC \ + --engine_file ${engine_file} \ + --datasets_dir ${datasets_dir} \ + --warm_up ${warm_up} \ + --bsz ${BSZ} \ + --acc_target ${TGT}; check_status \ No newline at end of file diff --git a/models/cv/classification/vit/ixrt/scripts/infer_vit_fp16_performance.sh b/models/cv/classification/vit/ixrt/scripts/infer_vit_fp16_performance.sh new file mode 100644 index 00000000..ea1f93cb --- /dev/null +++ b/models/cv/classification/vit/ixrt/scripts/infer_vit_fp16_performance.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Copyright (c) 2026, 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. +set -euo pipefail + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + echo "fails" + EXIT_STATUS=1 + fi +} +# Run paraments +warm_up=10 +loop_count=50 +BSZ=32 +TGT=-1 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +datasets_dir=${DATASETS_DIR} +onnx_model=${CHECKPOINTS_DIR}/vit_b_16_sim.onnx +engine_file=${CHECKPOINTS_DIR}/vit_b_16.engine + +echo "Build Fp16 Engine!" +python3 ${RUN_DIR}/build_engine.py \ + --precision float16 \ + --model ${onnx_model} \ + --engine ${engine_file}; check_status + +echo "Fp16 Inference Fps!" +python3 ${RUN_DIR}/inference.py \ + --test_mode FPS \ + --engine_file ${engine_file} \ + --datasets_dir ${datasets_dir} \ + --warm_up ${warm_up} \ + --bsz ${BSZ} \ + --loop_count ${loop_count} \ + --fps_target ${TGT}; check_status \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/README.md b/models/cv/ocr/dbnet/ixrt/README.md new file mode 100644 index 00000000..dccaaebf --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/README.md @@ -0,0 +1,67 @@ +# DBNet (ixRT) + +## Model Description + +DBNet (Differentiable Binarization Network) is a scene text detection model that uses a differentiable binarization process for robust text detection. + +## Supported Environments + +| GPU | [IXUCA SDK](https://gitee.com/deep-spark/deepspark#%E5%A4%A9%E6%95%B0%E6%99%BA%E7%AE%97%E8%BD%AF%E4%BB%B6%E6%A0%88-ixuca) | Release | +| :----: | :----: | :----: | +| MR-V100 | 4.4.0 | 26.06 | + +## Model Preparation + +### Prepare Resources + +Pretrained models: +- r50_en_dbnet: + +Dataset: ICDAR 2015 + +### Install Dependencies + +```bash +pip3 install shapely pyclipper opencv-python==4.6.0.66 tqdm +``` + +### Model Conversion + +```bash +mkdir checkpoints +cd checkpoints +wget http://files.deepspark.org.cn:880/deepspark/data/checkpoints/r50_en_dbnet.onnx +``` + +## Model Inference + +```bash +export DATASETS_DIR=/path/to/icdar2015/ +export CHECKPOINTS_DIR=./checkpoints +export RUN_DIR=./ +``` + +### FP16 + +```bash +# Test ACC +bash scripts/infer_dbnet_fp16_accuracy.sh +# Test FPS +bash scripts/infer_dbnet_fp16_performance.sh +``` + +### INT8 + +```bash +# Test ACC +bash scripts/infer_dbnet_int8_accuracy.sh +# Test FPS +bash scripts/infer_dbnet_int8_performance.sh +``` + +## Model Results + +| Model | Backbone | BatchSize | Precision | FPS | Hmean | +| ----------- | -------- | --------- | --------- | ------- | ------- | +| DBNet | r50_en | 32 | FP16 | 143.85 | 0.803 | +| DBNet | r50_en | 32 | INT8 | 143.73 | 0.803 | \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/build_engine.py b/models/cv/ocr/dbnet/ixrt/build_engine.py new file mode 100644 index 00000000..96c4435d --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/build_engine.py @@ -0,0 +1,49 @@ +import os +import cv2 +import argparse +import numpy as np + +import torch +import tensorrt + +from tensorrt import Dims + + +def main(config): + + input_shape = [args.batch_size,3, 736,1280] + IXRT_LOGGER = tensorrt.Logger(tensorrt.Logger.WARNING) + builder = tensorrt.Builder(IXRT_LOGGER) + EXPLICIT_BATCH = 1 << (int)(tensorrt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) + network = builder.create_network(EXPLICIT_BATCH) + build_config = builder.create_builder_config() + parser = tensorrt.OnnxParser(network, IXRT_LOGGER) + parser.parse_from_file(config.model) + + precision = tensorrt.BuilderFlag.INT8 if config.precision == "int8" else tensorrt.BuilderFlag.FP16 + build_config.set_flag(precision) + if config.precision == "int8": + build_config.set_flag(tensorrt.BuilderFlag.FP16) + + input_tensor = network.get_input(0) + input_tensor.shape = Dims(input_shape) + + plan = builder.build_serialized_network(network, build_config) + engine_file_path = config.engine + with open(engine_file_path, "wb") as f: + f.write(plan) + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--model", type=str,default="wide_deep.onnx") + parser.add_argument("--precision", type=str, choices=["float16", "int8", "float32"], default="float16", + help="The precision of datatype") + parser.add_argument("--engine", type=str, default="wide_deep.engine") + parser.add_argument("--batch_size", type=int, default=1) + + args = parser.parse_args() + return args + +if __name__ == "__main__": + args = parse_args() + main(args) \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/ci/prepare.sh b/models/cv/ocr/dbnet/ixrt/ci/prepare.sh new file mode 100644 index 00000000..d2101a9f --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/ci/prepare.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -x + +pip3 install shapely pyclipper opencv-python==4.6.0.66 tqdm + +mkdir -p checkpoints +cp /root/data/checkpoints/r50_en_dbnet.onnx checkpoints/ \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/dbnet_inference.py b/models/cv/ocr/dbnet/ixrt/dbnet_inference.py new file mode 100644 index 00000000..0f3bda1d --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/dbnet_inference.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import cv2 +import numpy as np +import argparse +import sys + + +from cuda import cuda, cudart +import torch +import tensorrt + +from util.common import eval_batch, create_engine_context, get_io_bindings + +from util import TextDetector + +def check_target(inference, target): + satisfied = False + if inference > target: + satisfied = True + return satisfied + +process_configs ={ + #pre process config + 'std': [0.229, 0.224, 0.225], + 'mean': [0.485, 0.456, 0.406], + 'scale': 1./255., + 'image_shape':(1280,736),#width height + + #post precess config + 'thresh':0.3, + 'box_thresh':0.5, + 'max_candidates':1000, + 'unclip_ratio':2, + 'use_dilation':False, + 'score_mode':'fast', + 'box_type':'quad', + 'batch_size':1 + +} + +def make_parser(): + parser = argparse.ArgumentParser("DBnet Eval") + parser.add_argument("--datasets_dir", type=str, default="data/icdar_2015_images", help="datasets dir ") + parser.add_argument("--engine_file", type=str, default="data/unit_test_r50_en_dbnet_bin/int8_r50_en_dbnet.engine", help="weights dir") + + parser.add_argument("-b", "--batch_size", type=int, default=1, help="batch size") + parser.add_argument("-d", "--device", default=1, type=int, help="device for val") + parser.add_argument("--img_height", default=736, type=int, help="test img height") + parser.add_argument("--img_width", default=1280, type=int, help="test img width") + parser.add_argument("--target_hmean", default=0.82, type=float, help="target Hmean") + parser.add_argument("--target_fps", default=30, type=float, help="target Hmean") + + parser.add_argument("--target", default="precision", type=str, help="precision or pref") + parser.add_argument("--warm_up", default=20, type=int , help="warm_up") + parser.add_argument("--loop_count", default=100, type=int , help="loop_count") + parser.add_argument("--seed", default=None, type=int, help="eval seed") + return parser + + + + +def eval(args): + + host_mem = tensorrt.IHostMemory + logger = tensorrt.Logger(tensorrt.Logger.ERROR) + engine, context = create_engine_context(args.engine_file, logger) + + process_configs["image_dir"]=args.datasets_dir + process_configs["label_dir"] = args.datasets_dir + process_configs["image_shape"]= (args.img_height,args.img_width) + process_configs["batch_size"] = args.batch_size + db_det = TextDetector(engine,context,process_configs) + if args.target=="precision": + metrics = db_det.eval_icdar_2015(args.datasets_dir,args.batch_size) + print("="*40) + print("Precision:{0},Recall:{1},Hmean:{2}".format(round(metrics["precision"],3),round(metrics["recall"],3),round(metrics["hmean"],3))) + print("="*40) + print(f"Check hmean Test : {round(metrics['hmean'],3)} Target:{args.target_hmean} \ + State : {'Pass' if round(metrics['hmean'],3) >= args.target_hmean else 'Fail'}") + status_hmean = check_target(metrics["hmean"], args.target_hmean) + metricResult = {"metricResult": {}} + metricResult["metricResult"]["hmean"] = round(metrics["hmean"], 3) + print(metricResult) + sys.exit(int(not (status_hmean))) + else: + fps = db_det.perf(args.warm_up,args.loop_count,args.batch_size) + print("="*40) + print("fps:{0}".format(round(fps,2))) + print("="*40) + print(f"Check fps Test : {round(fps,3)} Target:{args.target_fps} State : {'Pass' if fps >= args.target_fps else 'Fail'}") + status_fps = check_target(fps, args.target_fps) + metricResult = {"metricResult": {}} + metricResult["metricResult"]["fps"] = round(fps, 3) + print(metricResult) + sys.exit(int(not (status_fps))) + +if __name__ == "__main__": + args = make_parser().parse_args() + eval(args) + + + + diff --git a/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_fp16_accuracy.sh b/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_fp16_accuracy.sh new file mode 100644 index 00000000..8ca2a706 --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_fp16_accuracy.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +BSZ=16 +TGT=0.67 +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +DATASETS_DIR="/root/data/datasets/icdar_2015/icdar_2015_images" +CHECKPOINTS_DIR="./checkpoints" +RUN_DIR="${RUN_DIR:-.}" + +python3 ${RUN_DIR}/build_engine.py --model=${CHECKPOINTS_DIR}/r50_en_dbnet.onnx\ + --engine=${CHECKPOINTS_DIR}/int8_r50_en_dbnet.engine\ + --batch_size=${BSZ}\ + --precision="int8" + +python3 ${RUN_DIR}/dbnet_inference.py \ + --datasets_dir ${DATASETS_DIR} \ + --engine_file ${CHECKPOINTS_DIR}/int8_r50_en_dbnet.engine \ + --target "precision" \ + --batch_size ${BSZ} \ + --target_hmean ${TGT}; check_status + + exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_fp16_performance.sh b/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_fp16_performance.sh new file mode 100644 index 00000000..6dcde6c7 --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_fp16_performance.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +BSZ=16 +TGT=-1 +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +DATASETS_DIR="/root/data/datasets/icdar_2015/icdar_2015_images" +CHECKPOINTS_DIR="./checkpoints" +RUN_DIR="${RUN_DIR:-.}" + +python3 ${RUN_DIR}/build_engine.py --model=${CHECKPOINTS_DIR}/r50_en_dbnet.onnx\ + --engine=${CHECKPOINTS_DIR}/float16_r50_en_dbnet.engine\ + --batch_size=${BSZ}\ + --precision="float16" + +python3 ${RUN_DIR}/dbnet_inference.py \ + --engine_file ${CHECKPOINTS_DIR}/float16_r50_en_dbnet.engine \ + --target "perf" \ + --batch_size ${BSZ} \ + --target_fps ${TGT};check_status + + exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_int8_accuracy.sh b/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_int8_accuracy.sh new file mode 100644 index 00000000..8ca2a706 --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_int8_accuracy.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +BSZ=16 +TGT=0.67 +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +DATASETS_DIR="/root/data/datasets/icdar_2015/icdar_2015_images" +CHECKPOINTS_DIR="./checkpoints" +RUN_DIR="${RUN_DIR:-.}" + +python3 ${RUN_DIR}/build_engine.py --model=${CHECKPOINTS_DIR}/r50_en_dbnet.onnx\ + --engine=${CHECKPOINTS_DIR}/int8_r50_en_dbnet.engine\ + --batch_size=${BSZ}\ + --precision="int8" + +python3 ${RUN_DIR}/dbnet_inference.py \ + --datasets_dir ${DATASETS_DIR} \ + --engine_file ${CHECKPOINTS_DIR}/int8_r50_en_dbnet.engine \ + --target "precision" \ + --batch_size ${BSZ} \ + --target_hmean ${TGT}; check_status + + exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_int8_performance.sh b/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_int8_performance.sh new file mode 100644 index 00000000..701f2496 --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/scripts/infer_dbnet_int8_performance.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +BSZ=16 +TGT=-1 +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + esac +done + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +DATASETS_DIR="/root/data/datasets/icdar_2015/icdar_2015_images" +CHECKPOINTS_DIR="./checkpoints" +RUN_DIR="${RUN_DIR:-.}" + +python3 ${RUN_DIR}/build_engine.py --model=${CHECKPOINTS_DIR}/r50_en_dbnet.onnx\ + --engine=${CHECKPOINTS_DIR}/int8_r50_en_dbnet.engine\ + --batch_size=${BSZ}\ + --precision="int8" + +python3 ${RUN_DIR}/dbnet_inference.py \ + --engine_file ${CHECKPOINTS_DIR}/int8_r50_en_dbnet.engine \ + --target "perf" \ + --batch_size ${BSZ} \ + --target_fps ${TGT};check_status + + exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/util/__init__.py b/models/cv/ocr/dbnet/ixrt/util/__init__.py new file mode 100644 index 00000000..6b3184b7 --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/util/__init__.py @@ -0,0 +1,4 @@ +from .dbnet_det import TextDetector + + + diff --git a/models/cv/ocr/dbnet/ixrt/util/common.py b/models/cv/ocr/dbnet/ixrt/util/common.py new file mode 100644 index 00000000..1b88ab9e --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/util/common.py @@ -0,0 +1,66 @@ +import os +import cv2 +import glob +import torch +import tensorrt +import numpy as np +from cuda import cuda, cudart + +def eval_batch(batch_score, batch_label): + batch_score = torch.tensor(torch.from_numpy(batch_score), dtype=torch.float32) + values, indices = batch_score.topk(5) + top1, top5 = 0, 0 + for idx, label in enumerate(batch_label): + + if label == indices[idx][0]: + top1 += 1 + if label in indices[idx]: + top5 += 1 + return top1, top5 + +def create_engine_context(engine_path, logger): + with open(engine_path, "rb") as f: + runtime = tensorrt.Runtime(logger) + assert runtime + engine = runtime.deserialize_cuda_engine(f.read()) + assert engine + context = engine.create_execution_context() + assert context + + return engine, context + +def get_io_bindings(engine): + # Setup I/O bindings + inputs = [] + outputs = [] + allocations = [] + + for i in range(engine.num_bindings): + is_input = False + if engine.binding_is_input(i): + is_input = True + name = engine.get_binding_name(i) + dtype = engine.get_binding_dtype(i) + shape = engine.get_binding_shape(i) + if is_input: + batch_size = shape[0] + size = np.dtype(tensorrt.nptype(dtype)).itemsize + for s in shape: + size *= s + err, allocation = cudart.cudaMalloc(size) + assert err == cudart.cudaError_t.cudaSuccess + binding = { + "index": i, + "name": name, + "dtype": np.dtype(tensorrt.nptype(dtype)), + "shape": list(shape), + "allocation": allocation, + "nbytes": size, + } + print(f"binding {i}, name : {name} dtype : {np.dtype(tensorrt.nptype(dtype))} shape : {list(shape)}") + allocations.append(allocation) + if engine.binding_is_input(i): + inputs.append(binding) + else: + outputs.append(binding) + return inputs, outputs, allocations \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/util/db_postprocess.py b/models/cv/ocr/dbnet/ixrt/util/db_postprocess.py new file mode 100644 index 00000000..20212d7d --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/util/db_postprocess.py @@ -0,0 +1,249 @@ + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import cv2 +from shapely.geometry import Polygon +import pyclipper + + +class DBPostProcess(object): + """ + The post process for Differentiable Binarization (DB). + """ + + def __init__(self, + thresh=0.27, + box_thresh=0.6, + max_candidates=1000, + unclip_ratio=1.7, + use_dilation=False, + score_mode="fast", + box_type='quad', + **kwargs): + self.thresh = thresh + self.box_thresh = box_thresh + self.max_candidates = max_candidates + self.unclip_ratio = unclip_ratio + self.min_size = 3 + self.score_mode = score_mode + self.box_type = box_type + assert score_mode in [ + "slow", "fast" + ], "Score mode must be in [slow, fast] but got: {}".format(score_mode) + + self.dilation_kernel = None if not use_dilation else np.array( + [[1, 1], [1, 1]]) + + def polygons_from_bitmap(self, pred, _bitmap, dest_width, dest_height): + ''' + _bitmap: single map with shape (1, H, W), + whose values are binarized as {0, 1} + ''' + + bitmap = _bitmap + height, width = bitmap.shape + + boxes = [] + scores = [] + + contours, _ = cv2.findContours((bitmap * 255).astype(np.uint8), + cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) + + for contour in contours[:self.max_candidates]: + epsilon = 0.002 * cv2.arcLength(contour, True) + approx = cv2.approxPolyDP(contour, epsilon, True) + points = approx.reshape((-1, 2)) + if points.shape[0] < 4: + continue + + score = self.box_score_fast(pred, points.reshape(-1, 2)) + if self.box_thresh > score: + continue + + if points.shape[0] > 2: + box = self.unclip(points, self.unclip_ratio) + if len(box) > 1: + continue + else: + continue + box = box.reshape(-1, 2) + + _, sside = self.get_mini_boxes(box.reshape((-1, 1, 2))) + if sside < self.min_size + 2: + continue + + box = np.array(box) + box[:, 0] = np.clip( + np.round(box[:, 0] / width * dest_width), 0, dest_width) + box[:, 1] = np.clip( + np.round(box[:, 1] / height * dest_height), 0, dest_height) + boxes.append(box.tolist()) + scores.append(score) + return boxes, scores + + def boxes_from_bitmap(self, pred, _bitmap, dest_width, dest_height,pad_w,pad_h,scale): + ''' + _bitmap: single map with shape (1, H, W), + whose values are binarized as {0, 1} + ''' + + bitmap = _bitmap + height, width = bitmap.shape + + outs = cv2.findContours((bitmap * 255).astype(np.uint8), cv2.RETR_LIST, + cv2.CHAIN_APPROX_SIMPLE) + if len(outs) == 3: + img, contours, _ = outs[0], outs[1], outs[2] + elif len(outs) == 2: + contours, _ = outs[0], outs[1] + + num_contours = min(len(contours), self.max_candidates) + + boxes = [] + scores = [] + for index in range(num_contours): + contour = contours[index] + points, sside = self.get_mini_boxes(contour) + if sside < self.min_size: + continue + points = np.array(points) + if self.score_mode == "fast": + score = self.box_score_fast(pred, points.reshape(-1, 2)) + else: + score = self.box_score_slow(pred, contour) + if self.box_thresh > score: + continue + + box = self.unclip(points, self.unclip_ratio).reshape(-1, 1, 2) + box, sside = self.get_mini_boxes(box) + if sside < self.min_size + 2: + continue + box = np.array(box) + + box[:, 0] = box[:, 0]-pad_w + box[:, 1] = box[:, 1]-pad_h + + box[:, 0] = np.clip( + np.round(box[:, 0] / scale), 0, dest_width) + box[:, 1] = np.clip( + np.round(box[:, 1] / scale), 0, dest_height) + + # box[:, 0] = np.clip( + # np.round(box[:, 0] / width * dest_width), 0, dest_width) + # box[:, 1] = np.clip( + # np.round(box[:, 1] / height * dest_height), 0, dest_height) + + #box[:, 0] = box[:, 0]+(int)(pad_w*dest_width/width) + #box[:, 1] = box[:, 1]+(int)(pad_h*dest_height/height ) + + + #box[:, 0] = np.clip(np.round(box[:, 0]) , 0, 1280) + #box[:, 1] = np.clip(np.round(box[:, 1]) , 0, 736) + + boxes.append(box.astype("int32")) + scores.append(score) + return np.array(boxes, dtype="int32"), scores + + def unclip(self, box, unclip_ratio): + poly = Polygon(box) + distance = poly.area * unclip_ratio / poly.length + offset = pyclipper.PyclipperOffset() + offset.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) + expanded = np.array(offset.Execute(distance)) + return expanded + + def get_mini_boxes(self, contour): + bounding_box = cv2.minAreaRect(contour) + points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) + + index_1, index_2, index_3, index_4 = 0, 1, 2, 3 + if points[1][1] > points[0][1]: + index_1 = 0 + index_4 = 1 + else: + index_1 = 1 + index_4 = 0 + if points[3][1] > points[2][1]: + index_2 = 2 + index_3 = 3 + else: + index_2 = 3 + index_3 = 2 + + box = [ + points[index_1], points[index_2], points[index_3], points[index_4] + ] + return box, min(bounding_box[1]) + + def box_score_fast(self, bitmap, _box): + ''' + box_score_fast: use bbox mean score as the mean score + ''' + h, w = bitmap.shape[:2] + box = _box.copy() + xmin = np.clip(np.floor(box[:, 0].min()).astype("int32"), 0, w - 1) + xmax = np.clip(np.ceil(box[:, 0].max()).astype("int32"), 0, w - 1) + ymin = np.clip(np.floor(box[:, 1].min()).astype("int32"), 0, h - 1) + ymax = np.clip(np.ceil(box[:, 1].max()).astype("int32"), 0, h - 1) + + mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8) + box[:, 0] = box[:, 0] - xmin + box[:, 1] = box[:, 1] - ymin + cv2.fillPoly(mask, box.reshape(1, -1, 2).astype("int32"), 1) + return cv2.mean(bitmap[ymin:ymax + 1, xmin:xmax + 1], mask)[0] + + def box_score_slow(self, bitmap, contour): + ''' + box_score_slow: use polyon mean score as the mean score + ''' + h, w = bitmap.shape[:2] + contour = contour.copy() + contour = np.reshape(contour, (-1, 2)) + + xmin = np.clip(np.min(contour[:, 0]), 0, w - 1) + xmax = np.clip(np.max(contour[:, 0]), 0, w - 1) + ymin = np.clip(np.min(contour[:, 1]), 0, h - 1) + ymax = np.clip(np.max(contour[:, 1]), 0, h - 1) + + mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8) + + contour[:, 0] = contour[:, 0] - xmin + contour[:, 1] = contour[:, 1] - ymin + + cv2.fillPoly(mask, contour.reshape(1, -1, 2).astype("int32"), 1) + return cv2.mean(bitmap[ymin:ymax + 1, xmin:xmax + 1], mask)[0] + + def __call__(self, outs_dict, shape_list): + pred = outs_dict['maps'] + # if isinstance(pred, paddle.Tensor): + # pred = pred.numpy() + pred = pred[:, 0, :, :] + segmentation = pred > self.thresh + boxes_batch = [] + for batch_index in range(shape_list.shape[0]): + src_h, src_w,pad_h,pad_w,scale= shape_list[batch_index] + + if self.dilation_kernel is not None: + mask = cv2.dilate( + np.array(segmentation[batch_index]).astype(np.uint8), + self.dilation_kernel) + else: + mask = segmentation[batch_index] + if self.box_type == 'poly': + boxes, scores = self.polygons_from_bitmap(pred[batch_index], + mask, src_w, src_h) + elif self.box_type == 'quad': + boxes, scores = self.boxes_from_bitmap(pred[batch_index], mask, + src_w, src_h,pad_w,pad_h,scale) + else: + raise ValueError("box_type can only be one of ['quad', 'poly']") + + boxes_batch.append({'points': boxes,"scores":scores}) + return boxes_batch + + + + \ No newline at end of file diff --git a/models/cv/ocr/dbnet/ixrt/util/dbnet_det.py b/models/cv/ocr/dbnet/ixrt/util/dbnet_det.py new file mode 100644 index 00000000..4f56493d --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/util/dbnet_det.py @@ -0,0 +1,235 @@ +import numpy as np +import cv2 +import glob +import os +import math +from tqdm import tqdm +import time +from .db_postprocess import DBPostProcess +from .eval_det_iou import DetectionIoUEvaluator +from .common import get_io_bindings +import torch +from cuda import cuda, cudart + + +def img2label_paths(img_paths): + # Define label paths as a function of image paths + sa, sb = f'{os.sep}icdar_2015_images{os.sep}', f'{os.sep}icdar_2015_labels{os.sep}gt_' # /images/, /labels/ substrings + + return [sb.join(x.rsplit(sa, 1)).rsplit('.', 1)[0] + '.txt' for x in img_paths] + +def rotate(angle, x, y): + """ + 基于原点的弧度旋转 + + :param angle: 弧度 + :param x: x + :param y: y + :return: + """ + rotatex = math.cos(angle) * x - math.sin(angle) * y + rotatey = math.cos(angle) * y + math.sin(angle) * x + return rotatex, rotatey + +def xy_rorate(theta, x, y, centerx, centery): + """ + 针对中心点进行旋转 + + :param theta: + :param x: + :param y: + :param centerx: + :param centery: + :return: + """ + r_x, r_y = rotate(theta, x - centerx, y - centery) + return centerx+r_x, centery+r_y + +def rec_rotate(x, y, width, height, theta): + """ + 传入矩形的x,y和宽度高度,弧度,转成QUAD格式 + :param x: + :param y: + :param width: + :param height: + :param theta: + :return: + """ + centerx = x + width / 2 + centery = y + height / 2 + + x1, y1 = xy_rorate(theta, x, y, centerx, centery) + x2, y2 = xy_rorate(theta, x+width, y, centerx, centery) + x3, y3 = xy_rorate(theta, x, y+height, centerx, centery) + x4, y4 = xy_rorate(theta, x+width, y+height, centerx, centery) + + return [(int(x1), int(y1)), (int(x2), int(y2)), (int(x4), int(y4)), (int(x3), int(y3))] #clock wise + + +def letterbox(im, new_shape=(736, 1280), color=(114, 114, 114), auto=False, scaleup=True, stride=32): + # Resize and pad image while meeting stride-multiple constraints + + shape = im.shape[:2] # current shape [height, width] + if isinstance(new_shape, int): + new_shape = (new_shape, new_shape) + + # Scale ratio (new / old) + r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) + if not scaleup: # only scale down, do not scale up (for better val mAP) + r = min(r, 1.0) + + # Compute padding + new_unpad = int(round(shape[0] * r)), int(round(shape[1] * r)) + dw, dh = new_shape[1] - new_unpad[1], new_shape[0] - new_unpad[0] # wh padding + if auto: # minimum rectangle + dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding + + dw /= 2 # divide padding into 2 sides + dh /= 2 + + if shape != new_unpad: # resize + im = cv2.resize(im, new_unpad[::-1], interpolation=cv2.INTER_LINEAR) + + top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) + left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) + im1 = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border + return im1, r, dw, dh + + +def draw_det_res(img,dt_boxes): + if len(dt_boxes) > 0: + src_im = img + for box in dt_boxes: + box = np.array(box).astype(np.int32).reshape((-1, 1, 2)) + cv2.polylines(src_im, [box], True, color=(255, 114, 255), thickness=2) + cv2.imwrite("det3.jpg", src_im) + + +class TextDetector(object): + def __init__(self,engine,context, configs): + self.engine= engine + self.context = context + self.configs = configs + self.postprocess = DBPostProcess() + def batch_forward(self,inputs,outputs,allocations,batch_data,shape_list): + + output = np.zeros(outputs[0]["shape"], outputs[0]["dtype"]) + input = np.zeros(inputs[0]["shape"], inputs[0]["dtype"]) + real_batch = batch_data.shape[0] + batch_data= np.transpose(batch_data,[0,3,1,2]) + batch_data = batch_data.astype(inputs[0]["dtype"]) + batch_data = np.ascontiguousarray(batch_data) + input[:real_batch, :, :, :] = batch_data + + err, = cuda.cuMemcpyHtoD(inputs[0]["allocation"], batch_data, batch_data.nbytes) + assert(err == cuda.CUresult.CUDA_SUCCESS) + self.context.execute_v2(allocations) + err, = cuda.cuMemcpyDtoH(output, outputs[0]["allocation"], outputs[0]["nbytes"]) + assert(err == cuda.CUresult.CUDA_SUCCESS) + outs_dict={"maps":output} + post_result = self.postprocess(outs_dict,shape_list) + return post_result + + def get_dataloader(self,datasets_dir, bsz): + image_files= glob.glob(str(datasets_dir+"/*")) + batch_img, shape_list, batch_label_files = [],[],[] + label_files = img2label_paths(image_files) + for image_file,label_file in zip(image_files,label_files): + img = cv2.imread(image_file) + letter_img, img, org_img, scale, pad_w, pad_h = self.pre_process(image_file) + shape_list.append([img.shape[0],img.shape[1],pad_h,pad_w,scale]) + batch_img.append(np.expand_dims(img, 0)) + batch_label_files.append(label_file) + if len(batch_img) == bsz: + yield np.concatenate(batch_img, 0), np.array(shape_list).astype(np.int32),batch_label_files + batch_img, shape_list, batch_label_files = [],[],[] + + if len(batch_img) > 0: + yield np.concatenate(batch_img, 0), np.array(shape_list), batch_label_files + + def eval_icdar_2015(self,img_dir,batch_size): + dataloader = self.get_dataloader(img_dir,batch_size) + label_files =[] + evaluator = DetectionIoUEvaluator() + + inputs, outputs, allocations = get_io_bindings(self.engine) + gts =[] + preds=[] + all_boxes= [] + for i, data in enumerate(tqdm(dataloader,disable=False)): + batch_data, shape_list,batch_label = data + label_files.extend(batch_label) + post_result= self.batch_forward(inputs,outputs,allocations,batch_data,shape_list) + all_boxes.extend(post_result) + print("============start evel=========================") + for i, per_image_boxes in enumerate(all_boxes): + one_pred=[] + dt_boxes = per_image_boxes["points"] + for bbox in dt_boxes: + one_pred_res={} + one_pred_res["points"]=[tuple(x) for x in bbox.tolist()] + one_pred_res["text"]="text" + one_pred_res["ignore"] =False + one_pred.append(one_pred_res) + preds.append(one_pred) + label_file= label_files[i] + one_gt=[] + with open(label_file) as f: + lines = f.readlines() + for line in lines: + one_gt_res={} + line_label=line.strip().split(",")[:9] + x1,y1,x2,y2,x3,y3,x4,y4,label =line_label + gt_bbox= [(int(x1), int(y1)), (int(x2), int(y2)), (int(x3), int(y3)), (int(x4), int(y4))] + one_gt_res["points"]=gt_bbox + one_gt_res["text"]=label + if label=="###": + one_gt_res["ignore"] =True + else: + one_gt_res["ignore"] =False + one_gt.append(one_gt_res) + gts.append(one_gt) + + + results = [] + for gt, pred in zip(gts, preds): + results.append(evaluator.evaluate_image(gt, pred)) + metrics = evaluator.combine_results(results) + return metrics + + def perf(self,warm_up,loop_count,batch_size): + inputs, outputs, allocations = get_io_bindings(self.engine) + if warm_up > 0: + print("\nWarm Start.") + for i in range(warm_up): + self.context.execute_v2(allocations) + print("Warm Done.") + torch.cuda.synchronize() + start_time = time.time() + for i in range(loop_count): + self.context.execute_v2(allocations) + torch.cuda.synchronize() + end_time = time.time() + forward_time = end_time - start_time + fps = loop_count * batch_size / forward_time + fps = round(fps,2) + return fps + + + def pre_process(self,img_file): + org_img = cv2.imread(img_file) + image = org_img.copy() + #image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + letter_img, r, dw, dh= letterbox(image,self.configs["image_shape"]) + in_img = letter_img.copy() + #image = cv2.resize(image, (1280, 736)) + in_img = in_img.astype(np.float32) + in_img /= 255 + in_img =(in_img-0.456)/0.224 + return letter_img,in_img,org_img, r, dw, dh + + + + + + diff --git a/models/cv/ocr/dbnet/ixrt/util/eval_det_iou.py b/models/cv/ocr/dbnet/ixrt/util/eval_det_iou.py new file mode 100644 index 00000000..3b1a1702 --- /dev/null +++ b/models/cv/ocr/dbnet/ixrt/util/eval_det_iou.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from collections import namedtuple +import numpy as np +from shapely.geometry import Polygon +import glob +import os +""" +reference from : +https://github.com/MhLiao/DB/blob/3c32b808d4412680310d3d28eeb6a2d5bf1566c5/concern/icdar2015_eval/detection/iou.py#L8 +""" + + + +class DetectionIoUEvaluator(object): + def __init__(self, iou_constraint=0.5, area_precision_constraint=0.5): + self.iou_constraint = iou_constraint + self.area_precision_constraint = area_precision_constraint + + def evaluate_image(self, gt, pred): + def get_union(pD, pG): + return Polygon(pD).union(Polygon(pG)).area + + def get_intersection_over_union(pD, pG): + return get_intersection(pD, pG) / get_union(pD, pG) + + def get_intersection(pD, pG): + return Polygon(pD).intersection(Polygon(pG)).area + + def compute_ap(confList, matchList, numGtCare): + correct = 0 + AP = 0 + if len(confList) > 0: + confList = np.array(confList) + matchList = np.array(matchList) + sorted_ind = np.argsort(-confList) + confList = confList[sorted_ind] + matchList = matchList[sorted_ind] + for n in range(len(confList)): + match = matchList[n] + if match: + correct += 1 + AP += float(correct) / (n + 1) + + if numGtCare > 0: + AP /= numGtCare + + return AP + + perSampleMetrics = {} + + matchedSum = 0 + + Rectangle = namedtuple('Rectangle', 'xmin ymin xmax ymax') + + numGlobalCareGt = 0 + numGlobalCareDet = 0 + + arrGlobalConfidences = [] + arrGlobalMatches = [] + + recall = 0 + precision = 0 + hmean = 0 + + detMatched = 0 + + iouMat = np.empty([1, 1]) + + gtPols = [] + detPols = [] + + gtPolPoints = [] + detPolPoints = [] + + # Array of Ground Truth Polygons' keys marked as don't Care + gtDontCarePolsNum = [] + # Array of Detected Polygons' matched with a don't Care GT + detDontCarePolsNum = [] + + pairs = [] + detMatchedNums = [] + + arrSampleConfidences = [] + arrSampleMatch = [] + + evaluationLog = "" + + for n in range(len(gt)): + points = gt[n]['points'] + dontCare = gt[n]['ignore'] + if not Polygon(points).is_valid: + continue + + gtPol = points + gtPols.append(gtPol) + gtPolPoints.append(points) + if dontCare: + gtDontCarePolsNum.append(len(gtPols) - 1) + + evaluationLog += "GT polygons: " + str(len(gtPols)) + ( + " (" + str(len(gtDontCarePolsNum)) + " don't care)\n" + if len(gtDontCarePolsNum) > 0 else "\n") + + for n in range(len(pred)): + points = pred[n]['points'] + if not Polygon(points).is_valid: + continue + + detPol = points + detPols.append(detPol) + detPolPoints.append(points) + if len(gtDontCarePolsNum) > 0: + for dontCarePol in gtDontCarePolsNum: + dontCarePol = gtPols[dontCarePol] + intersected_area = get_intersection(dontCarePol, detPol) + pdDimensions = Polygon(detPol).area + precision = 0 if pdDimensions == 0 else intersected_area / pdDimensions + if (precision > self.area_precision_constraint): + detDontCarePolsNum.append(len(detPols) - 1) + break + + evaluationLog += "DET polygons: " + str(len(detPols)) + ( + " (" + str(len(detDontCarePolsNum)) + " don't care)\n" + if len(detDontCarePolsNum) > 0 else "\n") + + if len(gtPols) > 0 and len(detPols) > 0: + # Calculate IoU and precision matrixs + outputShape = [len(gtPols), len(detPols)] + iouMat = np.empty(outputShape) + gtRectMat = np.zeros(len(gtPols), np.int8) + detRectMat = np.zeros(len(detPols), np.int8) + for gtNum in range(len(gtPols)): + for detNum in range(len(detPols)): + pG = gtPols[gtNum] + pD = detPols[detNum] + iouMat[gtNum, detNum] = get_intersection_over_union(pD, pG) + + for gtNum in range(len(gtPols)): + for detNum in range(len(detPols)): + if gtRectMat[gtNum] == 0 and detRectMat[ + detNum] == 0 and gtNum not in gtDontCarePolsNum and detNum not in detDontCarePolsNum: + if iouMat[gtNum, detNum] > self.iou_constraint: + gtRectMat[gtNum] = 1 + detRectMat[detNum] = 1 + detMatched += 1 + pairs.append({'gt': gtNum, 'det': detNum}) + detMatchedNums.append(detNum) + evaluationLog += "Match GT #" + \ + str(gtNum) + " with Det #" + str(detNum) + "\n" + + numGtCare = (len(gtPols) - len(gtDontCarePolsNum)) + numDetCare = (len(detPols) - len(detDontCarePolsNum)) + if numGtCare == 0: + recall = float(1) + precision = float(0) if numDetCare > 0 else float(1) + else: + recall = float(detMatched) / numGtCare + precision = 0 if numDetCare == 0 else float(detMatched) / numDetCare + + hmean = 0 if (precision + recall) == 0 else 2.0 * \ + precision * recall / (precision + recall) + + matchedSum += detMatched + numGlobalCareGt += numGtCare + numGlobalCareDet += numDetCare + + perSampleMetrics = { + 'gtCare': numGtCare, + 'detCare': numDetCare, + 'detMatched': detMatched, + } + return perSampleMetrics + + def combine_results(self, results): + numGlobalCareGt = 0 + numGlobalCareDet = 0 + matchedSum = 0 + for result in results: + numGlobalCareGt += result['gtCare'] + numGlobalCareDet += result['detCare'] + matchedSum += result['detMatched'] + + methodRecall = 0 if numGlobalCareGt == 0 else float( + matchedSum) / numGlobalCareGt + methodPrecision = 0 if numGlobalCareDet == 0 else float( + matchedSum) / numGlobalCareDet + methodHmean = 0 if methodRecall + methodPrecision == 0 else 2 * \ + methodRecall * methodPrecision / ( + methodRecall + methodPrecision) + methodMetrics = { + 'precision': methodPrecision, + 'recall': methodRecall, + 'hmean': methodHmean + } + + return methodMetrics + +def read_label(label_file): + #gts = [] + one_gt = [] + with open(label_file) as f: + lines = f.readlines() + + + for line in lines: + one_res={} + cord,label = line.strip().split(",")[:8],line.strip().split(",")[-1] + cord=[int(x) for x in cord] + one_res["points"]=[(cord[0],cord[1]),(cord[2],cord[3]),(cord[4],cord[5]),(cord[6],cord[7])] + one_res["text"]=label + if label=="###": + one_res["ignore"] =True + else: + one_res["ignore"] =False + one_gt.append(one_res) + #gts.append(one_gt) + return one_gt + + + + +if __name__ == '__main__': + evaluator = DetectionIoUEvaluator() + + gt_files = glob.glob("/home/fangjian.hu/workspace/ixrt/test_data/MSRA/test_labels_icdar/*") + pred_path = "/home/fangjian.hu/workspace/ixrt/test_data/MSRA_pred_dt/" + + gts =[] + preds=[] + + for gt_file in gt_files: + label_name = os.path.split(gt_file)[-1] + pred_file = os.path.join(pred_path,label_name) + one_gt= read_label(gt_file) + one_pred= read_label(pred_file) + gts.append(one_gt) + preds.append(one_pred) + + results = [] + for gt, pred in zip(gts, preds): + results.append(evaluator.evaluate_image(gt, pred)) + metrics = evaluator.combine_results(results) + print(metrics) + + + + + + + + + + + + + + # for item in data["label"]: + # print(item) + # if item["transcription"]!="###": + # print(item) + + + + + + + + + + # gts = [[{ + # 'points': [(0, 0), (1, 0), (1, 1), (0, 1)], + # 'text': 1234, + # 'ignore': False, + # }, { + # 'points': [(2, 2), (3, 2), (3, 3), (2, 3)], + # 'text': 5678, + # 'ignore': False, + # }]] + # preds = [[{ + # 'points': [(0.1, 0.1), (1, 0), (1, 1), (0, 1)], + # 'text': 123, + # 'ignore': False, + # }]] + # results = [] + # for gt, pred in zip(gts, preds): + # results.append(evaluator.evaluate_image(gt, pred)) + # metrics = evaluator.combine_results(results) + # print(metrics) diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/README.md b/models/cv/semantic_segmentation/ddrnet/ixrt/README.md new file mode 100644 index 00000000..8ce594e2 --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/README.md @@ -0,0 +1,66 @@ +# DDRNet (ixRT) + +## Model Description + +DDRNet (Dual Resolution Network) is a real-time semantic segmentation network that learns rich representations through bilateral detail preservation and deep aggregation for high-resolution image understanding. + +## Supported Environments + +| GPU | [IXUCA SDK](https://gitee.com/deep-spark/deepspark#%E5%A4%A9%E6%95%B0%E6%99%BA%E7%AE%97%E8%BD%AF%E4%BB%B6%E6%A0%88-ixuca) | Release | +| :----: | :----: | :----: | +| MR-V100 | 4.4.0 | 26.06 | + +## Model Preparation + +### Prepare Resources + +Pretrained model: + +Dataset: to download the dataset. + +### Install Dependencies + +```bash +pip3 install xtcocotools tqdm munkres onnxsim opencv-python==4.6.0.66 +``` + +### Model Conversion + +```bash +mkdir checkpoints +cd checkpoints +wget http://files.deepspark.org.cn:880/deepspark/data/checkpoints/ddrnet23.onnx +``` + +## Model Inference + +```bash +export DATASETS_DIR=/Path/to/cityscapes/ +export CHECKPOINTS_DIR=./checkpoints +export RUN_DIR=./ +``` + +### FP16 + +```bash +# Test ACC (mIoU) +bash scripts/infer_ddrnet_fp16_accuracy.sh +# Test FPS +bash scripts/infer_ddrnet_fp16_performance.sh +``` + +### INT8 + +```bash +# Test ACC (mIoU) +bash scripts/infer_ddrnet_int8_accuracy.sh +# Test FPS +bash scripts/infer_ddrnet_int8_performance.sh +``` + +## Model Results + +| Model | BatchSize | Precision | FPS | mIoU(%) | mAcc(%) | +| ------ | --------- | --------- | ------- | ------- | ------- | +| DDRNet | 4 | FP16 | 98.278 | 12.8 | 25.8 | +| DDRNet | 4 | INT8 | 123.94 | 12.9 | 25.6 | \ No newline at end of file diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/build_engine.py b/models/cv/semantic_segmentation/ddrnet/ixrt/build_engine.py new file mode 100644 index 00000000..5acad1d9 --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/build_engine.py @@ -0,0 +1,68 @@ +import os +import json +import onnx +import logging +import argparse +import ctypes +from os.path import join, dirname, exists + +import tensorrt + +def load_ixrt_plugin(logger=tensorrt.Logger(tensorrt.Logger.INFO), namespace="", dynamic_path=""): + if not dynamic_path: + dynamic_path = join(dirname(tensorrt.__file__), "lib", "libixrt_plugin.so") + if not exists(dynamic_path): + raise FileNotFoundError( + f"The ixrt_plugin lib {dynamic_path} is not existed, please provided effective plugin path!") + ctypes.CDLL(dynamic_path) + tensorrt.init_libnvinfer_plugins(logger, namespace) + print(f"Loaded plugin from {dynamic_path}") + +load_ixrt_plugin() + + +def build_engine_trtapi(config): + onnx_model = config.model + assert os.path.isfile(onnx_model), f"The onnx model{onnx_model} must be existed!" + IXRT_LOGGER = tensorrt.Logger(tensorrt.Logger.WARNING) + builder = tensorrt.Builder(IXRT_LOGGER) + EXPLICIT_BATCH = 1 << (int)(tensorrt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) + network = builder.create_network(EXPLICIT_BATCH) + build_config = builder.create_builder_config() + parser = tensorrt.OnnxParser(network, IXRT_LOGGER) + + parser.parse_from_file(onnx_model) + if config.precision == "int8": + build_config.set_flag(tensorrt.BuilderFlag.INT8) + build_config.set_flag(tensorrt.BuilderFlag.FP16) + else: + build_config.set_flag(tensorrt.BuilderFlag.FP16) + + plan = builder.build_serialized_network(network, build_config) + with open(config.engine, "wb") as f: + f.write(plan) + + print("Build engine done!") + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--model", type=str, default="ddrnet23.onnx") + parser.add_argument("--bsz", type=int, default=4, help="batch size") + parser.add_argument("--precision", type=str, choices=["float16", "int8"], default="int8", help="The precision of datatype") + parser.add_argument("--imgsz_h", type=int, default=1024, help="inference size h") + parser.add_argument("--imgsz_w", type=int, default=2048, help="inference size w") + # engine args + parser.add_argument("--engine", type=str, default=None) + # device + parser.add_argument( + "--device", type=int, default=0, help="cuda device, i.e. 0 or 0,1,2,3,4" + ) + + args = parser.parse_args() + return args + + +if __name__ == "__main__": + config = parse_args() + build_engine_trtapi(config) diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/ci/prepare.sh b/models/cv/semantic_segmentation/ddrnet/ixrt/ci/prepare.sh new file mode 100644 index 00000000..e18dd4ef --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/ci/prepare.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -x + +pip3 install xtcocotools tqdm munkres onnxsim opencv-python==4.6.0.66 + +mkdir -p checkpoints +cp /root/data/checkpoints/ddrnet23.onnx checkpoints/ \ No newline at end of file diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/deploy.py b/models/cv/semantic_segmentation/ddrnet/ixrt/deploy.py new file mode 100644 index 00000000..6c68506d --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/deploy.py @@ -0,0 +1,127 @@ +import os +import cv2 +import argparse +import numpy as np +import torch + +from utils import input_transform + +from tensorrt import IxRT +from ixrt.common import RuntimeConfig, RuntimeContext +from tensorrt.deploy.api import * + + +def create_runtime_from_model(args): + model = args.model + quant_file = args.quant_file + precision = args.precision + + config = RuntimeConfig() + config.input_shapes = [("inputx", [args.bsz, 3, args.imgsz_h, args.imgsz_w])] + config.device_idx = args.device + if precision == "int8": + assert os.path.isfile(quant_file), "Quant file must provided for int8 inferencing" + + config.runtime_context = RuntimeContext( + precision, + "nhwc", + use_gpu=True, + pipeline_sync=True, + input_types={"inputx": "float32"}, + output_types={"outputy": "float32"} + ) + runtime = IxRT.from_onnx(model, quant_file, config) + runtime.Init(runtime.config) + return runtime + + +def create_runtime_from_engine(engine): + runtime = IxRT() + runtime.LoadEngine(engine) + return runtime + + +def pre_process(img_file): + assert os.path.isfile(img_file), "The input file {img_file} must be existed!" + img = cv2.imread(img_file, cv2.IMREAD_COLOR) + img = input_transform( + img, + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225] + ) + return img + + +def main(args): + print(args) + img_file = args.img_file + if args.engine is not None: + runtime = create_runtime_from_engine(args.engine) + else: + runtime = create_runtime_from_model(args) + + input_map = runtime.GetInputShape() + output_map = runtime.GetOutputShape() + print(f"input map is: {input_map}") + print(f"output map is: {output_map}") + + input_io_buffers = [] + output_io_buffers = [] + for name, shape in input_map.items(): + # 1. apply memory buffer for input of the shape, based on shape and padding + _shape, _padding = shape.dims, shape.padding + _shape = [i + j for i, j in zip(_shape, _padding)] + _shape = [_shape[0], *_shape[2:4], _shape[1]] + # currently we only support float32 as I/O + buffer = np.zeros(_shape, dtype=np.float32) + # 2. load image to the buffer, TODO batch load + img = pre_process(img_file) + print("image shape is:", img.shape) + + buffer[0, :, :, :3] = img + print(f"Allocated input buffer:{_shape}") + + # 3. put the buffer to a list + input_io_buffers.append([name, buffer, shape]) + + for name, shape in output_map.items(): + # 1. apply memory buffer for output of the shape + # output_buffer = np.zeros(shape.dims, dtype=np.float32) + bs, c, h, w = shape.dims + dims = [bs, h, w, c] + + output_buffer = np.zeros(dims, dtype=np.float32) + # 2. put the buffer to a list + output_io_buffers.append([name, output_buffer, shape]) + + runtime.LoadInput(input_io_buffers) + runtime.Execute() + runtime.FetchOutput(output_io_buffers) + + print(f"Test Achieved!") + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--model", type=str, default="ddrnet23.onnx") + parser.add_argument("--quant_file", type=str, default=None, help="the json of quantization") + parser.add_argument("--bsz", type=int, default=4, help="batch size") + parser.add_argument("--precision", type=str, choices=["float16", "int8"], default="int8", help="The precision of datatype") + parser.add_argument("--warm_up", type=int, default=5, help="warm_up count") + parser.add_argument("--imgsz_h", type=int, default=1024, help="inference size h") + parser.add_argument("--imgsz_w", type=int, default=2048, help="inference size w") + # engine args + parser.add_argument("--engine", type=str, default=None) + parser.add_argument("--img_file", type=str, default=None) + # device + parser.add_argument( + "--device", type=int, default=0, help="cuda device, i.e. 0 or 0,1,2,3,4" + ) + + args = parser.parse_args() + return args + + +if __name__ == "__main__": + args = parse_args() + main(args) diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/inference.py b/models/cv/semantic_segmentation/ddrnet/ixrt/inference.py new file mode 100644 index 00000000..bb178a2b --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/inference.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse +import glob +import json +import os +import sys +import time +import random +import ctypes +import numpy as np +from os.path import join, dirname, exists + +from tqdm import tqdm + +from utils import Dataset, get_confusion_matrix +import tensorrt +import cuda.cuda as cuda +import cuda.cudart as cudart + +def load_ixrt_plugin(logger=tensorrt.Logger(tensorrt.Logger.INFO), namespace="", dynamic_path=""): + if not dynamic_path: + dynamic_path = join(dirname(tensorrt.__file__), "lib", "libixrt_plugin.so") + if not exists(dynamic_path): + raise FileNotFoundError( + f"The ixrt_plugin lib {dynamic_path} is not existed, please provided effective plugin path!") + ctypes.CDLL(dynamic_path) + tensorrt.init_libnvinfer_plugins(logger, namespace) + print(f"Loaded plugin from {dynamic_path}") + +load_ixrt_plugin() + +def create_engine_context(config): + engine_path = config.engine_file + datatype = tensorrt.DataType.FLOAT + host_mem = tensorrt.IHostMemory + logger = tensorrt.Logger(tensorrt.Logger.ERROR) + with open(engine_path, "rb") as f, tensorrt.Runtime(logger) as runtime: + runtime = tensorrt.Runtime(logger) + assert runtime + engine = runtime.deserialize_cuda_engine(f.read()) + assert engine + context = engine.create_execution_context() + assert context + + return engine, context + + +def setup_io_bindings(engine, context): + # Setup I/O bindings + inputs = [] + outputs = [] + allocations = [] + + for i in range(engine.num_bindings): + is_input = False + if engine.binding_is_input(i): + is_input = True + name = engine.get_binding_name(i) + dtype = engine.get_binding_dtype(i) + shape = context.get_binding_shape(i) + + if is_input: + batch_size = shape[0] + size = np.dtype(tensorrt.nptype(dtype)).itemsize + for s in shape: + size *= s + err, allocation = cudart.cudaMalloc(size) + assert err == cudart.cudaError_t.cudaSuccess + binding = { + "index": i, + "name": name, + "dtype": np.dtype(tensorrt.nptype(dtype)), + "shape": list(shape), + "allocation": allocation, + "nbytes": size, + } + allocations.append(allocation) + if engine.binding_is_input(i): + inputs.append(binding) + else: + outputs.append(binding) + return inputs, outputs, allocations + +def check_target(inference, target): + satisfied = False + if inference > target: + satisfied = True + return satisfied + + +def test_mIoU_mAcc(dataset, config): + + confusion_matrix = np.zeros((config.num_classes, config.num_classes)) + + host_mem = tensorrt.IHostMemory + logger = tensorrt.Logger(tensorrt.Logger.ERROR) + + engine, context = create_engine_context(config) + inputs, outputs, allocations = setup_io_bindings(engine, context) + + run_times = [] + + for i, element in tqdm(enumerate(dataset), desc="Testing mIoU and mAcc"): + start_time = time.time() + img, label, pad_size, name = element + img = np.ascontiguousarray(img.transpose((0,3,1,2))) + b, c, h, w = img.shape + + output = np.zeros([b, 32, h, w], outputs[0]["dtype"]) + err, = cuda.cuMemcpyHtoD(inputs[0]["allocation"], img, img.nbytes) + assert(err == cuda.CUresult.CUDA_SUCCESS) + context.execute_v2(allocations) + err, = cuda.cuMemcpyDtoH(output, outputs[0]["allocation"], outputs[0]["nbytes"]) + assert(err == cuda.CUresult.CUDA_SUCCESS) + + pred = output[:, :config.num_classes, :, :] + # flip test + if config.flip: + flip_img = img.copy()[:, :, :, ::-1] + + err, = cuda.cuMemcpyHtoD(inputs[0]["allocation"], img, img.nbytes) + assert(err == cuda.CUresult.CUDA_SUCCESS) + context.execute_v2(allocations) + err, = cuda.cuMemcpyDtoH(output, outputs[0]["allocation"], outputs[0]["nbytes"]) + assert(err == cuda.CUresult.CUDA_SUCCESS) + flip_pred = output[:, :config.num_classes, :, :] + + pred += flip_pred + out = np.exp(pred * 0.5) + else: + out = np.exp(pred) + + out = out.transpose((0,2,3,1)) + + for j in range(b): + confusion_matrix += get_confusion_matrix( + label[j:j+1], + out[j:j+1], + pad_size[j], + config.num_classes, + config.ignore_label + ) + + end_time = time.time() + run_times.append(end_time - start_time) + + num_imgs = i * config.bsz + if num_imgs % 100 == 0: + print(f"[INFO] processing: {num_imgs} images") + pos = confusion_matrix.sum(1) + res = confusion_matrix.sum(0) + tp = np.diag(confusion_matrix) + IoU_array = (tp / np.maximum(1.0, pos + res - tp)) + mean_IoU = IoU_array.mean() + print("[INFO] mIoU: %.4f" % (mean_IoU)) + + pos = confusion_matrix.sum(1) + res = confusion_matrix.sum(0) + tp = np.diag(confusion_matrix) + pixel_acc = tp.sum() / pos.sum() + mean_acc = (tp / np.maximum(1.0, pos)).mean() + IoU_array = (tp / np.maximum(1.0, pos + res - tp)) + mean_IoU = IoU_array.mean() + + # Calculate FPS + run_times.remove(max(run_times)) + run_times.remove(min(run_times)) + avg_time = sum(run_times) / len(run_times) + fps = 1. / avg_time + print(f"Executing Done, Time: {avg_time}, FPS: {fps}, mIoU: {mean_IoU}, mAcc: {mean_acc}") + print(f"Class IoU:") + print(f"{IoU_array}") + metricResult = {"metricResult": {}} + metricResult["metricResult"]["mIoU"] = round(mean_IoU, 3) + metricResult["metricResult"]["mAcc"] = round(mean_acc, 3) + print(metricResult) + return mean_IoU, mean_acc + + +def test_fps(config, loop_count, dataset): + + host_mem = tensorrt.IHostMemory + logger = tensorrt.Logger(tensorrt.Logger.ERROR) + + engine, context = create_engine_context(config) + inputs, outputs, allocations = setup_io_bindings(engine, context) + + run_times = [] + + if config.warm_up > 0: + print("\nWarm Start.") + for i in range(config.warm_up): + context.execute_v2(allocations) + print("Warm Done.") + + batch_data0 = dataset[0] + for i in range(loop_count): + img, label, pad_size, name = batch_data0 + b, h, w, c = img.shape + output = np.zeros([b, 32, h, w], outputs[0]["dtype"]) + img = np.ascontiguousarray(img.transpose((0,3,1,2))) + err, = cuda.cuMemcpyHtoD(inputs[0]["allocation"], img, img.nbytes) + assert(err == cuda.CUresult.CUDA_SUCCESS) + start_time = time.time() + context.execute_v2(allocations) + end_time = time.time() + err, = cuda.cuMemcpyDtoH(output, outputs[0]["allocation"], outputs[0]["nbytes"]) + assert(err == cuda.CUresult.CUDA_SUCCESS) + + temp_time = end_time - start_time + fps = b / temp_time + print(f"time: {temp_time}, fps: {fps}") + run_times.append(temp_time) + + # Calculate FPS + run_times.remove(max(run_times)) + run_times.remove(min(run_times)) + + avg_time = sum(run_times) / len(run_times) + fps = b / avg_time + print(f"Executing {loop_count} done, Time: {avg_time}, FPS: {fps}") + metricResult = {"metricResult": {}} + metricResult["metricResult"]["FPS"] = round(fps, 3) + print(metricResult) + return fps + + +def main(config): + + num_samples = 1 + bsz = config.bsz + if config.loop_count > 0: + num_samples = bsz * config.loop_count + num_batch = (num_samples + bsz - 1) // bsz + + dataset = Dataset( + root=config.dataset_dir, + list_path=config.list_path, + batch_size=config.bsz, + ignore_label=255 + ) + + if config.test_mode == "MIOU": + mIoU, mAcc = test_mIoU_mAcc(dataset, config) + status_mIoU_mAcc = check_target(mIoU, config.target_mIoU) and check_target(mAcc, config.target_mAcc) + sys.exit(int(not (status_mIoU_mAcc))) + + elif config.test_mode == "FPS": + # Warm up + fps = test_fps(config, config.loop_count, dataset) + status_fps = check_target(fps, config.target_fps) + sys.exit(int(not (status_fps))) + + +def parse_config(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_type", + type=str, + default="DDRNET", + help="The semantic segmentation(ddrnet)", + ) + parser.add_argument("--engine_file", type=str, help="engine file path") + parser.add_argument("--test_mode", type=str, default="MIOU", help="FPS MIOU") + parser.add_argument( + "--dataset_dir", + type=str, + default="/root/data/datasets", + help="The directory of dataset(cityscapes)", + ) + parser.add_argument( + "--list_path", + type=str, + default="/root/data/datasets/cityscapes/val.lst", + help="The val name list of dataset(cityscapes)", + ) + parser.add_argument("--warm_up", type=int, default=5, help="warm_up count") + parser.add_argument("--flip", action='store_true', help="Flip test") + parser.add_argument("--bsz", type=int, default=4, help="batch size") + parser.add_argument("--num_classes", type=int, default=19, help="the category of dataset") + parser.add_argument("--ignore_label", type=int, default=255, help="the category of not used in calculate confusion matrix") + parser.add_argument("--imgsz_h", type=int, default=1024, help="inference size h") + parser.add_argument("--imgsz_w", type=int, default=2048, help="inference size w") + parser.add_argument("--pred_dir", type=str, default=".", help="pred save json dirs") + parser.add_argument("--target_fps", type=float, default=-1.0) + parser.add_argument("--target_mIoU", type=float, default=-1.0) + parser.add_argument("--target_mAcc", type=float, default=-1.0) + parser.add_argument("--loop_count", type=int, default=12) + parser.add_argument( + "--device", type=int, default=0, help="cuda device, i.e. 0 or 0,1,2,3,4" + ) + + config = parser.parse_args() + return config + + +if __name__ == "__main__": + config = parse_config() + main(config) diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/quant.py b/models/cv/semantic_segmentation/ddrnet/ixrt/quant.py new file mode 100644 index 00000000..b5347f66 --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/quant.py @@ -0,0 +1,70 @@ +import os +import cv2 +import random +import argparse +import numpy as np +from random import shuffle +from utils import input_transform +from tensorrt.deploy import static_quantize + +import torch +import torchvision.datasets +from torch.utils.data import DataLoader + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--model", type=str, default="ddrnet23.onnx") + parser.add_argument("--dataset_dir", type=str, default="/root/data/datasets/cityscapes") + parser.add_argument("--list_path", type=str, default="/root/data/datasets/cityscapes/val.lst", help="The path of val list.") + parser.add_argument("--save_dir", type=str, help="quant file", default=None) + args = parser.parse_args() + return args + + +def getdataloader(datadir, list_path, step=32, batch_size=4): + num = step * batch_size + + img_list = [line.strip().split()[0] for line in open(list_path)] + val_list = [os.path.join(datadir, x) for x in img_list] + random.shuffle(val_list) + pic_list = val_list[:num] + + dataloader = [] + # imgsz = (1024, 2048) + for file_path in pic_list: + img = cv2.imread(file_path, cv2.IMREAD_COLOR) + img = input_transform( + img, + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225] + ) + img = img.transpose((2, 0, 1)) + dataloader.append(img) + + calibration_dataset = dataloader + calibration_dataloader = DataLoader( + calibration_dataset, + shuffle=True, + batch_size=batch_size, + drop_last=True + ) + return calibration_dataloader + + +args = parse_args() +model_name = os.path.basename(args.model) +model_name = model_name.rsplit(".", maxsplit=1)[0] + +out_dir = os.path.dirname(args.model) +dataloader = getdataloader(args.dataset_dir, args.list_path) + +static_quantize(args.model, + calibration_dataloader=dataloader, + save_quant_onnx_path=os.path.join(out_dir, f"quantized_{model_name}.onnx"), + save_quant_params_path=os.path.join(out_dir, f"quantized_ddrnet23.json"), + observer="percentile", + analyze=True, + quant_format="qdq", + data_preprocess=lambda x: x.to("cuda"), + ) diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_fp16_accuracy.sh b/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_fp16_accuracy.sh new file mode 100644 index 00000000..e5d96ce2 --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_fp16_accuracy.sh @@ -0,0 +1,120 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -e + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +MODEL_NAME="ddrnet" +BSZ=4 +PRECISION="float16" +DEVICE=0 +FORCE_BUILD=0 +TGT_0=-1 +TGT_1=-1 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + -p | --precision) PRECISION=${arguments[index]};; + -d | --device) DEVICE=${arguments[index]};; + --bs) BSZ=${arguments[index]};; + --tgt_iou) TGT_0=${arguments[index]};; + --tgt_acc) TGT_1=${arguments[index]};; + -f | --force) FORCE_BUILD=1;; + esac +done + +CHECKPOINTS_DIR="./checkpoints" +DATASET_DIR="/root/data/datasets" +LIST_PATH="/root/data/datasets/cityscapes/val.lst" +RUN_DIR="${RUN_DIR:-.}" +ORIGINE_MODEL="${CHECKPOINTS_DIR}/ddrnet23.onnx" + +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} +echo; + +function run_cmd() +{ + echo "[CMD]: $@" + eval $@ +} + +step=1 + +# Simplify Model +echo [STEP ${step}] : Simplify Model +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model, ${SIM_MODEL} has been existed +else + run_cmd python3 ${RUN_DIR}/sim_onnx_model.py \ + --raw_model_path ${ORIGINE_MODEL} \ + --sim_model_path ${SIM_MODEL} + echo " "Generate ${SIM_MODEL} +fi +let step++ +echo; + +# Build Engine +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ ${FORCE_BUILD} -eq 1 ] && [ -e ${ENGINE_FILE} ];then + rm ${ENGINE_FILE} +fi +echo "Building engine(${PRECISION})" +if [ -e ${ENGINE_FILE} ];then + echo " "Build Engine Skip, ${ENGINE_FILE} has been existed +else + run_cmd python3 ${RUN_DIR}/build_engine.py \ + --model ${SIM_MODEL} \ + --bsz ${BSZ} \ + --precision ${PRECISION} \ + --engine ${ENGINE_FILE} \ + --device ${DEVICE} + echo " "Generate Engine ${ENGINE_FILE} +fi +let step++ +echo; + +# Inference +echo [STEP ${step}] : Inference +run_cmd python3 ${RUN_DIR}/inference.py \ + --model_type "DDRNET23" \ + --engine_file ${ENGINE_FILE} \ + --test_mode MIOU \ + --dataset_dir ${DATASET_DIR} \ + --list_path ${LIST_PATH} \ + --flip \ + --bsz ${BSZ} \ + --target_mIoU ${TGT_0} \ + --target_mAcc ${TGT_1} \ + --loop_count -1 \ + --device ${DEVICE}; check_status + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_fp16_performance.sh b/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_fp16_performance.sh new file mode 100644 index 00000000..42c4b2c7 --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_fp16_performance.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -e + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +MODEL_NAME="ddrnet" +BSZ=4 +PRECISION="float16" +DEVICE=0 +FORCE_BUILD=0 +TGT=1 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + -p | --precision) PRECISION=${arguments[index]};; + -d | --device) DEVICE=${arguments[index]};; + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + -f | --force) FORCE_BUILD=1;; + esac +done + +CHECKPOINTS_DIR="./checkpoints" +DATASET_DIR="/root/data/datasets" +LIST_PATH="/root/data/datasets/cityscapes/val.lst" +RUN_DIR="${RUN_DIR:-.}" +ORIGINE_MODEL="${CHECKPOINTS_DIR}/ddrnet23.onnx" + +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} +echo; + +function run_cmd() +{ + echo "[CMD]: $@" + eval $@ +} + +step=1 + +# Simplify Model +echo [STEP ${step}] : Simplify Model +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model, ${SIM_MODEL} has been existed +else + run_cmd python3 ${RUN_DIR}/sim_onnx_model.py \ + --raw_model_path ${ORIGINE_MODEL} \ + --sim_model_path ${SIM_MODEL} + echo " "Generate ${SIM_MODEL} +fi +let step++ +echo; + +# Build Engine +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ ${FORCE_BUILD} -eq 1 ] && [ -e ${ENGINE_FILE} ];then + rm ${ENGINE_FILE} +fi +echo "Building engine(${PRECISION})" +if [ -e ${ENGINE_FILE} ];then + echo " "Build Engine Skip, ${ENGINE_FILE} has been existed +else + run_cmd python3 ${RUN_DIR}/build_engine.py \ + --model ${SIM_MODEL} \ + --bsz ${BSZ} \ + --precision ${PRECISION} \ + --engine ${ENGINE_FILE} \ + --device ${DEVICE} + echo " "Generate Engine ${ENGINE_FILE} +fi +let step++ +echo; + +# Inference +echo [STEP ${step}] : Inference +run_cmd python3 ${RUN_DIR}/inference.py \ + --model_type "DDRNET23" \ + --engine_file ${ENGINE_FILE} \ + --test_mode FPS \ + --dataset_dir ${DATASET_DIR} \ + --list_path ${LIST_PATH} \ + --target_fps ${TGT} \ + --loop_count 12 \ + --device ${DEVICE} + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_int8_accuracy.sh b/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_int8_accuracy.sh new file mode 100644 index 00000000..b3e59bb8 --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_int8_accuracy.sh @@ -0,0 +1,138 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -e + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +MODEL_NAME="ddrnet" +BSZ=4 +PRECISION="int8" +DEVICE=0 +FORCE_BUILD=0 +TGT_0=-1 +TGT_1=-1 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + -p | --precision) PRECISION=${arguments[index]};; + -d | --device) DEVICE=${arguments[index]};; + --bs) BSZ=${arguments[index]};; + --tgt_iou) TGT_0=${arguments[index]};; + --tgt_acc) TGT_1=${arguments[index]};; + -f | --force) FORCE_BUILD=1;; + esac +done + +CHECKPOINTS_DIR="./checkpoints" +DATASET_DIR="/root/data/datasets" +LIST_PATH="/root/data/datasets/cityscapes/val.lst" +RUN_DIR="${RUN_DIR:-.}" +ORIGINE_MODEL="${CHECKPOINTS_DIR}/ddrnet23.onnx" + +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} +echo; + +function run_cmd() +{ + echo "[CMD]: $@" + eval $@ +} + +step=1 + +# Simplify Model +echo [STEP ${step}] : Simplify Model +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model, ${SIM_MODEL} has been existed +else + run_cmd python3 ${RUN_DIR}/sim_onnx_model.py \ + --raw_model_path ${ORIGINE_MODEL} \ + --sim_model_path ${SIM_MODEL} + echo " "Generate ${SIM_MODEL} +fi +let step++ +echo; + +# Quant Model +if [ $PRECISION == "int8" ];then + echo [STEP ${step}] : Quant Model + QUANT_MODEL=${CHECKPOINTS_DIR}/quantized_${MODEL_NAME}_sim.onnx + if [ -f ${QUANT_MODEL} ];then + echo " "Quant Model Skip, ${QUANT_MODEL} has been existed + else + run_cmd python3 ${RUN_DIR}/quant.py \ + --model ${SIM_MODEL} \ + --dataset_dir ${DATASET_DIR}/cityscapes \ + --save_dir ${CHECKPOINTS_DIR} + echo " "Generate ${QUANT_MODEL} + fi + SIM_MODEL=${QUANT_MODEL} + let step++ + echo; +fi + +# Build Engine +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ ${FORCE_BUILD} -eq 1 ] && [ -e ${ENGINE_FILE} ];then + rm ${ENGINE_FILE} +fi +echo "Building engine(${PRECISION})" +if [ -e ${ENGINE_FILE} ];then + echo " "Build Engine Skip, ${ENGINE_FILE} has been existed +else + run_cmd python3 ${RUN_DIR}/build_engine.py \ + --model ${SIM_MODEL} \ + --bsz ${BSZ} \ + --precision ${PRECISION} \ + --engine ${ENGINE_FILE} \ + --device ${DEVICE} + echo " "Generate Engine ${ENGINE_FILE} +fi +let step++ +echo; + +# Inference +echo [STEP ${step}] : Inference +run_cmd python3 ${RUN_DIR}/inference.py \ + --model_type "DDRNET23" \ + --engine_file ${ENGINE_FILE} \ + --test_mode MIOU \ + --dataset_dir ${DATASET_DIR} \ + --list_path ${LIST_PATH} \ + --flip \ + --bsz ${BSZ} \ + --target_mIoU ${TGT_0} \ + --target_mAcc ${TGT_1} \ + --loop_count -1 \ + --device ${DEVICE}; check_status + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_int8_performance.sh b/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_int8_performance.sh new file mode 100644 index 00000000..36fecd59 --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/scripts/infer_ddrnet_int8_performance.sh @@ -0,0 +1,133 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -e + +EXIT_STATUS=0 +check_status() +{ + if ((${PIPESTATUS[0]} != 0));then + EXIT_STATUS=1 + fi +} + +MODEL_NAME="ddrnet" +BSZ=4 +PRECISION="int8" +DEVICE=0 +FORCE_BUILD=0 +TGT=1 + +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + -p | --precision) PRECISION=${arguments[index]};; + -d | --device) DEVICE=${arguments[index]};; + --bs) BSZ=${arguments[index]};; + --tgt) TGT=${arguments[index]};; + -f | --force) FORCE_BUILD=1;; + esac +done + +CHECKPOINTS_DIR="./checkpoints" +DATASET_DIR="/root/data/datasets" +LIST_PATH="/root/data/datasets/cityscapes/val.lst" +RUN_DIR="${RUN_DIR:-.}" +ORIGINE_MODEL="${CHECKPOINTS_DIR}/ddrnet23.onnx" + +echo ====================== Model Info ====================== +echo Model Name : ${MODEL_NAME} +echo Onnx Path : ${ORIGINE_MODEL} +echo; + +function run_cmd() +{ + echo "[CMD]: $@" + eval $@ +} + +step=1 + +# Simplify Model +echo [STEP ${step}] : Simplify Model +SIM_MODEL=${CHECKPOINTS_DIR}/${MODEL_NAME}_sim.onnx +if [ -f ${SIM_MODEL} ];then + echo " "Simplify Model, ${SIM_MODEL} has been existed +else + run_cmd python3 ${RUN_DIR}/sim_onnx_model.py \ + --raw_model_path ${ORIGINE_MODEL} \ + --sim_model_path ${SIM_MODEL} + echo " "Generate ${SIM_MODEL} +fi +let step++ +echo; + +# Quant Model +if [ $PRECISION == "int8" ];then + echo [STEP ${step}] : Quant Model + QUANT_MODEL=${CHECKPOINTS_DIR}/quantized_${MODEL_NAME}_sim.onnx + if [ -f ${QUANT_MODEL} ];then + echo " "Quant Model Skip, ${QUANT_MODEL} has been existed + else + run_cmd python3 ${RUN_DIR}/quant.py \ + --model ${SIM_MODEL} \ + --dataset_dir ${DATASET_DIR}/cityscapes \ + --save_dir ${CHECKPOINTS_DIR} + echo " "Generate ${QUANT_MODEL} + fi + SIM_MODEL=${QUANT_MODEL} + let step++ + echo; +fi + +# Build Engine +echo [STEP ${step}] : Build Engine +ENGINE_FILE=${CHECKPOINTS_DIR}/${MODEL_NAME}_${PRECISION}_bs${BSZ}.engine +if [ ${FORCE_BUILD} -eq 1 ] && [ -e ${ENGINE_FILE} ];then + rm ${ENGINE_FILE} +fi +echo "Building engine(${PRECISION})" +if [ -e ${ENGINE_FILE} ];then + echo " "Build Engine Skip, ${ENGINE_FILE} has been existed +else + run_cmd python3 ${RUN_DIR}/build_engine.py \ + --model ${SIM_MODEL} \ + --bsz ${BSZ} \ + --precision ${PRECISION} \ + --engine ${ENGINE_FILE} \ + --device ${DEVICE} + echo " "Generate Engine ${ENGINE_FILE} +fi +let step++ +echo; + +# Inference +echo [STEP ${step}] : Inference +run_cmd python3 ${RUN_DIR}/inference.py \ + --model_type "DDRNET23" \ + --engine_file ${ENGINE_FILE} \ + --test_mode FPS \ + --dataset_dir ${DATASET_DIR} \ + --list_path ${LIST_PATH} \ + --target_fps ${TGT} \ + --loop_count 12 \ + --device ${DEVICE} + +exit ${EXIT_STATUS} \ No newline at end of file diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/sim_onnx_model.py b/models/cv/semantic_segmentation/ddrnet/ixrt/sim_onnx_model.py new file mode 100644 index 00000000..98aa36c2 --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/sim_onnx_model.py @@ -0,0 +1,17 @@ +import onnx +import argparse +from onnxsim import simplify + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--raw_model_path", type=str) + parser.add_argument("--sim_model_path", type=str) + args = parser.parse_args() + return args + + +args = parse_args() +onnx_model = onnx.load(args.raw_model_path) +model_simp, check = simplify(onnx_model) +onnx.save(model_simp, args.sim_model_path) +print('Simplify onnx Done.') diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/utils/__init__.py b/models/cv/semantic_segmentation/ddrnet/ixrt/utils/__init__.py new file mode 100644 index 00000000..6f0a54fe --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/utils/__init__.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# coding=utf-8 + +import numpy as np +from .dataset import Dataset +from .metrics import get_confusion_matrix + + +def input_transform(image, mean, std): + image = image.astype(np.float32)[:, :, ::-1] + image = image / 255.0 + image -= mean + image /= std + return image diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/utils/dataset.py b/models/cv/semantic_segmentation/ddrnet/ixrt/utils/dataset.py new file mode 100644 index 00000000..2d137c4b --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/utils/dataset.py @@ -0,0 +1,139 @@ +import os +import cv2 +import numpy as np +from math import ceil +from tqdm import tqdm + + +class Dataset: + def __init__(self, + root, + list_path, + batch_size=4, + num_classes=19, + ignore_label=255, + base_size=2048, + crop_size=(512, 1024), + downsample_rate=1, + scale_factor=16, + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]): + + self.root = root + self.list_path = list_path + self.batch_size = batch_size + self.num_classes = num_classes + self.mean = mean + self.std = std + self.downsample_rate = downsample_rate + + self.img_list = [line.strip().split() for line in open(list_path)] + self.files = self.read_files() + self.num_batches = ceil(len(self.files) / self.batch_size) + + self.label_mapping = {-1: ignore_label, 0: ignore_label, + 1: ignore_label, 2: ignore_label, + 3: ignore_label, 4: ignore_label, + 5: ignore_label, 6: ignore_label, + 7: 0, 8: 1, 9: ignore_label, + 10: ignore_label, 11: 2, 12: 3, + 13: 4, 14: ignore_label, 15: ignore_label, + 16: ignore_label, 17: 5, 18: ignore_label, + 19: 6, 20: 7, 21: 8, 22: 9, 23: 10, 24: 11, + 25: 12, 26: 13, 27: 14, 28: 15, + 29: ignore_label, 30: ignore_label, + 31: 16, 32: 17, 33: 18} + + self.batch_images, self.batch_labels, self.batch_sizes, self.batch_names = self.batching() + + def read_files(self): + files = [] + for i, item in enumerate(self.img_list): + image_path, label_path = item + name = os.path.splitext(os.path.basename(label_path))[0] + files.append({ + "img": image_path, + "label": label_path, + "name": name, + "weight": 1 + }) + # if i == 4: + # break + return files + + def input_transform(self, image): + image = image.astype(np.float32)[:, :, ::-1] + image = image / 255.0 + image -= self.mean + image /= self.std + return image + + def label_transform(self, label): + temp = label.copy() + for k, v in self.label_mapping.items(): + label[temp == k] = v + return np.array(label).astype('int32') + + def gen_sample(self, image, label): + + image = self.input_transform(image) + label = self.label_transform(label) + + if self.downsample_rate != 1: + label = cv2.resize( + label, + None, + fx=self.downsample_rate, + fy=self.downsample_rate, + interpolation=cv2.INTER_NEAREST + ) + return image, label + + def _preprocess(self, index): + item = self.files[index] + name = item["name"] + image = cv2.imread(os.path.join(self.root,'cityscapes',item["img"]), + cv2.IMREAD_COLOR) + size = image.shape + label = cv2.imread(os.path.join(self.root,'cityscapes',item["label"]), + cv2.IMREAD_GRAYSCALE) + image, label = self.gen_sample(image, label) + return image.copy(), label.copy(), np.array(size), name + + def __len__(self): + return self.num_batches + + def __getitem__(self, index): + return (self.batch_images[index], self.batch_labels[index], self.batch_sizes[index], self.batch_names[index]) + + def batching(self): + all_images = [] + all_labels = [] + all_sizes = [] + all_names = [] + + num_batches = self.num_batches + batch_size = self.batch_size + for i in tqdm(range(len(self.files)), desc="Loading Cityscapes Dataset"): + image, label, size, name = self._preprocess(i) + all_images.append(image) + all_labels.append(label) + all_sizes.append(size) + all_names.append(name) + + batch_images = [] + batch_labels = [] + batch_sizes = [] + batch_names = [] + + for j in range(num_batches): + start = j * batch_size + if j == num_batches - 1: + end = None + else: + end = (j + 1) * batch_size + batch_images.append(np.stack(all_images[start:end])) + batch_labels.append(np.stack(all_labels[start:end])) + batch_sizes.append(np.stack(all_sizes[start:end])) + batch_names.append(all_names[start:end]) + return (batch_images, batch_labels, batch_sizes, batch_names) diff --git a/models/cv/semantic_segmentation/ddrnet/ixrt/utils/metrics.py b/models/cv/semantic_segmentation/ddrnet/ixrt/utils/metrics.py new file mode 100644 index 00000000..f0bdfcdf --- /dev/null +++ b/models/cv/semantic_segmentation/ddrnet/ixrt/utils/metrics.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# coding=utf-8 + +""" +Define function to build confusion_matrix. +""" + +import numpy as np + + +def get_confusion_matrix(label, pred, size, num_class=19, ignore=-1): + """ + Calcute the confusion matrix by given label and pred + """ + output = pred + seg_pred = np.asarray(np.argmax(output, axis=3), dtype=np.uint8) + + seg_gt = np.asarray(label[:, :size[-3], :size[-2]], dtype=np.int32) + + ignore_index = seg_gt != ignore + seg_gt = seg_gt[ignore_index] + seg_pred = seg_pred[ignore_index] + + index = (seg_gt * num_class + seg_pred).astype('int32') + label_count = np.bincount(index) + confusion_matrix = np.zeros((num_class, num_class)) + + for i_label in range(num_class): + for i_pred in range(num_class): + cur_index = i_label * num_class + i_pred + if cur_index < len(label_count): + confusion_matrix[i_label, + i_pred] = label_count[cur_index] + return confusion_matrix + + +def get_confusion_matrix_batch(label, pred, size, num_class=19, ignore=-1): + """ + Calcute the confusion matrix by given label and pred in one batch. + Arguments: + label: (batch_size, h, w) + pred: (batch_size, h, w, c) + size: (batch_size, 2) + """ + batch_size, h, w, c = pred.shape + confusion_matrix = np.zeros((num_class, num_class)) + for i in range(batch_size): + confusion_matrix += get_confusion_matrix( + label[i], + pred[i:i+1], + size[i], + 19, + 255 + ) + return confusion_matrix diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/README.md b/models/speech/speech_recognition/deepspeech2/ixrt/README.md new file mode 100644 index 00000000..a903795d --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/README.md @@ -0,0 +1,74 @@ +# DeepSpeech2 (ixRT) + +## Model Description + +DeepSpeech2 is an end-to-end speech recognition model based on RNNs and CTC decoding, developed by Baidu. It uses CNN for acoustic feature extraction followed by RNN encoders and CTC decoder. + +## Supported Environments + +| GPU | [IXUCA SDK](https://gitee.com/deep-spark/deepspark#%E5%A4%A9%E6%95%B0%E6%99%BA%E7%AE%97%E8%BD%AF%E4%BB%B6%E6%A0%88-ixuca) | Release | +| :----: | :----: | :----: | +| MR-V100 | 4.4.0 | 26.06 | + +## Model Preparation + +### Prepare Resources + +Pretrained model: + +Dataset: LibriSpeech + +### Install Dependencies + +Contact the Iluvatar administrator to get the missing packages: +- paddlepaddle-*.whl + +```bash +pip3 install librosa psutil pysoundfile pytest requests tensorboardX editdistance textgrid onnxsim paddlespeech_ctcdecoders paddleaudio paddlespeech +pip3 install numpy==1.23.5 +``` + +### Model Conversion + +```bash +mkdir checkpoints +cd checkpoints +wget http://files.deepspark.org.cn:880/deepspark/data/checkpoints/deepspeech2.onnx +wget http://files.deepspark.org.cn:880/deepspark/data/checkpoints/common_crawl_00.prune01111.trie.klm + + +git clone https://gitee.com/deep-spark/iluvatar-corex-ixrt.git --depth=1 + +OPTIMIER_FILE=iluvatar-corex-ixrt/tools/optimizer/optimizer.py +echo "Build engine!" +python3 modify_model_to_dynamic.py --static_onnx checkpoints/deepspeech2.onnx --dynamic_onnx checkpoints/deepspeech2_dynamic.onnx +python3 ${OPTIMIER_FILE} --onnx checkpoints/deepspeech2_dynamic.onnx --model_type rnn --not_sim +python3 build_engine.py \ + --model_name deepspeech2 \ + --onnx_path checkpoints/deepspeech2_dynamic_end.onnx \ + --engine_path checkpoints/deepspeech2.engine + +``` + +## Model Inference + +```bash +export DATASETS_DIR=/path/to/LibriSpeech/ +export CHECKPOINTS_DIR=./checkpoints +export RUN_DIR=./ +``` + +### FP16 + +```bash +# Test ACC (WER) +bash scripts/infer_deepspeech2_fp16_accuracy.sh +# Test FPS +bash scripts/infer_deepspeech2_fp16_performance.sh +``` + +## Model Results + +| Model | BatchSize | Precision | ThroughPut | WER(%) | +| ------------ | --------- | --------- | ------- | ------ | +| DeepSpeech2 | 1 | FP16 | 1584.153 | 5.8 | diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/build_engine.py b/models/speech/speech_recognition/deepspeech2/ixrt/build_engine.py new file mode 100644 index 00000000..e1ff2a4c --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/build_engine.py @@ -0,0 +1,82 @@ +import os +import json +import onnx +import logging +import argparse +import ctypes +import tensorrt +from tensorrt import Dims +from load_ixrt_plugin import load_ixrt_plugin + +load_ixrt_plugin() + +def parse_args(): + parser = argparse.ArgumentParser(description="Build tensorrt engine of deepspeech2") + parser.add_argument("--model_name", type=str, required=True, help="model name deepspeech2") + parser.add_argument("--onnx_path", type=str, required=True, help="The onnx path") + parser.add_argument("--bsz", type=int, default=1, help="batch size") + parser.add_argument("--input_size", type=tuple, default=(-1, 161), help="inference size") + parser.add_argument("--engine_path", type=str, required=True, help="engine path to save") + parser.add_argument( "--device", type=int, default=0, help="cuda device, i.e. 0 or 0,1,2,3,4") + + args = parser.parse_args() + return args + + +def build_engine_trtapi_dynamicshape(args): + onnx_model = args.onnx_path + assert os.path.isfile(onnx_model), f"The onnx model{onnx_model} must be existed!" + IXRT_LOGGER = tensorrt.Logger(tensorrt.Logger.WARNING) + builder = tensorrt.Builder(IXRT_LOGGER) + EXPLICIT_BATCH = 1 << (int)(tensorrt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) + network = builder.create_network(EXPLICIT_BATCH) + build_config = builder.create_builder_config() + + profile = builder.create_optimization_profile() + + profile.set_shape( + "input", Dims([1, 100, 161]), Dims([1, 1193, 161]), Dims([1, 3494, 161]) + ) + + build_config.add_optimization_profile(profile) + + parser = tensorrt.OnnxParser(network, IXRT_LOGGER) + + parser.parse_from_file(onnx_model) + build_config.set_flag(tensorrt.BuilderFlag.FP16) + + # set dynamic + input_tensor = network.get_input(0) + input_tensor.shape = Dims([1, -1, 161]) + + plan = builder.build_serialized_network(network, build_config) + with open(args.engine_path, "wb") as f: + f.write(plan) + + print("Build dynamic shape engine done!") + + +def build_engine_trtapi_staticshape(args): + onnx_model = args.onnx_path + assert os.path.isfile(onnx_model), f"The onnx model{onnx_model} must be existed!" + IXRT_LOGGER = tensorrt.Logger(tensorrt.Logger.WARNING) + builder = tensorrt.Builder(IXRT_LOGGER) + EXPLICIT_BATCH = 1 << (int)(tensorrt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) + network = builder.create_network(EXPLICIT_BATCH) + build_config = builder.create_builder_config() + parser = tensorrt.OnnxParser(network, IXRT_LOGGER) + + parser.parse_from_file(onnx_model) + build_config.set_flag(tensorrt.BuilderFlag.FP16) + + plan = builder.build_serialized_network(network, build_config) + with open(args.engine_path, "wb") as f: + f.write(plan) + + print("Build static shape engine done!") + + +if __name__ == "__main__": + args = parse_args() + build_engine_trtapi_dynamicshape(args) + # build_engine_trtapi_staticshape(args) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/ci/prepare.sh b/models/speech/speech_recognition/deepspeech2/ixrt/ci/prepare.sh new file mode 100644 index 00000000..e6a6de44 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/ci/prepare.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +set -x + +pip3 install librosa psutil pysoundfile pytest requests tensorboardX editdistance textgrid onnxsim paddlespeech_ctcdecoders paddleaudio paddlespeech +pip3 install numpy==1.23.5 + +mkdir -p checkpoints +cp /root/data/checkpoints/deepspeech2.onnx checkpoints/ +cp /root/data/checkpoints/common_crawl_00.prune01111.trie.klm checkpoints/ + + +OPTIMIER_FILE=/root/data/3rd_party/iluvatar-corex-ixrt/tools/optimizer/optimizer.py +echo "Build engine!" +python3 modify_model_to_dynamic.py --static_onnx checkpoints/deepspeech2.onnx --dynamic_onnx checkpoints/deepspeech2_dynamic.onnx +python3 ${OPTIMIER_FILE} --onnx checkpoints/deepspeech2_dynamic.onnx --model_type rnn --not_sim +python3 build_engine.py \ + --model_name deepspeech2 \ + --onnx_path checkpoints/deepspeech2_dynamic_end.onnx \ + --engine_path checkpoints/deepspeech2.engine diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/convert_weights.py b/models/speech/speech_recognition/deepspeech2/ixrt/convert_weights.py new file mode 100644 index 00000000..16131d6a --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/convert_weights.py @@ -0,0 +1,141 @@ +import os +import onnx +import argparse +import numpy as np +from onnx import TensorProto, numpy_helper, helper + +def parse_args(): + parser = argparse.ArgumentParser(description="Convert the weight of lstm in model.") + parser.add_argument("--input_onnx", type=str, default="/home/yanlong.hao/DeepSpeech2/ixrt-modelzoo/data/checkpoints/deepspeech2/deepspeech2_part.onnx") + parser.add_argument("--output_onnx", type=str, default="/home/yanlong.hao/DeepSpeech2/ixrt-modelzoo/data/checkpoints/deepspeech2/deepspeech2.onnx") + + args = parser.parse_args() + return args + + +def convert_weights(args): + onnx_model = onnx.load(args.input_onnx) + graph = onnx_model.graph + node = graph.node + initializer = graph.initializer + + for i in range(len(node)): + if node[i].op_type == "LSTM": + count = 0 + for t in node[i].input: + if not t: + count += 1 + print("count: ", count) + for _ in range(count): + node[i].input.remove("") + + hidden_size = 0 + for j in range(len(node[i].attribute)): + if node[i].attribute[j].name == "hidden_size": + hidden_size = node[i].attribute[j].i + + w_name = node[i].input[1] + r_name = node[i].input[2] + b_name = node[i].input[3] + + w_data = None + r_data = None + b_data = None + + for data in initializer: + if data.name == node[i].input[1]: + dims = list(data.dims).copy() + dims_A = dims.copy() + w_origin_data = np.frombuffer(data.raw_data, dtype=np.float32) + W_save = np.transpose(w_origin_data.reshape(dims), [0, 2, 1]) + w1 = W_save[0, :, :hidden_size].reshape(-1) + w2 = W_save[0, :, hidden_size : hidden_size * 2].reshape(-1) + w3 = W_save[0, :, hidden_size * 2 : hidden_size * 3].reshape(-1) + w4 = W_save[0, :, hidden_size * 3 : hidden_size * 4].reshape(-1) + + w_r1 = W_save[1, :, :hidden_size].reshape(-1) + w_r2 = W_save[1, :, hidden_size : hidden_size * 2].reshape(-1) + w_r3 = W_save[1, :, hidden_size * 2 : hidden_size * 3].reshape(-1) + w_r4 = W_save[1, :, hidden_size * 3 : hidden_size * 4].reshape(-1) + + w_data = np.concatenate([w1, w2, w3, w4, w_r1, w_r2, w_r3, w_r4]) + print("w_data shape: ", w_data.shape) + + if data.name == node[i].input[2]: + dims = list(data.dims).copy() + dims_B = dims.copy() + r_origin_data = np.frombuffer(data.raw_data, dtype=np.float32) + R_save = np.transpose(r_origin_data.reshape(dims), [0, 2, 1]) + r1 = R_save[0, :, :hidden_size].reshape(-1) + r2 = R_save[0, :, hidden_size : hidden_size * 2].reshape(-1) + r3 = R_save[0, :, hidden_size * 2 : hidden_size * 3].reshape(-1) + r4 = R_save[0, :, hidden_size * 3 : hidden_size * 4].reshape(-1) + + r_r1 = R_save[1, :, :hidden_size].reshape(-1) + r_r2 = R_save[1, :, hidden_size : hidden_size * 2].reshape(-1) + r_r3 = R_save[1, :, hidden_size * 2 : hidden_size * 3].reshape(-1) + r_r4 = R_save[1, :, hidden_size * 3 : hidden_size * 4].reshape(-1) + + r_data = np.concatenate([r1, r2, r3, r4, r_r1, r_r2, r_r3, r_r4]) + print("r_data shape: ", r_data.shape) + + if data.name == node[i].input[3]: + dims = data.dims + b_origin_data = np.frombuffer(data.raw_data, dtype=np.float32) + B_save = b_origin_data.reshape(dims) + bias_ih = B_save[0, : hidden_size * 4] + bias_hh = B_save[0, hidden_size * 4 : hidden_size * 8] + bias_f = bias_ih + bias_hh # bias add merge + bias_r_ih = B_save[1, : hidden_size * 4] + bias_r_hh = B_save[1, hidden_size * 4 : hidden_size * 8] + bias_r = bias_r_ih + bias_r_hh # bias add merge + b_data = np.concatenate([bias_f, bias_r]) + print("b_data shape: ", b_data.shape) + + for save_data in initializer: + if w_name == save_data.name: + save_data.raw_data=w_data.astype(np.float32).tobytes() + + elif r_name == save_data.name: + save_data.raw_data=r_data.astype(np.float32).tobytes() + + elif b_name == save_data.name: + save_data.raw_data=b_data.astype(np.float32).tobytes() + save_data.dims[1] = int(save_data.dims[1] / 2) + + + for data in initializer: + + if data.name == "p2o.helper.constant.2": + raw_data = np.frombuffer(data.raw_data, dtype=np.int64) + tmp_data = raw_data.copy() + tmp_data[0] = 1 + # tmp_data[0] = 16 + tmp_data[1] = -1 + tmp_data[2] = 1248 + data.raw_data = tmp_data.tobytes() + + lstm_reshape_name = "p2o.helper.constant.4" + # batch size: 1 + lstm_reshape_params = helper.make_tensor(lstm_reshape_name, onnx.TensorProto.INT64, [3], [-1,1,2048]) + # batch size: 16 + # lstm_reshape_params = helper.make_tensor(lstm_reshape_name, onnx.TensorProto.INT64, [3], [-1,16,2048]) + initializer.append(lstm_reshape_params) + + first_reshape_node = True + for i in range(len(node)): + if node[i].op_type == "Reshape": + if first_reshape_node: + first_reshape_node = False + continue + else: + node[i].input[1] = lstm_reshape_name + + onnx.save(onnx_model, args.output_onnx) + + + +if __name__ == "__main__": + args = parse_args() + convert_weights(args) + print("Save Down!") diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/cut_onnx_model.py b/models/speech/speech_recognition/deepspeech2/ixrt/cut_onnx_model.py new file mode 100644 index 00000000..9f7ad073 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/cut_onnx_model.py @@ -0,0 +1,12 @@ +import os +import onnx + +base_path = "../../../../../data/checkpoints/deepspeech2" + +raw_path = os.path.join(base_path, "deepspeech2_all.onnx") +save_path = os.path.join(base_path, "deepspeech2_part.onnx") + +input_names = ["input"] +output_names = ["layer_norm_9.tmp_2"] + +onnx.utils.extract_model(raw_path, save_path, input_names, output_names) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/data/decoder.pdparams b/models/speech/speech_recognition/deepspeech2/ixrt/data/decoder.pdparams new file mode 100644 index 0000000000000000000000000000000000000000..c56060aa93728a5d471e95912f491ff0e021dcbe GIT binary patch literal 254370 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 z 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�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ªO8q_t4L8W&$FoHZ` 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 N-v`UY4}yb7{U49TfxrL& literal 0 HcmV?d00001 diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/data/demo_002_en.wav b/models/speech/speech_recognition/deepspeech2/ixrt/data/demo_002_en.wav new file mode 100644 index 0000000000000000000000000000000000000000..6dec925262b87ad659421edce892b0ab3b5039c4 GIT binary patch literal 270226 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±lbsUn0vE_L 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 &%H*cw$Eu&o9bw=Mt_u2e0d?x_(Er4 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 literal 0 HcmV?d00001 diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/data/mean_std.json b/models/speech/speech_recognition/deepspeech2/ixrt/data/mean_std.json new file mode 100644 index 00000000..0867476f --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/data/mean_std.json @@ -0,0 +1 @@ +{"mean_stat": [24156894.0, 12346911.0, 22422352.0, 24839050.0, -39564016.0, 26636840.0, 25011566.0, 24835082.0, 29086770.0, -39564016.0, 30332006.0, 27864978.0, 29913940.0, 30884468.0, 27886950.0, 32087904.0, -39564016.0, 32251384.0, 22983980.0, 31812588.0, 27079002.0, 31846070.0, 27868144.0, 32247188.0, 27818408.0, 32808634.0, 28970758.0, 33002436.0, 30088680.0, 32556118.0, 31661602.0, 31608772.0, 32695986.0, 31184330.0, 32831278.0, 31139706.0, 31246538.0, 32284240.0, 30891642.0, 31133906.0, 31388504.0, 30680138.0, 30491506.0, 31091484.0, 30377426.0, 30421270.0, 30813302.0, 30274476.0, 30598140.0, 30471758.0, 30579814.0, 30329296.0, 30493232.0, 30623062.0, 30493558.0, 30204112.0, 30423514.0, 30253188.0, 30602832.0, 30749188.0, 30818314.0, 30849054.0, 30869560.0, 30913844.0, 30980630.0, 31086216.0, 31331224.0, 31680480.0, 31968040.0, 32049322.0, 32063286.0, 32256122.0, 32434294.0, 32573840.0, 32674094.0, 32730916.0, 32978416.0, 33109402.0, 32979748.0, 33051350.0, 33052606.0, 33441594.0, 33257368.0, 33116758.0, 33340022.0, 33602192.0, 33161626.0, 33500680.0, 33578800.0, 33243446.0, 33923204.0, 33347320.0, 34025008.0, 33657892.0, 34250184.0, 33890276.0, 34463444.0, 34195792.0, 34383156.0, 34520940.0, 34386284.0, 34438136.0, 34548276.0, 34413796.0, 34502292.0, 34560776.0, 34626944.0, 34570476.0, 34526264.0, 34546036.0, 34544248.0, 34544372.0, 34543380.0, 34524356.0, 34496476.0, 34466152.0, 34515864.0, 34529828.0, 34519284.0, 34534880.0, 34563896.0, 34623720.0, 34452040.0, 34501760.0, 34289436.0, 34102164.0, 34146876.0, 33918084.0, 33886240.0, 33774224.0, 33625140.0, 33574368.0, 33387480.0, 33303508.0, 33195294.0, 33030560.0, 32926086.0, 32853062.0, 32793650.0, 32764236.0, 32693808.0, 32635580.0, 32590254.0, 32567152.0, 32705322.0, 32613326.0, 32610814.0, 32676314.0, 32564762.0, 32590186.0, 32465998.0, 32398008.0, 32458644.0, 32346022.0, 32200392.0, 32081072.0, 31974116.0, 31883154.0, 31762528.0, 31613754.0, 31392360.0], "var_stat": [272001248.0, 94793536.0, 243428896.0, 290309088.0, 637701760.0, 336307072.0, 301866720.0, 305879168.0, 399924352.0, 637701760.0, 435734816.0, 376857024.0, 429138816.0, 453332832.0, 381586304.0, 484897120.0, 637701760.0, 487575968.0, 277610784.0, 473961248.0, 359646656.0, 474523328.0, 377121184.0, 486117824.0, 377040512.0, 502208736.0, 403991680.0, 507742560.0, 432004512.0, 495375488.0, 471045696.0, 469675360.0, 497274720.0, 456176672.0, 499777376.0, 454574560.0, 457307136.0, 483500736.0, 445531520.0, 452447296.0, 457813120.0, 438017440.0, 434063200.0, 448154080.0, 428310304.0, 428972224.0, 439074464.0, 424764320.0, 431150144.0, 427725120.0, 430921920.0, 424025312.0, 426546304.0, 429184832.0, 425736928.0, 419302016.0, 424773664.0, 419875488.0, 427891680.0, 431239648.0, 432819488.0, 433514496.0, 433968800.0, 434929376.0, 436460768.0, 438922048.0, 443221952.0, 449724736.0, 456049792.0, 459802528.0, 463549376.0, 469183936.0, 474003584.0, 477739808.0, 480394752.0, 481895008.0, 488445952.0, 491934816.0, 488405504.0, 490087968.0, 489980800.0, 500284928.0, 495156320.0, 491229760.0, 496754208.0, 503509888.0, 491718784.0, 500877728.0, 503081248.0, 494274336.0, 512897952.0, 497379520.0, 516031520.0, 506352192.0, 523052768.0, 513294720.0, 529277536.0, 521843712.0, 527096864.0, 530821984.0, 526553824.0, 527675936.0, 530968416.0, 527220224.0, 529501184.0, 530768864.0, 532748704.0, 531494624.0, 529949632.0, 530147264.0, 529992288.0, 529933504.0, 529808640.0, 529262784.0, 528516896.0, 527792320.0, 529182784.0, 529615936.0, 529410560.0, 529778816.0, 530344224.0, 531732640.0, 525803968.0, 526245088.0, 522054464.0, 517174528.0, 518361600.0, 511909600.0, 510847392.0, 507652000.0, 503427968.0, 501848896.0, 496581568.0, 493911488.0, 490921440.0, 486737472.0, 483823584.0, 481722496.0, 480019008.0, 478763264.0, 476989056.0, 475359136.0, 473979872.0, 473168672.0, 476439520.0, 474125792.0, 473999968.0, 475610112.0, 472580128.0, 473176800.0, 469737056.0, 467798176.0, 468517056.0, 465801504.0, 462322720.0, 459281952.0, 456547808.0, 454174240.0, 450991168.0, 447175136.0, 441819584.0], "frame_num": 2454662} \ No newline at end of file diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/data/preprocess.yaml b/models/speech/speech_recognition/deepspeech2/ixrt/data/preprocess.yaml new file mode 100644 index 00000000..3f526e0a --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/data/preprocess.yaml @@ -0,0 +1,25 @@ +process: + # extract kaldi fbank from PCM + - type: fbank_kaldi + fs: 16000 + n_mels: 161 + n_shift: 160 + win_length: 400 + dither: 0.1 + - type: cmvn_json + cmvn_path: data/mean_std.json + # these three processes are a.k.a. SpecAugument + - type: time_warp + max_time_warp: 5 + inplace: true + mode: PIL + - type: freq_mask + F: 30 + n_mask: 2 + inplace: true + replace_with_zero: false + - type: time_mask + T: 40 + n_mask: 2 + inplace: true + replace_with_zero: false diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/dataset/__init__.py b/models/speech/speech_recognition/deepspeech2/ixrt/dataset/__init__.py new file mode 100644 index 00000000..bf3a39f6 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/dataset/__init__.py @@ -0,0 +1,2 @@ + +from .librispeech import LibriSpeech diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/dataset/librispeech.py b/models/speech/speech_recognition/deepspeech2/ixrt/dataset/librispeech.py new file mode 100644 index 00000000..1b0a88a6 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/dataset/librispeech.py @@ -0,0 +1,70 @@ +""" +Define the dataset of LibriSpeech +""" + +import os +import glob +import soundfile +import numpy as np + + +class LibriSpeech: + def __init__(self, dataroot, transform=None): + assert os.path.exists(dataroot), f"The {dataroot} must be existed!" + self.dataroot = dataroot + self._parse_file() + self.transform = transform + + def _parse_file(self): + """ + Parse the test-clean data and groundtruth + """ + audio_names = [] + text_transcripts = [] + text_pattern = os.path.join(self.dataroot, "test-clean", "*", "*", "*.trans.txt") + text_files = glob.glob(text_pattern) + print(f"[INFO]: text files length: {len(text_files)}") + + for text_file in text_files: + # print(f"Processing: {os.path.basename(text_file)}") + + with open(text_file, 'r') as f: + lines = f.readlines() + + lines = [line.strip().split(' ', maxsplit=1) for line in lines] + for line in lines: + audio_name, text = line + audio_names.append(audio_name) + text_transcripts.append(text) + + self.audio_names = audio_names + self.text_transcripts = text_transcripts + print("[INFO]: Achieve Parsing!") + + def __len__(self): + return len(self.audio_names) + + def __getitem__(self, idx): + + audio_name = self.audio_names[idx] + text_gt = self.text_transcripts[idx] + + # print(f"audio_name: {audio_name}") + # print(f"text_gt: {text_gt}") + + name, subname, _ = audio_name.split('-') + audio_file = os.path.join(self.dataroot, "test-clean", name, subname, audio_name + ".flac") + + audio, sample_rate = soundfile.read(audio_file, dtype="int16", always_2d=True) + audio = audio[:, 0] + # print(f"audio shape: {audio.shape}") + + if self.transform is None: + input_data = audio + else: + preprocess_args = {"train": False} + input_data = self.transform(audio, **preprocess_args) + + input_data = np.expand_dims(input_data.astype(np.float32), axis=0) + return input_data, text_gt + diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/__init__.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/__init__.py new file mode 100644 index 00000000..1f235fe7 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/__init__.py @@ -0,0 +1,2 @@ + +from .ctc import CTCDecoder diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/align.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/align.py new file mode 100644 index 00000000..34d79614 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/align.py @@ -0,0 +1,162 @@ +# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import math + +import paddle +from paddle import nn +""" + To align the initializer between paddle and torch, + the API below are set defalut initializer with priority higger than global initializer. +""" +global_init_type = None + + +class LayerNorm(nn.LayerNorm): + def __init__(self, + normalized_shape, + epsilon=1e-05, + weight_attr=None, + bias_attr=None, + name=None): + if weight_attr is None: + weight_attr = paddle.ParamAttr( + initializer=nn.initializer.Constant(1.0)) + if bias_attr is None: + bias_attr = paddle.ParamAttr( + initializer=nn.initializer.Constant(0.0)) + super(LayerNorm, self).__init__(normalized_shape, epsilon, weight_attr, + bias_attr, name) + + +class BatchNorm1D(nn.BatchNorm1D): + def __init__(self, + num_features, + momentum=0.9, + epsilon=1e-05, + weight_attr=None, + bias_attr=None, + data_format='NCL', + name=None): + if weight_attr is None: + weight_attr = paddle.ParamAttr( + initializer=nn.initializer.Constant(1.0)) + if bias_attr is None: + bias_attr = paddle.ParamAttr( + initializer=nn.initializer.Constant(0.0)) + super(BatchNorm1D, + self).__init__(num_features, momentum, epsilon, weight_attr, + bias_attr, data_format, name) + + +class Embedding(nn.Embedding): + def __init__(self, + num_embeddings, + embedding_dim, + padding_idx=None, + sparse=False, + weight_attr=None, + name=None): + if weight_attr is None: + weight_attr = paddle.ParamAttr(initializer=nn.initializer.Normal()) + super(Embedding, self).__init__(num_embeddings, embedding_dim, + padding_idx, sparse, weight_attr, name) + + +class Linear(nn.Linear): + def __init__(self, + in_features, + out_features, + weight_attr=None, + bias_attr=None, + name=None): + if weight_attr is None: + if global_init_type == "kaiming_uniform": + weight_attr = paddle.ParamAttr( + initializer=nn.initializer.KaimingUniform( + fan_in=None, + negative_slope=math.sqrt(5), + nonlinearity='leaky_relu')) + if bias_attr is None: + if global_init_type == "kaiming_uniform": + bias_attr = paddle.ParamAttr( + initializer=nn.initializer.KaimingUniform( + fan_in=None, + negative_slope=math.sqrt(5), + nonlinearity='leaky_relu')) + super(Linear, self).__init__(in_features, out_features, weight_attr, + bias_attr, name) + + +class Conv1D(nn.Conv1D): + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + padding_mode='zeros', + weight_attr=None, + bias_attr=None, + data_format='NCL'): + if weight_attr is None: + if global_init_type == "kaiming_uniform": + weight_attr = paddle.ParamAttr( + initializer=nn.initializer.KaimingUniform( + fan_in=None, + negative_slope=math.sqrt(5), + nonlinearity='leaky_relu')) + if bias_attr is None: + if global_init_type == "kaiming_uniform": + bias_attr = paddle.ParamAttr( + initializer=nn.initializer.KaimingUniform( + fan_in=None, + negative_slope=math.sqrt(5), + nonlinearity='leaky_relu')) + super(Conv1D, self).__init__( + in_channels, out_channels, kernel_size, stride, padding, dilation, + groups, padding_mode, weight_attr, bias_attr, data_format) + + +class Conv2D(nn.Conv2D): + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + padding_mode='zeros', + weight_attr=None, + bias_attr=None, + data_format='NCHW'): + if weight_attr is None: + if global_init_type == "kaiming_uniform": + weight_attr = paddle.ParamAttr( + initializer=nn.initializer.KaimingUniform( + fan_in=None, + negative_slope=math.sqrt(5), + nonlinearity='leaky_relu')) + if bias_attr is None: + if global_init_type == "kaiming_uniform": + bias_attr = paddle.ParamAttr( + initializer=nn.initializer.KaimingUniform( + fan_in=None, + negative_slope=math.sqrt(5), + nonlinearity='leaky_relu')) + super(Conv2D, self).__init__( + in_channels, out_channels, kernel_size, stride, padding, dilation, + groups, padding_mode, weight_attr, bias_attr, data_format) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctc.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctc.py new file mode 100644 index 00000000..1069bec8 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctc.py @@ -0,0 +1,443 @@ +import sys +from typing import Union + +import paddle +from paddle import nn +from paddle.nn import functional as F + +from .align import Linear +from .loss import CTCLoss + +from . import ctc_utils +from .ctcdecoder import ctc_beam_search_decoding_batch # noqa: F401 +from .ctcdecoder import ctc_greedy_decoding # noqa: F401 +from .ctcdecoder import Scorer # noqa: F401 +from .ctcdecoder import CTCBeamSearchDecoder # noqa: F401 + + +__all__ = ['CTCDecoder'] + + +class CTCDecoderBase(nn.Layer): + def __init__(self, + odim, + enc_n_units, + blank_id=0, + dropout_rate: float=0.0, + reduction: Union[str, bool]=True, + batch_average: bool=True, + grad_norm_type: Union[str, None]=None): + """CTC decoder + + Args: + odim ([int]): text vocabulary size + enc_n_units ([int]): encoder output dimention + dropout_rate (float): dropout rate (0.0 ~ 1.0) + reduction (bool): reduce the CTC loss into a scalar, True for 'sum' or 'none' + batch_average (bool): do batch dim wise average. + grad_norm_type (str): Default, None. one of 'instance', 'batch', 'frame', None. + """ + super().__init__() + + self.blank_id = blank_id + self.odim = odim + self.dropout = nn.Dropout(dropout_rate) + self.ctc_lo = Linear(enc_n_units, self.odim) + if isinstance(reduction, bool): + reduction_type = "sum" if reduction else "none" + else: + reduction_type = reduction + self.criterion = CTCLoss( + blank=self.blank_id, + reduction=reduction_type, + batch_average=batch_average, + grad_norm_type=grad_norm_type) + + def forward(self, hs_pad, hlens, ys_pad, ys_lens): + """Calculate CTC loss. + + Args: + hs_pad (Tensor): batch of padded hidden state sequences (B, Tmax, D) + hlens (Tensor): batch of lengths of hidden state sequences (B) + ys_pad (Tensor): batch of padded character id sequence tensor (B, Lmax) + ys_lens (Tensor): batch of lengths of character sequence (B) + Returns: + loss (Tensor): ctc loss value, scalar. + """ + logits = self.ctc_lo(self.dropout(hs_pad)) + loss = self.criterion(logits, ys_pad, hlens, ys_lens) + return loss + + def softmax(self, eouts: paddle.Tensor, temperature: float=1.0): + """Get CTC probabilities. + Args: + eouts (FloatTensor): `[B, T, enc_units]` + Returns: + probs (FloatTensor): `[B, T, odim]` + """ + self.probs = F.softmax(self.ctc_lo(eouts) / temperature, axis=2) + return self.probs + + def log_softmax(self, hs_pad: paddle.Tensor, + temperature: float=1.0) -> paddle.Tensor: + """log_softmax of frame activations + Args: + Tensor hs_pad: 3d tensor (B, Tmax, eprojs) + Returns: + paddle.Tensor: log softmax applied 3d tensor (B, Tmax, odim) + """ + return F.log_softmax(self.ctc_lo(hs_pad) / temperature, axis=2) + + def argmax(self, hs_pad: paddle.Tensor) -> paddle.Tensor: + """argmax of frame activations + Args: + paddle.Tensor hs_pad: 3d tensor (B, Tmax, eprojs) + Returns: + paddle.Tensor: argmax applied 2d tensor (B, Tmax) + """ + return paddle.argmax(self.ctc_lo(hs_pad), dim=2) + + def forced_align(self, + ctc_probs: paddle.Tensor, + y: paddle.Tensor, + blank_id=0) -> list: + """ctc forced alignment. + Args: + ctc_probs (paddle.Tensor): hidden state sequence, 2d tensor (T, D) + y (paddle.Tensor): label id sequence tensor, 1d tensor (L) + blank_id (int): blank symbol index + Returns: + paddle.Tensor: best alignment result, (T). + """ + return ctc_utils.forced_align(ctc_probs, y, blank_id) + + +class CTCDecoder(CTCDecoderBase): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # CTCDecoder LM Score handle + self._ext_scorer = None + self.beam_search_decoder = None + + def _decode_batch_greedy_offline(self, probs_split, vocab_list): + """This function will be deprecated in future. + Decode by best path for a batch of probs matrix input. + :param probs_split: List of 2-D probability matrix, and each consists + of prob vectors for one speech utterancce. + :param probs_split: List of matrix + :param vocab_list: List of tokens in the vocabulary, for decoding. + :type vocab_list: list + :return: List of transcription texts. + :rtype: List of str + """ + results = [] + for i, probs in enumerate(probs_split): + output_transcription = ctc_greedy_decoding( + probs_seq=probs, vocabulary=vocab_list, blank_id=self.blank_id) + results.append(output_transcription) + return results + + def _init_ext_scorer(self, beam_alpha, beam_beta, language_model_path, + vocab_list): + """Initialize the external scorer. + :param beam_alpha: Parameter associated with language model. + :type beam_alpha: float + :param beam_beta: Parameter associated with word count. + :type beam_beta: float + :param language_model_path: Filepath for language model. If it is + empty, the external scorer will be set to + None, and the decoding method will be pure + beam search without scorer. + :type language_model_path: str|None + :param vocab_list: List of tokens in the vocabulary, for decoding. + :type vocab_list: list + """ + # init once + if self._ext_scorer is not None: + return + + if language_model_path != '': + print("begin to initialize the external scorer " + "for decoding") + self._ext_scorer = Scorer(beam_alpha, beam_beta, + language_model_path, vocab_list) + lm_char_based = self._ext_scorer.is_character_based() + lm_max_order = self._ext_scorer.get_max_order() + lm_dict_size = self._ext_scorer.get_dict_size() + print("language model: " + "is_character_based = %d," % lm_char_based + + " max_order = %d," % lm_max_order + " dict_size = %d" % + lm_dict_size) + print("end initializing scorer") + else: + self._ext_scorer = None + print("no language model provided, " + "decoding by pure beam search without scorer.") + + def _decode_batch_beam_search_offline( + self, probs_split, beam_alpha, beam_beta, beam_size, cutoff_prob, + cutoff_top_n, vocab_list, num_processes): + """ + This function will be deprecated in future. + Decode by beam search for a batch of probs matrix input. + :param probs_split: List of 2-D probability matrix, and each consists + of prob vectors for one speech utterancce. + :param probs_split: List of matrix + :param beam_alpha: Parameter associated with language model. + :type beam_alpha: float + :param beam_beta: Parameter associated with word count. + :type beam_beta: float + :param beam_size: Width for Beam search. + :type beam_size: int + :param cutoff_prob: Cutoff probability in pruning, + default 1.0, no pruning. + :type cutoff_prob: float + :param cutoff_top_n: Cutoff number in pruning, only top cutoff_top_n + characters with highest probs in vocabulary will be + used in beam search, default 40. + :type cutoff_top_n: int + :param vocab_list: List of tokens in the vocabulary, for decoding. + :type vocab_list: list + :param num_processes: Number of processes (CPU) for decoder. + :type num_processes: int + :return: List of transcription texts. + :rtype: List of str + """ + if self._ext_scorer is not None: + self._ext_scorer.reset_params(beam_alpha, beam_beta) + + # beam search decode + num_processes = min(num_processes, len(probs_split)) + beam_search_results = ctc_beam_search_decoding_batch( + probs_split=probs_split, + vocabulary=vocab_list, + beam_size=beam_size, + num_processes=num_processes, + ext_scoring_func=self._ext_scorer, + cutoff_prob=cutoff_prob, + cutoff_top_n=cutoff_top_n, + blank_id=self.blank_id) + + results = [result[0][1] for result in beam_search_results] + return results + + def init_decoder(self, batch_size, vocab_list, decoding_method, + lang_model_path, beam_alpha, beam_beta, beam_size, + cutoff_prob, cutoff_top_n, num_processes): + """ + init ctc decoders + Args: + batch_size(int): Batch size for input data + vocab_list (list): List of tokens in the vocabulary, for decoding + decoding_method (str): ctc_beam_search + lang_model_path (str): language model path + beam_alpha (float): beam_alpha + beam_beta (float): beam_beta + beam_size (int): beam_size + cutoff_prob (float): cutoff probability in beam search + cutoff_top_n (int): cutoff_top_n + num_processes (int): num_processes + + Raises: + ValueError: when decoding_method not support. + + Returns: + CTCBeamSearchDecoder + """ + self.batch_size = batch_size + self.vocab_list = vocab_list + self.decoding_method = decoding_method + self.beam_size = beam_size + self.cutoff_prob = cutoff_prob + self.cutoff_top_n = cutoff_top_n + self.num_processes = num_processes + if decoding_method == "ctc_beam_search": + self._init_ext_scorer(beam_alpha, beam_beta, lang_model_path, + vocab_list) + if self.beam_search_decoder is None: + self.beam_search_decoder = self.get_decoder( + vocab_list, batch_size, beam_alpha, beam_beta, beam_size, + num_processes, cutoff_prob, cutoff_top_n) + return self.beam_search_decoder + elif decoding_method == "ctc_greedy": + self._init_ext_scorer(beam_alpha, beam_beta, lang_model_path, + vocab_list) + else: + raise ValueError(f"Not support: {decoding_method}") + + def decode_probs_offline(self, probs, logits_lens, vocab_list, + decoding_method, lang_model_path, beam_alpha, + beam_beta, beam_size, cutoff_prob, cutoff_top_n, + num_processes): + """ + This function will be deprecated in future. + ctc decoding with probs. + Args: + probs (Tensor): activation after softmax + logits_lens (Tensor): audio output lens + vocab_list (list): List of tokens in the vocabulary, for decoding + decoding_method (str): ctc_beam_search + lang_model_path (str): language model path + beam_alpha (float): beam_alpha + beam_beta (float): beam_beta + beam_size (int): beam_size + cutoff_prob (float): cutoff probability in beam search + cutoff_top_n (int): cutoff_top_n + num_processes (int): num_processes + + Raises: + ValueError: when decoding_method not support. + + Returns: + List[str]: transcripts. + """ + probs_split = [probs[i, :l, :] for i, l in enumerate(logits_lens)] + if decoding_method == "ctc_greedy": + result_transcripts = self._decode_batch_greedy_offline( + probs_split=probs_split, vocab_list=vocab_list) + elif decoding_method == "ctc_beam_search": + result_transcripts = self._decode_batch_beam_search_offline( + probs_split=probs_split, + beam_alpha=beam_alpha, + beam_beta=beam_beta, + beam_size=beam_size, + cutoff_prob=cutoff_prob, + cutoff_top_n=cutoff_top_n, + vocab_list=vocab_list, + num_processes=num_processes) + else: + raise ValueError(f"Not support: {decoding_method}") + return result_transcripts + + def get_decoder(self, vocab_list, batch_size, beam_alpha, beam_beta, + beam_size, num_processes, cutoff_prob, cutoff_top_n): + """ + init get ctc decoder + Args: + vocab_list (list): List of tokens in the vocabulary, for decoding. + batch_size(int): Batch size for input data + beam_alpha (float): beam_alpha + beam_beta (float): beam_beta + beam_size (int): beam_size + num_processes (int): num_processes + cutoff_prob (float): cutoff probability in beam search + cutoff_top_n (int): cutoff_top_n + + Raises: + ValueError: when decoding_method not support. + + Returns: + CTCBeamSearchDecoder + """ + num_processes = min(num_processes, batch_size) + if self._ext_scorer is not None: + self._ext_scorer.reset_params(beam_alpha, beam_beta) + if self.decoding_method == "ctc_beam_search": + beam_search_decoder = CTCBeamSearchDecoder( + vocab_list, batch_size, beam_size, num_processes, cutoff_prob, + cutoff_top_n, self._ext_scorer, self.blank_id) + else: + raise ValueError(f"Not support: {decoding_method}") + return beam_search_decoder + + def next(self, probs, logits_lens): + """ + Input probs into ctc decoder + Args: + probs (list(list(float))): probs for a batch of data + logits_lens (list(int)): logits lens for a batch of data + Raises: + Exception: when the ctc decoder is not initialized + ValueError: when decoding_method not support. + """ + + if self.beam_search_decoder is None: + raise Exception( + "You need to initialize the beam_search_decoder firstly") + beam_search_decoder = self.beam_search_decoder + + has_value = (logits_lens > 0).tolist() + has_value = [ + "true" if has_value[i] is True else "false" + for i in range(len(has_value)) + ] + probs_split = [ + probs[i, :l, :].tolist() if has_value[i] else probs[i].tolist() + for i, l in enumerate(logits_lens) + ] + if self.decoding_method == "ctc_beam_search": + beam_search_decoder.next(probs_split, has_value) + else: + raise ValueError(f"Not support: {decoding_method}") + + return + + def decode(self): + """ + Get the decoding result + Raises: + Exception: when the ctc decoder is not initialized + ValueError: when decoding_method not support. + Returns: + results_best (list(str)): The best result for a batch of data + results_beam (list(list(str))): The beam search result for a batch of data + """ + if self.beam_search_decoder is None: + raise Exception( + "You need to initialize the beam_search_decoder firstly") + + beam_search_decoder = self.beam_search_decoder + if self.decoding_method == "ctc_beam_search": + batch_beam_results = beam_search_decoder.decode() + batch_beam_results = [[(res[0], res[1]) for res in beam_results] + for beam_results in batch_beam_results] + results_best = [result[0][1] for result in batch_beam_results] + results_beam = [[trans[1] for trans in result] + for result in batch_beam_results] + + else: + raise ValueError(f"Not support: {decoding_method}") + + return results_best, results_beam + + def reset_decoder(self, + batch_size=-1, + beam_size=-1, + num_processes=-1, + cutoff_prob=-1.0, + cutoff_top_n=-1): + if batch_size > 0: + self.batch_size = batch_size + if beam_size > 0: + self.beam_size = beam_size + if num_processes > 0: + self.num_processes = num_processes + if cutoff_prob > 0: + self.cutoff_prob = cutoff_prob + if cutoff_top_n > 0: + self.cutoff_top_n = cutoff_top_n + """ + Reset the decoder state + Args: + batch_size(int): Batch size for input data + beam_size (int): beam_size + num_processes (int): num_processes + cutoff_prob (float): cutoff probability in beam search + cutoff_top_n (int): cutoff_top_n + Raises: + Exception: when the ctc decoder is not initialized + """ + if self.beam_search_decoder is None: + raise Exception( + "You need to initialize the beam_search_decoder firstly") + self.beam_search_decoder.reset_state( + self.batch_size, self.beam_size, self.num_processes, + self.cutoff_prob, self.cutoff_top_n) + + def del_decoder(self): + """ + Delete the decoder + """ + if self.beam_search_decoder is not None: + del self.beam_search_decoder + self.beam_search_decoder = None diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctc_utils.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctc_utils.py new file mode 100644 index 00000000..df60028c --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctc_utils.py @@ -0,0 +1,195 @@ +from pathlib import Path +from typing import List + +import numpy as np +import paddle + +from .utils import text_grid +from .utils import utility + + +__all__ = ["forced_align", "remove_duplicates_and_blank", "insert_blank"] + + +def remove_duplicates_and_blank(hyp: List[int], blank_id=0) -> List[int]: + """ctc alignment to ctc label ids. + + "abaa-acee-" -> "abaace" + + Args: + hyp (List[int]): hypotheses ids, (L) + blank_id (int, optional): blank id. Defaults to 0. + + Returns: + List[int]: remove dupicate ids, then remove blank id. + """ + new_hyp: List[int] = [] + cur = 0 + while cur < len(hyp): + # add non-blank into new_hyp + if hyp[cur] != blank_id: + new_hyp.append(hyp[cur]) + # skip repeat label + prev = cur + while cur < len(hyp) and hyp[cur] == hyp[prev]: + cur += 1 + return new_hyp + + +def insert_blank(label: np.ndarray, blank_id: int=0) -> np.ndarray: + """Insert blank token between every two label token. + + "abcdefg" -> "-a-b-c-d-e-f-g-" + + Args: + label ([np.ndarray]): label ids, List[int], (L). + blank_id (int, optional): blank id. Defaults to 0. + + Returns: + [np.ndarray]: (2L+1). + """ + label = np.expand_dims(label, 1) #[L, 1] + blanks = np.zeros((label.shape[0], 1), dtype=np.int64) + blank_id + label = np.concatenate([blanks, label], axis=1) #[L, 2] + label = label.reshape(-1) #[2L], -l-l-l + label = np.append(label, label[0]) #[2L + 1], -l-l-l- + return label + + +def forced_align(ctc_probs: paddle.Tensor, y: paddle.Tensor, + blank_id=0) -> List[int]: + """ctc forced alignment. + + https://distill.pub/2017/ctc/ + + Args: + ctc_probs (paddle.Tensor): hidden state sequence, 2d tensor (T, D) + y (paddle.Tensor): label id sequence tensor, 1d tensor (L) + blank_id (int): blank symbol index + Returns: + List[int]: best alignment result, (T). + """ + y_insert_blank = insert_blank(y, blank_id) #(2L+1) + + log_alpha = paddle.zeros( + (ctc_probs.shape[0], len(y_insert_blank))) #(T, 2L+1) + log_alpha = log_alpha - float('inf') # log of zero + + # TODO(Hui Zhang): zeros not support paddle.int16 + # self.__setitem_varbase__(item, value) When assign a value to a paddle.Tensor, the data type of the paddle.Tensor not support int16 + state_path = (paddle.zeros( + (ctc_probs.shape[0], len(y_insert_blank)), dtype=paddle.int32) - 1 + ) # state path, Tuple((T, 2L+1)) + + # init start state + # TODO(Hui Zhang): VarBase.__getitem__() not support np.int64 + log_alpha[0, 0] = ctc_probs[0][int(y_insert_blank[0])] # State-b, Sb + log_alpha[0, 1] = ctc_probs[0][int(y_insert_blank[1])] # State-nb, Snb + + for t in range(1, ctc_probs.shape[0]): # T + for s in range(len(y_insert_blank)): # 2L+1 + if y_insert_blank[s] == blank_id or s < 2 or y_insert_blank[ + s] == y_insert_blank[s - 2]: + candidates = paddle.to_tensor( + [log_alpha[t - 1, s], log_alpha[t - 1, s - 1]]) + prev_state = [s, s - 1] + else: + candidates = paddle.to_tensor([ + log_alpha[t - 1, s], + log_alpha[t - 1, s - 1], + log_alpha[t - 1, s - 2], + ]) + prev_state = [s, s - 1, s - 2] + # TODO(Hui Zhang): VarBase.__getitem__() not support np.int64 + log_alpha[t, s] = paddle.max(candidates) + ctc_probs[t][int( + y_insert_blank[s])] + state_path[t, s] = prev_state[paddle.argmax(candidates)] + # TODO(Hui Zhang): zeros not support paddle.int16 + # self.__setitem_varbase__(item, value) When assign a value to a paddle.Tensor, the data type of the paddle.Tensor not support int16 + state_seq = -1 * paddle.ones((ctc_probs.shape[0], 1), dtype=paddle.int32) + + candidates = paddle.to_tensor([ + log_alpha[-1, len(y_insert_blank) - 1], # Sb + log_alpha[-1, len(y_insert_blank) - 2] # Snb + ]) + prev_state = [len(y_insert_blank) - 1, len(y_insert_blank) - 2] + state_seq[-1] = prev_state[paddle.argmax(candidates)] + for t in range(ctc_probs.shape[0] - 2, -1, -1): + state_seq[t] = state_path[t + 1, state_seq[t + 1, 0]] + + output_alignment = [] + for t in range(0, ctc_probs.shape[0]): + output_alignment.append(y_insert_blank[state_seq[t, 0]]) + + return output_alignment + + +def ctc_align(config, model, dataloader, batch_size, stride_ms, token_dict, + result_file): + """ctc alignment. + + Args: + config (cfgNode): config + model (nn.Layer): U2 Model. + dataloader (io.DataLoader): dataloader. + batch_size (int): decoding batchsize. + stride_ms (int): audio feature stride in ms unit. + token_dict (List[str]): vocab list, e.g. ['blank', 'unk', 'a', 'b', '']. + result_file (str): alignment output file, e.g. /path/to/xxx.align. + """ + if batch_size > 1: + print('alignment mode must be running with batch_size == 1') + sys.exit(1) + assert result_file and result_file.endswith('.align') + + model.eval() + # conv subsampling rate + subsample = utility.get_subsample(config) + print(f"Align Total Examples: {len(dataloader.dataset)}") + + with open(result_file, 'w') as fout: + # one example in batch + for i, batch in enumerate(dataloader): + key, feat, feats_length, target, target_length = batch + + # 1. Encoder + encoder_out, encoder_mask = model._forward_encoder( + feat, feats_length) # (B, maxlen, encoder_dim) + maxlen = encoder_out.shape[1] + ctc_probs = model.ctc.log_softmax( + encoder_out) # (1, maxlen, vocab_size) + + # 2. alignment + ctc_probs = ctc_probs.squeeze(0) + target = target.squeeze(0) + alignment = forced_align(ctc_probs, target) + + print(f"align ids: {key[0]} {alignment}") + fout.write('{} {}\n'.format(key[0], alignment)) + + # 3. gen praat + # segment alignment + align_segs = text_grid.segment_alignment(alignment) + print(f"align tokens: {key[0]}, {align_segs}") + + # IntervalTier, List["start end token\n"] + tierformat = text_grid.align_to_tierformat(align_segs, subsample, + token_dict) + + # write tier + align_output_path = Path(result_file).parent / "align" + align_output_path.mkdir(parents=True, exist_ok=True) + tier_path = align_output_path / (key[0] + ".tier") + with tier_path.open('w') as f: + f.writelines(tierformat) + + # write textgrid + textgrid_path = align_output_path / (key[0] + ".TextGrid") + second_per_frame = 1. / (1000. / + stride_ms) # 25ms window, 10ms stride + second_per_example = ( + len(alignment) + 1) * subsample * second_per_frame + text_grid.generate_textgrid( + maxtime=second_per_example, + intervals=tierformat, + output=str(textgrid_path)) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/__init__.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/__init__.py new file mode 100644 index 00000000..37ceae6e --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .swig_wrapper import ctc_beam_search_decoding +from .swig_wrapper import ctc_beam_search_decoding_batch +from .swig_wrapper import ctc_greedy_decoding +from .swig_wrapper import CTCBeamSearchDecoder +from .swig_wrapper import Scorer diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/decoders_deprecated.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/decoders_deprecated.py new file mode 100644 index 00000000..0c391ead --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/decoders_deprecated.py @@ -0,0 +1,235 @@ +"""Contains various CTC decoders.""" +import multiprocessing +from itertools import groupby +from math import log + +import numpy as np + + +def ctc_greedy_decoder(probs_seq, vocabulary): + """CTC greedy (best path) decoder. + + Path consisting of the most probable tokens are further post-processed to + remove consecutive repetitions and all blanks. + + :param probs_seq: 2-D list of probabilities over the vocabulary for each + character. Each element is a list of float probabilities + for one character. + :type probs_seq: list + :param vocabulary: Vocabulary list. + :type vocabulary: list + :return: Decoding result string. + :rtype: baseline + """ + # dimension verification + for probs in probs_seq: + if not len(probs) == len(vocabulary) + 1: + raise ValueError("probs_seq dimension mismatchedd with vocabulary") + # argmax to get the best index for each time step + max_index_list = list(np.array(probs_seq).argmax(axis=1)) + # remove consecutive duplicate indexes + index_list = [index_group[0] for index_group in groupby(max_index_list)] + # remove blank indexes + blank_index = len(vocabulary) + index_list = [index for index in index_list if index != blank_index] + # convert index list to string + return ''.join([vocabulary[index] for index in index_list]) + + +def ctc_beam_search_decoder(probs_seq, + beam_size, + vocabulary, + cutoff_prob=1.0, + cutoff_top_n=40, + ext_scoring_func=None, + nproc=False): + """CTC Beam search decoder. + + It utilizes beam search to approximately select top best decoding + labels and returning results in the descending order. + The implementation is based on Prefix Beam Search + (https://arxiv.org/abs/1408.2873), and the unclear part is + redesigned. Two important modifications: 1) in the iterative computation + of probabilities, the assignment operation is changed to accumulation for + one prefix may comes from different paths; 2) the if condition "if l^+ not + in A_prev then" after probabilities' computation is deprecated for it is + hard to understand and seems unnecessary. + + :param probs_seq: 2-D list of probability distributions over each time + step, with each element being a list of normalized + probabilities over vocabulary and blank. + :type probs_seq: 2-D list + :param beam_size: Width for beam search. + :type beam_size: int + :param vocabulary: Vocabulary list. + :type vocabulary: list + :param cutoff_prob: Cutoff probability in pruning, + default 1.0, no pruning. + :type cutoff_prob: float + :param ext_scoring_func: External scoring function for + partially decoded sentence, e.g. word count + or language model. + :type external_scoring_func: callable + :param nproc: Whether the decoder used in multiprocesses. + :type nproc: bool + :return: List of tuples of log probability and sentence as decoding + results, in descending order of the probability. + :rtype: list + """ + # dimension check + for prob_list in probs_seq: + if not len(prob_list) == len(vocabulary) + 1: + raise ValueError("The shape of prob_seq does not match with the " + "shape of the vocabulary.") + + # blank_id assign + blank_id = len(vocabulary) + + # If the decoder called in the multiprocesses, then use the global scorer + # instantiated in ctc_beam_search_decoder_batch(). + if nproc is True: + global ext_nproc_scorer + ext_scoring_func = ext_nproc_scorer + + # initialize + # prefix_set_prev: the set containing selected prefixes + # probs_b_prev: prefixes' probability ending with blank in previous step + # probs_nb_prev: prefixes' probability ending with non-blank in previous step + prefix_set_prev = {'\t': 1.0} + probs_b_prev, probs_nb_prev = {'\t': 1.0}, {'\t': 0.0} + + # extend prefix in loop + for time_step in range(len(probs_seq)): + # prefix_set_next: the set containing candidate prefixes + # probs_b_cur: prefixes' probability ending with blank in current step + # probs_nb_cur: prefixes' probability ending with non-blank in current step + prefix_set_next, probs_b_cur, probs_nb_cur = {}, {}, {} + + prob_idx = list(enumerate(probs_seq[time_step])) + cutoff_len = len(prob_idx) + # If pruning is enabled + if cutoff_prob < 1.0 or cutoff_top_n < cutoff_len: + prob_idx = sorted(prob_idx, key=lambda asd: asd[1], reverse=True) + cutoff_len, cum_prob = 0, 0.0 + for i in range(len(prob_idx)): + cum_prob += prob_idx[i][1] + cutoff_len += 1 + if cum_prob >= cutoff_prob: + break + cutoff_len = min(cutoff_len, cutoff_top_n) + prob_idx = prob_idx[0:cutoff_len] + + for l in prefix_set_prev: + if l not in prefix_set_next: + probs_b_cur[l], probs_nb_cur[l] = 0.0, 0.0 + + # extend prefix by travering prob_idx + for index in range(cutoff_len): + c, prob_c = prob_idx[index][0], prob_idx[index][1] + + if c == blank_id: + probs_b_cur[l] += prob_c * ( + probs_b_prev[l] + probs_nb_prev[l]) + else: + last_char = l[-1] + new_char = vocabulary[c] + l_plus = l + new_char + if l_plus not in prefix_set_next: + probs_b_cur[l_plus], probs_nb_cur[l_plus] = 0.0, 0.0 + + if new_char == last_char: + probs_nb_cur[l_plus] += prob_c * probs_b_prev[l] + probs_nb_cur[l] += prob_c * probs_nb_prev[l] + elif new_char == ' ': + if (ext_scoring_func is None) or (len(l) == 1): + score = 1.0 + else: + prefix = l[1:] + score = ext_scoring_func(prefix) + probs_nb_cur[l_plus] += score * prob_c * ( + probs_b_prev[l] + probs_nb_prev[l]) + else: + probs_nb_cur[l_plus] += prob_c * ( + probs_b_prev[l] + probs_nb_prev[l]) + # add l_plus into prefix_set_next + prefix_set_next[l_plus] = probs_nb_cur[ + l_plus] + probs_b_cur[l_plus] + # add l into prefix_set_next + prefix_set_next[l] = probs_b_cur[l] + probs_nb_cur[l] + # update probs + probs_b_prev, probs_nb_prev = probs_b_cur, probs_nb_cur + + # store top beam_size prefixes + prefix_set_prev = sorted( + prefix_set_next.items(), key=lambda asd: asd[1], reverse=True) + if beam_size < len(prefix_set_prev): + prefix_set_prev = prefix_set_prev[:beam_size] + prefix_set_prev = dict(prefix_set_prev) + + beam_result = [] + for seq, prob in prefix_set_prev.items(): + if prob > 0.0 and len(seq) > 1: + result = seq[1:] + # score last word by external scorer + if (ext_scoring_func is not None) and (result[-1] != ' '): + prob = prob * ext_scoring_func(result) + log_prob = log(prob) + beam_result.append((log_prob, result)) + else: + beam_result.append((float('-inf'), '')) + + # output top beam_size decoding results + beam_result = sorted(beam_result, key=lambda asd: asd[0], reverse=True) + return beam_result + + +def ctc_beam_search_decoder_batch(probs_split, + beam_size, + vocabulary, + num_processes, + cutoff_prob=1.0, + cutoff_top_n=40, + ext_scoring_func=None): + """CTC beam search decoder using multiple processes. + + :param probs_seq: 3-D list with each element as an instance of 2-D list + of probabilities used by ctc_beam_search_decoder(). + :type probs_seq: 3-D list + :param beam_size: Width for beam search. + :type beam_size: int + :param vocabulary: Vocabulary list. + :type vocabulary: list + :param num_processes: Number of parallel processes. + :type num_processes: int + :param cutoff_prob: Cutoff probability in pruning, + default 1.0, no pruning. + :type cutoff_prob: float + :param num_processes: Number of parallel processes. + :type num_processes: int + :param ext_scoring_func: External scoring function for + partially decoded sentence, e.g. word count + or language model. + :type external_scoring_function: callable + :return: List of tuples of log probability and sentence as decoding + results, in descending order of the probability. + :rtype: list + """ + if not num_processes > 0: + raise ValueError("Number of processes must be positive!") + + # use global variable to pass the externnal scorer to beam search decoder + global ext_nproc_scorer + ext_nproc_scorer = ext_scoring_func + nproc = True + + pool = multiprocessing.Pool(processes=num_processes) + results = [] + for i, probs_list in enumerate(probs_split): + args = (probs_list, beam_size, vocabulary, cutoff_prob, cutoff_top_n, + None, nproc) + results.append(pool.apply_async(ctc_beam_search_decoder, args)) + + pool.close() + pool.join() + beam_search_results = [result.get() for result in results] + return beam_search_results diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/scorer_deprecated.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/scorer_deprecated.py new file mode 100644 index 00000000..362098fe --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/scorer_deprecated.py @@ -0,0 +1,78 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""External Scorer for Beam Search Decoder.""" +import os + +import kenlm +import numpy as np + + +class Scorer(object): + """External scorer to evaluate a prefix or whole sentence in + beam search decoding, including the score from n-gram language + model and word count. + + :param alpha: Parameter associated with language model. Don't use + language model when alpha = 0. + :type alpha: float + :param beta: Parameter associated with word count. Don't use word + count when beta = 0. + :type beta: float + :model_path: Path to load language model. + :type model_path: str + """ + + def __init__(self, alpha, beta, model_path): + self._alpha = alpha + self._beta = beta + if not os.path.isfile(model_path): + raise IOError("Invaid language model path: %s" % model_path) + self._language_model = kenlm.LanguageModel(model_path) + + # n-gram language model scoring + def _language_model_score(self, sentence): + #log10 prob of last word + log_cond_prob = list( + self._language_model.full_scores(sentence, eos=False))[-1][0] + return np.power(10, log_cond_prob) + + # word insertion term + def _word_count(self, sentence): + words = sentence.strip().split(' ') + return len(words) + + # reset alpha and beta + def reset_params(self, alpha, beta): + self._alpha = alpha + self._beta = beta + + # execute evaluation + def __call__(self, sentence, log=False): + """Evaluation function, gathering all the different scores + and return the final one. + + :param sentence: The input sentence for evaluation + :type sentence: str + :param log: Whether return the score in log representation. + :type log: bool + :return: Evaluation score, in the decimal or log. + :rtype: float + """ + lm = self._language_model_score(sentence) + word_cnt = self._word_count(sentence) + if log is False: + score = np.power(lm, self._alpha) * np.power(word_cnt, self._beta) + else: + score = self._alpha * np.log(lm) + self._beta * np.log(word_cnt) + return score diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/swig_wrapper.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/swig_wrapper.py new file mode 100644 index 00000000..e4eb43a5 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/swig_wrapper.py @@ -0,0 +1,146 @@ +"""Wrapper for various CTC decoders in SWIG.""" +import paddlespeech_ctcdecoders + + +class Scorer(paddlespeech_ctcdecoders.Scorer): + """Wrapper for Scorer. + + :param alpha: Parameter associated with language model. Don't use + language model when alpha = 0. + :type alpha: float + :param beta: Parameter associated with word count. Don't use word + count when beta = 0. + :type beta: float + :model_path: Path to load language model. + :type model_path: str + :param vocabulary: Vocabulary list. + :type vocabulary: list + """ + + def __init__(self, alpha, beta, model_path, vocabulary): + paddlespeech_ctcdecoders.Scorer.__init__(self, alpha, beta, model_path, + vocabulary) + + +def ctc_greedy_decoding(probs_seq, vocabulary, blank_id): + """Wrapper for ctc best path decodeing function in swig. + + :param probs_seq: 2-D list of probability distributions over each time + step, with each element being a list of normalized + probabilities over vocabulary and blank. + :type probs_seq: 2-D list + :param vocabulary: Vocabulary list. + :type vocabulary: list + :return: Decoding result string. + :rtype: str + """ + result = paddlespeech_ctcdecoders.ctc_greedy_decoding(probs_seq.tolist(), + vocabulary, blank_id) + return result + + +def ctc_beam_search_decoding(probs_seq, + vocabulary, + beam_size, + cutoff_prob=1.0, + cutoff_top_n=40, + ext_scoring_func=None, + blank_id=0): + """Wrapper for the CTC Beam Search Decoding function. + + :param probs_seq: 2-D list of probability distributions over each time + step, with each element being a list of normalized + probabilities over vocabulary and blank. + :type probs_seq: 2-D list + :param vocabulary: Vocabulary list. + :type vocabulary: list + :param beam_size: Width for beam search. + :type beam_size: int + :param cutoff_prob: Cutoff probability in pruning, + default 1.0, no pruning. + :type cutoff_prob: float + :param cutoff_top_n: Cutoff number in pruning, only top cutoff_top_n + characters with highest probs in vocabulary will be + used in beam search, default 40. + :type cutoff_top_n: int + :param ext_scoring_func: External scoring function for + partially decoded sentence, e.g. word count + or language model. + :type external_scoring_func: callable + :return: List of tuples of log probability and sentence as decoding + results, in descending order of the probability. + :rtype: list + """ + beam_results = paddlespeech_ctcdecoders.ctc_beam_search_decoding( + probs_seq.tolist(), vocabulary, beam_size, cutoff_prob, cutoff_top_n, + ext_scoring_func, blank_id) + beam_results = [(res[0], res[1].decode('utf-8')) for res in beam_results] + return beam_results + + +def ctc_beam_search_decoding_batch(probs_split, + vocabulary, + beam_size, + num_processes, + cutoff_prob=1.0, + cutoff_top_n=40, + ext_scoring_func=None, + blank_id=0): + """Wrapper for the batched CTC beam search decodeing batch function. + + :param probs_seq: 3-D list with each element as an instance of 2-D list + of probabilities used by ctc_beam_search_decoder(). + :type probs_seq: 3-D list + :param vocabulary: Vocabulary list. + :type vocabulary: list + :param beam_size: Width for beam search. + :type beam_size: int + :param num_processes: Number of parallel processes. + :type num_processes: int + :param cutoff_prob: Cutoff probability in vocabulary pruning, + default 1.0, no pruning. + :type cutoff_prob: float + :param cutoff_top_n: Cutoff number in pruning, only top cutoff_top_n + characters with highest probs in vocabulary will be + used in beam search, default 40. + :type cutoff_top_n: int + :param num_processes: Number of parallel processes. + :type num_processes: int + :param ext_scoring_func: External scoring function for + partially decoded sentence, e.g. word count + or language model. + :type external_scoring_function: callable + :return: List of tuples of log probability and sentence as decoding + results, in descending order of the probability. + :rtype: list + """ + probs_split = [probs_seq.tolist() for probs_seq in probs_split] + + batch_beam_results = paddlespeech_ctcdecoders.ctc_beam_search_decoding_batch( + probs_split, vocabulary, beam_size, num_processes, cutoff_prob, + cutoff_top_n, ext_scoring_func, blank_id) + batch_beam_results = [[(res[0], res[1]) for res in beam_results] + for beam_results in batch_beam_results] + return batch_beam_results + + +class CTCBeamSearchDecoder(paddlespeech_ctcdecoders.CtcBeamSearchDecoderBatch): + """Wrapper for CtcBeamSearchDecoderBatch. + Args: + vocab_list (list): Vocabulary list. + beam_size (int): Width for beam search. + num_processes (int): Number of parallel processes. + param cutoff_prob (float): Cutoff probability in vocabulary pruning, + default 1.0, no pruning. + cutoff_top_n (int): Cutoff number in pruning, only top cutoff_top_n + characters with highest probs in vocabulary will be + used in beam search, default 40. + param ext_scorer (Scorer): External scorer for partially decoded sentence, e.g. word count + or language model. + """ + + def __init__(self, vocab_list, batch_size, beam_size, num_processes, + cutoff_prob, cutoff_top_n, _ext_scorer, blank_id): + paddlespeech_ctcdecoders.CtcBeamSearchDecoderBatch.__init__( + self, vocab_list, batch_size, beam_size, num_processes, cutoff_prob, + cutoff_top_n, _ext_scorer, blank_id) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/test_decoders.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/test_decoders.py new file mode 100644 index 00000000..dc344b76 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/ctcdecoder/test_decoders.py @@ -0,0 +1,87 @@ +"""Test decoders.""" +import unittest + +import decoders_deprecated as decoder + + +class TestDecoders(unittest.TestCase): + def setUp(self): + self.vocab_list = ["\'", ' ', 'a', 'b', 'c', 'd'] + self.beam_size = 20 + self.probs_seq1 = [[ + 0.06390443, 0.21124858, 0.27323887, 0.06870235, 0.0361254, + 0.18184413, 0.16493624 + ], [ + 0.03309247, 0.22866108, 0.24390638, 0.09699597, 0.31895462, + 0.0094893, 0.06890021 + ], [ + 0.218104, 0.19992557, 0.18245131, 0.08503348, 0.14903535, + 0.08424043, 0.08120984 + ], [ + 0.12094152, 0.19162472, 0.01473646, 0.28045061, 0.24246305, + 0.05206269, 0.09772094 + ], [ + 0.1333387, 0.00550838, 0.00301669, 0.21745861, 0.20803985, + 0.41317442, 0.01946335 + ], [ + 0.16468227, 0.1980699, 0.1906545, 0.18963251, 0.19860937, + 0.04377724, 0.01457421 + ]] + self.probs_seq2 = [[ + 0.08034842, 0.22671944, 0.05799633, 0.36814645, 0.11307441, + 0.04468023, 0.10903471 + ], [ + 0.09742457, 0.12959763, 0.09435383, 0.21889204, 0.15113123, + 0.10219457, 0.20640612 + ], [ + 0.45033529, 0.09091417, 0.15333208, 0.07939558, 0.08649316, + 0.12298585, 0.01654384 + ], [ + 0.02512238, 0.22079203, 0.19664364, 0.11906379, 0.07816055, + 0.22538587, 0.13483174 + ], [ + 0.17928453, 0.06065261, 0.41153005, 0.1172041, 0.11880313, + 0.07113197, 0.04139363 + ], [ + 0.15882358, 0.1235788, 0.23376776, 0.20510435, 0.00279306, + 0.05294827, 0.22298418 + ]] + self.greedy_result = ["ac'bdc", "b'da"] + self.beam_search_result = ['acdc', "b'a"] + + def test_greedy_decoder_1(self): + bst_result = decoder.ctc_greedy_decoder(self.probs_seq1, + self.vocab_list) + self.assertEqual(bst_result, self.greedy_result[0]) + + def test_greedy_decoder_2(self): + bst_result = decoder.ctc_greedy_decoder(self.probs_seq2, + self.vocab_list) + self.assertEqual(bst_result, self.greedy_result[1]) + + def test_beam_search_decoder_1(self): + beam_result = decoder.ctc_beam_search_decoder( + probs_seq=self.probs_seq1, + beam_size=self.beam_size, + vocabulary=self.vocab_list) + self.assertEqual(beam_result[0][1], self.beam_search_result[0]) + + def test_beam_search_decoder_2(self): + beam_result = decoder.ctc_beam_search_decoder( + probs_seq=self.probs_seq2, + beam_size=self.beam_size, + vocabulary=self.vocab_list) + self.assertEqual(beam_result[0][1], self.beam_search_result[1]) + + def test_beam_search_decoder_batch(self): + beam_results = decoder.ctc_beam_search_decoder_batch( + probs_split=[self.probs_seq1, self.probs_seq2], + beam_size=self.beam_size, + vocabulary=self.vocab_list, + num_processes=24) + self.assertEqual(beam_results[0][0][1], self.beam_search_result[0]) + self.assertEqual(beam_results[1][0][1], self.beam_search_result[1]) + + +if __name__ == '__main__': + unittest.main() diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/loss.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/loss.py new file mode 100644 index 00000000..d082135a --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/loss.py @@ -0,0 +1,166 @@ +import inspect + +import paddle +from paddle import nn +from paddle.nn import functional as F + + +__all__ = ['CTCLoss', "LabelSmoothingLoss"] + + +class CTCLoss(nn.Layer): + def __init__(self, + blank=0, + reduction='sum', + batch_average=False, + grad_norm_type=None): + super().__init__() + # last token id as blank id + self.loss = nn.CTCLoss(blank=blank, reduction=reduction) + self.batch_average = batch_average + + print(f"CTCLoss Loss reduction: {reduction}, div-bs: {batch_average}") + print(f"CTCLoss Grad Norm Type: {grad_norm_type}") + + assert grad_norm_type in ('instance', 'batch', 'frame', None) + self.norm_by_times = False + self.norm_by_batchsize = False + self.norm_by_total_logits_len = False + if grad_norm_type is None: + # no grad norm + pass + elif grad_norm_type == 'instance': + self.norm_by_times = True + elif grad_norm_type == 'batch': + self.norm_by_batchsize = True + elif grad_norm_type == 'frame': + self.norm_by_total_logits_len = True + else: + raise ValueError(f"CTCLoss Grad Norm no support {grad_norm_type}") + kwargs = { + "norm_by_times": self.norm_by_times, + "norm_by_batchsize": self.norm_by_batchsize, + "norm_by_total_logits_len": self.norm_by_total_logits_len, + } + + # Derive only the args which the func has + try: + param = inspect.signature(self.loss.forward).parameters + except ValueError: + # Some function, e.g. built-in function, are failed + param = {} + self._kwargs = {k: v for k, v in kwargs.items() if k in param} + _notin = {k: v for k, v in kwargs.items() if k not in param} + print(f"{self.loss} kwargs:{self._kwargs}, not support: {_notin}") + + def forward(self, logits, ys_pad, hlens, ys_lens): + """Compute CTC loss. + + Args: + logits ([paddle.Tensor]): [B, Tmax, D] + ys_pad ([paddle.Tensor]): [B, Tmax] + hlens ([paddle.Tensor]): [B] + ys_lens ([paddle.Tensor]): [B] + + Returns: + [paddle.Tensor]: scalar. If reduction is 'none', then (N), where N = \text{batch size}. + """ + B = logits.shape[0] + # warp-ctc need logits, and do softmax on logits by itself + # warp-ctc need activation with shape [T, B, V + 1] + # logits: (B, L, D) -> (L, B, D) + logits = logits.transpose([1, 0, 2]) + ys_pad = ys_pad.astype(paddle.int32) + loss = self.loss(logits, ys_pad, hlens, ys_lens, **self._kwargs) + if self.batch_average: + # Batch-size average + loss = loss / B + return loss + + +class LabelSmoothingLoss(nn.Layer): + """Label-smoothing loss. + In a standard CE loss, the label's data distribution is: + [0,1,2] -> + [ + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 1.0], + ] + In the smoothing version CE Loss,some probabilities + are taken from the true label prob (1.0) and are divided + among other labels. + e.g. + smoothing=0.1 + [0,1,2] -> + [ + [0.9, 0.05, 0.05], + [0.05, 0.9, 0.05], + [0.05, 0.05, 0.9], + ] + + """ + + def __init__(self, + size: int, + padding_idx: int, + smoothing: float, + normalize_length: bool=False): + """Label-smoothing loss. + + Args: + size (int): the number of class + padding_idx (int): padding class id which will be ignored for loss + smoothing (float): smoothing rate (0.0 means the conventional CE) + normalize_length (bool): + True, normalize loss by sequence length; + False, normalize loss by batch size. + Defaults to False. + """ + super().__init__() + self.size = size + self.padding_idx = padding_idx + self.smoothing = smoothing + self.confidence = 1.0 - smoothing + self.normalize_length = normalize_length + self.criterion = nn.KLDivLoss(reduction="none") + + def forward(self, x: paddle.Tensor, target: paddle.Tensor) -> paddle.Tensor: + """Compute loss between x and target. + The model outputs and data labels tensors are flatten to + (batch*seqlen, class) shape and a mask is applied to the + padding part which should not be calculated for loss. + + Args: + x (paddle.Tensor): prediction (batch, seqlen, class) + target (paddle.Tensor): + target signal masked with self.padding_id (batch, seqlen) + Returns: + loss (paddle.Tensor) : The KL loss, scalar float value + """ + B, T, D = x.shape + assert D == self.size + x = x.reshape((-1, self.size)) + target = target.reshape([-1]) + + # use zeros_like instead of torch.no_grad() for true_dist, + # since no_grad() can not be exported by JIT + true_dist = paddle.full_like(x, self.smoothing / (self.size - 1)) + ignore = target == self.padding_idx # (B,) + + #TODO(Hui Zhang): target = target * (1 - ignore) # avoid -1 index + target = target.masked_fill(ignore, 0) # avoid -1 index + # true_dist.scatter_(1, target.unsqueeze(1), self.confidence) + target_mask = F.one_hot(target, self.size) + true_dist *= (1 - target_mask) + true_dist += target_mask * self.confidence + + kl = self.criterion(F.log_softmax(x, axis=1), true_dist) + + #TODO(Hui Zhang): sum not support bool type + #total = len(target) - int(ignore.sum()) + total = len(target) - int(ignore.type_as(target).sum()) + denom = total if self.normalize_length else B + #numer = (kl * (1 - ignore)).sum() + numer = kl.masked_fill(ignore.unsqueeze(1), 0).sum() + return numer / denom diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/utils/text_grid.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/utils/text_grid.py new file mode 100644 index 00000000..4865249c --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/utils/text_grid.py @@ -0,0 +1,114 @@ +from typing import Dict +from typing import List +from typing import Text + +import textgrid + + +def segment_alignment(alignment: List[int], blank_id=0) -> List[List[int]]: + """segment ctc alignment ids by continuous blank and repeat label. + + Args: + alignment (List[int]): ctc alignment id sequence. + e.g. [0, 0, 0, 1, 1, 1, 2, 0, 0, 3] + blank_id (int, optional): blank id. Defaults to 0. + + Returns: + List[List[int]]: token align, segment aligment id sequence. + e.g. [[0, 0, 0, 1, 1, 1], [2], [0, 0, 3]] + """ + # convert alignment to a praat format, which is a doing phonetics + # by computer and helps analyzing alignment + align_segs = [] + # get frames level duration for each token + start = 0 + end = 0 + while end < len(alignment): + while end < len(alignment) and alignment[end] == blank_id: # blank + end += 1 + if end == len(alignment): + align_segs[-1].extend(alignment[start:]) + break + end += 1 + while end < len(alignment) and alignment[end - 1] == alignment[ + end]: # repeat label + end += 1 + align_segs.append(alignment[start:end]) + start = end + return align_segs + + +def align_to_tierformat(align_segs: List[List[int]], + subsample: int, + token_dict: Dict[int, Text], + blank_id=0) -> List[Text]: + """Generate textgrid.Interval format from alignment segmentations. + + Args: + align_segs (List[List[int]]): segmented ctc alignment ids. + subsample (int): 25ms frame_length, 10ms hop_length, 1/subsample + token_dict (Dict[int, Text]): int -> str map. + + Returns: + List[Text]: list of textgrid.Interval text, str(start, end, text). + """ + hop_length = 10 # ms + second_ms = 1000 # ms + frame_per_second = second_ms / hop_length # 25ms frame_length, 10ms hop_length + second_per_frame = 1.0 / frame_per_second + + begin = 0 + duration = 0 + tierformat = [] + + for idx, tokens in enumerate(align_segs): + token_len = len(tokens) + token = tokens[-1] + # time duration in second + duration = token_len * subsample * second_per_frame + if idx < len(align_segs) - 1: + print(f"{begin:.2f} {begin + duration:.2f} {token_dict[token]}") + tierformat.append( + f"{begin:.2f} {begin + duration:.2f} {token_dict[token]}\n") + else: + for i in tokens: + if i != blank_id: + token = i + break + print(f"{begin:.2f} {begin + duration:.2f} {token_dict[token]}") + tierformat.append( + f"{begin:.2f} {begin + duration:.2f} {token_dict[token]}\n") + begin = begin + duration + + return tierformat + + +def generate_textgrid(maxtime: float, + intervals: List[Text], + output: Text, + name: Text='ali') -> None: + """Create alignment textgrid file. + + Args: + maxtime (float): audio duartion. + intervals (List[Text]): ctc output alignment. e.g. "start-time end-time word" per item. + output (Text): textgrid filepath. + name (Text, optional): tier or layer name. Defaults to 'ali'. + """ + # Download Praat: https://www.fon.hum.uva.nl/praat/ + avg_interval = maxtime / (len(intervals) + 1) + print(f"average second/token: {avg_interval}") + margin = 0.0001 + + tg = textgrid.TextGrid(maxTime=maxtime) + tier = textgrid.IntervalTier(name=name, maxTime=maxtime) + + i = 0 + for dur in intervals: + s, e, text = dur.split() + tier.add(minTime=float(s) + margin, maxTime=float(e), mark=text) + + tg.append(tier) + + tg.write(output) + print("successfully generator textgrid {}.".format(output)) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/decoder/utils/utility.py b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/utils/utility.py new file mode 100644 index 00000000..29e75873 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/decoder/utils/utility.py @@ -0,0 +1,79 @@ +"""Contains common utility functions.""" +import math +import os +import random +import sys +from contextlib import contextmanager +from pprint import pformat +from typing import List + +import distutils.util +import numpy as np +import paddle +import soundfile + + +__all__ = ["all_version", "UpdateConfig", "seed_all", "log_add"] + + +def all_version(): + vers = { + "python": sys.version, + "paddle": paddle.__version__, + "paddle_commit": paddle.version.commit, + "soundfile": soundfile.__version__, + } + print(f"Deps Module Version:{pformat(list(vers.items()))}") + + +@contextmanager +def UpdateConfig(config): + """Update yacs config""" + config.defrost() + yield + config.freeze() + + +def seed_all(seed: int=20210329): + """freeze random generator seed.""" + np.random.seed(seed) + random.seed(seed) + paddle.seed(seed) + + +def log_add(args: List[int]) -> float: + """Stable log add + + Args: + args (List[int]): log scores + + Returns: + float: sum of log scores + """ + if all(a == -float('inf') for a in args): + return -float('inf') + a_max = max(args) + lsp = math.log(sum(math.exp(a - a_max) for a in args)) + return a_max + lsp + + +def get_subsample(config): + """Subsample rate from config. + + Args: + config (yacs.config.CfgNode): yaml config + + Returns: + int: subsample rate. + """ + if config['encoder'] == 'squeezeformer': + return 4 + else: + input_layer = config["encoder_conf"]["input_layer"] + assert input_layer in ["conv2d", "conv2d6", "conv2d8"] + if input_layer == "conv2d": + return 4 + elif input_layer == "conv2d6": + return 6 + elif input_layer == "conv2d8": + return 8 diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/inference.py b/models/speech/speech_recognition/deepspeech2/ixrt/inference.py new file mode 100644 index 00000000..ac4e4ddb --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/inference.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +import time +import glob +import json +import random +import argparse +import numpy as np +from tqdm import tqdm + +import torch +import paddle +import tensorrt +from tensorrt import Dims +from cuda import cuda, cudart + +from transform import Transformation +from dataset import LibriSpeech +from decoder import CTCDecoder + +from utils import VOCABLIST as vocab_list +from utils.error_rate import wer +from utils import deepspeech2_trtapi_ixrt, setup_io_bindings +from load_ixrt_plugin import load_ixrt_plugin + +load_ixrt_plugin() + +def parse_config(): + parser = argparse.ArgumentParser(description="The DeepSpeech2 network Inference on LibriSpeech dataset.") + parser.add_argument( + "--model_type", + type=str, + default="DeepSpeech2", + help="The speech recognition model(DeepSpeech2)", + ) + parser.add_argument( + "--preprocess_config", + type=str, + default="data/preprocess.yaml", + help="The preprocess input file", + ) + parser.add_argument( + "--engine_file", + type=str, + default="../../../../../data/checkpoints/deepspeech2/deepspeech2.engine", + help="engine file path" + ) + parser.add_argument( + "--decoder_file", + type=str, + default="../../../../../data/checkpoints/deepspeech2/decoder.pdparams", + help="ctcdecoder checkpoints file" + ) + parser.add_argument( + "--lang_model_path", + type=str, + default="../../../../../data/checkpoints/deepspeech2/lm/common_crawl_00.prune01111.trie.klm", + help="The language model path" + ) + # dataset + parser.add_argument( + '--dataroot', + default="../../../../../data/datasets/LibriSpeech", + help='location to download dataset(s)' + ) + parser.add_argument("--bsz", type=int, default=1, help="Dynamic input") + parser.add_argument("--device", type=int, default=0, help="cuda device, i.e. 0 or 0,1,2,3,4") + parser.add_argument("--use_async", action="store_true") + parser.add_argument("--wer_target", type=float, default=-1.0) + parser.add_argument("--test_num_samples", type=int, default=-1) + + config = parser.parse_args() + return config + + +def test_result(data, engine, context, decoder, test_num_samples): + + input_name = "input" + output_name = "output" + + data_len = len(data) + wer_sum = 0.0 + + if test_num_samples != -1: + data_len = test_num_samples + + for i in tqdm(range(data_len), desc="Testing WER"): + + start_time = time.time() + audio, text = data[i] + audio_shape = audio.shape + # print(f"audio_shape: {audio_shape}") + + # Set the input shape + input_idx = engine.get_binding_index(input_name) + context.set_binding_shape(input_idx, Dims(audio_shape)) + + inputs, outputs, allocations = setup_io_bindings(engine, context) + pred_output = np.zeros(outputs[0]["shape"], outputs[0]["dtype"]) + err, = cuda.cuMemcpyHtoD(inputs[0]["allocation"], audio, audio.nbytes) + assert(err == cuda.CUresult.CUDA_SUCCESS) + + if config.use_async: + stream = cuda.Stream() + context.execute_async_v2(allocations, stream.handle) + stream.synchronize() + else: + context.execute_v2(allocations) + + err, = cuda.cuMemcpyDtoH(pred_output, outputs[0]["allocation"], outputs[0]["nbytes"]) + assert(err == cuda.CUresult.CUDA_SUCCESS) + + eouts = paddle.to_tensor(pred_output) + eouts_len = paddle.to_tensor([eouts.shape[1]]) + probs = decoder.softmax(eouts) + batch_size = probs.shape[0] + + decoder.init_decoder( + batch_size, + vocab_list, + "ctc_beam_search", + config.lang_model_path, + 1.9, + 0.3, + 500, + 1.0, + 40, + 8 + ) + decoder.reset_decoder(batch_size=batch_size) + decoder.next(probs, eouts_len) + trans_best, trans_beam = decoder.decode() + # print(f"result_transcripts: {trans_best}") + # print(f"text: {text}") + cur_wer = wer(text, trans_best[0], True) + print(f"wer: {cur_wer}") + wer_sum += cur_wer + + wer_avg = wer_sum / data_len + print(f"wer_avg: {wer_avg}") + metricResult = {"metricResult": {}} + metricResult["metricResult"]["wer_avg"] = round(wer_avg, 3) + print(metricResult) + return wer_avg + + +def main(config): + + # Step1:build dataset + preprocessing = Transformation(config.preprocess_config) + dataset = LibriSpeech(config.dataroot, preprocessing) + + # Step2: load engine + engine, context = deepspeech2_trtapi_ixrt(config.engine_file) + + # Step3: load decoder + decoder = CTCDecoder( + odim=31, + enc_n_units=2048, + blank_id=0, + dropout_rate=0.0, + reduction=True, + batch_average=True, + grad_norm_type=None + ) + decoder_state_dict = paddle.load(config.decoder_file) + decoder.set_state_dict(decoder_state_dict) + + # Step4: run test + wer = test_result(dataset, engine, context, decoder, config.test_num_samples) + status = 'Pass' if wer <= config.wer_target else 'Fail' + + print("="*30) + print(f"\nCheck AUC: Test : {wer} Target:{config.wer_target} State : {status}") + print("="*30) + + +if __name__ == "__main__": + config = parse_config() + main(config) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/inference_demo.py b/models/speech/speech_recognition/deepspeech2/ixrt/inference_demo.py new file mode 100644 index 00000000..3385eadc --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/inference_demo.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +import time +import glob +import json +import random +import argparse +import soundfile +import numpy as np +from tqdm import tqdm + +import torch +import paddle +import tensorrt +from tensorrt import Dims +from cuda import cuda, cudart + +from transform import Transformation +from decoder import CTCDecoder + +from utils import VOCABLIST as vocab_list +from utils import deepspeech2_trtapi_ixrt, setup_io_bindings + +from load_ixrt_plugin import load_ixrt_plugin + +load_ixrt_plugin() + +def parse_config(): + parser = argparse.ArgumentParser(description="The DeepSpeech2 network Inference demo and performance.") + parser.add_argument( + "--model_type", + type=str, + default="DeepSpeech2", + help="The speech recognition model(DeepSpeech2)", + ) + parser.add_argument( + "--audio_file", + type=str, + default="data/demo_002_en.wav", + help="The input speech wave", + ) + parser.add_argument( + "--preprocess_config", + type=str, + default="data/preprocess.yaml", + help="The preprocess input file", + ) + parser.add_argument( + "--engine_file", + type=str, + default="../../../../../data/checkpoints/deepspeech2/deepspeech2.engine", + help="engine file path" + ) + parser.add_argument( + "--decoder_file", + type=str, + default="../../../../../data/checkpoints/deepspeech2/decoder.pdparams", + help="ctcdecoder checkpoints file" + ) + parser.add_argument( + "--lang_model_path", + type=str, + default="../../../../../data/checkpoints/deepspeech2/lm/common_crawl_00.prune01111.trie.klm", + help="The language model path" + ) + parser.add_argument("--bsz", type=int, default=1, help="Dynamic input") + parser.add_argument("--device", type=int, default=0, help="cuda device, i.e. 0 or 0,1,2,3,4") + parser.add_argument("--use_async", action="store_true") + parser.add_argument("--run_loop", type=int, default=-1) + parser.add_argument("--warm_up", type=int, default=-1) + parser.add_argument("--throughput_target", type=float, default=-1.0) + + config = parser.parse_args() + return config + + +def main(config): + # Step1: Load the input wave + assert os.path.isfile(config.audio_file), "The input audio file must be existed!" + audio, sample_rate = soundfile.read(config.audio_file, dtype="int16", always_2d=True) + audio = audio[:, 0] + print(f"audio shape: {audio.shape}") + + # fbank + preprocess_args = {"train": False} + preprocessing = Transformation(config.preprocess_config) + input_data = preprocessing(audio, **preprocess_args) + input_data = np.expand_dims(input_data.astype(np.float32), axis=0) + print(f"feat shape: {input_data.shape}") + + # Step2: Load the engine + engine, context = deepspeech2_trtapi_ixrt(config.engine_file) + + input_shape = input_data.shape + print("input shape: ", input_shape) + + input_idx = engine.get_binding_index("input") + context.set_binding_shape(input_idx, Dims(input_shape)) + + inputs, outputs, allocations = setup_io_bindings(engine, context) + pred_output = np.zeros(outputs[0]["shape"], outputs[0]["dtype"]) + + err, = cuda.cuMemcpyHtoD(inputs[0]["allocation"], input_data, input_data.nbytes) + assert(err == cuda.CUresult.CUDA_SUCCESS) + + print("\n Warm Up Start.") + for i in range(config.warm_up): context.execute_v2(allocations) + print("Warm Up Done.") + + run_times = [] + for i in range(config.run_loop): + start_time = time.time() + context.execute_v2(allocations) + end_time = time.time() + run_times.append(end_time - start_time) + + run_times.remove(max(run_times)) + run_times.remove(min(run_times)) + + avg_time = sum(run_times) / len(run_times) + throughput = pred_output.shape[1] / avg_time + print(f"Executing {config.run_loop} done, Time: {avg_time}, ThroughPut: {throughput}") + + err, = cuda.cuMemcpyDtoH(pred_output, outputs[0]["allocation"], outputs[0]["nbytes"]) + assert(err == cuda.CUresult.CUDA_SUCCESS) + + # Step3: Load the CTCDecoder + decoder = CTCDecoder( + odim=31, + enc_n_units=2048, + blank_id=0, + dropout_rate=0.0, + reduction=True, + batch_average=True, + grad_norm_type=None + ) + decoder_state_dict = paddle.load(config.decoder_file) + decoder.set_state_dict(decoder_state_dict) + + eouts = paddle.to_tensor(pred_output) + eouts_len = paddle.to_tensor([eouts.shape[1]]) + probs = decoder.softmax(eouts) + batch_size = probs.shape[0] + + decoder.init_decoder( + batch_size, + vocab_list, + "ctc_beam_search", + config.lang_model_path, + 1.9, + 0.3, + 500, + 1.0, + 40, + 8 + ) + + decoder.reset_decoder(batch_size=batch_size) + decoder.next(probs, eouts_len) + trans_best, trans_beam = decoder.decode() + print("result_transcripts: ", trans_best) + + status = 'Pass' if throughput >= config.throughput_target else 'Fail' + + print("="*30) + print(f"\nCheck ThroughPut: Test : {throughput} Target:{config.throughput_target} State : {status}") + print("="*30) + + metricResult = {"metricResult": {}} + metricResult["metricResult"]["ThroughPut"] = round(throughput, 3) + print(metricResult) + + +if __name__ == "__main__": + config = parse_config() + main(config) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/load_ixrt_plugin.py b/models/speech/speech_recognition/deepspeech2/ixrt/load_ixrt_plugin.py new file mode 100644 index 00000000..b40f6910 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/load_ixrt_plugin.py @@ -0,0 +1,13 @@ +from os.path import join, dirname, exists +import tensorrt as trt +import ctypes + +def load_ixrt_plugin(logger=trt.Logger(trt.Logger.WARNING), namespace="", dynamic_path=""): + if not dynamic_path: + dynamic_path = join(dirname(trt.__file__), "lib", "libixrt_plugin.so") + if not exists(dynamic_path): + raise FileNotFoundError( + f"The ixrt_plugin lib {dynamic_path} is not existed, please provided effective plugin path!") + ctypes.CDLL(dynamic_path, mode=ctypes.RTLD_GLOBAL) + trt.init_libnvinfer_plugins(logger, namespace) + print(f"Loaded plugin from {dynamic_path}") \ No newline at end of file diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/modify_model_to_dynamic.py b/models/speech/speech_recognition/deepspeech2/ixrt/modify_model_to_dynamic.py new file mode 100644 index 00000000..b168d3c4 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/modify_model_to_dynamic.py @@ -0,0 +1,28 @@ +import onnx +from onnx import helper +import argparse + +def modify_to_dynamic(arg): + model = onnx.load(args.static_onnx) + + graph = model.graph + for input_node in graph.input: + if input_node.name == 'input': + input_shape = input_node.type.tensor_type.shape.dim + input_shape[0].dim_value = 1 + input_shape[1].dim_param = 'None' + input_shape[2].dim_value = 161 + + onnx.save(model, args.dynamic_onnx) + onnx.checker.check_model(model, full_check=True) + +def parse_args(): + parser = argparse.ArgumentParser(description="modify static shape to dynamic for deepspeech2") + parser.add_argument("--static_onnx", type=str, required=True, help="The input static onnx path") + parser.add_argument("--dynamic_onnx", type=str, required=True, help="The ouput dynamic onnx path") + args = parser.parse_args() + return args + +if __name__ == "__main__": + args = parse_args() + modify_to_dynamic(args) \ No newline at end of file diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/scripts/infer_deepspeech2_fp16_accuracy.sh b/models/speech/speech_recognition/deepspeech2/ixrt/scripts/infer_deepspeech2_fp16_accuracy.sh new file mode 100644 index 00000000..1d10f6f3 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/scripts/infer_deepspeech2_fp16_accuracy.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +DATASETS_DIR="${DATASETS_DIR:-/path/to/LibriSpeech}" +CHECKPOINTS_DIR="${CHECKPOINTS_DIR:-./checkpoints}" +RUN_DIR="${RUN_DIR:-.}" + +TGT=-1 +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --tgt) TGT=${arguments[index]};; + esac +done + +cd ${RUN_DIR} +python3 inference.py \ + --model_type "deepspeech2" \ + --engine_file "${CHECKPOINTS_DIR}/deepspeech2.engine" \ + --decoder_file "data/decoder.pdparams" \ + --lang_model_path "${CHECKPOINTS_DIR}/common_crawl_00.prune01111.trie.klm" \ + --dataroot "${DATASETS_DIR}" \ + --wer_target ${TGT} \ + --test_num_samples 500 \ No newline at end of file diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/scripts/infer_deepspeech2_fp16_performance.sh b/models/speech/speech_recognition/deepspeech2/ixrt/scripts/infer_deepspeech2_fp16_performance.sh new file mode 100644 index 00000000..e3adad2a --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/scripts/infer_deepspeech2_fp16_performance.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright (c) 2026, 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. + +DATASETS_DIR="${DATASETS_DIR:-/path/to/LibriSpeech}" +CHECKPOINTS_DIR="${CHECKPOINTS_DIR:-./checkpoints}" +RUN_DIR="${RUN_DIR:-.}" + +TGT=-1 +index=0 +options=$@ +arguments=($options) +for argument in $options +do + index=`expr $index + 1` + case $argument in + --tgt) TGT=${arguments[index]};; + esac +done + +cd ${RUN_DIR} +python3 inference_demo.py \ + --model_type "deepspeech2" \ + --engine_file "${CHECKPOINTS_DIR}/deepspeech2.engine" \ + --decoder_file "data/decoder.pdparams" \ + --lang_model_path "${CHECKPOINTS_DIR}/common_crawl_00.prune01111.trie.klm" \ + --run_loop 12 \ + --warm_up 5 \ + --throughput_target ${TGT} \ No newline at end of file diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/transform/__init__.py b/models/speech/speech_recognition/deepspeech2/ixrt/transform/__init__.py new file mode 100644 index 00000000..cbf4f9ce --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/transform/__init__.py @@ -0,0 +1,2 @@ +from .transformation import Transformation + diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/transform/cmvn.py b/models/speech/speech_recognition/deepspeech2/ixrt/transform/cmvn.py new file mode 100644 index 00000000..ab75807b --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/transform/cmvn.py @@ -0,0 +1,187 @@ +import io +import json + +import h5py +import kaldiio +import numpy as np + + +class CMVN(): + "Apply Global/Spk CMVN/iverserCMVN." + + def __init__( + self, + stats, + norm_means=True, + norm_vars=False, + filetype="mat", + utt2spk=None, + spk2utt=None, + reverse=False, + std_floor=1.0e-20, ): + self.stats_file = stats + self.norm_means = norm_means + self.norm_vars = norm_vars + self.reverse = reverse + + if isinstance(stats, dict): + stats_dict = dict(stats) + else: + # Use for global CMVN + if filetype == "mat": + stats_dict = {None: kaldiio.load_mat(stats)} + # Use for global CMVN + elif filetype == "npy": + stats_dict = {None: np.load(stats)} + # Use for speaker CMVN + elif filetype == "ark": + self.accept_uttid = True + stats_dict = dict(kaldiio.load_ark(stats)) + # Use for speaker CMVN + elif filetype == "hdf5": + self.accept_uttid = True + stats_dict = h5py.File(stats) + else: + raise ValueError("Not supporting filetype={}".format(filetype)) + + if utt2spk is not None: + self.utt2spk = {} + with io.open(utt2spk, "r", encoding="utf-8") as f: + for line in f: + utt, spk = line.rstrip().split(None, 1) + self.utt2spk[utt] = spk + elif spk2utt is not None: + self.utt2spk = {} + with io.open(spk2utt, "r", encoding="utf-8") as f: + for line in f: + spk, utts = line.rstrip().split(None, 1) + for utt in utts.split(): + self.utt2spk[utt] = spk + else: + self.utt2spk = None + + # Kaldi makes a matrix for CMVN which has a shape of (2, feat_dim + 1), + # and the first vector contains the sum of feats and the second is + # the sum of squares. The last value of the first, i.e. stats[0,-1], + # is the number of samples for this statistics. + self.bias = {} + self.scale = {} + for spk, stats in stats_dict.items(): + assert len(stats) == 2, stats.shape + + count = stats[0, -1] + + # If the feature has two or more dimensions + if not (np.isscalar(count) or isinstance(count, (int, float))): + # The first is only used + count = count.flatten()[0] + + mean = stats[0, :-1] / count + # V(x) = E(x^2) - (E(x))^2 + var = stats[1, :-1] / count - mean * mean + std = np.maximum(np.sqrt(var), std_floor) + self.bias[spk] = -mean + self.scale[spk] = 1 / std + + def __repr__(self): + return ("{name}(stats_file={stats_file}, " + "norm_means={norm_means}, norm_vars={norm_vars}, " + "reverse={reverse})".format( + name=self.__class__.__name__, + stats_file=self.stats_file, + norm_means=self.norm_means, + norm_vars=self.norm_vars, + reverse=self.reverse, )) + + def __call__(self, x, uttid=None): + if self.utt2spk is not None: + spk = self.utt2spk[uttid] + else: + spk = uttid + + if not self.reverse: + # apply cmvn + if self.norm_means: + x = np.add(x, self.bias[spk]) + if self.norm_vars: + x = np.multiply(x, self.scale[spk]) + + else: + # apply reverse cmvn + if self.norm_vars: + x = np.divide(x, self.scale[spk]) + if self.norm_means: + x = np.subtract(x, self.bias[spk]) + + return x + + +class UtteranceCMVN(): + "Apply Utterance CMVN" + + def __init__(self, norm_means=True, norm_vars=False, std_floor=1.0e-20): + self.norm_means = norm_means + self.norm_vars = norm_vars + self.std_floor = std_floor + + def __repr__(self): + return "{name}(norm_means={norm_means}, norm_vars={norm_vars})".format( + name=self.__class__.__name__, + norm_means=self.norm_means, + norm_vars=self.norm_vars, ) + + def __call__(self, x, uttid=None): + # x: [Time, Dim] + square_sums = (x**2).sum(axis=0) + mean = x.mean(axis=0) + + if self.norm_means: + x = np.subtract(x, mean) + + if self.norm_vars: + var = square_sums / x.shape[0] - mean**2 + std = np.maximum(np.sqrt(var), self.std_floor) + x = np.divide(x, std) + + return x + + +class GlobalCMVN(): + "Apply Global CMVN" + + def __init__(self, + cmvn_path, + norm_means=True, + norm_vars=True, + std_floor=1.0e-20): + # cmvn_path: Option[str, dict] + cmvn = cmvn_path + self.cmvn = cmvn + self.norm_means = norm_means + self.norm_vars = norm_vars + self.std_floor = std_floor + if isinstance(cmvn, dict): + cmvn_stats = cmvn + else: + with open(cmvn) as f: + cmvn_stats = json.load(f) + self.count = cmvn_stats['frame_num'] + self.mean = np.array(cmvn_stats['mean_stat']) / self.count + self.square_sums = np.array(cmvn_stats['var_stat']) + self.var = self.square_sums / self.count - self.mean**2 + self.std = np.maximum(np.sqrt(self.var), self.std_floor) + + def __repr__(self): + return f"""{self.__class__.__name__}( + cmvn_path={self.cmvn}, + norm_means={self.norm_means}, + norm_vars={self.norm_vars},)""" + + def __call__(self, x, uttid=None): + # x: [Time, Dim] + if self.norm_means: + x = np.subtract(x, self.mean) + + if self.norm_vars: + x = np.divide(x, self.std) + return x diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/transform/functional.py b/models/speech/speech_recognition/deepspeech2/ixrt/transform/functional.py new file mode 100644 index 00000000..688a0bed --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/transform/functional.py @@ -0,0 +1,110 @@ +import inspect + + +def check_kwargs(func, kwargs, name=None): + """check kwargs are valid for func + + If kwargs are invalid, raise TypeError as same as python default + :param function func: function to be validated + :param dict kwargs: keyword arguments for func + :param str name: name used in TypeError (default is func name) + """ + try: + params = inspect.signature(func).parameters + except ValueError: + return + if name is None: + name = func.__name__ + for k in kwargs.keys(): + if k not in params: + raise TypeError( + f"{name}() got an unexpected keyword argument '{k}'") + + +class TransformInterface: + """Transform Interface""" + + def __call__(self, x): + raise NotImplementedError("__call__ method is not implemented") + + @classmethod + def add_arguments(cls, parser): + return parser + + def __repr__(self): + return self.__class__.__name__ + "()" + + +class Identity(TransformInterface): + """Identity Function""" + + def __call__(self, x): + return x + + +class FuncTrans(TransformInterface): + """Functional Transformation + + WARNING: + Builtin or C/C++ functions may not work properly + because this class heavily depends on the `inspect` module. + + Usage: + + >>> def foo_bar(x, a=1, b=2): + ... '''Foo bar + ... :param x: input + ... :param int a: default 1 + ... :param int b: default 2 + ... ''' + ... return x + a - b + + + >>> class FooBar(FuncTrans): + ... _func = foo_bar + ... __doc__ = foo_bar.__doc__ + """ + + _func = None + + def __init__(self, **kwargs): + self.kwargs = kwargs + check_kwargs(self.func, kwargs) + + def __call__(self, x): + return self.func(x, **self.kwargs) + + @classmethod + def add_arguments(cls, parser): + fname = cls._func.__name__.replace("_", "-") + group = parser.add_argument_group(fname + " transformation setting") + for k, v in cls.default_params().items(): + # TODO(karita): get help and choices from docstring? + attr = k.replace("_", "-") + group.add_argument(f"--{fname}-{attr}", default=v, type=type(v)) + return parser + + @property + def func(self): + return type(self)._func + + @classmethod + def default_params(cls): + try: + d = dict(inspect.signature(cls._func).parameters) + except ValueError: + d = dict() + return { + k: v.default + for k, v in d.items() if v.default != inspect.Parameter.empty + } + + def __repr__(self): + params = self.default_params() + params.update(**self.kwargs) + ret = self.__class__.__name__ + "(" + if len(params) == 0: + return ret + ")" + for k, v in params.items(): + ret += "{}={}, ".format(k, v) + return ret[:-2] + ")" diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/transform/spec_augment.py b/models/speech/speech_recognition/deepspeech2/ixrt/transform/spec_augment.py new file mode 100644 index 00000000..e83efa12 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/transform/spec_augment.py @@ -0,0 +1,193 @@ +"""Spec Augment module for preprocessing i.e., data augmentation""" +import random + +import numpy +from PIL import Image + +from .functional import FuncTrans + + +def time_warp(x, max_time_warp=80, inplace=False, mode="PIL"): + """time warp for spec augment + + move random center frame by the random width ~ uniform(-window, window) + :param numpy.ndarray x: spectrogram (time, freq) + :param int max_time_warp: maximum time frames to warp + :param bool inplace: overwrite x with the result + :param str mode: "PIL" (default, fast, not differentiable) or "sparse_image_warp" + (slow, differentiable) + :returns numpy.ndarray: time warped spectrogram (time, freq) + """ + window = max_time_warp + if window == 0: + return x + + if mode == "PIL": + t = x.shape[0] + if t - window <= window: + return x + # NOTE: randrange(a, b) emits a, a + 1, ..., b - 1 + center = random.randrange(window, t - window) + warped = random.randrange(center - window, center + + window) + 1 # 1 ... t - 1 + + left = Image.fromarray(x[:center]).resize((x.shape[1], warped), + Image.BICUBIC) + right = Image.fromarray(x[center:]).resize((x.shape[1], t - warped), + Image.BICUBIC) + if inplace: + x[:warped] = left + x[warped:] = right + return x + return numpy.concatenate((left, right), 0) + else: + raise NotImplementedError("unknown resize mode: " + mode + + ", choose one from (PIL, sparse_image_warp).") + + +class TimeWarp(FuncTrans): + _func = time_warp + __doc__ = time_warp.__doc__ + + def __call__(self, x, train): + if not train: + return x + return super().__call__(x) + + +def freq_mask(x, F=30, n_mask=2, replace_with_zero=True, inplace=False): + """freq mask for spec agument + + :param numpy.ndarray x: (time, freq) + :param int n_mask: the number of masks + :param bool inplace: overwrite + :param bool replace_with_zero: pad zero on mask if true else use mean + """ + if inplace: + cloned = x + else: + cloned = x.copy() + + num_mel_channels = cloned.shape[1] + fs = numpy.random.randint(0, F, size=(n_mask, 2)) + + for f, mask_end in fs: + f_zero = random.randrange(0, num_mel_channels - f) + mask_end += f_zero + + # avoids randrange error if values are equal and range is empty + if f_zero == f_zero + f: + continue + + if replace_with_zero: + cloned[:, f_zero:mask_end] = 0 + else: + cloned[:, f_zero:mask_end] = cloned.mean() + return cloned + + +class FreqMask(FuncTrans): + _func = freq_mask + __doc__ = freq_mask.__doc__ + + def __call__(self, x, train): + if not train: + return x + return super().__call__(x) + + +def time_mask(spec, T=40, n_mask=2, replace_with_zero=True, inplace=False): + """freq mask for spec agument + + :param numpy.ndarray spec: (time, freq) + :param int n_mask: the number of masks + :param bool inplace: overwrite + :param bool replace_with_zero: pad zero on mask if true else use mean + """ + if inplace: + cloned = spec + else: + cloned = spec.copy() + len_spectro = cloned.shape[0] + ts = numpy.random.randint(0, T, size=(n_mask, 2)) + for t, mask_end in ts: + # avoid randint range error + if len_spectro - t <= 0: + continue + t_zero = random.randrange(0, len_spectro - t) + + # avoids randrange error if values are equal and range is empty + if t_zero == t_zero + t: + continue + + mask_end += t_zero + if replace_with_zero: + cloned[t_zero:mask_end] = 0 + else: + cloned[t_zero:mask_end] = cloned.mean() + return cloned + + +class TimeMask(FuncTrans): + _func = time_mask + __doc__ = time_mask.__doc__ + + def __call__(self, x, train): + if not train: + return x + return super().__call__(x) + + +def spec_augment( + x, + resize_mode="PIL", + max_time_warp=80, + max_freq_width=27, + n_freq_mask=2, + max_time_width=100, + n_time_mask=2, + inplace=True, + replace_with_zero=True, ): + """spec agument + + apply random time warping and time/freq masking + default setting is based on LD (Librispeech double) in Table 2 + https://arxiv.org/pdf/1904.08779.pdf + + :param numpy.ndarray x: (time, freq) + :param str resize_mode: "PIL" (fast, nondifferentiable) or "sparse_image_warp" + (slow, differentiable) + :param int max_time_warp: maximum frames to warp the center frame in spectrogram (W) + :param int freq_mask_width: maximum width of the random freq mask (F) + :param int n_freq_mask: the number of the random freq mask (m_F) + :param int time_mask_width: maximum width of the random time mask (T) + :param int n_time_mask: the number of the random time mask (m_T) + :param bool inplace: overwrite intermediate array + :param bool replace_with_zero: pad zero on mask if true else use mean + """ + assert isinstance(x, numpy.ndarray) + assert x.ndim == 2 + x = time_warp(x, max_time_warp, inplace=inplace, mode=resize_mode) + x = freq_mask( + x, + max_freq_width, + n_freq_mask, + inplace=inplace, + replace_with_zero=replace_with_zero, ) + x = time_mask( + x, + max_time_width, + n_time_mask, + inplace=inplace, + replace_with_zero=replace_with_zero, ) + return x + + +class SpecAugment(FuncTrans): + _func = spec_augment + __doc__ = spec_augment.__doc__ + + def __call__(self, x, train): + if not train: + return x + return super().__call__(x) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/transform/spectrogram.py b/models/speech/speech_recognition/deepspeech2/ixrt/transform/spectrogram.py new file mode 100644 index 00000000..0c2f0636 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/transform/spectrogram.py @@ -0,0 +1,505 @@ +import librosa +import numpy as np +# import torch +# from torchaudio.compliance import kaldi +import paddle +from paddleaudio.compliance import kaldi +from python_speech_features import logfbank + + +def stft(x, + n_fft, + n_shift, + win_length=None, + window="hann", + center=True, + pad_mode="reflect"): + # x: [Time, Channel] + if x.ndim == 1: + single_channel = True + # x: [Time] -> [Time, Channel] + x = x[:, None] + else: + single_channel = False + x = x.astype(np.float32) + + # FIXME(kamo): librosa.stft can't use multi-channel? + # x: [Time, Channel, Freq] + x = np.stack( + [ + librosa.stft( + y=x[:, ch], + n_fft=n_fft, + hop_length=n_shift, + win_length=win_length, + window=window, + center=center, + pad_mode=pad_mode, ).T for ch in range(x.shape[1]) + ], + axis=1, ) + + if single_channel: + # x: [Time, Channel, Freq] -> [Time, Freq] + x = x[:, 0] + return x + + +def istft(x, n_shift, win_length=None, window="hann", center=True): + # x: [Time, Channel, Freq] + if x.ndim == 2: + single_channel = True + # x: [Time, Freq] -> [Time, Channel, Freq] + x = x[:, None, :] + else: + single_channel = False + + # x: [Time, Channel] + x = np.stack( + [ + librosa.istft( + stft_matrix=x[:, ch].T, # [Time, Freq] -> [Freq, Time] + hop_length=n_shift, + win_length=win_length, + window=window, + center=center, ) for ch in range(x.shape[1]) + ], + axis=1, ) + + if single_channel: + # x: [Time, Channel] -> [Time] + x = x[:, 0] + return x + + +def stft2logmelspectrogram(x_stft, + fs, + n_mels, + n_fft, + fmin=None, + fmax=None, + eps=1e-10): + # x_stft: (Time, Channel, Freq) or (Time, Freq) + fmin = 0 if fmin is None else fmin + fmax = fs / 2 if fmax is None else fmax + + # spc: (Time, Channel, Freq) or (Time, Freq) + spc = np.abs(x_stft) + # mel_basis: (Mel_freq, Freq) + mel_basis = librosa.filters.mel( + sr=fs, n_fft=n_fft, n_mels=n_mels, fmin=fmin, fmax=fmax) + # lmspc: (Time, Channel, Mel_freq) or (Time, Mel_freq) + lmspc = np.log10(np.maximum(eps, np.dot(spc, mel_basis.T))) + + return lmspc + + +def spectrogram(x, n_fft, n_shift, win_length=None, window="hann"): + # x: (Time, Channel) -> spc: (Time, Channel, Freq) + spc = np.abs(stft(x, n_fft, n_shift, win_length, window=window)) + return spc + + +def logmelspectrogram( + x, + fs, + n_mels, + n_fft, + n_shift, + win_length=None, + window="hann", + fmin=None, + fmax=None, + eps=1e-10, + pad_mode="reflect", ): + # stft: (Time, Channel, Freq) or (Time, Freq) + x_stft = stft( + x, + n_fft=n_fft, + n_shift=n_shift, + win_length=win_length, + window=window, + pad_mode=pad_mode, ) + + return stft2logmelspectrogram( + x_stft, + fs=fs, + n_mels=n_mels, + n_fft=n_fft, + fmin=fmin, + fmax=fmax, + eps=eps) + + +class Spectrogram(): + def __init__(self, n_fft, n_shift, win_length=None, window="hann"): + self.n_fft = n_fft + self.n_shift = n_shift + self.win_length = win_length + self.window = window + + def __repr__(self): + return ("{name}(n_fft={n_fft}, n_shift={n_shift}, " + "win_length={win_length}, window={window})".format( + name=self.__class__.__name__, + n_fft=self.n_fft, + n_shift=self.n_shift, + win_length=self.win_length, + window=self.window, )) + + def __call__(self, x): + return spectrogram( + x, + n_fft=self.n_fft, + n_shift=self.n_shift, + win_length=self.win_length, + window=self.window, ) + + +class LogMelSpectrogram(): + def __init__( + self, + fs, + n_mels, + n_fft, + n_shift, + win_length=None, + window="hann", + fmin=None, + fmax=None, + eps=1e-10, ): + self.fs = fs + self.n_mels = n_mels + self.n_fft = n_fft + self.n_shift = n_shift + self.win_length = win_length + self.window = window + self.fmin = fmin + self.fmax = fmax + self.eps = eps + + def __repr__(self): + return ("{name}(fs={fs}, n_mels={n_mels}, n_fft={n_fft}, " + "n_shift={n_shift}, win_length={win_length}, window={window}, " + "fmin={fmin}, fmax={fmax}, eps={eps}))".format( + name=self.__class__.__name__, + fs=self.fs, + n_mels=self.n_mels, + n_fft=self.n_fft, + n_shift=self.n_shift, + win_length=self.win_length, + window=self.window, + fmin=self.fmin, + fmax=self.fmax, + eps=self.eps, )) + + def __call__(self, x): + return logmelspectrogram( + x, + fs=self.fs, + n_mels=self.n_mels, + n_fft=self.n_fft, + n_shift=self.n_shift, + win_length=self.win_length, + window=self.window, ) + + +class Stft2LogMelSpectrogram(): + def __init__(self, fs, n_mels, n_fft, fmin=None, fmax=None, eps=1e-10): + self.fs = fs + self.n_mels = n_mels + self.n_fft = n_fft + self.fmin = fmin + self.fmax = fmax + self.eps = eps + + def __repr__(self): + return ("{name}(fs={fs}, n_mels={n_mels}, n_fft={n_fft}, " + "fmin={fmin}, fmax={fmax}, eps={eps}))".format( + name=self.__class__.__name__, + fs=self.fs, + n_mels=self.n_mels, + n_fft=self.n_fft, + fmin=self.fmin, + fmax=self.fmax, + eps=self.eps, )) + + def __call__(self, x): + return stft2logmelspectrogram( + x, + fs=self.fs, + n_mels=self.n_mels, + n_fft=self.n_fft, + fmin=self.fmin, + fmax=self.fmax, ) + + +class Stft(): + def __init__( + self, + n_fft, + n_shift, + win_length=None, + window="hann", + center=True, + pad_mode="reflect", ): + self.n_fft = n_fft + self.n_shift = n_shift + self.win_length = win_length + self.window = window + self.center = center + self.pad_mode = pad_mode + + def __repr__(self): + return ("{name}(n_fft={n_fft}, n_shift={n_shift}, " + "win_length={win_length}, window={window}," + "center={center}, pad_mode={pad_mode})".format( + name=self.__class__.__name__, + n_fft=self.n_fft, + n_shift=self.n_shift, + win_length=self.win_length, + window=self.window, + center=self.center, + pad_mode=self.pad_mode, )) + + def __call__(self, x): + return stft( + x, + self.n_fft, + self.n_shift, + win_length=self.win_length, + window=self.window, + center=self.center, + pad_mode=self.pad_mode, ) + + +class IStft(): + def __init__(self, n_shift, win_length=None, window="hann", center=True): + self.n_shift = n_shift + self.win_length = win_length + self.window = window + self.center = center + + def __repr__(self): + return ("{name}(n_shift={n_shift}, " + "win_length={win_length}, window={window}," + "center={center})".format( + name=self.__class__.__name__, + n_shift=self.n_shift, + win_length=self.win_length, + window=self.window, + center=self.center, )) + + def __call__(self, x): + return istft( + x, + self.n_shift, + win_length=self.win_length, + window=self.window, + center=self.center, ) + + +class LogMelSpectrogramKaldi(): + def __init__( + self, + fs=16000, + n_mels=80, + n_shift=160, # unit:sample, 10ms + win_length=400, # unit:sample, 25ms + energy_floor=0.0, + dither=0.1): + """ + The Kaldi implementation of LogMelSpectrogram + Args: + fs (int): sample rate of the audio + n_mels (int): number of mel filter banks + n_shift (int): number of points in a frame shift + win_length (int): number of points in a frame windows + energy_floor (float): Floor on energy in Spectrogram computation (absolute) + dither (float): Dithering constant + + Returns: + LogMelSpectrogramKaldi + """ + + self.fs = fs + self.n_mels = n_mels + num_point_ms = fs / 1000 + self.n_frame_length = win_length / num_point_ms + self.n_frame_shift = n_shift / num_point_ms + self.energy_floor = energy_floor + self.dither = dither + + def __repr__(self): + return ( + "{name}(fs={fs}, n_mels={n_mels}, " + "n_frame_shift={n_frame_shift}, n_frame_length={n_frame_length}, " + "dither={dither}))".format( + name=self.__class__.__name__, + fs=self.fs, + n_mels=self.n_mels, + n_frame_shift=self.n_frame_shift, + n_frame_length=self.n_frame_length, + dither=self.dither, )) + + def __call__(self, x, train): + """ + Args: + x (np.ndarray): shape (Ti,) + train (bool): True, train mode. + + Raises: + ValueError: not support (Ti, C) + + Returns: + np.ndarray: (T, D) + """ + dither = self.dither if train else 0.0 + if x.ndim != 1: + raise ValueError("Not support x: [Time, Channel]") + + + # torchaudio + """ + waveform = torch.from_numpy(np.expand_dims(x, 0)).type(torch.float32) + mat = kaldi.fbank( + waveform, + num_mel_bins=self.n_mels, + frame_length=self.n_frame_length, + frame_shift=self.n_frame_shift, + dither=dither, + energy_floor=self.energy_floor, + sample_frequency=self.fs) + """ + # paddlespeech + waveform = paddle.to_tensor(np.expand_dims(x, 0), dtype=paddle.float32) + mat = kaldi.fbank( + waveform, + n_mels=self.n_mels, + frame_length=self.n_frame_length, + frame_shift=self.n_frame_shift, + dither=dither, + energy_floor=self.energy_floor, + sr=self.fs) + mat = np.squeeze(mat.numpy()) + return mat + + +class WavProcess(): + def __init__(self): + """ + Args: + dither (float): Dithering constant + + Returns: + """ + + def __call__(self, x): + """ + Args: + x (np.ndarray): shape (Ti,) + train (bool): True, train mode. + + Raises: + ValueError: not support (Ti, C) + + Returns: + np.ndarray: (T, D) + """ + if x.ndim != 1: + raise ValueError("Not support x: [Time, Channel]") + waveform = x.astype("float32") / 32768.0 + waveform = np.expand_dims(waveform, -1) + return waveform + + +class LogMelSpectrogramKaldi_decay(): + def __init__( + self, + fs=16000, + n_mels=80, + n_fft=512, # fft point + n_shift=160, # unit:sample, 10ms + win_length=400, # unit:sample, 25ms + window="povey", + fmin=20, + fmax=None, + eps=1e-10, + dither=1.0): + self.fs = fs + self.n_mels = n_mels + self.n_fft = n_fft + if n_shift > win_length: + raise ValueError("Stride size must not be greater than " + "window size.") + self.n_shift = n_shift / fs # unit: ms + self.win_length = win_length / fs # unit: ms + + self.window = window + self.fmin = fmin + if fmax is None: + fmax_ = fmax if fmax else self.fs / 2 + elif fmax > int(self.fs / 2): + raise ValueError("fmax must not be greater than half of " + "sample rate.") + self.fmax = fmax_ + + self.eps = eps + self.remove_dc_offset = True + self.preemph = 0.97 + self.dither = dither # only work in train mode + + def __repr__(self): + return ( + "{name}(fs={fs}, n_mels={n_mels}, n_fft={n_fft}, " + "n_shift={n_shift}, win_length={win_length}, preemph={preemph}, window={window}, " + "fmin={fmin}, fmax={fmax}, eps={eps}, dither={dither}))".format( + name=self.__class__.__name__, + fs=self.fs, + n_mels=self.n_mels, + n_fft=self.n_fft, + n_shift=self.n_shift, + preemph=self.preemph, + win_length=self.win_length, + window=self.window, + fmin=self.fmin, + fmax=self.fmax, + eps=self.eps, + dither=self.dither, )) + + def __call__(self, x, train): + """ + + Args: + x (np.ndarray): shape (Ti,) + train (bool): True, train mode. + + Raises: + ValueError: not support (Ti, C) + + Returns: + np.ndarray: (T, D) + """ + dither = self.dither if train else 0.0 + if x.ndim != 1: + raise ValueError("Not support x: [Time, Channel]") + + if x.dtype in np.sctypes['float']: + # PCM32 -> PCM16 + bits = np.iinfo(np.int16).bits + x = x * 2**(bits - 1) + + # logfbank need PCM16 input + y = logfbank( + signal=x, + samplerate=self.fs, + winlen=self.win_length, # unit ms + winstep=self.n_shift, # unit ms + nfilt=self.n_mels, + nfft=self.n_fft, + lowfreq=self.fmin, + highfreq=self.fmax, + dither=dither, + remove_dc_offset=self.remove_dc_offset, + preemph=self.preemph, + wintype=self.window) + return y diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/transform/transformation.py b/models/speech/speech_recognition/deepspeech2/ixrt/transform/transformation.py new file mode 100644 index 00000000..39e97816 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/transform/transformation.py @@ -0,0 +1,156 @@ +"""Transformation module.""" +import copy +import yaml +import io +import logging +import importlib +from collections import OrderedDict +from collections.abc import Sequence +from inspect import signature + + +import_alias = dict( + identity="transform.functional:Identity", + time_warp="transform.spec_augment:TimeWarp", + time_mask="transform.spec_augment:TimeMask", + freq_mask="transform.spec_augment:FreqMask", + cmvn="transform.cmvn:CMVN", + fbank="transform.spectrogram:LogMelSpectrogram", + spectrogram="transform.spectrogram:Spectrogram", + wav_process="transform.spectrogram:WavProcess", + stft="transform.spectrogram:Stft", + istft="transform.spectrogram:IStft", + stft2fbank="transform.spectrogram:Stft2LogMelSpectrogram", + fbank_kaldi="transform.spectrogram:LogMelSpectrogramKaldi", + cmvn_json="transform.cmvn:GlobalCMVN") + + +def dynamic_import(import_path, alias=dict()): + """dynamic import module and class + + :param str import_path: syntax 'module_name:class_name' + e.g., 'paddlespeech.s2t.models.u2:U2Model' + :param dict alias: shortcut for registered class + :return: imported class + """ + if import_path not in alias and ":" not in import_path: + raise ValueError( + "import_path should be one of {} or " + 'include ":", e.g. "paddlespeech.s2t.models.u2:U2Model" : ' + "{}".format(set(alias), import_path)) + if ":" not in import_path: + import_path = alias[import_path] + + module_name, objname = import_path.split(":") + m = importlib.import_module(module_name) + return getattr(m, objname) + + +class Transformation(): + """Apply some functions to the mini-batch + + Examples: + >>> kwargs = {"process": [{"type": "fbank", + ... "n_mels": 80, + ... "fs": 16000}, + ... {"type": "cmvn", + ... "stats": "data/train/cmvn.ark", + ... "norm_vars": True}, + ... {"type": "delta", "window": 2, "order": 2}]} + >>> transform = Transformation(kwargs) + >>> bs = 10 + >>> xs = [np.random.randn(100, 80).astype(np.float32) + ... for _ in range(bs)] + >>> xs = transform(xs) + """ + + def __init__(self, conffile=None): + if conffile is not None: + if isinstance(conffile, dict): + self.conf = copy.deepcopy(conffile) + else: + with io.open(conffile, encoding="utf-8") as f: + self.conf = yaml.safe_load(f) + assert isinstance(self.conf, dict), type(self.conf) + else: + self.conf = {"mode": "sequential", "process": []} + + self.functions = OrderedDict() + if self.conf.get("mode", "sequential") == "sequential": + for idx, process in enumerate(self.conf["process"]): + assert isinstance(process, dict), type(process) + opts = dict(process) + process_type = opts.pop("type") + class_obj = dynamic_import(process_type, import_alias) + # TODO(karita): assert issubclass(class_obj, TransformInterface) + try: + self.functions[idx] = class_obj(**opts) + except TypeError: + try: + signa = signature(class_obj) + except ValueError: + # Some function, e.g. built-in function, are failed + pass + else: + logging.error("Expected signature: {}({})".format( + class_obj.__name__, signa)) + raise + else: + raise NotImplementedError( + "Not supporting mode={}".format(self.conf["mode"])) + + def __repr__(self): + rep = "\n" + "\n".join(" {}: {}".format(k, v) + for k, v in self.functions.items()) + return "{}({})".format(self.__class__.__name__, rep) + + def __call__(self, xs, uttid_list=None, **kwargs): + """Return new mini-batch + + :param Union[Sequence[np.ndarray], np.ndarray] xs: + :param Union[Sequence[str], str] uttid_list: + :return: batch: + :rtype: List[np.ndarray] + """ + if not isinstance(xs, Sequence): + is_batch = False + xs = [xs] + else: + is_batch = True + + if isinstance(uttid_list, str): + uttid_list = [uttid_list for _ in range(len(xs))] + + if self.conf.get("mode", "sequential") == "sequential": + for idx in range(len(self.conf["process"])): + func = self.functions[idx] + + # TODO(karita): use TrainingTrans and UttTrans to check __call__ args + # Derive only the args which the func has + try: + param = signature(func).parameters + except ValueError: + # Some function, e.g. built-in function, are failed + param = {} + _kwargs = {k: v for k, v in kwargs.items() if k in param} + try: + if uttid_list is not None and "uttid" in param: + xs = [ + func(x, u, **_kwargs) + for x, u in zip(xs, uttid_list) + ] + else: + xs = [func(x, **_kwargs) for x in xs] + + except Exception: + logging.fatal("Catch a exception from {}th func: {}".format( + idx, func)) + raise + else: + raise NotImplementedError( + "Not supporting mode={}".format(self.conf["mode"])) + + if is_batch: + return xs + else: + return xs[0] diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/utils/__init__.py b/models/speech/speech_recognition/deepspeech2/ixrt/utils/__init__.py new file mode 100644 index 00000000..309f97ed --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/utils/__init__.py @@ -0,0 +1,16 @@ +from .load_tensorrt import deepspeech2_trtapi_ixrt, setup_io_bindings + + +VOCABLIST = ['', + '', + "'", + '', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + ''] + + +def check_target(inference, target): + satisfied = False + if inference > target: + satisfied = True + return satisfied diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/utils/error_rate.py b/models/speech/speech_recognition/deepspeech2/ixrt/utils/error_rate.py new file mode 100644 index 00000000..1e81f911 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/utils/error_rate.py @@ -0,0 +1,351 @@ +"""This module provides functions to calculate error rate in different level. +e.g. wer for word-level, cer for char-level. +""" +from itertools import groupby + +import editdistance +import numpy as np + +__all__ = ['word_errors', 'char_errors', 'wer', 'cer', "ErrorCalculator"] + + +def _levenshtein_distance(ref, hyp): + """Levenshtein distance is a string metric for measuring the difference + between two sequences. Informally, the levenshtein disctance is defined as + the minimum number of single-character edits (substitutions, insertions or + deletions) required to change one word into the other. We can naturally + extend the edits to word level when calculate levenshtein disctance for + two sentences. + """ + m = len(ref) + n = len(hyp) + + # special case + if ref == hyp: + return 0 + if m == 0: + return n + if n == 0: + return m + + if m < n: + ref, hyp = hyp, ref + m, n = n, m + + # use O(min(m, n)) space + distance = np.zeros((2, n + 1), dtype=np.int32) + + # initialize distance matrix + for j in range(n + 1): + distance[0][j] = j + + # calculate levenshtein distance + for i in range(1, m + 1): + prev_row_idx = (i - 1) % 2 + cur_row_idx = i % 2 + distance[cur_row_idx][0] = i + for j in range(1, n + 1): + if ref[i - 1] == hyp[j - 1]: + distance[cur_row_idx][j] = distance[prev_row_idx][j - 1] + else: + s_num = distance[prev_row_idx][j - 1] + 1 + i_num = distance[cur_row_idx][j - 1] + 1 + d_num = distance[prev_row_idx][j] + 1 + distance[cur_row_idx][j] = min(s_num, i_num, d_num) + + return distance[m % 2][n] + + +def word_errors(reference, hypothesis, ignore_case=False, delimiter=' '): + """Compute the levenshtein distance between reference sequence and + hypothesis sequence in word-level. + + :param reference: The reference sentence. + :type reference: str + :param hypothesis: The hypothesis sentence. + :type hypothesis: str + :param ignore_case: Whether case-sensitive or not. + :type ignore_case: bool + :param delimiter: Delimiter of input sentences. + :type delimiter: char + :return: Levenshtein distance and word number of reference sentence. + :rtype: list + """ + if ignore_case: + reference = reference.lower() + hypothesis = hypothesis.lower() + + ref_words = list(filter(None, reference.split(delimiter))) + hyp_words = list(filter(None, hypothesis.split(delimiter))) + + edit_distance = _levenshtein_distance(ref_words, hyp_words) + # `editdistance.eavl precision` less than `_levenshtein_distance` + # edit_distance = editdistance.eval(ref_words, hyp_words) + return float(edit_distance), len(ref_words) + + +def char_errors(reference, hypothesis, ignore_case=False, remove_space=False): + """Compute the levenshtein distance between reference sequence and + hypothesis sequence in char-level. + + :param reference: The reference sentence. + :type reference: str + :param hypothesis: The hypothesis sentence. + :type hypothesis: str + :param ignore_case: Whether case-sensitive or not. + :type ignore_case: bool + :param remove_space: Whether remove internal space characters + :type remove_space: bool + :return: Levenshtein distance and length of reference sentence. + :rtype: list + """ + if ignore_case: + reference = reference.lower() + hypothesis = hypothesis.lower() + + join_char = ' ' + if remove_space: + join_char = '' + + reference = join_char.join(list(filter(None, reference.split(' ')))) + hypothesis = join_char.join(list(filter(None, hypothesis.split(' ')))) + + edit_distance = _levenshtein_distance(reference, hypothesis) + # `editdistance.eavl precision` less than `_levenshtein_distance` + # edit_distance = editdistance.eval(reference, hypothesis) + return float(edit_distance), len(reference) + + +def wer(reference, hypothesis, ignore_case=False, delimiter=' '): + """Calculate word error rate (WER). WER compares reference text and + hypothesis text in word-level. WER is defined as: + + .. math:: + WER = (Sw + Dw + Iw) / Nw + + where + + .. code-block:: text + + Sw is the number of words subsituted, + Dw is the number of words deleted, + Iw is the number of words inserted, + Nw is the number of words in the reference + + We can use levenshtein distance to calculate WER. Please draw an attention + that empty items will be removed when splitting sentences by delimiter. + + :param reference: The reference sentence. + :type reference: str + :param hypothesis: The hypothesis sentence. + :type hypothesis: str + :param ignore_case: Whether case-sensitive or not. + :type ignore_case: bool + :param delimiter: Delimiter of input sentences. + :type delimiter: char + :return: Word error rate. + :rtype: float + :raises ValueError: If word number of reference is zero. + """ + edit_distance, ref_len = word_errors(reference, hypothesis, ignore_case, + delimiter) + + if ref_len == 0: + raise ValueError("Reference's word number should be greater than 0.") + + wer = float(edit_distance) / ref_len + return wer + + +def cer(reference, hypothesis, ignore_case=False, remove_space=False): + """Calculate charactor error rate (CER). CER compares reference text and + hypothesis text in char-level. CER is defined as: + + .. math:: + CER = (Sc + Dc + Ic) / Nc + + where + + .. code-block:: text + + Sc is the number of characters substituted, + Dc is the number of characters deleted, + Ic is the number of characters inserted + Nc is the number of characters in the reference + + We can use levenshtein distance to calculate CER. Chinese input should be + encoded to unicode. Please draw an attention that the leading and tailing + space characters will be truncated and multiple consecutive space + characters in a sentence will be replaced by one space character. + + :param reference: The reference sentence. + :type reference: str + :param hypothesis: The hypothesis sentence. + :type hypothesis: str + :param ignore_case: Whether case-sensitive or not. + :type ignore_case: bool + :param remove_space: Whether remove internal space characters + :type remove_space: bool + :return: Character error rate. + :rtype: float + :raises ValueError: If the reference length is zero. + """ + edit_distance, ref_len = char_errors(reference, hypothesis, ignore_case, + remove_space) + + if ref_len == 0: + raise ValueError("Length of reference should be greater than 0.") + + cer = float(edit_distance) / ref_len + return cer + + +class ErrorCalculator(): + """Calculate CER and WER for E2E_ASR and CTC models during training. + + :param y_hats: numpy array with predicted text + :param y_pads: numpy array with true (target) text + :param char_list: List[str] + :param sym_space: + :param sym_blank: + :return: + """ + + def __init__(self, + char_list, + sym_space, + sym_blank, + report_cer=False, + report_wer=False): + """Construct an ErrorCalculator object.""" + super().__init__() + + self.report_cer = report_cer + self.report_wer = report_wer + + self.char_list = char_list + self.space = sym_space + self.blank = sym_blank + self.idx_blank = self.char_list.index(self.blank) + if self.space in self.char_list: + self.idx_space = self.char_list.index(self.space) + else: + self.idx_space = None + + def __call__(self, ys_hat, ys_pad, is_ctc=False): + """Calculate sentence-level WER/CER score. + + :param paddle.Tensor ys_hat: prediction (batch, seqlen) + :param paddle.Tensor ys_pad: reference (batch, seqlen) + :param bool is_ctc: calculate CER score for CTC + :return: sentence-level WER score + :rtype float + :return: sentence-level CER score + :rtype float + """ + cer, wer = None, None + if is_ctc: + return self.calculate_cer_ctc(ys_hat, ys_pad) + elif not self.report_cer and not self.report_wer: + return cer, wer + + seqs_hat, seqs_true = self.convert_to_char(ys_hat, ys_pad) + if self.report_cer: + cer = self.calculate_cer(seqs_hat, seqs_true) + + if self.report_wer: + wer = self.calculate_wer(seqs_hat, seqs_true) + return cer, wer + + def calculate_cer_ctc(self, ys_hat, ys_pad): + """Calculate sentence-level CER score for CTC. + + :param paddle.Tensor ys_hat: prediction (batch, seqlen) + :param paddle.Tensor ys_pad: reference (batch, seqlen) + :return: average sentence-level CER score + :rtype float + """ + cers, char_ref_lens = [], [] + for i, y in enumerate(ys_hat): + y_hat = [x[0] for x in groupby(y)] + y_true = ys_pad[i] + seq_hat, seq_true = [], [] + for idx in y_hat: + idx = int(idx) + if idx != -1 and idx != self.idx_blank and idx != self.idx_space: + seq_hat.append(self.char_list[int(idx)]) + + for idx in y_true: + idx = int(idx) + if idx != -1 and idx != self.idx_blank and idx != self.idx_space: + seq_true.append(self.char_list[int(idx)]) + + hyp_chars = "".join(seq_hat) + ref_chars = "".join(seq_true) + if len(ref_chars) > 0: + cers.append(editdistance.eval(hyp_chars, ref_chars)) + char_ref_lens.append(len(ref_chars)) + + cer_ctc = float(sum(cers)) / sum(char_ref_lens) if cers else None + return cer_ctc + + def convert_to_char(self, ys_hat, ys_pad): + """Convert index to character. + + :param paddle.Tensor seqs_hat: prediction (batch, seqlen) + :param paddle.Tensor seqs_true: reference (batch, seqlen) + :return: token list of prediction + :rtype list + :return: token list of reference + :rtype list + """ + seqs_hat, seqs_true = [], [] + for i, y_hat in enumerate(ys_hat): + y_true = ys_pad[i] + eos_true = np.where(y_true == -1)[0] + ymax = eos_true[0] if len(eos_true) > 0 else len(y_true) + # NOTE: padding index (-1) in y_true is used to pad y_hat + seq_hat = [self.char_list[int(idx)] for idx in y_hat[:ymax]] + seq_true = [ + self.char_list[int(idx)] for idx in y_true if int(idx) != -1 + ] + seq_hat_text = "".join(seq_hat).replace(self.space, " ") + seq_hat_text = seq_hat_text.replace(self.blank, "") + seq_true_text = "".join(seq_true).replace(self.space, " ") + seqs_hat.append(seq_hat_text) + seqs_true.append(seq_true_text) + return seqs_hat, seqs_true + + def calculate_cer(self, seqs_hat, seqs_true): + """Calculate sentence-level CER score. + + :param list seqs_hat: prediction + :param list seqs_true: reference + :return: average sentence-level CER score + :rtype float + """ + char_eds, char_ref_lens = [], [] + for i, seq_hat_text in enumerate(seqs_hat): + seq_true_text = seqs_true[i] + hyp_chars = seq_hat_text.replace(" ", "") + ref_chars = seq_true_text.replace(" ", "") + char_eds.append(editdistance.eval(hyp_chars, ref_chars)) + char_ref_lens.append(len(ref_chars)) + return float(sum(char_eds)) / sum(char_ref_lens) + + def calculate_wer(self, seqs_hat, seqs_true): + """Calculate sentence-level WER score. + + :param list seqs_hat: prediction + :param list seqs_true: reference + :return: average sentence-level WER score + :rtype float + """ + word_eds, word_ref_lens = [], [] + for i, seq_hat_text in enumerate(seqs_hat): + seq_true_text = seqs_true[i] + hyp_words = seq_hat_text.split() + ref_words = seq_true_text.split() + word_eds.append(editdistance.eval(hyp_words, ref_words)) + word_ref_lens.append(len(ref_words)) + return float(sum(word_eds)) / sum(word_ref_lens) diff --git a/models/speech/speech_recognition/deepspeech2/ixrt/utils/load_tensorrt.py b/models/speech/speech_recognition/deepspeech2/ixrt/utils/load_tensorrt.py new file mode 100644 index 00000000..164a9394 --- /dev/null +++ b/models/speech/speech_recognition/deepspeech2/ixrt/utils/load_tensorrt.py @@ -0,0 +1,56 @@ +import numpy as np +import tensorrt +from tensorrt import Dims +from cuda import cuda, cudart + + +def deepspeech2_trtapi_ixrt(engine_file): + datatype = tensorrt.DataType.FLOAT + host_mem = tensorrt.IHostMemory + logger = tensorrt.Logger(tensorrt.Logger.ERROR) + with open(engine_file, "rb") as f, tensorrt.Runtime(logger) as runtime: + runtime = tensorrt.Runtime(logger) + assert runtime + engine = runtime.deserialize_cuda_engine(f.read()) + assert engine + context = engine.create_execution_context() + assert context + + return engine, context + + +def setup_io_bindings(engine, context): + # Setup I/O bindings + inputs = [] + outputs = [] + allocations = [] + + for i in range(engine.num_bindings): + is_input = False + if engine.binding_is_input(i): + is_input = True + name = engine.get_binding_name(i) + dtype = engine.get_binding_dtype(i) + shape = context.get_binding_shape(i) + if is_input: + batch_size = shape[0] + size = np.dtype(tensorrt.nptype(dtype)).itemsize + for s in shape: + size *= s + err, allocation = cudart.cudaMalloc(size) + assert err == cudart.cudaError_t.cudaSuccess + binding = { + "index": i, + "name": name, + "dtype": np.dtype(tensorrt.nptype(dtype)), + "shape": list(shape), + "allocation": allocation, + "nbytes": size, + } + allocations.append(allocation) + if engine.binding_is_input(i): + inputs.append(binding) + else: + outputs.append(binding) + return inputs, outputs, allocations + diff --git a/tests/model_info.json b/tests/model_info.json index 1eb8591f..18488e26 100644 --- a/tests/model_info.json +++ b/tests/model_info.json @@ -10424,6 +10424,207 @@ "type": "inference", "hasDemo": false, "demoType": "" + }, + { + "display_name": "ViT", + "model_name": "vit", + "framework": "ixrt", + "release_version": "26.06", + "release_sdk": "4.4.0", + "release_gpgpu": "MR-V100", + "latest_sdk": "4.4.0", + "latest_gpgpu": "", + "category": "cv/classification", + "toolbox": "", + "mdims": "", + "dataset": "", + "license": "", + "model_path": "models/cv/classification/vit/ixrt/", + "readme_file": "models/cv/classification/vit/ixrt/README.md", + "bitbucket_repo": "", + "bitbucket_branch": "", + "bitbucket_path": "", + "develop_owner": "", + "github_repo": "", + "github_branch": "", + "github_path": "", + "datasets": "https://www.image-net.org/download.php", + "download_url": "https://local/vit.onnx", + "need_third_part": true, + "precisions": [ + "fp16" + ], + "type": "inference", + "hasDemo": false, + "demoType": "" + }, + { + "display_name": "DeiT-B", + "model_name": "deit_b", + "framework": "ixrt", + "release_version": "26.06", + "release_sdk": "4.4.0", + "release_gpgpu": "MR-V100", + "latest_sdk": "4.4.0", + "latest_gpgpu": "", + "category": "cv/classification", + "toolbox": "", + "mdims": "", + "dataset": "", + "license": "", + "model_path": "models/cv/classification/deit_b/ixrt/", + "readme_file": "models/cv/classification/deit_b/ixrt/README.md", + "bitbucket_repo": "", + "bitbucket_branch": "", + "bitbucket_path": "", + "develop_owner": "", + "github_repo": "", + "github_branch": "", + "github_path": "", + "datasets": "https://www.image-net.org/download.php", + "download_url": "https://local/deit_b.onnx", + "need_third_part": true, + "precisions": [ + "fp16" + ], + "type": "inference", + "hasDemo": false, + "demoType": "" + }, + { + "display_name": "MobileNetV1", + "model_name": "mobilenet_v1", + "framework": "ixrt", + "release_version": "26.06", + "release_sdk": "4.4.0", + "release_gpgpu": "MR-V100", + "latest_sdk": "4.4.0", + "latest_gpgpu": "", + "category": "cv/classification", + "toolbox": "", + "mdims": "", + "dataset": "", + "license": "", + "model_path": "models/cv/classification/mobilenet_v1/ixrt/", + "readme_file": "models/cv/classification/mobilenet_v1/ixrt/README.md", + "bitbucket_repo": "", + "bitbucket_branch": "", + "bitbucket_path": "", + "develop_owner": "", + "github_repo": "", + "github_branch": "", + "github_path": "", + "datasets": "https://www.image-net.org/download.php", + "download_url": "https://local/mobilenet_v1.onnx", + "need_third_part": true, + "precisions": [ + "fp16", + "int8" + ], + "type": "inference", + "hasDemo": false, + "demoType": "" + }, + { + "display_name": "DBNet", + "model_name": "dbnet", + "framework": "ixrt", + "release_version": "26.06", + "release_sdk": "4.4.0", + "release_gpgpu": "MR-V100", + "latest_sdk": "4.4.0", + "latest_gpgpu": "", + "category": "cv/ocr", + "toolbox": "", + "mdims": "", + "dataset": "", + "license": "", + "model_path": "models/cv/ocr/dbnet/ixrt/", + "readme_file": "models/cv/ocr/dbnet/ixrt/README.md", + "bitbucket_repo": "", + "bitbucket_branch": "", + "bitbucket_path": "", + "develop_owner": "", + "github_repo": "", + "github_branch": "", + "github_path": "", + "datasets": "local/icdar2015", + "download_url": "http://local/dbnet.onnx", + "need_third_part": true, + "precisions": [ + "fp16", + "int8" + ], + "type": "inference", + "hasDemo": false, + "demoType": "" + }, + { + "display_name": "DDRNet", + "model_name": "ddrnet", + "framework": "ixrt", + "release_version": "26.06", + "release_sdk": "4.4.0", + "release_gpgpu": "MR-V100", + "latest_sdk": "4.4.0", + "latest_gpgpu": "", + "category": "cv/semantic_segmentation", + "toolbox": "", + "mdims": "", + "dataset": "", + "license": "", + "model_path": "models/cv/semantic_segmentation/ddrnet/ixrt/", + "readme_file": "models/cv/semantic_segmentation/ddrnet/ixrt/README.md", + "bitbucket_repo": "", + "bitbucket_branch": "", + "bitbucket_path": "", + "develop_owner": "", + "github_repo": "", + "github_branch": "", + "github_path": "", + "datasets": "local/cityscapes", + "download_url": "http://local/ddrnet.onnx", + "need_third_part": true, + "precisions": [ + "fp16", + "int8" + ], + "type": "inference", + "hasDemo": false, + "demoType": "" + }, + { + "display_name": "DeepSpeech2", + "model_name": "deepspeech2", + "framework": "ixrt", + "release_version": "26.06", + "release_sdk": "4.4.0", + "release_gpgpu": "MR-V100", + "latest_sdk": "4.4.0", + "latest_gpgpu": "", + "category": "speech/speech_recognition", + "toolbox": "", + "mdims": "", + "dataset": "", + "license": "", + "model_path": "models/speech/speech_recognition/deepspeech2/ixrt/", + "readme_file": "models/speech/speech_recognition/deepspeech2/ixrt/README.md", + "bitbucket_repo": "", + "bitbucket_branch": "", + "bitbucket_path": "", + "develop_owner": "", + "github_repo": "", + "github_branch": "", + "github_path": "", + "datasets": "local/LibriSpeech", + "download_url": "http://local/deepspeech2.onnx", + "need_third_part": true, + "precisions": [ + "fp16" + ], + "type": "inference", + "hasDemo": false, + "demoType": "" } ] } \ No newline at end of file diff --git a/tests/run_ixrt.py b/tests/run_ixrt.py index 61a86fad..420ee2a6 100644 --- a/tests/run_ixrt.py +++ b/tests/run_ixrt.py @@ -124,11 +124,11 @@ def main(): logging.info(f"End running {model['model_name']} test case.") # multi_object_tracking模型 - if model["category"] in ["cv/multi_object_tracking", "cv/semantic_segmentation", "cv/ocr", "multimodal/diffusion_model", "speech/speech_synthesis"]: + if model["category"] in ["cv/multi_object_tracking", "cv/semantic_segmentation", "cv/ocr", "multimodal/diffusion_model", "speech/speech_synthesis", "speech/speech_recognition"]: logging.info(f"Start running {model['model_name']} test case:\n{json.dumps(model, indent=4)}") d_url = model["download_url"] if d_url is not None: - result = run_multi_object_tracking_testcase(model) + result = run_multi_object_tracking_testcase(model, whl_url) check_model_result(result) logging.debug(f"The result of {model['model_name']} is\n{json.dumps(result, indent=4)}") logging.info(f"End running {model['model_name']} test case.") @@ -507,7 +507,7 @@ def run_segmentation_and_face_testcase(model): logging.debug(f"matchs:\n{matchs}") return result -def run_multi_object_tracking_testcase(model): +def run_multi_object_tracking_testcase(model, whl_url): model_name = model["model_name"] result = { "name": model_name, @@ -527,6 +527,11 @@ def run_multi_object_tracking_testcase(model): ls -l | grep onnx """ + if model_name == "deepspeech2": + prepare_script += f""" + pip install {whl_url}`curl -s {whl_url} | grep -o 'paddlepaddle-[^"]*\.whl' | head -n1` + """ + # add pip list info when in debug mode if utils.is_debug(): pip_list_script = "pip list | grep -E 'numpy|transformer|igie|mmcv|onnx'\n" @@ -567,9 +572,10 @@ def run_multi_object_tracking_testcase(model): result["result"][prec] = result["result"][prec] | {m[0]: m[1], m[2]: m[3]} pattern = METRIC_PATTERN matchs = re.findall(pattern, sout) - if matchs and len(matchs) == 1: - result["result"][prec].update(get_metric_result(matchs[0])) - result["result"][prec]["status"] = "PASS" + if matchs: + for m in matchs: + result["result"][prec].update(get_metric_result(m)) + result["result"][prec]["status"] = "PASS" result["result"][prec]["Cost time (s)"] = t logging.debug(f"matchs:\n{matchs}") return result -- Gitee