From ddf0ed6fbf832d37f5381365210ba0d316b44e07 Mon Sep 17 00:00:00 2001 From: Mark Kurtz Date: Mon, 2 May 2022 11:11:01 -0400 Subject: [PATCH] [cherry-pick] Release 0.12.1 cherry pick (#343) * SparseServer.UI example [WIP] (#308) * add megasparse dir * edited readme to support DS integration * small edit to docstring * edited settings.py module so only 2 models are loaded. * added more context to the readme about adding more models. * fixed image * added different host to streamlit client as default * quality check edits * quality check commit * passing copyright quality check * content edits * rename dir to sparseserver-ui * added new config file for quick start * edited multipipelineclient in settings.py * changed name of config file * edited model stubs * edited readme * added dependency pins * changed server pin * edited model choice logic * altered front-end features * style update * renamed samples file * added variant descriptions * style changes * edited samples.py module * style changes * added new pic * edited README * updated readme cmds * SparseServer edit (#314) * edit number of models * edit settings.py * edit readme * Update label mapping for deepsparse.transformers.eval_downstream (#323) * Update label mapping for deepsparse.transformers.eval_downstream * Fix MNLI as well * bump up main to 0.13.0 (#313) Co-authored-by: dhuang * AWS Sagemaker example integration (#305) * AWS Sagemaker example integration * documentation, sample config, dockerfile fixes * fix ecr repo name * readme code changes from testing * Update huggingface-transformers/README.md with new models (#329) * Update README.md (#330) * Update README.md various grammatical edits additional edits for section headline consistency * Topology file for HB120rs_v3 (#334) * Topology file for HB120rs_v3 Specifies core-per-CCX grouping for HB120rs_v3 VM's, used by multi-process script. * Update README.md to reference Azure topo file * Move all benchmarking within deepsparse/benchmark/ (#333) * Move all benchmarking within deepsparse/benchmark/ * Update benchmark_model * Expose results at benchmark base * isort * Skip flake8 * server integration check bug fix (#331) * server integration check bug fix need to verify integration is set before calling `integration.lower()` * respond to review - click choice * add default integration val to server config schema (#337) * deepsparse.Pipeline - generic pipeline, deepsparse.server support, NLP,IC,OD pipelines (#317) * base commit - make pydantic a general req * Pipeline base class implementation (#315) * Pipeline base class implementation * constructor default values * __call__ inputs/outputs parsing + validation * documentation * pipeline 'alias' argument * review fixes * [feature/Pipeline] PipelineConfig (#318) * PipelineConfig pydantic model + Pipeline.from_config * Pipeline.to_config() function * [feature/Pipeline] refactor deepsparse.server to use deepsparse.Pipeline (#319) * PipelineConfig pydantic model + Pipeline.from_config * Pipeline.to_config() function * refactor deepsparse.server to use deepsparse.Pipeline * review nit fix remove files for separate feature * Image Classification Pipeline Integration (#322) * Create a command line installable for image classification pipeline * Intermediate Commit * Image Classification pipeline implementation * Remove faulty entry point * Apply suggestions from @bogunowicz * Changed function name from `_infer_input_shape` to `_infer_image_shape` * Add validation script for Image Classification pipeline (#328) * Add Validation Script for Image Classification Models * Update pipelines and corresponding schemas to work with numpy arrays * Bugfix if prediction to be converted to int if it's a string * Update docstring * Update src/deepsparse/image_classification/validation_script.py * [feature/Pipeline] fixes for ic-pipelines implementation (#336) * fixes for ic-pipelines implementation * sparsezoo support Co-authored-by: Benjamin Fineran * Update src/deepsparse/pipeline.py * quality * [feature/Pipeline] deepsparse.Pipeline implementations for transformers (#320) * add parsing layer for deepsparse.Pipeline for implementation flexibility * initial deepsparse.transformers.pipelines implementation base class + text_classification * make tokenizer and config attributes 'public', but not properties * decorator fix * use kwargs for transforemrs pipeline parent class args * token classification impl * token classification output schema parsing * quality * question answering pipeline impl * fixes for pipline impls - bs1 santity check inferences working * [feature/Pipeline] deprecate and migrate existing transformers pipelines (#335) * remove old pipeline pathway and files, add API for deprecated pathway * migrate eval_downstream * update readme * server pipeline input fix * hf license attribution * `YOLO` pipeline integration for deepsparse (#327) * Added YOLO pipeline Add an installable for yolo integration Added a task for YOLO To install run: * `pip install --editable "./[yolo]"` * Changed function name from `_infer_input_shape` to `_infer_image_shape` * Update docstring * Comments from @bogunowicz * Moved COCO classes to a file * Adds support to annotate images using YOLO (#332) * Adds support to annotate images using YOLO * Makes `YOLOOutput` iterable * Returns a named tuple of image outputs when `next` is called on `YOLOOutput` * Adds an annotate function to yolo utils * Adds an annotation script, testing + minor fixes remain * Intermediate-commit * Intermediate WIP * Working State with required bugfixes * style fixes Co-authored-by: Benjamin Co-authored-by: Benjamin * [feature/Pipeline] rename input/output _model to _schema (#340) * rename input/output _model to _schema * refactor yolo pipeline * default model support for Pipeline.register (#339) * default model support for Pipeline.register * update default stubs for transformers and IC * yolo default model * minor fixes * model->schema for server Co-authored-by: Rahul Tuli * Remove: startlette dep (#338) * Update src/deepsparse/version.py Co-authored-by: Ricky Costa <79061523+InquestGeronimo@users.noreply.github.com> Co-authored-by: Michael Goin Co-authored-by: dhuangnm <74931910+dhuangnm@users.noreply.github.com> Co-authored-by: dhuang Co-authored-by: Benjamin Fineran Co-authored-by: Jeannie Finks <74554921+jeanniefinks@users.noreply.github.com> Co-authored-by: Govind Ramnarayan <77341216+govindr-nm@users.noreply.github.com> Co-authored-by: Rahul Tuli Co-authored-by: Konstantin Gulin <66528950+KSGulin@users.noreply.github.com> --- README.md | 4 +- examples/amd-azure/HB120rs_v3.json | 16 + examples/amd-azure/README.md | 6 +- examples/amd-azure/multi_process_benchmark.py | 2 +- examples/aws-sagemaker/Dockerfile | 33 + examples/aws-sagemaker/README.md | 266 ++++ examples/aws-sagemaker/config.yaml | 5 + examples/benchmark/check_correctness.py | 2 +- examples/benchmark/run_benchmark.py | 6 +- examples/huggingface-transformers/README.md | 14 +- examples/sparseserver-ui/README.md | 88 + examples/sparseserver-ui/client/app.py | 54 + .../sparseserver-ui/client/pipelineclient.py | 47 + examples/sparseserver-ui/client/samples.py | 55 + examples/sparseserver-ui/client/settings.py | 128 ++ .../sparseserver-ui/img/demo_screenshot.png | Bin 0 -> 491015 bytes examples/sparseserver-ui/requirements.txt | 2 + .../sparseserver-ui/server/big-config.yaml | 91 ++ examples/sparseserver-ui/server/config.yaml | 32 + setup.py | 18 +- src/deepsparse/__init__.py | 1 + .../{benchmark_model => benchmark}/README.md | 0 src/deepsparse/benchmark/__init__.py | 18 + .../benchmark_model.py | 4 +- .../img/json_output.png | Bin .../ort_engine.py | 0 .../{benchmark.py => benchmark/results.py} | 0 .../stream_benchmark.py | 0 .../__init__.py | 0 .../image_classification/constants.py | 16 + .../image_classification/pipelines.py | 197 +++ .../image_classification/schemas.py | 42 + .../image_classification/validation_script.py | 162 ++ src/deepsparse/pipeline.py | 546 +++++++ src/deepsparse/server/config.py | 91 +- src/deepsparse/server/main.py | 64 +- src/deepsparse/server/pipelines.py | 89 -- src/deepsparse/tasks.py | 44 + src/deepsparse/transformers/__init__.py | 1 - .../transformers/eval_downstream.py | 40 +- src/deepsparse/transformers/pipelines.py | 1414 ----------------- .../transformers/pipelines/__init__.py | 20 + .../transformers/pipelines/pipeline.py | 219 +++ .../pipelines/question_answering.py | 409 +++++ .../pipelines/text_classification.py | 221 +++ .../pipelines/token_classification.py | 499 ++++++ src/deepsparse/transformers/server.py | 186 --- src/deepsparse/version.py | 2 +- src/deepsparse/yolo/__init__.py | 13 + src/deepsparse/yolo/annotate.py | 232 +++ src/deepsparse/yolo/pipelines.py | 248 +++ src/deepsparse/yolo/schemas.py | 70 + src/deepsparse/yolo/utils/__init__.py | 18 + src/deepsparse/yolo/utils/cli_helpers.py | 46 + src/deepsparse/yolo/utils/coco_classes.py | 96 ++ src/deepsparse/yolo/utils/utils.py | 795 +++++++++ 56 files changed, 4861 insertions(+), 1811 deletions(-) create mode 100644 examples/amd-azure/HB120rs_v3.json create mode 100644 examples/aws-sagemaker/Dockerfile create mode 100644 examples/aws-sagemaker/README.md create mode 100644 examples/aws-sagemaker/config.yaml create mode 100644 examples/sparseserver-ui/README.md create mode 100644 examples/sparseserver-ui/client/app.py create mode 100644 examples/sparseserver-ui/client/pipelineclient.py create mode 100644 examples/sparseserver-ui/client/samples.py create mode 100644 examples/sparseserver-ui/client/settings.py create mode 100644 examples/sparseserver-ui/img/demo_screenshot.png create mode 100644 examples/sparseserver-ui/requirements.txt create mode 100644 examples/sparseserver-ui/server/big-config.yaml create mode 100644 examples/sparseserver-ui/server/config.yaml rename src/deepsparse/{benchmark_model => benchmark}/README.md (100%) create mode 100644 src/deepsparse/benchmark/__init__.py rename src/deepsparse/{benchmark_model => benchmark}/benchmark_model.py (98%) rename src/deepsparse/{benchmark_model => benchmark}/img/json_output.png (100%) rename src/deepsparse/{benchmark_model => benchmark}/ort_engine.py (100%) rename src/deepsparse/{benchmark.py => benchmark/results.py} (100%) rename src/deepsparse/{benchmark_model => benchmark}/stream_benchmark.py (100%) rename src/deepsparse/{benchmark_model => image_classification}/__init__.py (100%) create mode 100644 src/deepsparse/image_classification/constants.py create mode 100644 src/deepsparse/image_classification/pipelines.py create mode 100644 src/deepsparse/image_classification/schemas.py create mode 100644 src/deepsparse/image_classification/validation_script.py create mode 100644 src/deepsparse/pipeline.py delete mode 100644 src/deepsparse/server/pipelines.py delete mode 100644 src/deepsparse/transformers/pipelines.py create mode 100644 src/deepsparse/transformers/pipelines/__init__.py create mode 100644 src/deepsparse/transformers/pipelines/pipeline.py create mode 100644 src/deepsparse/transformers/pipelines/question_answering.py create mode 100644 src/deepsparse/transformers/pipelines/text_classification.py create mode 100644 src/deepsparse/transformers/pipelines/token_classification.py delete mode 100644 src/deepsparse/transformers/server.py create mode 100644 src/deepsparse/yolo/__init__.py create mode 100644 src/deepsparse/yolo/annotate.py create mode 100644 src/deepsparse/yolo/pipelines.py create mode 100644 src/deepsparse/yolo/schemas.py create mode 100644 src/deepsparse/yolo/utils/__init__.py create mode 100644 src/deepsparse/yolo/utils/cli_helpers.py create mode 100644 src/deepsparse/yolo/utils/coco_classes.py create mode 100644 src/deepsparse/yolo/utils/utils.py diff --git a/README.md b/README.md index 91e4d21b22..e0ea89bcc3 100644 --- a/README.md +++ b/README.md @@ -139,12 +139,12 @@ deepsparse.benchmark [-h] [-b BATCH_SIZE] [-shapes INPUT_SHAPES] ## 👩‍💻 NLP Inference Example ```python -from deepsparse.transformers import pipeline +from deepsparse import Pipeline # SparseZoo model stub or path to ONNX file model_path = "zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/12layer_pruned80_quant-none-vnni" -qa_pipeline = pipeline( +qa_pipeline = Pipeline.create( task="question-answering", model_path=model_path, ) diff --git a/examples/amd-azure/HB120rs_v3.json b/examples/amd-azure/HB120rs_v3.json new file mode 100644 index 0000000000..f0ea7c7758 --- /dev/null +++ b/examples/amd-azure/HB120rs_v3.json @@ -0,0 +1,16 @@ +[[24,25,26,27,28,29], +[0,1,2,3,4,5,6,7], +[8,9,10,11,12,13,14,15], +[16,17,18,19,20,21,22,23], +[54,55,56,57,58,59], +[30,31,32,33,34,35,36,37], +[38,39,40,41,42,43,44,45], +[46,47,48,49,50,51,52,53], +[84,85,86,87,88,89], +[60,61,62,63,64,65,66,67], +[68,69,70,71,72,73,74,75], +[76,77,78,79,80,81,82,83], +[114,115,116,117,118,119], +[90,91,92,93,94,95,96,97], +[98,99,100,101,102,103,104,105], +[106,107,108,109,110,111,112,113]] diff --git a/examples/amd-azure/README.md b/examples/amd-azure/README.md index 1621005462..fec685ad04 100644 --- a/examples/amd-azure/README.md +++ b/examples/amd-azure/README.md @@ -46,7 +46,11 @@ For this benchmarking script, users must specify the topology of their system wi One list of cores will contain the processor IDs that one of the worker processes will run on, and should reflect the topology of the system. For performance, one list of cores in the JSON topology file should contain the list of cores that are on the same socket, are on the same NUMA node, or share the same L3 cache. -The `/examples/amd-azure` directory contains an example JSON file that can be used. `amd_epyc_7713.json` is suitable for a two-socket system with AMD EPYC 7713 processors. This file will also work for a one-socket system if the proper parameter for `nstreams` is passed into `multi_process_benchmark.py`. +The `/examples/amd-azure` directory contains two example JSON files that can be used. + +`amd_epyc_7713.json` is suitable for a two-socket system with AMD EPYC 7713 processors. This file will also work for a one-socket system if the proper parameter for `nstreams` is passed into `multi_process_benchmark.py`. + +`HB120rs_v3.json` is suitable for an Azure HB120rs_v3 virtual machine, a two-socket machine with AMD Milan-X processors and 120 cores in total. You may notice that not every process will use the same number of cores when using this topology. This is because some of the CCXs on this instance type have some cores dedicated to running the hypervisor. ## Usage diff --git a/examples/amd-azure/multi_process_benchmark.py b/examples/amd-azure/multi_process_benchmark.py index d1b975d137..378764a631 100644 --- a/examples/amd-azure/multi_process_benchmark.py +++ b/examples/amd-azure/multi_process_benchmark.py @@ -23,7 +23,7 @@ import numa from deepsparse import compile_model -from deepsparse.benchmark_model.stream_benchmark import singlestream_benchmark +from deepsparse.benchmark.stream_benchmark import singlestream_benchmark from deepsparse.log import set_logging_level from deepsparse.utils import ( generate_random_inputs, diff --git a/examples/aws-sagemaker/Dockerfile b/examples/aws-sagemaker/Dockerfile new file mode 100644 index 0000000000..6d678aeac1 --- /dev/null +++ b/examples/aws-sagemaker/Dockerfile @@ -0,0 +1,33 @@ +FROM python:3.8 + +ARG config_path=./config.yaml + +USER root + +RUN apt-get -qq -y update && \ + apt-get -qq -y upgrade && \ + apt-get -y autoclean && \ + apt-get -y autoremove && \ + rm -rf /var/lib/apt/lists/* + + +COPY ${config_path} /root/server-config.yaml + +ENV VIRTUAL_ENV=/venv +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + + +RUN python3 -m venv $VIRTUAL_ENV && \ + pip3 install --no-cache-dir --upgrade pip && \ + pip3 install --no-cache-dir "deepsparse-nightly[server]" # TODO: switch to deepsparse[server] >= 0.12 + +# create 'serve' command for sagemaker entrypoint +RUN mkdir /opt/server/ +RUN echo "#! /bin/bash" > /opt/server/serve +RUN echo "deepsparse.server --port 8080 --config_file /root/server-config.yaml" >> /opt/server/serve +RUN chmod 777 /opt/server/serve + +ENV PATH="/opt/server:${PATH}" +WORKDIR /opt/server + +ENTRYPOINT ["bash", "/opt/server/serve"] diff --git a/examples/aws-sagemaker/README.md b/examples/aws-sagemaker/README.md new file mode 100644 index 0000000000..9f7a4f7590 --- /dev/null +++ b/examples/aws-sagemaker/README.md @@ -0,0 +1,266 @@ + + +# Deploying DeepSparse with Amazon SageMaker + +[Amazon SageMaker](https://docs.aws.amazon.com/sagemaker/index.html) +offers an easy-to-use infrastructure for deploying deep learning models at scale. +This directory provides a guided example for deploying a +[DeepSparse](https://github.com/neuralmagic/deepsparse) inference server on SageMaker. +Deployments benefit from both sparse-CPU acceleration with +DeepSparse and automatic scaling from SageMaker. + + +## Contents +In addition to the step-by-step instructions in this guide, the directory contains +additional files to aid in the deployment. + +### Dockerfile +The included `Dockerfile` builds an image on top of the standard `python:3.8` image +with `deepsparse` installed and creates an executable command `serve` that runs +`deepsparse.server` on port 8080. SageMaker will execute this image by running +`docker run serve` and expects the image to serve inference requests at the +`invocations/` endpoint. + +For general customization of the server, changes should not need to be made +to the Dockerfile, but to the `config.yaml` file that the Dockerfile reads from +instead. + +### config.yaml +`config.yaml` is used to configure the DeepSparse server running in the Dockerfile. +The config must contain the line `integration: sagemaker` so +endpoints may be provisioned correctly to match SageMaker specifications. + +Notice that the `model_path` and `task` are set to run a sparse-quantized +question-answering model from [SparseZoo](https://sparsezoo.neuralmagic.com/). +To use a model directory stored in `s3`, set `model_path` to `/opt/ml/model` in +the config and add `ModelDataUrl=` to the `CreateModel` arguments. +SageMaker will automatically copy the files from the s3 path into `/opt/ml/model` +which the server can then read from. + +More information on the DeepSparse server and its configuration can be found +[here](https://github.com/neuralmagic/deepsparse/tree/main/src/deepsparse/server#readme). + + +## Deploying to SageMaker +The following steps are required to provision and deploy DeepSparse to SageMaker +for inference: +* Build the DeepSparse-SageMaker `Dockerfile` into a local docker image +* Create an [Amazon ECR](https://aws.amazon.com/ecr/) repository to host the image +* Push the image to the ECR repository +* Create a SageMaker `Model` that reads from the hosted ECR image +* Build a SageMaker `EndpointConfig` that defines how to provision the model deployment +* Launch the SageMaker `Endpoint` defined by the `Model` and `EndpointConfig` + +### Requirements +The listed steps can be easily completed using a `python` and `bash`. The following +credentials, tools, and libraries are also required: +* The [`aws` cli](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) that is [configured](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html) +* The [ARN of an AWS role](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-roles.html) your user has access to that has full SageMaker and ECR permissions. In the following steps, we will refer to this as `ROLE_ARN`. It should take the form `"arn:aws:iam::XXX:role/service-role/XXX"` +* [Docker and the `docker` cli](https://docs.docker.com/get-docker/) +* The `boto3` python AWS sdk (`pip install boto3`) + +### Building the DeepSparse-SageMaker Image Locally +The `Dockerfile` can be build from this directory from a bash shell using the following command. +The image will be tagged locally as `deepsparse-sagemaker-example`. + +```bash +docker build -t deepsparse-sagemaker-example . +``` + +### Creating an ECR Repository +The following code snippet can be used in Python to create an ECR repository. +The `region_name` can be swapped to a preferred region. The repository will be named +`deepsparse-sagemaker`. If the repository is already created, this step may be skipped. + +```python +import boto3 + +ecr = boto3.client("ecr", region_name='us-east-1') +create_repository_res = ecr.create_repository(repositoryName="deepsparse-sagemaker") +``` + +### Pushing the Local Image to the ECR Repository +Once the image is built and the ECR repository is created, the image can be pushed using the following +bash commands. + +```bash +account=$(aws sts get-caller-identity --query Account | sed -e 's/^"//' -e 's/"$//') +region=$(aws configure get region) +ecr_account=${account}.dkr.ecr.${region}.amazonaws.com + +aws ecr get-login-password --region $region | docker login --username AWS --password-stdin $ecr_account +fullname=$ecr_account/deepsparse-sagemaker:latest + +docker tag deepsparse-sagemaker-example:latest $fullname +docker push $fullname +``` + +An abbreviated successful output will look like: +``` +Login Succeeded +The push refers to repository [XXX.dkr.ecr.us-east-1.amazonaws.com/deepsparse-example] +3c2284f66840: Preparing +08fa02ce37eb: Preparing +a037458de4e0: Preparing +bafdbe68e4ae: Preparing +a13c519c6361: Preparing +6817758dd480: Waiting +6d95196cbe50: Waiting +e9872b0f234f: Waiting +c18b71656bcf: Waiting +2174eedecc00: Waiting +03ea99cd5cd8: Pushed +585a375d16ff: Pushed +5bdcc8e2060c: Pushed +latest: digest: sha256:XXX size: 3884 +``` + +### Creating a SageMaker Model +A SageMaker `Model` can now be created referencing the pushed image. +The example model will be named `question-answering-example`. +As mentioned in the requirements, `ROLE_ARN` should be a string arn of an AWS +role with full access to SageMaker. + +```python +sm_boto3 = boto3.client("sagemaker", region_name="us-east-1") + +region = boto3.Session().region_name +account_id = boto3.client("sts").get_caller_identity()["Account"] + +image_uri = "{}.dkr.ecr.{}.amazonaws.com/deepsparse-sagemaker:latest".format(account_id, region) + +create_model_res = sm_boto3.create_model( + ModelName="question-answering-example", + Containers=[ + { + "Image": image_uri, + }, + ], + ExecutionRoleArn=ROLE_ARN, + EnableNetworkIsolation=False, +) +``` + +More information about options for configuring SageMaker `Model` instances can +be found [here](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateModel.html). + + +### Building a SageMaker EndpointConfig +The `EndpointConfig` is used to set the instance type to provision, how many, scaling +rules, and other deployment settings. The following code snippet defines an endpoint +with a single machine using an `ml.c5.large` CPU. + +* [Full list of available instances](https://docs.aws.amazon.com/sagemaker/latest/dg/notebooks-available-instance-types.html) (See Compute optimized (no GPUs) section) +* [EndpointConfig documentation and options](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateEndpointConfig.html) + +```python +model_name = "question-answering-example" # model defined above +initial_instance_count = 1 +instance_type = "ml.c5.large" + +variant_name = "QuestionAnsweringDeepSparseDemo" # ^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62} + +production_variants = [ + { + "VariantName": variant_name, + "ModelName": model_name, + "InitialInstanceCount": initial_instance_count, + "InstanceType": instance_type, + } +] + +endpoint_config_name = "QuestionAnsweringExampleConfig" # ^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62} + +endpoint_config = { + "EndpointConfigName": endpoint_config_name, + "ProductionVariants": production_variants, +} + +endpoint_config_res = sm_boto3.create_endpoint_config(**endpoint_config) +``` + +### Launching a SageMaker Endpoint +Once the `EndpointConfig` is defined, the endpoint can be easily launched using +the `create_endpoint` command: + +```python +endpoint_name = "question-answering-example-endpoint" +endpoint_res = sm_boto3.create_endpoint( + EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name +) +``` + +After creating the endpoint, its status can be checked by running the following. +Initially, the `EndpointStatus` will be `Creating`. Checking after the image is +successfully launched, it will be `InService`. If there are any errors, it will +become `Failed`. + +```python +from pprint import pprint +pprint(sm_boto3.describe_endpoint(EndpointName=endpoint_name)) +``` + + +## Making a Request to the Endpoint +After the endpoint is in service, requests can be made to it through the +`invoke_endpoint` api. Inputs will be passed as a JSON payload. + +```python +import json + +sm_runtime = boto3.client("sagemaker-runtime", region_name="us-east-1") + +body = json.dumps( + dict( + question="Where do I live?", + context="I am a student and I live in Cambridge", + ) +) + +content_type = "application/json" +accept = "text/plain" + +res = sm_runtime.invoke_endpoint( + EndpointName=endpoint_name, + Body=body, + ContentType=content_type, + Accept=accept, +) + +print(res["Body"].readlines()) +``` + + +### Cleanup +The model and endpoint can be deleted with the following commands: +```python +sm_boto3.delete_endpoint(EndpointName=endpoint_name) +sm_boto3.delete_endpoint_config(EndpointConfigName=endpoint_config_name) +sm_boto3.delete_model(ModelName=model_name) +``` + +## Next Steps +These steps create an invokable SageMaker inference endpoint powered by the DeepSparse +Engine. The `EndpointConfig` settings may be adjusted to set instance scaling rules based +on deployment needs. + +More information on deploying custom models with SageMaker can be found +[here](https://docs.aws.amazon.com/sagemaker/latest/dg/your-algorithms-inference-code.html). + +Open an [issue](https://github.com/neuralmagic/deepsparse/issues) +or reach out to the [DeepSparse community](https://join.slack.com/t/discuss-neuralmagic/shared_invite/zt-q1a1cnvo-YBoICSIw3L1dmQpjBeDurQ) +with any issues, questions, or ideas. diff --git a/examples/aws-sagemaker/config.yaml b/examples/aws-sagemaker/config.yaml new file mode 100644 index 0000000000..cfca35a27c --- /dev/null +++ b/examples/aws-sagemaker/config.yaml @@ -0,0 +1,5 @@ +models: + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_quant-moderate + batch_size: 1 +integration: sagemaker diff --git a/examples/benchmark/check_correctness.py b/examples/benchmark/check_correctness.py index 67d1af429c..59d6a61ae3 100644 --- a/examples/benchmark/check_correctness.py +++ b/examples/benchmark/check_correctness.py @@ -45,7 +45,7 @@ import argparse from deepsparse import compile_model, cpu -from deepsparse.benchmark_model.ort_engine import ORTEngine +from deepsparse.benchmark.ort_engine import ORTEngine from deepsparse.utils import ( generate_random_inputs, model_to_path, diff --git a/examples/benchmark/run_benchmark.py b/examples/benchmark/run_benchmark.py index 0f3b487155..43d33be815 100644 --- a/examples/benchmark/run_benchmark.py +++ b/examples/benchmark/run_benchmark.py @@ -145,13 +145,13 @@ def main(): inputs, num_iterations, num_warmup_iterations, include_outputs=True ) - for dse_output, ort_output in zip(dse_results.outputs, ort_results.outputs): - verify_outputs(dse_output, ort_output) - print("ONNXRuntime", ort_results) print() print("DeepSparse Engine", dse_results) + for dse_output, ort_output in zip(dse_results.outputs, ort_results.outputs): + verify_outputs(dse_output, ort_output) + if __name__ == "__main__": main() diff --git a/examples/huggingface-transformers/README.md b/examples/huggingface-transformers/README.md index b8871e3f8d..863da102a8 100644 --- a/examples/huggingface-transformers/README.md +++ b/examples/huggingface-transformers/README.md @@ -28,7 +28,7 @@ The DeepSparse-Hugging Face pipeline integration provides a simple API dedicated ```python from deepsparse.transformers import pipeline -model_path = "zoo:nlp/token_classification/bert-base/pytorch/huggingface/conll2003/base-none" +model_path = "zoo:nlp/token_classification/bert-base/pytorch/huggingface/conll2003/12layer_pruned80_quant-none-vnni" token_classification = pipeline( task="token-classification", @@ -43,7 +43,7 @@ inference = token_classification("I saw Snorlax in Texas!") ```python from deepsparse.transformers import pipeline -model_path = "zoo:nlp/text_classification/bert-base/pytorch/huggingface/sst2/base-none" +model_path = "zoo:nlp/sentiment_analysis/bert-base/pytorch/huggingface/sst2/12layer_pruned80_quant-none-vnni" text_classification = pipeline( task="text-classification", @@ -58,7 +58,7 @@ inference = text_classification("Snorlax loves my Tesla!") ```python from deepsparse.transformers import pipeline -model_path="zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned-aggressive_98" +model_path="zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/12layer_pruned80_quant-none-vnni" qa_pipeline = pipeline( task="question-answering", @@ -86,10 +86,10 @@ inference = qa_pipeline(question="What's my name?", context="My name is Snorlax" Want to find out how fast our sparse Hugging Face ONNX models perform inference? You can quickly do benchmarking tests with CLI commands; you only need to provide the model path of a SparseZoo ONNX model or your own local ONNX model to get started: ```bash -deepsparse.benchmark zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned-aggressive_98 +deepsparse.benchmark zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/12layer_pruned80_quant-none-vnni ``` -For a more in-depth discussion on benchmarking, check out the [Benchmarking tutorial](https://github.com/neuralmagic/deepsparse/tree/main/src/deepsparse/benchmark_model)! +For a more in-depth discussion on benchmarking, check out the [Benchmarking tutorial](https://github.com/neuralmagic/deepsparse/tree/main/src/deepsparse/benchmark)! ### 🔌 DeepSparse Server @@ -106,5 +106,5 @@ Example CLI command: ```bash deepsparse.server \ --task question_answering \ - --model_path "zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned-aggressive_98" -``` \ No newline at end of file + --model_path "zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/12layer_pruned80_quant-none-vnni" +``` diff --git a/examples/sparseserver-ui/README.md b/examples/sparseserver-ui/README.md new file mode 100644 index 0000000000..8810b84b74 --- /dev/null +++ b/examples/sparseserver-ui/README.md @@ -0,0 +1,88 @@ + +![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+) [**NEURAL MAGIC**](https://neuralmagic.com) ![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+) + + ███████╗██████╗ █████╗ ██████╗ ███████╗███████╗ ███████╗███████╗██████╗ ██╗ ██╗███████╗██████╗ ██╗ ██╗ ██╗ + ██╔════╝██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔════╝ ██╔════╝██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗ ██║ ██║ ██║ + ███████╗██████╔╝███████║██████╔╝███████╗█████╗ ███████╗█████╗ ██████╔╝██║ ██║█████╗ ██████╔╝ ██║ ██║ ██║ + ╚════██║██╔═══╝ ██╔══██║██╔══██╗╚════██║██╔══╝ ╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██╔══╝ ██╔══██╗ ██║ ██║ ██║ + ███████║██║ ██║ ██║██║ ██║███████║███████╗ ███████║███████╗██║ ██║ ╚████╔╝ ███████╗██║ ██║ ██╗ ╚██████╔ ██║ + ╚══════╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ + + + *** A Streamlit app for deploying the DeepSparse Server *** +![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+)![#00F](https://via.placeholder.com/15/00F/000000?text=+) + + +##
`INTRO`
+ + + +
+SparseServer.UI allows you to serve a streamlit app running on top of the DeepSparse Server for comparing the latency speeds of sparse transformer models. The purpose of this app is for you to familiarize and compare the inference performance of transformers trained with various sparse approaches. +
+ +
+ +[Getting Started with the DeepSparse Server](https://github.com/neuralmagic/deepsparse/tree/main/src/deepsparse/server) + +
+ +![alt text](./img/demo_screenshot.png) + +
+ +##
`INSTALLATION`
+ +```bash +git clone https://github.com/neuralmagic/deepsparse.git +cd deepsparse/examples/sparseserver-ui +pip install -r requirements.txt +``` +
+ +The `config.yaml` file in the `server` directory includes a list of four BERT QA models for the DeepSparse Server to get started. If you prefer to add additional models to the `config.yaml` file, make sure to also add a `MultiPipelineClient` object to the `variants` attribute in the `settings.py` module. + +Currently, the SparseZoo holds a vast list of BERT models, and the `big-config.yaml` file contains 19 models in case you want to load them 🤯. To load all of the 19 models at once, make sure you have at least 16GB of RAM available, otherwise you will get out of memory errors. In addition, uncomment the pipelines in the `settings.py` module. + +For more details on question answering models, please refer to our [updated list](https://sparsezoo.neuralmagic.com/?domain=nlp&sub_domain=question_answering&page=1). + +##
`START SERVER`
+ +To download and initialize the four models in the `config.yaml` file, run: +```bash +deepsparse.server --config_file server/config.yaml +``` + +After downloading, the DeepSparse Server should now be running on host `0.0.0.0` and port `5543`. + +##
`START CLIENT`
+ +Open a new terminal (make sure you are in your environment) and run the following command to start the Streamlit app: + +```bash +streamlit run client/app.py --browser.serverAddress="localhost" +``` + +This will start the Streamlit app on `localhost` and port `8501`. +Visit `http://localhost:8501` in your browser to view the demo. + +### Testing + +- 19 models should fit on 16GB RAM of a c2-standard-4 VM instance on GCP +- Ubuntu 20.04.4 LTS +- Python 3.8.10 +
diff --git a/examples/sparseserver-ui/client/app.py b/examples/sparseserver-ui/client/app.py new file mode 100644 index 0000000000..8e8a7b323d --- /dev/null +++ b/examples/sparseserver-ui/client/app.py @@ -0,0 +1,54 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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 time import perf_counter + +import streamlit as st +from samples import sample +from settings import FeatureHandler as feat + + +# Titles +st.markdown(feat.title, unsafe_allow_html=True) +st.markdown(feat.subtitle, unsafe_allow_html=True) + +# Sidebar +st.sidebar.selectbox(feat.tasks_desc, feat.tasks) +model_choice = st.sidebar.radio(feat.variants_desc, feat.variants.keys()) +st.sidebar.markdown(feat.code_banner) +st.sidebar.code(body=feat.code_text, language=feat.language) +st.sidebar.markdown(feat.repo_test) + +# Footer +st.markdown(feat.footer, unsafe_allow_html=True) + +# Inference +model = feat.variants[model_choice] +selection = st.selectbox(feat.example_index_label, feat.example_index) +context = st.text_area( + label=feat.example_context_label, value=sample[selection]["context"], height=300 +) +question = st.text_area( + label=feat.example_question_label, value=sample[selection]["question"] +) +start = perf_counter() +answer = model(question=question, context=context) +end = perf_counter() +infer_time = end - start +infer_time = round(infer_time, 4) +st.markdown(feat.markdown_style, unsafe_allow_html=True) +st.markdown( + f'

ANSWER: {answer["answer"]}

', unsafe_allow_html=True +) +st.markdown(f'

{infer_time} secs.

', unsafe_allow_html=True) diff --git a/examples/sparseserver-ui/client/pipelineclient.py b/examples/sparseserver-ui/client/pipelineclient.py new file mode 100644 index 0000000000..dda88347ad --- /dev/null +++ b/examples/sparseserver-ui/client/pipelineclient.py @@ -0,0 +1,47 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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 json +from typing import List + +import numpy +import requests + + +class MultiPipelineClient: + """ + Client object for making requests to the example DeepSparse BERT inference server + + :param model: model alias of FastAPI route + :param address: IP address of the server, default is 0.0.0.0 + :param port: Port the server is hosted on, default is 5543 + """ + + def __init__(self, model: str, address: str = "0.0.0.0", port: str = "5543"): + + self.model = model + self._url = f"http://{address}:{port}/predict/{self.model}" + + def __call__(self, **kwargs) -> List[numpy.ndarray]: + + """ + :param kwargs: named inputs to the model server pipeline. e.g. for + question-answering - `question="...", context="..." + + :return: json outputs from running the model server pipeline with the given + input(s) + """ + + response = requests.post(self._url, json=kwargs) + return json.loads(response.content) diff --git a/examples/sparseserver-ui/client/samples.py b/examples/sparseserver-ui/client/samples.py new file mode 100644 index 0000000000..a3e09bed5d --- /dev/null +++ b/examples/sparseserver-ui/client/samples.py @@ -0,0 +1,55 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +sample = { + "example 1": { + "context": ( + "The DeepSparse Engine is a CPU runtime that delivers " + "GPU-class performance by taking advantage of sparsity within neural " + "networks to reduce compute required as well as accelerate memory bound " + "workloads. It is focused on model deployment and scaling machine " + "learning pipelines, fitting seamlessly into your existing deployments " + "as an inference backend. " + ), + "question": ( + "What does the DeepSparse Engine take advantage of within neural networks?" + ), + }, + "example 2": { + "context": ( + "Concerns were raised over whether Levi's Stadium's field was of a high " + "enough quality to host a Super Bowl; during the inaugural season, the " + "field had to be re-sodded multiple times due to various issues, and " + "during a week 6 game earlier in the 2015 season, a portion of the turf " + "collapsed under Baltimore Ravens kicker Justin Tucker, causing him " + "to slip and miss a field goal. " + ), + "question": ("What collapsed on Justin Tucker?"), + }, + "example 3": { + "context": ( + "The league announced on October 16, 2012, that the two finalists were Sun " + "Life Stadium and Levi's Stadium. The South Florida/Miami area has " + "previously hosted the event 10 times (tied for most with New Orleans), " + "with the most recent one being Super Bowl XLIV in 2010. The San Francisco " + "Bay Area last hosted in 1985 (Super Bowl XIX), held at Stanford Stadium " + "in Stanford, California, won by the home team 49ers. The Miami bid " + "depended on whether the stadium underwent renovations. " + ), + "question": ( + "What was the most recent Super Bowl that took place at Sun " + "Life Stadium in Miami?" + ), + }, +} diff --git a/examples/sparseserver-ui/client/settings.py b/examples/sparseserver-ui/client/settings.py new file mode 100644 index 0000000000..5cb71a5d53 --- /dev/null +++ b/examples/sparseserver-ui/client/settings.py @@ -0,0 +1,128 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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 pipelineclient import MultiPipelineClient + + +class FeatureHandler: + + """ + Class with front-end streamlit content features. + """ + + tasks_desc = "Select task:" + tasks = [ + "Question Answering", + ] + + variants_desc = "Select your sparse model:" + variants = { + "12-Layer BERT Base, Not Sparsified 😢": MultiPipelineClient( + model="question_answering/base" + ), + "12-Layer BERT, Quantized, 99% of Base Accuracy": MultiPipelineClient( + model="question_answering/12l_pruned80_quant" + ), + "6-Layer BERT, Quantized, 96% of Base Accuracy": MultiPipelineClient( + model="question_answering/quant6lagg96" + ), + "3-Layer BERT, Quantized, 89% of Base Accuracy": MultiPipelineClient( + model="question_answering/quant3lagg89" + ), + # "12-Layer BERT, Quantized, 95% of Base Accuracy": MultiPipelineClient( + # model="question_answering/pruned_quant" + # ), + # "12-Layer BERT, Quantized, 99% of Base Accuracy": MultiPipelineClient( + # model="question_answering/quantmod" + # ), + # "12-Layer BERT, 98% of Base Accuracy ": MultiPipelineClient( + # model="question_answering/agg98" + # ), + # "12-Layer BERT, 94% of Base Accuracy ": MultiPipelineClient( + # model="question_answering/agg94" + # ), + # "12-Layer BERT, 100% of Base Accuracy": MultiPipelineClient( + # model="question_answering/conserv" + # ), + # "6-Layer BERT, Quantized, 91% of Base Accuracy": MultiPipelineClient( + # model="question_answering/quant6lagg91" + # ), + # "6-Layer BERT, 98% of Base Accuracy": MultiPipelineClient( + # model="question_answering/6lagg98" + # ), + # "6-Layer BERT, 97% of Base Accuracy": MultiPipelineClient( + # model="question_answering/6lagg97" + # ), + # "6-Layer BERT, 96% of Base Accuracy": MultiPipelineClient( + # model="question_answering/6lagg96" + # ), + # "6-Layer BERT, 94% of Base Accuracy": MultiPipelineClient( + # model="question_answering/6lagg94" + # ), + # "3-Layer BERT, Quantized, 84% of Base Accuracy": MultiPipelineClient( + # model="question_answering/quant3lagg84" + # ), + # "3-Layer BERT, 90% of Base Accuracy": MultiPipelineClient( + # model="question_answering/3lagg90" + # ), + # "3-Layer BERT, 89% of Base Accuracy": MultiPipelineClient( + # model="question_answering/3lagg89" + # ), + # "3-Layer BERT, 86% of Base Accuracy": MultiPipelineClient( + # model="question_answering/3lagg86" + # ), + # "3-Layer BERT, 83% of Base Accuracy": MultiPipelineClient( + # model="question_answering/3lagg83" + # ), + } + + title = "

✨ Neural Magic ✨

" + subtitle = "

DeepSparse Server

" + + code_banner = "Get started with faster inference 👇" + code_text = "pip install deepsparse[server]" + language = "python" + repo_test = ( + "For code: [DeepSparse repo](https://github.com/neuralmagic/deepsparse)." + ) + + example_context_label = "Enter Context" + example_question_label = "Enter Question" + example_index_label = "Choose an example" + example_index = ["example 1", "example 2", "example 3"] + markdown_style = """ + + """ + footer = """ + + + """ diff --git a/examples/sparseserver-ui/img/demo_screenshot.png b/examples/sparseserver-ui/img/demo_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6ee72f6161d557e81e3aaeaad2c46da988b2e9f4 GIT binary patch literal 491015 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
>;Hzzg2{r|hQm{Qcdn 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鍧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�W0e3*R!wWH*SqOz=AdPI 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 BL>B-6 literal 0 HcmV?d00001 diff --git a/examples/sparseserver-ui/requirements.txt b/examples/sparseserver-ui/requirements.txt new file mode 100644 index 0000000000..3b75e7e6a2 --- /dev/null +++ b/examples/sparseserver-ui/requirements.txt @@ -0,0 +1,2 @@ +deepsparse[server]>=0.11 +streamlit==1.8.1 \ No newline at end of file diff --git a/examples/sparseserver-ui/server/big-config.yaml b/examples/sparseserver-ui/server/big-config.yaml new file mode 100644 index 0000000000..ade88d1252 --- /dev/null +++ b/examples/sparseserver-ui/server/big-config.yaml @@ -0,0 +1,91 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +models: + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_quant-aggressive_95 + batch_size: 1 + alias: question_answering/pruned_quant + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_quant-moderate + batch_size: 1 + alias: question_answering/quantmod + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned-aggressive_98 + batch_size: 1 + alias: question_answering/agg98 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned-aggressive_94 + batch_size: 1 + alias: question_answering/agg94 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned-conservative + batch_size: 1 + alias: question_answering/conserv + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_quant_6layers-aggressive_96 + batch_size: 1 + alias: question_answering/quant6lagg96 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_quant_6layers-aggressive_91 + batch_size: 1 + alias: question_answering/quant6lagg91 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_6layers-aggressive_98 + batch_size: 1 + alias: question_answering/6lagg98 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_6layers-aggressive_97 + batch_size: 1 + alias: question_answering/6lagg97 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_6layers-aggressive_96 + batch_size: 1 + alias: question_answering/6lagg96 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_6layers-aggressive_94 + batch_size: 1 + alias: question_answering/6lagg94 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_quant_3layers-aggressive_89 + batch_size: 1 + alias: question_answering/quant3lagg89 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_quant_3layers-aggressive_84 + batch_size: 1 + alias: question_answering/quant3lagg84 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_3layers-aggressive_90 + batch_size: 1 + alias: question_answering/3lagg90 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_3layers-aggressive_89 + batch_size: 1 + alias: question_answering/3lagg89 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_3layers-aggressive_86 + batch_size: 1 + alias: question_answering/3lagg86 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_3layers-aggressive_83 + batch_size: 1 + alias: question_answering/3lagg83 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/12layer_pruned80_quant-none-vnni + batch_size: 1 + alias: question_answering/12l_pruned80_quant + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/base-none + batch_size: 1 + alias: question_answering/base diff --git a/examples/sparseserver-ui/server/config.yaml b/examples/sparseserver-ui/server/config.yaml new file mode 100644 index 0000000000..fe63b81d18 --- /dev/null +++ b/examples/sparseserver-ui/server/config.yaml @@ -0,0 +1,32 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +models: + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/12layer_pruned80_quant-none-vnni + batch_size: 1 + alias: question_answering/12l_pruned80_quant + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_quant_6layers-aggressive_96 + batch_size: 1 + alias: question_answering/quant6lagg96 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/pruned_quant_3layers-aggressive_89 + batch_size: 1 + alias: question_answering/quant3lagg89 + - task: question_answering + model_path: zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/base-none + batch_size: 1 + alias: question_answering/base + \ No newline at end of file diff --git a/setup.py b/setup.py index 855f119dd5..c6299ca473 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,7 @@ _deps = [ "numpy>=1.16.3", "onnx>=1.5.0,<=1.10.1", + "pydantic>=1.8.2", "requests>=2.0.0", "tqdm>=4.0.0", "protobuf>=3.12.2", @@ -73,7 +74,6 @@ _server_deps = [ "uvicorn>=0.15.0", "fastapi>=0.70.0", - "starlette>=0.16.0", "pydantic>=1.8.2", "requests>=2.26.0", ] @@ -81,6 +81,16 @@ "onnxruntime>=1.7.0", ] +_ic_integration_deps = [ + "click<8.1", + "opencv-python", +] + +_yolo_integration_deps = [ + "torchvision>=0.3.0,<=0.10.1", + "opencv-python", +] + class OverrideInstall(install): """ @@ -173,20 +183,24 @@ def _setup_extras() -> Dict: "dev": _dev_deps, "server": _server_deps, "onnxruntime": _onnxruntime_deps, + "image_classification": _ic_integration_deps, + "yolo": _yolo_integration_deps, } def _setup_entry_points() -> Dict: data_api_entrypoint = "deepsparse.transformers.pipelines_cli:cli" eval_downstream = "deepsparse.transformers.eval_downstream:main" + return { "console_scripts": [ f"deepsparse.transformers.run_inference={data_api_entrypoint}", f"deepsparse.transformers.eval_downstream={eval_downstream}", "deepsparse.analyze=deepsparse.analyze:main", "deepsparse.check_hardware=deepsparse.cpu:print_hardware_capability", - "deepsparse.benchmark=deepsparse.benchmark_model.benchmark_model:main", + "deepsparse.benchmark=deepsparse.benchmark.benchmark_model:main", "deepsparse.server=deepsparse.server.main:start_server", + "deepsparse.object_detection.annotate=deepsparse.yolo.annotate:main", ] } diff --git a/src/deepsparse/__init__.py b/src/deepsparse/__init__.py index 3d3113b74b..d9c28dc591 100644 --- a/src/deepsparse/__init__.py +++ b/src/deepsparse/__init__.py @@ -31,6 +31,7 @@ cpu_vnni_compatible, ) from .engine import * +from .pipeline import * from .version import __version__, is_release diff --git a/src/deepsparse/benchmark_model/README.md b/src/deepsparse/benchmark/README.md similarity index 100% rename from src/deepsparse/benchmark_model/README.md rename to src/deepsparse/benchmark/README.md diff --git a/src/deepsparse/benchmark/__init__.py b/src/deepsparse/benchmark/__init__.py new file mode 100644 index 0000000000..4900c1c550 --- /dev/null +++ b/src/deepsparse/benchmark/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +# flake8: noqa + +from .ort_engine import ORTEngine +from .results import * diff --git a/src/deepsparse/benchmark_model/benchmark_model.py b/src/deepsparse/benchmark/benchmark_model.py similarity index 98% rename from src/deepsparse/benchmark_model/benchmark_model.py rename to src/deepsparse/benchmark/benchmark_model.py index 4ff874b0c6..caed1a5c28 100644 --- a/src/deepsparse/benchmark_model/benchmark_model.py +++ b/src/deepsparse/benchmark/benchmark_model.py @@ -97,8 +97,8 @@ import os from deepsparse import Scheduler, compile_model -from deepsparse.benchmark_model.ort_engine import ORTEngine -from deepsparse.benchmark_model.stream_benchmark import model_stream_benchmark +from deepsparse.benchmark.ort_engine import ORTEngine +from deepsparse.benchmark.stream_benchmark import model_stream_benchmark from deepsparse.log import set_logging_level from deepsparse.utils import ( generate_random_inputs, diff --git a/src/deepsparse/benchmark_model/img/json_output.png b/src/deepsparse/benchmark/img/json_output.png similarity index 100% rename from src/deepsparse/benchmark_model/img/json_output.png rename to src/deepsparse/benchmark/img/json_output.png diff --git a/src/deepsparse/benchmark_model/ort_engine.py b/src/deepsparse/benchmark/ort_engine.py similarity index 100% rename from src/deepsparse/benchmark_model/ort_engine.py rename to src/deepsparse/benchmark/ort_engine.py diff --git a/src/deepsparse/benchmark.py b/src/deepsparse/benchmark/results.py similarity index 100% rename from src/deepsparse/benchmark.py rename to src/deepsparse/benchmark/results.py diff --git a/src/deepsparse/benchmark_model/stream_benchmark.py b/src/deepsparse/benchmark/stream_benchmark.py similarity index 100% rename from src/deepsparse/benchmark_model/stream_benchmark.py rename to src/deepsparse/benchmark/stream_benchmark.py diff --git a/src/deepsparse/benchmark_model/__init__.py b/src/deepsparse/image_classification/__init__.py similarity index 100% rename from src/deepsparse/benchmark_model/__init__.py rename to src/deepsparse/image_classification/__init__.py diff --git a/src/deepsparse/image_classification/constants.py b/src/deepsparse/image_classification/constants.py new file mode 100644 index 0000000000..d035e44513 --- /dev/null +++ b/src/deepsparse/image_classification/constants.py @@ -0,0 +1,16 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +IMAGENET_RGB_MEANS = [0.485, 0.456, 0.406] +IMAGENET_RGB_STDS = [0.229, 0.224, 0.225] diff --git a/src/deepsparse/image_classification/pipelines.py b/src/deepsparse/image_classification/pipelines.py new file mode 100644 index 0000000000..e085937728 --- /dev/null +++ b/src/deepsparse/image_classification/pipelines.py @@ -0,0 +1,197 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +""" +Image classification pipeline +""" +import json +from typing import Dict, List, Optional, Tuple, Type, Union + +import numpy +import onnx + +from deepsparse.image_classification.constants import ( + IMAGENET_RGB_MEANS, + IMAGENET_RGB_STDS, +) +from deepsparse.image_classification.schemas import ( + ImageClassificationInput, + ImageClassificationOutput, +) +from deepsparse.pipeline import Pipeline +from deepsparse.utils import model_to_path + + +try: + import cv2 + + cv2_error = None +except ModuleNotFoundError as cv2_import_error: + cv2 = None + cv2_error = cv2_import_error + + +@Pipeline.register( + task="image_classification", + default_model_path=( + "zoo:cv/classification/resnet_v1-50/pytorch/sparseml/" + "imagenet/pruned85_quant-none-vnni" + ), +) +class ImageClassificationPipeline(Pipeline): + """ + Image classification pipeline for DeepSparse + + :param model_path: path on local system or SparseZoo stub to load the model from + :param engine_type: inference engine to use. Currently supported values include + 'deepsparse' and 'onnxruntime'. Default is 'deepsparse' + :param batch_size: static batch size to use for inference. Default is 1 + :param num_cores: number of CPU cores to allocate for inference engine. None + specifies all available cores. Default is None + :param scheduler: (deepsparse only) kind of scheduler to execute with. + Pass None for the default + :param input_shapes: list of shapes to set ONNX the inputs to. Pass None + to use model as-is. Default is None + :param alias: optional name to give this pipeline instance, useful when + inferencing with multiple models. Default is None + :param class_names: Optional dict, or json file of class names to use for + mapping class ids to class labels. Default is None + """ + + def __init__( + self, + *, + class_names: Union[None, str, Dict[str, str]] = None, + **kwargs, + ): + super().__init__(**kwargs) + + if isinstance(class_names, str) and class_names.endswith(".json"): + self._class_names = json.load(open(class_names)) + elif isinstance(class_names, dict): + self._class_names = class_names + else: + self._class_names = None + + self._image_size = self._infer_image_size() + + @property + def class_names(self) -> Optional[Dict[str, str]]: + """ + :return: Optional dict, or json file of class names to use for + mapping class ids to class labels + """ + return self._class_names + + @property + def input_schema(self) -> Type[ImageClassificationInput]: + """ + :return: pydantic model class that inputs to this pipeline must comply to + """ + return ImageClassificationInput + + @property + def output_schema(self) -> Type[ImageClassificationOutput]: + """ + :return: pydantic model class that outputs of this pipeline must comply to + """ + return ImageClassificationOutput + + def setup_onnx_file_path(self) -> str: + """ + Performs any setup to unwrap and process the given `model_path` and other + class properties into an inference ready onnx file to be compiled by the + engine of the pipeline + + :return: file path to the ONNX file for the engine to compile + """ + + return model_to_path(self.model_path) + + def process_inputs(self, inputs: ImageClassificationInput) -> List[numpy.ndarray]: + """ + Pre-Process the Inputs for DeepSparse Engine + + :param inputs: input model + :return: list of preprocessed numpy arrays + """ + + if isinstance(inputs.images, numpy.ndarray): + image_batch = inputs.images + else: + + image_batch = [] + + if isinstance(inputs.images, str): + inputs.images = [inputs.images] + + for image in inputs.images: + if cv2 is None: + raise RuntimeError( + "cv2 is required to load image inputs from file " + f"Unable to import: {cv2_error}" + ) + img = cv2.imread(image) if isinstance(image, str) else image + + img = cv2.resize(img, dsize=self._image_size) + img = img[:, :, ::-1].transpose(2, 0, 1) + image_batch.append(img) + + image_batch = numpy.stack(image_batch, axis=0) + + original_dtype = image_batch.dtype + image_batch = numpy.ascontiguousarray(image_batch, dtype=numpy.float32) + + if original_dtype == numpy.uint8: + + image_batch /= 255 + + # normalize entire batch + image_batch -= numpy.asarray(IMAGENET_RGB_MEANS).reshape((-1, 3, 1, 1)) + image_batch /= numpy.asarray(IMAGENET_RGB_STDS).reshape((-1, 3, 1, 1)) + + return [image_batch] + + def process_engine_outputs( + self, + engine_outputs: List[numpy.ndarray], + ) -> ImageClassificationOutput: + """ + :param engine_outputs: list of numpy arrays that are the output of the engine + forward pass + :return: outputs of engine post-processed into an object in the `output_schema` + format of this pipeline + """ + labels = numpy.argmax(engine_outputs[0], axis=1).tolist() + + if self.class_names is not None: + labels = [self.class_names[str(class_id)] for class_id in labels] + + return self.output_schema( + scores=numpy.max(engine_outputs[0], axis=1).tolist(), + labels=labels, + ) + + def _infer_image_size(self) -> Tuple[int, ...]: + """ + Infer and return the expected shape of the input tensor + + :return: The expected shape of the input tensor from onnx graph + """ + onnx_model = onnx.load(self.onnx_file_path) + input_tensor = onnx_model.graph.input[0] + return ( + input_tensor.type.tensor_type.shape.dim[2].dim_value, + input_tensor.type.tensor_type.shape.dim[3].dim_value, + ) diff --git a/src/deepsparse/image_classification/schemas.py b/src/deepsparse/image_classification/schemas.py new file mode 100644 index 0000000000..5a92b90e3b --- /dev/null +++ b/src/deepsparse/image_classification/schemas.py @@ -0,0 +1,42 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +""" +Input/Output Schemas for Image Classification. +""" + +from typing import List, Union + +import numpy +from pydantic import BaseModel + + +class ImageClassificationInput(BaseModel): + """ + Input model for image classification + """ + + images: Union[str, numpy.ndarray, List[str]] + + class Config: + arbitrary_types_allowed = True + + +class ImageClassificationOutput(BaseModel): + """ + Output model for image classification + """ + + labels: List[Union[int, str]] + scores: List[float] diff --git a/src/deepsparse/image_classification/validation_script.py b/src/deepsparse/image_classification/validation_script.py new file mode 100644 index 0000000000..e176b4072c --- /dev/null +++ b/src/deepsparse/image_classification/validation_script.py @@ -0,0 +1,162 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +""" +Usage: validation_script.py [OPTIONS] + + Validation Script for Image Classification Models + +Options: + --dataset-path, --dataset_path DIRECTORY + Path to the validation dataset [required] + --model-path, --model_path TEXT + Path/SparseZoo stub for the Image + Classification model to be evaluated. + Defaults to resnet50 trained on + Imagenette [default: zoo:cv/classification/ + resnet_v1-50/pytorch/sparseml/imagenette/ + base-none] + --batch-size, --batch_size INTEGER + Test batch size, must divide the dataset + evenly, else the last batch will be dropped + [default: 1] + --help Show this message and exit. + +######### +EXAMPLES +######### + +########## +Example command for validating pruned resnet50 on imagenette dataset: +python validation_script.py \ + --dataset-path /path/to/imagenette/ + +""" +from tqdm import tqdm + +from deepsparse.pipeline import Pipeline +from torch.utils.data import DataLoader +from torchvision import transforms + + +try: + import torchvision + +except ModuleNotFoundError as torchvision_error: # noqa: F841 + print( + "Torchvision not installed. Please install it using the command:" + "pip install torchvision>=0.3.0,<=0.10.1" + ) + exit(1) + +import click + + +resnet50_imagenet_pruned = ( + "zoo:cv/classification/resnet_v1-50/pytorch/sparseml/imagenette/base-none" +) + + +@click.command() +@click.option( + "--dataset-path", + "--dataset_path", + required=True, + type=click.Path(dir_okay=True, file_okay=False), + help="Path to the validation dataset", +) +@click.option( + "--model-path", + "--model_path", + type=str, + default=resnet50_imagenet_pruned, + help="Path/SparseZoo stub for the Image Classification model to be " + "evaluated. Defaults to dense (vanilla) resnet50 trained on Imagenette", + show_default=True, +) +@click.option( + "--batch-size", + "--batch_size", + type=int, + default=1, + show_default=True, + help="Test batch size, must divide the dataset evenly, else last " + "batch will be dropped", +) +@click.option( + "--image-size", + "--image_size", + type=int, + default=224, + show_default=True, + help="Test batch size, must divide the dataset evenly, else last " + "batch will be dropped", +) +def main(dataset_path: str, model_path: str, batch_size: int, image_size: int): + """ + Validation Script for Image Classification Models + """ + + dataset = torchvision.datasets.ImageFolder( + root=dataset_path, + transform=transforms.Compose( + [ + transforms.ToTensor(), + transforms.Resize(size=(image_size, image_size)), + ] + ), + ) + + data_loader = DataLoader( + dataset=dataset, + batch_size=batch_size, + drop_last=True, + ) + + pipeline = Pipeline.create( + task="image_classification", + model_path=model_path, + batch_size=batch_size, + ) + correct = total = 0 + progress_bar = tqdm(data_loader) + + for batch in progress_bar: + batch, actual_labels = batch + batch = batch.numpy() + outs = pipeline(images=batch) + predicted_labels = outs.labels + + for actual, predicted in zip(actual_labels, predicted_labels): + total += 1 + if isinstance(predicted, str): + predicted = int(predicted) + if actual.item() == predicted: + correct += 1 + + if total > 0: + progress_bar.set_postfix( + {"Running Accuracy": f"{correct * 100 / total:.2f}%"} + ) + + # prevent division by zero + if total == 0: + epsilon = 1e-5 + total += epsilon + + print(f"Accuracy: {correct * 100 / total:.2f} %") + + +if __name__ == "__main__": + main() diff --git a/src/deepsparse/pipeline.py b/src/deepsparse/pipeline.py new file mode 100644 index 0000000000..5ab6b9ec63 --- /dev/null +++ b/src/deepsparse/pipeline.py @@ -0,0 +1,546 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +""" +Classes and registry for end to end inference pipelines that wrap an underlying +inference engine and include pre/postprocessing +""" + + +import os +from abc import ABC, abstractmethod +from pathlib import Path +from typing import Any, Dict, List, Optional, Tuple, Type, Union + +import numpy +from pydantic import BaseModel, Field + +from deepsparse import Engine, Scheduler +from deepsparse.benchmark import ORTEngine +from deepsparse.tasks import SupportedTasks + + +__all__ = [ + "DEEPSPARSE_ENGINE", + "ORT_ENGINE", + "SUPPORTED_PIPELINE_ENGINES", + "Pipeline", + "PipelineConfig", +] + + +DEEPSPARSE_ENGINE = "deepsparse" +ORT_ENGINE = "onnxruntime" + +SUPPORTED_PIPELINE_ENGINES = [DEEPSPARSE_ENGINE, ORT_ENGINE] + + +_REGISTERED_PIPELINES = {} + + +class Pipeline(ABC): + """ + Generic Pipeline abstract class meant to wrap inference engine objects to include + data pre/post-processing. Inputs and outputs of pipelines should be serialized + as pydantic Models. + + Pipelines should not be instantiated by their constructors, but rather the + `Pipeline.create()` method. The task name given to `create` will be used to + load the appropriate pipeline. When creating a Pipeline, the pipeline should + inherit from `Pipeline` and implement the `setup_onnx_file_path`, `process_inputs`, + `process_engine_outputs`, `input_schema`, and `output_schema` abstract methods. + + Finally, the class definition should be decorated by the `Pipeline.register` + function. This defines the task name and task aliases for the pipeline and + ensures that it will be accessible by `Pipeline.create`. The implemented + `Pipeline` subclass must be imported at runtime to be accessible. + + Pipeline lifecycle: + - On instantiation + * `onnx_file_path` <- `setup_onnx_file_path` + * `engine` <- `_initialize_engine` + + - on __call__: + * `parsed_inputs: input_schema` <- `parse_inputs(*args, **kwargs)` + * `pre_processed_inputs` <- `process_inputs(parsed_inputs)` + * `engine_outputs` <- `engine(pre_processed_inputs)` + * `outputs: output_schema` <- `process_engine_outputs(engine_outputs)` + + Example use of register: + ```python + @Pipeline.register( + task="example_task", + task_aliases=["example_alias_1", "example_alias_2"], + ) + class PipelineImplementation(Pipeline): + # implementation of Pipeline abstract methods here + ``` + + Example use of pipeline: + ```python + example_pipeline = Pipeline.create( + task="example_task", + model_path="model.onnx", + ) + pipeline_outputs = example_pipeline(pipeline_inputs) + ``` + + :param model_path: path on local system or SparseZoo stub to load the model from + :param engine_type: inference engine to use. Currently supported values include + 'deepsparse' and 'onnxruntime'. Default is 'deepsparse' + :param batch_size: static batch size to use for inference. Default is 1 + :param num_cores: number of CPU cores to allocate for inference engine. None + specifies all available cores. Default is None + :param scheduler: (deepsparse only) kind of scheduler to execute with. + Pass None for the default + :param input_shapes: list of shapes to set ONNX the inputs to. Pass None + to use model as-is. Default is None + :param alias: optional name to give this pipeline instance, useful when + inferencing with multiple models. Default is None + """ + + def __init__( + self, + model_path: str, + engine_type: str = DEEPSPARSE_ENGINE, + batch_size: int = 1, + num_cores: int = None, + scheduler: Scheduler = None, + input_shapes: List[List[int]] = None, + alias: Optional[str] = None, + ): + self._model_path_orig = model_path + self._model_path = model_path + self._engine_type = engine_type + self._alias = alias + + self._engine_args = dict( + batch_size=batch_size, + num_cores=num_cores, + input_shapes=input_shapes, + ) + if engine_type.lower() == DEEPSPARSE_ENGINE: + self._engine_args["scheduler"] = scheduler + + self.onnx_file_path = self.setup_onnx_file_path() + self.engine = self._initialize_engine() + + def __call__(self, *args, **kwargs) -> BaseModel: + # parse inputs into input_schema schema if necessary + pipeline_inputs = self.parse_inputs(*args, **kwargs) + if not isinstance(pipeline_inputs, self.input_schema): + raise RuntimeError( + f"Unable to parse {self.__class__} inputs into a " + f"{self.input_schema} object. Inputs parsed to {type(pipeline_inputs)}" + ) + + # run pipeline + engine_inputs: List[numpy.ndarray] = self.process_inputs(pipeline_inputs) + + if isinstance(engine_inputs, tuple): + engine_inputs, postprocess_kwargs = engine_inputs + else: + postprocess_kwargs = {} + + engine_outputs: List[numpy.ndarray] = self.engine(engine_inputs) + pipeline_outputs = self.process_engine_outputs( + engine_outputs, **postprocess_kwargs + ) + + # validate outputs format + if not isinstance(pipeline_outputs, self.output_schema): + raise ValueError( + f"Outputs of {self.__class__} must be instances of " + f"{self.output_schema} found output of type {type(pipeline_outputs)}" + ) + + return pipeline_outputs + + @staticmethod + def create( + task: str, + model_path: str = None, + engine_type: str = DEEPSPARSE_ENGINE, + batch_size: int = 1, + num_cores: int = None, + scheduler: Scheduler = None, + input_shapes: List[List[int]] = None, + alias: Optional[str] = None, + **kwargs, + ) -> "Pipeline": + """ + :param task: name of task to create a pipeline for + :param model_path: path on local system or SparseZoo stub to load the model + from. Some tasks may have a default model path + :param engine_type: inference engine to use. Currently supported values + include 'deepsparse' and 'onnxruntime'. Default is 'deepsparse' + :param batch_size: static batch size to use for inference. Default is 1 + :param num_cores: number of CPU cores to allocate for inference engine. None + specifies all available cores. Default is None + :param scheduler: (deepsparse only) kind of scheduler to execute with. + Pass None for the default + :param input_shapes: list of shapes to set ONNX the inputs to. Pass None + to use model as-is. Default is None + :param alias: optional name to give this pipeline instance, useful when + inferencing with multiple models. Default is None + :param kwargs: extra task specific kwargs to be passed to task Pipeline + implementation + :return: pipeline object initialized for the given task + """ + task = task.lower().replace("-", "_") + + # extra step to register pipelines for a given task domain + # for cases where imports should only happen once a user specifies + # that domain is to be used. (ie deepsparse.transformers will auto + # install extra packages so should only import and register once a + # transformers task is specified) + SupportedTasks.check_register_task(task) + + if task not in _REGISTERED_PIPELINES: + raise ValueError( + f"Unknown Pipeline task {task}. Pipeline tasks should be " + "must be declared with the Pipeline.register decorator. Currently " + f"registered pipelines: {list(_REGISTERED_PIPELINES.keys())}" + ) + + pipeline_constructor = _REGISTERED_PIPELINES[task] + + if ( + model_path is None + and hasattr(pipeline_constructor, "default_model_path") + and pipeline_constructor.default_model_path + ): + model_path = pipeline_constructor.default_model_path + + if model_path is None: + raise ValueError( + f"No model_path provided for pipeline {pipeline_constructor}. Must " + "provide a model path for pipelines that do not have a default defined" + ) + + return pipeline_constructor( + model_path=model_path, + engine_type=engine_type, + batch_size=batch_size, + num_cores=num_cores, + scheduler=scheduler, + input_shapes=input_shapes, + alias=alias, + **kwargs, + ) + + @classmethod + def register( + cls, + task: str, + task_aliases: Optional[List[str]] = None, + default_model_path: Optional[str] = None, + ): + """ + Pipeline implementer class decorator that registers the pipeline + task name and its aliases as valid tasks that can be used to load + the pipeline through `Pipeline.create()`. + + Multiple pipelines may not have the same task name. An error will + be raised if two different pipelines attempt to register the same task name + + :param task: main task name of this pipeline + :param task_aliases: list of extra task names that may be used to reference + this pipeline. Default is None + :param default_model_path: path (ie zoo stub) to use as default for this + task if None is provided + """ + task_names = [task] + if task_aliases: + task_names.extend(task_aliases) + + def _register_task(task_name, pipeline_class): + if task_name in _REGISTERED_PIPELINES and ( + pipeline_class is not _REGISTERED_PIPELINES[task_name] + ): + raise RuntimeError( + f"task {task_name} already registered by Pipeline.register. " + f"attempting to register pipeline: {pipeline_class}, but" + f"pipeline: {_REGISTERED_PIPELINES[task_name]}, already registered" + ) + _REGISTERED_PIPELINES[task_name] = pipeline_class + + def _register_pipeline_tasks_decorator(pipeline_class: Pipeline): + if not issubclass(pipeline_class, cls): + raise RuntimeError( + f"Attempting to register pipeline pipeline_class. " + f"Registered pipelines must inherit from {cls}" + ) + for task_name in task_names: + _register_task(task_name, pipeline_class) + + # set task and task_aliases as class level property + pipeline_class.task = task + pipeline_class.task_aliases = task_aliases + pipeline_class.default_model_path = default_model_path + + return pipeline_class + + return _register_pipeline_tasks_decorator + + @classmethod + def from_config(cls, config: Union["PipelineConfig", str, Path]) -> "Pipeline": + """ + :param config: PipelineConfig object, filepath to a json serialized + PipelineConfig, or raw string of a json serialized PipelineConfig + :return: loaded Pipeline object from the config + """ + if isinstance(config, Path) or ( + isinstance(config, str) and os.path.exists(config) + ): + if isinstance(config, str): + config = Path(config) + config = PipelineConfig.parse_file(config) + if isinstance(config, str): + config = PipelineConfig.parse_raw(config) + + return cls.create( + task=config.task, + model_path=config.model_path, + engine_type=config.engine_type, + batch_size=config.batch_size, + num_cores=config.num_cores, + scheduler=config.scheduler, + input_shapes=config.input_shapes, + alias=config.alias, + **config.kwargs, + ) + + @abstractmethod + def setup_onnx_file_path(self) -> str: + """ + Performs any setup to unwrap and process the given `model_path` and other + class properties into an inference ready onnx file to be compiled by the + engine of the pipeline + + :return: file path to the ONNX file for the engine to compile + """ + raise NotImplementedError() + + @abstractmethod + def process_inputs( + self, + inputs: BaseModel, + ) -> Union[List[numpy.ndarray], Tuple[List[numpy.ndarray], Dict[str, Any]]]: + """ + :param inputs: inputs to the pipeline. Must be the type of the `input_schema` + of this pipeline + :return: inputs of this model processed into a list of numpy arrays that + can be directly passed into the forward pass of the pipeline engine. Can + also include a tuple with engine inputs and special key word arguments + to pass to process_engine_outputs to facilitate information from the raw + inputs to postprocessing that may not be included in the engine inputs + """ + raise NotImplementedError() + + @abstractmethod + def process_engine_outputs( + self, + engine_outputs: List[numpy.ndarray], + **kwargs, + ) -> BaseModel: + """ + :param engine_outputs: list of numpy arrays that are the output of the engine + forward pass + :return: outputs of engine post-processed into an object in the `output_schema` + format of this pipeline + """ + raise NotImplementedError() + + @property + @abstractmethod + def input_schema(self) -> Type[BaseModel]: + """ + :return: pydantic model class that inputs to this pipeline must comply to + """ + raise NotImplementedError() + + @property + @abstractmethod + def output_schema(self) -> Type[BaseModel]: + """ + :return: pydantic model class that outputs of this pipeline must comply to + """ + raise NotImplementedError() + + @property + def alias(self) -> str: + """ + :return: optional name to give this pipeline instance, useful when + inferencing with multiple models + """ + return self._alias + + @property + def model_path_orig(self) -> str: + """ + :return: value originally passed to the `model_path` argument to initialize + this Pipeline + """ + return self._model_path_orig + + @property + def model_path(self) -> str: + """ + :return: path on local system to the onnx file of this model or directory + containing a model.onnx file along with supporting files + """ + return self._model_path + + @property + def engine_args(self) -> Dict[str, Any]: + """ + :return: arguments besides onnx filepath used to instantiate engine + """ + return self._engine_args + + @property + def engine_type(self) -> str: + """ + :return: type of inference engine used for model forward pass + """ + return self._engine_type + + def to_config(self) -> "PipelineConfig": + """ + :return: PipelineConfig that can be used to reload this object + """ + + if not hasattr(self, "task"): + raise RuntimeError( + f"{self.__class__} instance has no attribute task. Pipeline objects " + "must have a task to be serialized to a config. Pipeline objects " + "must be declared with the Pipeline.register object to be assigned a " + "task" + ) + + # parse any additional properties as kwargs + kwargs = {} + for attr_name, attr in self.__class__.__dict__.items(): + if isinstance(attr, property) and attr_name not in dir(PipelineConfig): + kwargs[attr_name] = getattr(self, attr_name) + + return PipelineConfig( + task=self.task, + model_path=self.model_path_orig, + engine_type=self.engine_type, + batch_size=self.batch_size, + num_cores=self.num_cores, + scheduler=self.scheduler, + input_shapes=self.input_shapes, + alias=self.alias, + kwargs=kwargs, + ) + + def parse_inputs(self, *args, **kwargs) -> BaseModel: + """ + :param args: ordered arguments to pipeline, only an input_schema object + is supported as an arg for this function + :param kwargs: keyword arguments to pipeline + :return: pipeline arguments parsed into the given `input_schema` + schema if necessary. If an instance of the `input_schema` is provided + it will be returned + """ + # passed input_schema schema directly + if len(args) == 1 and isinstance(args[0], self.input_schema) and not kwargs: + return args[0] + + if args: + raise ValueError( + f"pipeline {self.__class__} only supports either only a " + f"{self.input_schema} object. or keyword arguments to be construct " + f"one. Found {len(args)} args and {len(kwargs)} kwargs" + ) + + return self.input_schema(**kwargs) + + def _initialize_engine(self) -> Union[Engine, ORTEngine]: + engine_type = self.engine_type.lower() + + if engine_type == DEEPSPARSE_ENGINE: + return Engine(self.onnx_file_path, **self._engine_args) + elif engine_type == ORT_ENGINE: + return ORTEngine(self.onnx_file_path, **self._engine_args) + else: + raise ValueError( + f"Unknown engine_type {self.engine_type}. Supported values include: " + f"{SUPPORTED_PIPELINE_ENGINES}" + ) + + +class PipelineConfig(BaseModel): + """ + Configuration for creating a Pipeline object + + Can be used to create a Pipeline from a config object or file with + Pipeline.from_config(), or used as a building block for other configs + such as for deepsparse.server + """ + + task: str = Field( + description="name of task to create a pipeline for", + ) + model_path: str = Field( + description="path on local system or SparseZoo stub to load the model from", + ) + engine_type: str = Field( + default=DEEPSPARSE_ENGINE, + description=( + "inference engine to use. Currently supported values include " + "'deepsparse' and 'onnxruntime'. Default is 'deepsparse'" + ), + ) + batch_size: int = Field( + default=1, + description=("static batch size to use for inference. Default is 1"), + ) + num_cores: int = Field( + default=None, + description=( + "number of CPU cores to allocate for inference engine. None" + "specifies all available cores. Default is None" + ), + ) + scheduler: str = Field( + default="async", + description=( + "(deepsparse only) kind of scheduler to execute with. Defaults to async" + ), + ) + input_shapes: List[List[int]] = Field( + default=None, + description=( + "list of shapes to set ONNX the inputs to. Pass None to use model as-is. " + "Default is None" + ), + ) + alias: str = Field( + default=None, + description=( + "optional name to give this pipeline instance, useful when inferencing " + "with multiple models. Default is None" + ), + ) + kwargs: Dict[str, Any] = Field( + default={}, + description=( + "Additional arguments for inference with the model that will be passed " + "into the pipeline as kwargs" + ), + ) diff --git a/src/deepsparse/server/config.py b/src/deepsparse/server/config.py index d74ed10231..0d0be42ec0 100644 --- a/src/deepsparse/server/config.py +++ b/src/deepsparse/server/config.py @@ -19,18 +19,18 @@ import json import os from functools import lru_cache -from typing import Any, Dict, List +from typing import List import yaml from pydantic import BaseModel, Field +from deepsparse import PipelineConfig from deepsparse.cpu import cpu_architecture __all__ = [ "ENV_DEEPSPARSE_SERVER_CONFIG", "ENV_SINGLE_PREFIX", - "ServeModelConfig", "ServerConfig", ] @@ -39,75 +39,15 @@ ENV_SINGLE_PREFIX = "DEEPSPARSE_SINGLE_MODEL:" -class ServeModelConfig(BaseModel): - """ - Configuration for serving a model for a given task in the DeepSparse server - """ - - task: str = Field( - description=( - "The task the model_path is serving. For example, one of: " - "question_answering, text_classification, token_classification." - ), - ) - model_path: str = Field( - description=( - "The path to a model.onnx file, " - "a model folder containing the model.onnx and supporting files, " - "or a SparseZoo model stub." - ), - ) - batch_size: int = Field( - default=1, - description=( - "The batch size to instantiate the model with and use for serving" - ), - ) - alias: str = Field( - default=None, - description=( - "Alias name for model pipeline to be served. A convenience route of " - "/predict/alias will be added to the server if present. " - ), - ) - kwargs: Dict[str, Any] = Field( - default={}, - description=( - "Additional arguments for inference with the model that will be passed " - "into the pipeline as kwargs" - ), - ) - engine: str = Field( - default="deepsparse", - description=( - "The engine to use for serving the models such as deepsparse or onnxruntime" - ), - ) - num_cores: int = Field( - default=None, - description=( - "The number of physical cores to restrict the DeepSparse Engine to. " - "Defaults to all cores." - ), - ) - scheduler: str = Field( - default="async", - description=( - "The scheduler to use with the DeepSparse Engine such as sync or async. " - "Defaults to async" - ), - ) - - class ServerConfig(BaseModel): """ A configuration for serving models in the DeepSparse inference server """ - models: List[ServeModelConfig] = Field( + models: List[PipelineConfig] = Field( default=[], description=( - "The models to serve in the server defined by the additional arguments" + "The models to serve in the server defined by PipelineConfig objects" ), ) workers: str = Field( @@ -117,6 +57,14 @@ class ServerConfig(BaseModel): "Defaults to the number of physical cores on the device." ), ) + integration: str = Field( + default="default", + description=( + "Name of deployment integration that this server will be deployed to " + "Currently supported options are None for default inference and " + "'sagemaker' for inference deployment with AWS Sagemaker" + ), + ) @lru_cache() @@ -140,7 +88,7 @@ def server_config_from_env(env_key: str = ENV_DEEPSPARSE_SERVER_CONFIG): config_dict = json.loads(config_file.replace(ENV_SINGLE_PREFIX, "")) config = ServerConfig() config.models.append( - ServeModelConfig( + PipelineConfig( task=config_dict["task"], model_path=config_dict["model_path"], batch_size=config_dict["batch_size"], @@ -150,7 +98,7 @@ def server_config_from_env(env_key: str = ENV_DEEPSPARSE_SERVER_CONFIG): with open(config_file) as file: config_dict = yaml.safe_load(file.read()) config_dict["models"] = ( - [ServeModelConfig(**model) for model in config_dict["models"]] + [PipelineConfig(**model) for model in config_dict["models"]] if "models" in config_dict else [] ) @@ -170,6 +118,7 @@ def server_config_to_env( task: str, model_path: str, batch_size: int, + integration: str, env_key: str = ENV_DEEPSPARSE_SERVER_CONFIG, ): """ @@ -186,6 +135,9 @@ def server_config_to_env( If config_file is supplied, this is ignored. :param batch_size: the batch size to serve the model from model_path with. If config_file is supplied, this is ignored. + :param integration: name of deployment integration that this server will be + deployed to. Supported options include None for default inference and + sagemaker for inference deployment on AWS Sagemaker :param env_key: the environment variable to set the configuration in. Defaults to ENV_DEEPSPARSE_SERVER_CONFIG """ @@ -199,7 +151,12 @@ def server_config_to_env( ) single_str = json.dumps( - {"task": task, "model_path": model_path, "batch_size": batch_size} + { + "task": task, + "model_path": model_path, + "batch_size": batch_size, + "integration": integration, + } ) config = f"{ENV_SINGLE_PREFIX}{single_str}" diff --git a/src/deepsparse/server/main.py b/src/deepsparse/server/main.py index 564bc5e42b..dc31f6427f 100644 --- a/src/deepsparse/server/main.py +++ b/src/deepsparse/server/main.py @@ -58,6 +58,12 @@ supplied. --batch_size INTEGER The batch size to serve the model from model_path with. Ignored if config_file is supplied. + --integration [default|sagemaker] + Name of deployment integration that this + server will be deployed to Currently + supported options are 'default' and + 'sagemaker' for inference deployment with + Amazon Sagemaker --help Show this message and exit. @@ -78,6 +84,7 @@ import click +from deepsparse import Pipeline from deepsparse.log import set_logging_level from deepsparse.server.asynchronous import execute_async, initialize_aysnc from deepsparse.server.config import ( @@ -85,7 +92,6 @@ server_config_from_env, server_config_to_env, ) -from deepsparse.server.pipelines import load_pipelines_definitions from deepsparse.server.utils import serializable_response from deepsparse.version import version @@ -123,29 +129,44 @@ def _home(): _LOGGER.info("created general routes, visit `/docs` to view available") -def _add_pipeline_route(app, pipeline_def, num_models: int, defined_tasks: set): +def _add_pipeline_route( + app, + pipeline: Pipeline, + num_models: int, + defined_tasks: set, + integration: str, +): path = "/predict" - if pipeline_def.config.alias: - path = f"/predict/{pipeline_def.config.alias}" + if integration.lower() == "sagemaker": + if num_models > 1: + raise ValueError( + "Sagemaker inference with deepsparse.server currently supports " + f"serving one model, received config for {num_models} models" + ) + # required path name for Sagemaker + path = "/invocations" + elif pipeline.alias: + path = f"/predict/{pipeline.alias}" elif num_models > 1: - if pipeline_def.config.task in defined_tasks: + if pipeline.task in defined_tasks: raise ValueError( - f"Multiple tasks defined for {pipeline_def.config.task} and no alias " - f"given for {pipeline_def.config}. " + f"Multiple tasks defined for {pipeline.task} and no alias " + f"given for pipeline with model {pipeline.model_path_orig}. " "Either define an alias or supply a single model for the task" ) - path = f"/predict/{pipeline_def.config.task}" - defined_tasks.add(pipeline_def.config.task) + path = f"/predict/{pipeline.task}" + defined_tasks.add(pipeline.task) @app.post( path, - response_model=pipeline_def.response_model, + response_model=pipeline.output_schema, tags=["prediction"], ) - async def _predict_func(request: pipeline_def.request_model): + async def _predict_func(request: pipeline.input_schema): results = await execute_async( - pipeline_def.pipeline, **vars(request), **pipeline_def.kwargs + pipeline, + request, ) return serializable_response(results) @@ -167,12 +188,12 @@ def server_app_factory(): _LOGGER.debug("loaded server config %s", config) _add_general_routes(app, config) - pipeline_defs = load_pipelines_definitions(config) - _LOGGER.debug("loaded pipeline definitions from config %s", pipeline_defs) + pipelines = [Pipeline.from_config(model_config) for model_config in config.models] + _LOGGER.debug("loaded pipeline definitions from config %s", pipelines) num_tasks = len(config.models) defined_tasks = set() - for pipeline_def in pipeline_defs: - _add_pipeline_route(app, pipeline_def, num_tasks, defined_tasks) + for pipeline in pipelines: + _add_pipeline_route(app, pipeline, num_tasks, defined_tasks, config.integration) return app @@ -235,6 +256,14 @@ def server_app_factory(): help="The batch size to serve the model from model_path with. " "Ignored if config_file is supplied.", ) +@click.option( + "--integration", + type=click.Choice(["default", "sagemaker"], case_sensitive=False), + default="default", + help="Name of deployment integration that this server will be deployed to " + "Currently supported options are 'default' and 'sagemaker' for " + "inference deployment with Amazon Sagemaker", +) def start_server( host: str, port: int, @@ -244,6 +273,7 @@ def start_server( task: str, model_path: str, batch_size: int, + integration: str, ): """ Start a DeepSparse inference server for serving the models and pipelines given @@ -263,7 +293,7 @@ def start_server( alias: question_answering/sparse_quantized """ set_logging_level(getattr(logging, log_level.upper())) - server_config_to_env(config_file, task, model_path, batch_size) + server_config_to_env(config_file, task, model_path, batch_size, integration) filename = Path(__file__).stem package = "deepsparse.server" app_name = f"{package}.{filename}:server_app_factory" diff --git a/src/deepsparse/server/pipelines.py b/src/deepsparse/server/pipelines.py deleted file mode 100644 index ef07c68ca2..0000000000 --- a/src/deepsparse/server/pipelines.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. - -""" -Pipelines that run preprocessing, postprocessing, and model inference -within the DeepSparse model server. -""" - -from typing import Any, Dict, List - -from pydantic import BaseModel, Field - -from deepsparse.server.config import ServeModelConfig, ServerConfig -from deepsparse.tasks import SupportedTasks - - -__all__ = ["PipelineDefinition", "load_pipelines_definitions"] - - -class PipelineDefinition(BaseModel): - """ - A definition of a pipeline to be served by the model server. - Used to create a prediction route on construction of the server app. - """ - - pipeline: Any = Field(description="the callable pipeline to invoke on each request") - request_model: Any = Field( - description="the pydantic model to validate the request body with" - ) - response_model: Any = Field( - description="the pydantic model to validate the response payload with" - ) - kwargs: Dict[str, Any] = Field( - description="any additional kwargs that should be passed into the pipeline" - ) - config: ServeModelConfig = Field( - description="the config for the model the pipeline is serving" - ) - - -def load_pipelines_definitions(config: ServerConfig) -> List[PipelineDefinition]: - """ - Load the pipeline definitions to use for creating prediction routes from - the given server configuration. - - :param config: the configuration to load pipeline definitions for - :return: the loaded pipeline definitions to use for serving inference requests - """ - defs = [] - - for model_config in config.models: - if SupportedTasks.is_nlp(model_config.task): - # dynamically import so we don't install dependencies when unneeded - from deepsparse.transformers.server import create_pipeline_definitions - - ( - pipeline, - request_model, - response_model, - kwargs, - ) = create_pipeline_definitions(model_config) - else: - raise ValueError( - f"unsupported task given of {model_config.task} " - f"for serve model config {model_config}" - ) - - defs.append( - PipelineDefinition( - pipeline=pipeline, - request_model=request_model, - response_model=response_model, - kwargs=kwargs, - config=model_config, - ) - ) - - return defs diff --git a/src/deepsparse/tasks.py b/src/deepsparse/tasks.py index 6ffaad7ec3..690de5276e 100644 --- a/src/deepsparse/tasks.py +++ b/src/deepsparse/tasks.py @@ -78,6 +78,32 @@ class SupportedTasks: token_classification=AliasedTask("token_classification", ["ner"]), ) + image_classification = namedtuple("image_classification", ["image_classification"])( + image_classification=AliasedTask( + "image_classification", + ["image_classification"], + ), + ) + + yolo = namedtuple("yolo", ["yolo"])( + yolo=AliasedTask("yolo", ["yolo"]), + ) + + @classmethod + def check_register_task(cls, task: str): + if cls.is_nlp(task): + # trigger transformers pipelines to register with Pipeline.register + import deepsparse.transformers.pipelines # noqa: F401 + + elif cls.is_image_classification(task): + # trigger image classification pipelines to + # register with Pipeline.register + import deepsparse.image_classification.pipelines # noqa: F401 + + elif cls.is_yolo(task): + # trigger yolo pipelines to register with Pipeline.register + import deepsparse.yolo.pipelines # noqa: F401 + @classmethod def is_nlp(cls, task: str) -> bool: """ @@ -90,3 +116,21 @@ def is_nlp(cls, task: str) -> bool: or cls.nlp.text_classification.matches(task) or cls.nlp.token_classification.matches(task) ) + + @classmethod + def is_image_classification(cls, task: str) -> bool: + """ + :param task: the name of the task to check whether it is an image + classification task + :return: True if it is an image classification task, False otherwise + """ + return cls.image_classification.image_classification.matches(task) + + @classmethod + def is_yolo(cls, task: str) -> bool: + """ + :param task: the name of the task to check whether it is an image + segmentation task using YOLO + :return: True if it is an segmentation task using YOLO, False otherwise + """ + return cls.yolo.yolo.matches(task) diff --git a/src/deepsparse/transformers/__init__.py b/src/deepsparse/transformers/__init__.py index 89c7eb68ef..1264aa316d 100644 --- a/src/deepsparse/transformers/__init__.py +++ b/src/deepsparse/transformers/__init__.py @@ -120,4 +120,3 @@ def _check_transformers_install(): from .helpers import * from .loaders import * from .pipelines import * -from .server import * diff --git a/src/deepsparse/transformers/eval_downstream.py b/src/deepsparse/transformers/eval_downstream.py index b434dec625..c01a649fbf 100644 --- a/src/deepsparse/transformers/eval_downstream.py +++ b/src/deepsparse/transformers/eval_downstream.py @@ -58,7 +58,7 @@ from tqdm.auto import tqdm -from deepsparse.transformers import pipeline +from deepsparse import Pipeline from datasets import load_dataset, load_metric # isort: skip @@ -79,14 +79,14 @@ def squad_eval(args): squad_metrics = load_metric("squad") # load QA pipeline - question_answer = pipeline( + question_answer = Pipeline.create( task="question-answering", model_path=args.onnx_filepath, engine_type=args.engine, num_cores=args.num_cores, - max_length=args.max_sequence_length, + sequence_length=args.max_sequence_length, ) - print(f"Engine info: {question_answer.model}") + print(f"Engine info: {question_answer.engine}") for idx, sample in enumerate(tqdm(squad)): pred = question_answer( @@ -96,7 +96,7 @@ def squad_eval(args): ) squad_metrics.add_batch( - predictions=[{"prediction_text": pred["answer"], "id": sample["id"]}], + predictions=[{"prediction_text": pred.answer, "id": sample["id"]}], references=[{"answers": sample["answers"], "id": sample["id"]}], ) @@ -114,21 +114,23 @@ def mnli_eval(args): mnli_metrics = load_metric("glue", "mnli") # load pipeline - text_classify = pipeline( + text_classify = Pipeline.create( task="text-classification", model_path=args.onnx_filepath, engine_type=args.engine, num_cores=args.num_cores, - max_length=args.max_sequence_length, + sequence_length=args.max_sequence_length, ) - print(f"Engine info: {text_classify.model}") + print(f"Engine info: {text_classify.engine}") + + label_map = {"entailment": 0, "neutral": 1, "contradiction": 2} label_map = {"entailment": 0, "neutral": 1, "contradiction": 2} for idx, sample in enumerate(tqdm(mnli_matched)): pred = text_classify([[sample["premise"], sample["hypothesis"]]]) mnli_metrics.add_batch( - predictions=[label_map.get(pred[0]["label"])], + predictions=[label_map.get(pred.labels[0])], references=[sample["label"]], ) @@ -154,14 +156,16 @@ def qqp_eval(args): qqp_metrics = load_metric("glue", "qqp") # load pipeline - text_classify = pipeline( + text_classify = Pipeline.create( task="text-classification", model_path=args.onnx_filepath, engine_type=args.engine, num_cores=args.num_cores, - max_length=args.max_sequence_length, + sequence_length=args.max_sequence_length, ) - print(f"Engine info: {text_classify.model}") + print(f"Engine info: {text_classify.engine}") + + label_map = {"not_duplicate": 0, "duplicate": 1} label_map = {"not_duplicate": 0, "duplicate": 1} @@ -169,7 +173,7 @@ def qqp_eval(args): pred = text_classify([[sample["question1"], sample["question2"]]]) qqp_metrics.add_batch( - predictions=[label_map.get(pred[0]["label"])], + predictions=[label_map.get(pred.labels[0])], references=[sample["label"]], ) @@ -185,14 +189,16 @@ def sst2_eval(args): sst2_metrics = load_metric("glue", "sst2") # load pipeline - text_classify = pipeline( + text_classify = Pipeline.create( task="text-classification", model_path=args.onnx_filepath, engine_type=args.engine, num_cores=args.num_cores, - max_length=args.max_sequence_length, + sequence_length=args.max_sequence_length, ) - print(f"Engine info: {text_classify.model}") + print(f"Engine info: {text_classify.engine}") + + label_map = {"negative": 0, "positive": 1} label_map = {"negative": 0, "positive": 1} @@ -202,7 +208,7 @@ def sst2_eval(args): ) sst2_metrics.add_batch( - predictions=[label_map.get(pred[0]["label"])], + predictions=[label_map.get(pred.labels[0])], references=[sample["label"]], ) diff --git a/src/deepsparse/transformers/pipelines.py b/src/deepsparse/transformers/pipelines.py deleted file mode 100644 index 7725a0e2c2..0000000000 --- a/src/deepsparse/transformers/pipelines.py +++ /dev/null @@ -1,1414 +0,0 @@ -# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. - -""" -Adaptation of transformers.pipelines and onnx_transformers.pipelines - -adapted from: -https://github.com/huggingface/transformers/blob/master/src/transformers/pipelines/base.py -https://github.com/patil-suraj/onnx_transformers/blob/master/onnx_transformers/pipelines.py - -""" - -import json -from abc import ABC, abstractmethod -from dataclasses import dataclass -from itertools import chain -from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union - -import numpy as np -from transformers.configuration_utils import PretrainedConfig -from transformers.data import ( - SquadExample, - SquadFeatures, - squad_convert_examples_to_features, -) -from transformers.file_utils import ExplicitEnum -from transformers.models.auto import AutoConfig, AutoTokenizer -from transformers.tokenization_utils import PreTrainedTokenizer -from transformers.tokenization_utils_base import PaddingStrategy, TruncationStrategy -from transformers.utils import logging - -from deepsparse import Engine, compile_model, cpu -from deepsparse.transformers.helpers import ( - fix_numpy_types, - get_onnx_path_and_configs, - overwrite_transformer_onnx_model_inputs, -) -from deepsparse.transformers.loaders import get_batch_loader - - -try: - import onnxruntime - - ort_import_error = None -except Exception as ort_import_err: - onnxruntime = None - ort_import_error = ort_import_err - -__all__ = [ - "ArgumentHandler", - "Pipeline", - "TextClassificationPipeline", - "TokenClassificationPipeline", - "QuestionAnsweringPipeline", - "pipeline", - "overwrite_transformer_onnx_model_inputs", - "SUPPORTED_ENGINES", - "SUPPORTED_TASKS", -] - -logger = logging.get_logger(__name__) if logging else None - - -class ArgumentHandler(ABC): - """ - Base interface for handling arguments for each Pipeline. - """ - - @abstractmethod - def __call__(self, *args, **kwargs): - raise NotImplementedError() - - -class DefaultArgumentHandler(ArgumentHandler): - """ - Default argument parser handling parameters for each Pipeline`. - """ - - @staticmethod - def handle_kwargs(kwargs: Dict) -> List: - """ - :param kwargs: key word arguments for a pipeline - :return: list of the processed key word arguments - """ - if len(kwargs) == 1: - output = list(kwargs.values()) - else: - output = list(chain(kwargs.values())) - - return DefaultArgumentHandler.handle_args(output) - - @staticmethod - def handle_args(args: Sequence[Any]) -> List[str]: - """ - :param args: sequence of arguments to a pipeline - :return: list of formatted, processed arguments - """ - - # Only one argument, let's do case by case - if len(args) == 1: - if isinstance(args[0], str): - return [args[0]] - elif not isinstance(args[0], list): - return list(args) - else: - return args[0] - - # Multiple arguments (x1, x2, ...) - elif len(args) > 1: - if all([isinstance(arg, str) for arg in args]): - return list(args) - - # If not instance of list, then it should be an instance of iterable - elif isinstance(args, Iterable): - return list(chain.from_iterable(chain(args))) - else: - raise ValueError( - f"Invalid input type {type(args)}. Pipeline supports " - "Union[str, Iterable[str]]" - ) - else: - return [] - - def __call__(self, *args, **kwargs): - if len(kwargs) > 0 and len(args) > 0: - raise ValueError("Pipeline cannot handle mixed args and kwargs") - - if len(kwargs) > 0: - return DefaultArgumentHandler.handle_kwargs(kwargs) - else: - return DefaultArgumentHandler.handle_args(args) - - -class _ScikitCompat(ABC): - """ - Interface layer for the Scikit and Keras compatibility. - """ - - @abstractmethod - def transform(self, X): - raise NotImplementedError() - - @abstractmethod - def predict(self, X): - raise NotImplementedError() - - -class Pipeline(_ScikitCompat): - """ - The Pipeline class is the class from which all pipelines inherit. - Refer to this class for methods shared across different pipelines. - This base Pipeline class provides support for multiple inference engine backends. - - Base class implementing pipelined operations. - Pipeline workflow is defined as a sequence of the following operations: - - Input -> Tokenization -> Model Inference -> - Post-Processing (task dependent) -> Output - - Pipeline supports running with the DeepSparse engine or onnxruntime. - - :param model: loaded inference engine to run the model with, can be a - deepsparse Engine or onnxruntime InferenceSession - :param tokenizer: tokenizer to be used for preprocessing - :param config: transformers model config for this model - :param engine_type: name of inference engine that is used. Options are - deepsparse and onnxruntime - :param max_length: maximum sequence length to set for model inputs by default. - default value is 128 - :param input_names: list of input names to the neural network - :param args_parser: Reference to the object in charge of parsing supplied - pipeline parameters. A default is provided if None - :param binary_output: if True, stores outputs as pickled binaries to avoid - storing large amount of textual data. Default is False - """ - - default_input_names = None - - def __init__( - self, - model: Union[Engine, "onnxruntime.InferenceSession"], - tokenizer: PreTrainedTokenizer, - config: PretrainedConfig, - engine_type: str, - max_length: int = 128, - input_names: Optional[List[str]] = None, - args_parser: ArgumentHandler = None, - binary_output: bool = False, - ): - - self.model = model - self.tokenizer = tokenizer - self.config = config - self.engine_type = engine_type - self.max_length = max_length - self.input_names = input_names - self.binary_output = binary_output - self._args_parser = args_parser or DefaultArgumentHandler() - self._framework = ( - "np" if self.engine_type in [DEEPSPARSE_ENGINE, ORT_ENGINE] else "pt" - ) - - def transform(self, X): - """ - Scikit / Keras interface to transformers' pipelines. - This method will forward to __call__(). - """ - return self(X=X) - - def predict(self, X): - """ - Scikit / Keras interface to transformers' pipelines. - This method will forward to __call__(). - """ - return self(X=X) - - def _parse_and_tokenize( - self, *args, padding=True, add_special_tokens=True, **kwargs - ): - # Parse arguments - inputs = self._args_parser(*args, **kwargs) - inputs = self.tokenizer( - inputs, - add_special_tokens=add_special_tokens, - return_tensors=self._framework, - padding=PaddingStrategy.MAX_LENGTH.value, - truncation=TruncationStrategy.LONGEST_FIRST.value, - ) - - return inputs - - def __call__(self, *args, **kwargs): - inputs = self._parse_and_tokenize(*args, **kwargs) - return self._forward(inputs) - - def _forward(self, inputs): - if not all(name in inputs for name in self.input_names): - raise ValueError( - f"pipeline expected arrays with names {self.input_names}, received " - f"inputs: {list(inputs.keys())}" - ) - - if self.engine_type == ORT_ENGINE: - inputs = {k: v for k, v in inputs.items() if k in self.input_names} - return self.model.run(None, inputs) - elif self.engine_type == DEEPSPARSE_ENGINE: - return self.model.run([inputs[name] for name in self.input_names]) - # TODO: torch - # with self.device_placement(): - # with torch.no_grad(): - # inputs = self.ensure_tensor_on_device(**inputs) - # predictions = self.model(**inputs)[0].cpu() - # if return_tensors: - # return predictions - # else: - # return predictions.numpy() - - -class TokenClassificationArgumentHandler(ArgumentHandler): - """ - Handles arguments for token classification. - """ - - def __call__(self, inputs: Union[str, List[str]], **kwargs): - - if inputs is not None and isinstance(inputs, (list, tuple)) and len(inputs) > 0: - inputs = list(inputs) - batch_size = len(inputs) - elif isinstance(inputs, str): - inputs = [inputs] - batch_size = 1 - else: - raise ValueError("At least one input is required.") - - offset_mapping = kwargs.get("offset_mapping") - if offset_mapping: - if isinstance(offset_mapping, list) and isinstance( - offset_mapping[0], tuple - ): - offset_mapping = [offset_mapping] - if len(offset_mapping) != batch_size: - raise ValueError( - "offset_mapping should have the same batch size as the input" - ) - return inputs, offset_mapping - - -class QuestionAnsweringArgumentHandler(ArgumentHandler): - """ - QuestionAnsweringPipeline requires the user to provide multiple arguments - (i.e. question & context) to be mapped - to internal `transformers.SquadExample` - - QuestionAnsweringArgumentHandler manages all the possible to create a - `transformers.SquadExample` from the command-line supplied arguments - """ - - def __call__(self, *args, **kwargs): - # Position args, handling is sensibly the same as X and data, - # so forwarding to avoid duplicating - if args is not None and len(args) > 0: - if len(args) == 1: - kwargs["X"] = args[0] - else: - kwargs["X"] = list(args) - - # Generic compatibility with sklearn and Keras - # Batched data - if "X" in kwargs or "data" in kwargs: - inputs = kwargs["X"] if "X" in kwargs else kwargs["data"] - - if isinstance(inputs, dict): - inputs = [inputs] - else: - # Copy to avoid overriding arguments - inputs = [i for i in inputs] - - for i, item in enumerate(inputs): - if isinstance(item, dict): - if any(k not in item for k in ["question", "context"]): - raise KeyError( - "You need to provide a dictionary with keys " - "{question:..., context:...}" - ) - - inputs[i] = QuestionAnsweringPipeline.create_sample(**item) - - elif not isinstance(item, SquadExample): - arg_name = "X" if "X" in kwargs else "data" - raise ValueError( - f"{arg_name} argument needs to be of type " - "(list[SquadExample | dict], SquadExample, dict)" - ) - - # Tabular input - elif "question" in kwargs and "context" in kwargs: - if isinstance(kwargs["question"], str): - kwargs["question"] = [kwargs["question"]] - - if isinstance(kwargs["context"], str): - kwargs["context"] = [kwargs["context"]] - - inputs = [ - QuestionAnsweringPipeline.create_sample(q, c) - for q, c in zip(kwargs["question"], kwargs["context"]) - ] - else: - raise ValueError(f"Unknown arguments {kwargs}") - - if not isinstance(inputs, list): - inputs = [inputs] - - return inputs - - -class TextClassificationPipeline(Pipeline): - """ - Text classification pipeline using any `ModelForSequenceClassification`. - - This text classification pipeline can currently be loaded from `pipeline()` - using the following task identifier: `"text-classification"`. - - The models that this pipeline can use are models that have been fine-tuned on - a text classification task. - - :param return_all_scores: set True to return all model scores. Default False - """ - - def __init__(self, return_all_scores: bool = False, **kwargs): - super().__init__(**kwargs) - - self.return_all_scores = return_all_scores - - def __call__(self, *args, **kwargs): - """ - Classify the text(s) given as inputs. - - :param args: One or several texts (or one list of prompts) to classify - :param args: kwargs for inner call function - :return: A list or a list of list of dicts: Each result comes as list of dicts - with the following keys: - - `label` -- The label predicted. - - `score` -- The corresponding probability. - If ``self.return_all_scores=True``, one dictionary is returned per label - """ - outputs = super().__call__(*args, **kwargs) - - if isinstance(outputs, list) and outputs: - outputs = outputs[0] - - if self.config.num_labels == 1: - scores = 1.0 / (1.0 + np.exp(-outputs)) - else: - scores = np.exp(outputs) / np.exp(outputs).sum(-1, keepdims=True) - if self.return_all_scores: - return [ - [ - {"label": self.config.id2label[i], "score": score.item()} - for i, score in enumerate(item) - ] - for item in scores - ] - else: - return [ - { - "label": self.config.id2label[item.argmax()], - "score": item.max().item(), - } - for item in scores - ] - - -class AggregationStrategy(ExplicitEnum): - """ - All the valid aggregation strategies for TokenClassificationPipeline - """ - - NONE = "none" - SIMPLE = "simple" - FIRST = "first" - AVERAGE = "average" - MAX = "max" - - -class TokenClassificationPipeline(Pipeline): - """ - Named Entity Recognition pipeline using any `ModelForTokenClassification`. - - This token classification pipeline can currently be loaded from `pipeline()` - using the following task identifier: `"token-classification"`. - - The models that this pipeline can use are models that have been fine-tuned on - a token classification task. - - :param args_parser: argument parser to use default is - TokenClassificationArgumentHandler - :param aggregation_strategy: AggregationStrategy Enum object to determine - the pipeline aggregation strategy. Default is AggregationStrategy.NONE - :param ignore_labels: list of labels to ignore. Default is `["O"]` - """ - - default_input_names = "sequences" - - def __init__( - self, - args_parser: ArgumentHandler = None, - aggregation_strategy: AggregationStrategy = AggregationStrategy.NONE, - ignore_labels: List[str] = False, - **kwargs, - ): - super().__init__( - args_parser=args_parser or TokenClassificationArgumentHandler(), - **kwargs, - ) - - self.ignore_labels = ignore_labels or ["O"] - - if isinstance(aggregation_strategy, str): - aggregation_strategy = AggregationStrategy[aggregation_strategy.upper()] - - if ( - aggregation_strategy - in { - AggregationStrategy.FIRST, - AggregationStrategy.MAX, - AggregationStrategy.AVERAGE, - } - and not self.tokenizer.is_fast - ): - raise ValueError( - "Slow tokenizers cannot handle subwords. Please set the " - '`aggregation_strategy` option to `"simple"` or use a fast tokenizer.' - ) - - self.aggregation_strategy = aggregation_strategy - - def __call__(self, inputs: Union[str, List[str]], **kwargs): - """ - Classify each token of the text(s) given as inputs. - - - :param inputs: One or several texts (or one list of texts) for token - classification - :return: A list or a list of list of :obj:`dict`: Each result comes as a list - of dictionaries (one for each token in the corresponding input, or each - entity if this pipeline was instantiated with an aggregation_strategy) - with the following keys: - - `word` -- The token/word classified. - - `score` -- The corresponding probability for `entity`. - - `entity` -- The entity predicted for that token/word (it is named - `entity_group` when `aggregation_strategy` is not `"none"`. - - `index` -- The index of the corresponding token in the sentence. - - `start` -- index of the start of the corresponding entity in the sentence - Only exists if the offsets are available within the tokenizer - - `end` -- The index of the end of the corresponding entity in the sentence. - Only exists if the offsets are available within the tokenizer - """ - - _inputs, offset_mappings = self._args_parser(inputs, **kwargs) - - answers = [] - - tokens = self.tokenizer( - _inputs, - return_tensors=self._framework, - truncation=TruncationStrategy.LONGEST_FIRST.value, - padding=PaddingStrategy.MAX_LENGTH.value, - return_special_tokens_mask=True, - return_offsets_mapping=self.tokenizer.is_fast, - ) - - if self.tokenizer.is_fast: - offset_mapping = tokens.pop("offset_mapping") - elif not offset_mappings: - offset_mapping = [None] * len(_inputs) - - special_tokens_mask = tokens.pop("special_tokens_mask") - - # Forward - _forward_pass = self._forward(tokens) - for entities_index, current_entities in enumerate(_forward_pass[0]): - input_ids = tokens["input_ids"][entities_index] - - scores = np.exp(current_entities) / np.exp(current_entities).sum( - -1, keepdims=True - ) - pre_entities = self.gather_pre_entities( - _inputs[entities_index], - input_ids, - scores, - offset_mapping[entities_index], - special_tokens_mask[entities_index], - ) - grouped_entities = self.aggregate(pre_entities, self.aggregation_strategy) - # Filter anything that is in self.ignore_labels - current_entities = [ - entity - for entity in grouped_entities - if entity.get("entity", None) not in self.ignore_labels - and entity.get("entity_group", None) not in self.ignore_labels - ] - answers.append(current_entities) - - if len(answers) == 1: - return answers[0] - return answers - - def gather_pre_entities( - self, - sentence: str, - input_ids: np.ndarray, - scores: np.ndarray, - offset_mapping: Optional[List[Tuple[int, int]]], - special_tokens_mask: np.ndarray, - ) -> List[dict]: - pre_entities = [] - for idx, token_scores in enumerate(scores): - # Filter special_tokens, they should only occur - # at the sentence boundaries since we're not encoding pairs of - # sentences so we don't have to keep track of those. - if special_tokens_mask[idx]: - continue - - word = self.tokenizer.convert_ids_to_tokens(int(input_ids[idx])) - if offset_mapping is not None: - start_ind, end_ind = offset_mapping[idx] - word_ref = sentence[start_ind:end_ind] - is_subword = len(word_ref) != len(word) - - if int(input_ids[idx]) == self.tokenizer.unk_token_id: - word = word_ref - is_subword = False - else: - start_ind = None - end_ind = None - is_subword = False - - pre_entity = { - "word": word, - "scores": token_scores, - "start": start_ind, - "end": end_ind, - "index": idx, - "is_subword": is_subword, - } - pre_entities.append(pre_entity) - return pre_entities - - def aggregate( - self, pre_entities: List[dict], aggregation_strategy: AggregationStrategy - ) -> List[dict]: - if aggregation_strategy in { - AggregationStrategy.NONE, - AggregationStrategy.SIMPLE, - }: - entities = [] - for pre_entity in pre_entities: - entity_idx = pre_entity["scores"].argmax() - score = pre_entity["scores"][entity_idx] - entity = { - "entity": self.config.id2label[entity_idx], - "score": score, - "index": pre_entity["index"], - "word": pre_entity["word"], - "start": pre_entity["start"], - "end": pre_entity["end"], - } - entities.append(entity) - else: - entities = self.aggregate_words(pre_entities, aggregation_strategy) - - if aggregation_strategy == AggregationStrategy.NONE: - return entities - return self.group_entities(entities) - - def aggregate_word( - self, entities: List[dict], aggregation_strategy: AggregationStrategy - ) -> dict: - word = self.tokenizer.convert_tokens_to_string( - [entity["word"] for entity in entities] - ) - if aggregation_strategy == AggregationStrategy.FIRST: - scores = entities[0]["scores"] - idx = scores.argmax() - score = scores[idx] - entity = self.config.id2label[idx] - elif aggregation_strategy == AggregationStrategy.MAX: - max_entity = max(entities, key=lambda entity: entity["scores"].max()) - scores = max_entity["scores"] - idx = scores.argmax() - score = scores[idx] - entity = self.config.id2label[idx] - elif aggregation_strategy == AggregationStrategy.AVERAGE: - scores = np.stack([entity["scores"] for entity in entities]) - average_scores = np.nanmean(scores, axis=0) - entity_idx = average_scores.argmax() - entity = self.config.id2label[entity_idx] - score = average_scores[entity_idx] - else: - raise ValueError("Invalid aggregation_strategy") - new_entity = { - "entity": entity, - "score": score, - "word": word, - "start": entities[0]["start"], - "end": entities[-1]["end"], - } - return new_entity - - def aggregate_words( - self, entities: List[dict], aggregation_strategy: AggregationStrategy - ) -> List[dict]: - assert aggregation_strategy not in { - AggregationStrategy.NONE, - AggregationStrategy.SIMPLE, - }, "NONE and SIMPLE strategies are invalid" - - word_entities = [] - word_group = None - for entity in entities: - if word_group is None: - word_group = [entity] - elif entity["is_subword"]: - word_group.append(entity) - else: - word_entities.append( - self.aggregate_word(word_group, aggregation_strategy) - ) - word_group = [entity] - # Last item - word_entities.append(self.aggregate_word(word_group, aggregation_strategy)) - return word_entities - - def group_sub_entities(self, entities: List[dict]) -> dict: - # Get the first entity in the entity group - entity = entities[0]["entity"].split("-")[-1] - scores = np.nanmean([entity["score"] for entity in entities]) - tokens = [entity["word"] for entity in entities] - - entity_group = { - "entity_group": entity, - "score": np.mean(scores), - "word": self.tokenizer.convert_tokens_to_string(tokens), - "start": entities[0]["start"], - "end": entities[-1]["end"], - } - return entity_group - - def get_tag(self, entity_name: str) -> Tuple[str, str]: - if entity_name.startswith("B-"): - bi = "B" - tag = entity_name[2:] - elif entity_name.startswith("I-"): - bi = "I" - tag = entity_name[2:] - else: - # It's not in B-, I- format - bi = "B" - tag = entity_name - return bi, tag - - def group_entities(self, entities: List[dict]) -> List[dict]: - - entity_groups = [] - entity_group_disagg = [] - - for entity in entities: - if not entity_group_disagg: - entity_group_disagg.append(entity) - continue - - # If the current entity is similar and adjacent to the previous entity, - # append it to the disaggregated entity group - # The split is meant to account for the "B" and "I" prefixes - # Shouldn't merge if both entities are B-type - bi, tag = self.get_tag(entity["entity"]) - last_bi, last_tag = self.get_tag(entity_group_disagg[-1]["entity"]) - - if tag == last_tag and bi != "B": - # Modify subword type to be previous_type - entity_group_disagg.append(entity) - else: - # If the current entity is different from the previous entity - # aggregate the disaggregated entity group - entity_groups.append(self.group_sub_entities(entity_group_disagg)) - entity_group_disagg = [entity] - if entity_group_disagg: - # it's the last entity, add it to the entity groups - entity_groups.append(self.group_sub_entities(entity_group_disagg)) - - return entity_groups - - -class QuestionAnsweringPipeline(Pipeline): - """ - Question Answering pipeline using any `ModelForQuestionAnswering` - - This question answering pipeline can currently be loaded from `pipeline()` - using the following task identifier: `"question-answering"`. - - The models that this pipeline can use are models that have been fine-tuned on - a question answering task. - - :param model: loaded inference engine to run the model with, can be a - deepsparse Engine or onnxruntime InferenceSession - :param tokenizer: tokenizer to be used for preprocessing - :param config: transformers model config for this model - :param engine_type: name of inference engine that is used. Options are - deepsparse and onnxruntime - :param input_names: list of input names to the neural network - :param args_parser: Reference to the object in charge of parsing supplied - pipeline parameters. A default is provided if None - :param binary_output: if True, stores outputs as pickled binaries to avoid - storing large amount of textual data. Default is False - """ - - default_input_names = "question,context" - - def __init__( - self, - model: Union[Engine, "onnxruntime.InferenceSession"], - tokenizer: PreTrainedTokenizer, - engine_type: str, - input_names: Optional[List[str]] = None, - **kwargs, - ): - super().__init__( - model=model, - tokenizer=tokenizer, - engine_type=engine_type, - args_parser=QuestionAnsweringArgumentHandler(), - input_names=input_names, - **kwargs, - ) - - @staticmethod - def create_sample( - question: Union[str, List[str]], context: Union[str, List[str]] - ) -> Union[SquadExample, List[SquadExample]]: - """ - :param question: single question or list of question strings - :param context: single context or list of context strings - :return: processed SquadExample object(s) for each question/context pair given - """ - if isinstance(question, list): - return [ - SquadExample(None, q, c, None, None, None) - for q, c in zip(question, context) - ] - else: - return SquadExample(None, question, context, None, None, None) - - def __call__(self, *args, **kwargs): - """ - Answer the question(s) given as inputs by using the context(s). - Multiple arguments can be used to pass the context, question data - - :param args: SquadExample or list of them containing the question and context - :param X: SquadExample or list of them containing the question and context - :param data: SquadExample or list of them containing the question and context - :param question: single question or list of question strings - :param context: single context or list of context strings - :param topk: the number of answers to return. Will be chosen by - order of likelihood) - :param doc_stride: if the context is too long to fit with the question for the - model, it will be split in several chunks with some overlap. This argument - controls the size of that overlap - :param max_answer_len: maximum length of predicted answers (e.g., only - answers with a shorter length are considered) - :param max_seq_len: maximum length of the total sentence (context + question) - after tokenization. The context will be split in several chunks - (using the doc_stride) if needed - :param max_question_len: maximum length of the question after tokenization. - It will be truncated if needed - :param handle_impossible_answer: whether or not we accept impossible as an - answer - :param num_spans: maximum number of span to use as input from a long - context. Default is to stride the entire context string - :param preprocessed_inputs: if provided, preprocessing will be skipped in favor - of these inputs. Expected format is the output of self.preprocess; a tuple - of (examples, features_list) - :return: dict or list of dictionaries, each containing the following keys: - `"score"` - The probability associated to the answer - `"start"` - The start index of the answer - `"end"` - The end index of the answer - `"answer"` - The answer to the question - """ - # Set defaults values - kwargs.setdefault("topk", 1) - kwargs.setdefault("max_answer_len", 15) - kwargs.setdefault("handle_impossible_answer", False) - kwargs.setdefault("preprocessed_inputs", None) # (examples, features_list) - - if kwargs["topk"] < 1: - raise ValueError(f"topk parameter should be >= 1 (got {kwargs['topk']})") - - if kwargs["max_answer_len"] < 1: - raise ValueError( - "max_answer_len parameter should be >= 1 " - f"(got {kwargs['max_answer_len']})" - ) - - # run pre-processing if not provided - examples, features_list = kwargs["preprocessed_inputs"] or self.preprocess( - *args, **kwargs - ) - - # forward pass and post-processing - all_answers = [] - for features, example in zip(features_list, examples): - model_input_names = self.tokenizer.model_input_names + ["input_ids"] - fw_args = { - k: [feature.__dict__[k] for feature in features] - for k in model_input_names - } - - # Manage tensor allocation on correct device - fw_args = {k: np.array(v) for (k, v) in fw_args.items()} - start, end = self._forward(fw_args)[:2] - - # TODO: torch - # fw_args = {k: torch.tensor(v, device=self.device) - # for (k, v) in fw_args.items()} - # start, end = self.model(**fw_args)[:2] - # start, end = start.cpu().numpy(), end.cpu().numpy() - - min_null_score = 1000000 # large and positive - answers = [] - for (feature, start_, end_) in zip(features, start, end): - # Ensure padded tokens & question tokens cannot belong - undesired_tokens = ( - np.abs(np.array(feature.p_mask) - 1) & feature.attention_mask - ) - - # Generate mask - undesired_tokens_mask = undesired_tokens == 0.0 - - # Make sure non-context indexes cannot contribute to the softmax - start_ = np.where(undesired_tokens_mask, -10000.0, start_) - end_ = np.where(undesired_tokens_mask, -10000.0, end_) - - # Normalize logits and spans to retrieve the answer - start_ = np.exp( - start_ - np.log(np.sum(np.exp(start_), axis=-1, keepdims=True)) - ) - end_ = np.exp( - end_ - np.log(np.sum(np.exp(end_), axis=-1, keepdims=True)) - ) - - if kwargs["handle_impossible_answer"]: - min_null_score = min(min_null_score, (start_[0] * end_[0]).item()) - - # Mask CLS - start_[0] = end_[0] = 0.0 - - starts, ends, scores = self.decode( - start_, end_, kwargs["topk"], kwargs["max_answer_len"] - ) - - if not self.tokenizer.is_fast: - char_to_word = np.array(example.char_to_word_offset) - answers += [ - { - "score": score.item(), - "start": np.where( - char_to_word == feature.token_to_orig_map[s] - )[0][0].item(), - "end": np.where( - char_to_word == feature.token_to_orig_map[e] - )[0][-1].item(), - "answer": " ".join( - example.doc_tokens[ - feature.token_to_orig_map[ - s - ] : feature.token_to_orig_map[e] - + 1 - ] - ), - } - for s, e, score in zip(starts, ends, scores) - ] - else: - question_first = bool(self.tokenizer.padding_side == "right") - - # Sometimes the max probability token is in the middle of a word so: - # we start by finding the right word containing the token with - # `token_to_word` then we convert this word in a character span - answers += [ - { - "score": score.item(), - "start": feature.encoding.word_to_chars( - feature.encoding.token_to_word(s), - sequence_index=1 if question_first else 0, - )[0], - "end": feature.encoding.word_to_chars( - feature.encoding.token_to_word(e), - sequence_index=1 if question_first else 0, - )[1], - "answer": example.context_text[ - feature.encoding.word_to_chars( - feature.encoding.token_to_word(s), - sequence_index=1 if question_first else 0, - )[0] : feature.encoding.word_to_chars( - feature.encoding.token_to_word(e), - sequence_index=1 if question_first else 0, - )[ - 1 - ] - ], - } - for s, e, score in zip(starts, ends, scores) - ] - - if kwargs["handle_impossible_answer"]: - answers.append( - {"score": min_null_score, "start": 0, "end": 0, "answer": ""} - ) - - answers = sorted(answers, key=lambda x: x["score"], reverse=True)[ - : kwargs["topk"] - ] - all_answers += answers - - if len(all_answers) == 1: - return all_answers[0] - return all_answers - - def preprocess(self, *args, **kwargs) -> Tuple[Any, Any]: - """ - preprocess the given QA model inputs using squad_convert_examples_to_features - - :param args: SquadExample or list of them containing the question and context - :param X: SquadExample or list of them containing the question and context - :param data: SquadExample or list of them containing the question and context - :param question: single question or list of question strings - :param context: single context or list of context strings - :param doc_stride: if the context is too long to fit with the question for the - model, it will be split in several chunks with some overlap. This argument - controls the size of that overlap - :param max_seq_len: maximum length of the total sentence (context + question) - after tokenization. The context will be split in several chunks - (using the doc_stride) if needed - :param max_question_len: maximum length of the question after tokenization. - It will be truncated if needed - :param num_spans: maximum number of spans to use as input from a long - context. Default is to stride the entire context string - :return: tuple of SquadExample inputs and preprocessed features list - """ - kwargs.setdefault("doc_stride", 128) - kwargs.setdefault("max_seq_len", self.max_length) - kwargs.setdefault("max_question_len", 64) - kwargs.setdefault("num_spans", None) - - # Convert inputs to features - examples = self._args_parser(*args, **kwargs) - if not self.tokenizer.is_fast: - features_list = [ - squad_convert_examples_to_features( - examples=[example], - tokenizer=self.tokenizer, - max_seq_length=kwargs["max_seq_len"], - doc_stride=kwargs["doc_stride"], - max_query_length=kwargs["max_question_len"], - padding_strategy=PaddingStrategy.MAX_LENGTH.value, - is_training=False, - tqdm_enabled=False, - ) - for example in examples - ] - else: - features_list = self._encode_features_fast(examples, **kwargs) - - if kwargs["num_spans"]: - features_list = [ - features[: kwargs["num_spans"]] for features in features_list - ] - - return examples, features_list - - def decode( - self, start: np.ndarray, end: np.ndarray, topk: int, max_answer_len: int - ) -> Tuple: - """ - :param start: Individual start probabilities for each token - :param end: Individual end probabilities for each token - :param topk: Indicates how many possible answer span(s) to extract from the - model output - :param max_answer_len: Maximum size of the answer to extract from the model - output - :return: probabilities for each span to be the actual answer. Will filter out - unwanted and impossible cases - """ - # Ensure we have batch axis - if start.ndim == 1: - start = start[None] - - if end.ndim == 1: - end = end[None] - - # Compute the score of each tuple(start, end) to be the real answer - outer = np.matmul(np.expand_dims(start, -1), np.expand_dims(end, 1)) - - # Remove candidate with end < start and end - start > max_answer_len - candidates = np.tril(np.triu(outer), max_answer_len - 1) - - # Inspired by Chen & al. (https://github.com/facebookresearch/DrQA) - scores_flat = candidates.flatten() - if topk == 1: - idx_sort = [np.argmax(scores_flat)] - elif len(scores_flat) < topk: - idx_sort = np.argsort(-scores_flat) - else: - idx = np.argpartition(-scores_flat, topk)[0:topk] - idx_sort = idx[np.argsort(-scores_flat[idx])] - - start, end = np.unravel_index(idx_sort, candidates.shape)[1:] - return start, end, candidates[0, start, end] - - def span_to_answer( - self, text: str, start: int, end: int - ) -> Dict[str, Union[str, int]]: - """ - When decoding from token probabilities, this method maps token indexes to - actual word in the initial context. - - :param text: The actual context to extract the answer from - :param start: The answer starting token index - :param end: The answer end token index - :return: Dictionary containing the start, end, and answer - """ - words = [] - token_idx = char_start_idx = char_end_idx = chars_idx = 0 - - for i, word in enumerate(text.split(" ")): - token = self.tokenizer.tokenize(word) - - # Append words if they are in the span - if start <= token_idx <= end: - if token_idx == start: - char_start_idx = chars_idx - - if token_idx == end: - char_end_idx = chars_idx + len(word) - - words += [word] - - # Stop if we went over the end of the answer - if token_idx > end: - break - - # Append the subtokenization length to the running index - token_idx += len(token) - chars_idx += len(word) + 1 - - # Join text with spaces - return { - "answer": " ".join(words), - "start": max(0, char_start_idx), - "end": min(len(text), char_end_idx), - } - - def _encode_features_fast(self, examples: Any, **kwargs) -> List[SquadFeatures]: - features_list = [] - for example in examples: - # Define the side we want to truncate / pad and the text/pair sorting - question_first = bool(self.tokenizer.padding_side == "right") - - encoded_inputs = self.tokenizer( - text=example.question_text if question_first else example.context_text, - text_pair=( - example.context_text if question_first else example.question_text - ), - padding=PaddingStrategy.MAX_LENGTH.value, - truncation="only_second" if question_first else "only_first", - max_length=kwargs["max_seq_len"], - stride=kwargs["doc_stride"], - return_tensors="np", - return_token_type_ids=True, - return_overflowing_tokens=True, - return_offsets_mapping=True, - return_special_tokens_mask=True, - ) - - total_spans = len(encoded_inputs["input_ids"]) - - # p_mask: mask with 1 for token than cannot be in the answer - # We put 0 on the tokens from the context and 1 everywhere else - p_mask = np.asarray( - [ - [ - tok != 1 if question_first else 0 - for tok in encoded_inputs.sequence_ids(span_id) - ] - for span_id in range(total_spans) - ] - ) - - # keep the cls_token unmasked - if self.tokenizer.cls_token_id is not None: - cls_index = np.nonzero( - encoded_inputs["input_ids"] == self.tokenizer.cls_token_id - ) - p_mask[cls_index] = 0 - - features = [] - for span_idx in range(total_spans): - features.append( - SquadFeatures( - input_ids=encoded_inputs["input_ids"][span_idx], - attention_mask=encoded_inputs["attention_mask"][span_idx], - token_type_ids=encoded_inputs["token_type_ids"][span_idx], - p_mask=p_mask[span_idx].tolist(), - encoding=encoded_inputs[span_idx], - # the following values are unused for fast tokenizers - cls_index=None, - token_to_orig_map={}, - example_index=0, - unique_id=0, - paragraph_len=0, - token_is_max_context=0, - tokens=[], - start_position=0, - end_position=0, - is_impossible=False, - qas_id=None, - ) - ) - features_list.append(features) - return features_list - - -@dataclass -class TaskInfo: - """ - Information about an NLP task - - :param pipeline_constructor: reference to constructor for the given pipeline task - :param default model name: the transformers canonical name for the default model - :param base_stub: sparsezoo stub path for the base model for this task - :param default_pruned_stub: sparsezoo stub path for the default pruned model - for this task - :param default_quant_stub: sparsezoo stub path for the default quantized model - for this task - """ - - pipeline_constructor: Callable[[Any], Pipeline] - default_model_name: str - base_stub: Optional[str] = None - default_pruned_stub: Optional[str] = None - default_quant_stub: Optional[str] = None - - -# Register all the supported tasks here -SUPPORTED_TASKS = { - "ner": TaskInfo( - pipeline_constructor=TokenClassificationPipeline, - default_model_name="bert-base-uncased", - ), - "question-answering": TaskInfo( - pipeline_constructor=QuestionAnsweringPipeline, - default_model_name="bert-base-uncased", - base_stub=( - "zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/base-none" - ), - default_pruned_stub=( - "zoo:nlp/question_answering/bert-base/pytorch/huggingface/squad/" - "pruned-aggressive_98" - ), - ), - "sentiment-analysis": TaskInfo( - pipeline_constructor=TextClassificationPipeline, - default_model_name="bert-base-uncased", - ), - "text-classification": TaskInfo( - pipeline_constructor=TextClassificationPipeline, - default_model_name="bert-base-uncased", - ), - "token-classification": TaskInfo( - pipeline_constructor=TokenClassificationPipeline, - default_model_name="bert-base-uncased", - ), -} - -DEEPSPARSE_ENGINE = "deepsparse" -ORT_ENGINE = "onnxruntime" - -SUPPORTED_ENGINES = [DEEPSPARSE_ENGINE, ORT_ENGINE] - - -def pipeline( - task: str, - model_name: Optional[str] = None, - model_path: Optional[str] = None, - engine_type: str = DEEPSPARSE_ENGINE, - config: Optional[Union[str, PretrainedConfig]] = None, - tokenizer: Optional[Union[str, PreTrainedTokenizer]] = None, - max_length: int = 128, - num_cores: Optional[int] = None, - scheduler: Optional[str] = None, - batch_size: Optional[int] = 1, - **kwargs, -) -> Pipeline: - """ - Utility factory method to build a Pipeline - - :param task: name of the task to define which pipeline to create. Currently - supported task - "question-answering" - :param model_name: canonical name of the hugging face model this model is based on - :param model_path: path to model directory containing `model.onnx`, `config.json`, - and `tokenizer.json` files, ONNX model file, or SparseZoo stub - :param engine_type: inference engine name to use. Supported options are 'deepsparse' - and 'onnxruntime' - :param config: huggingface model config, if none provided, default will be used - which will be from the model name or sparsezoo stub if given for model path - :param tokenizer: huggingface tokenizer, if none provided, default will be used - :param max_length: maximum sequence length of model inputs. default is 128 - :param num_cores: number of CPU cores to run engine with. Default is the maximum - available - :param scheduler: The scheduler to use for the engine. Can be None, single or multi. - :param batch_size: The batch_size to use for the pipeline. Defaults to 1 - Note: `question-answering` pipeline only supports a batch_size of 1. - :param kwargs: additional key word arguments for task specific pipeline constructor - :return: Pipeline object for the given taks and model - """ - - # Retrieve the task - if task not in SUPPORTED_TASKS: - raise KeyError( - f"Unknown task {task}, available tasks are {list(SUPPORTED_TASKS.keys())}" - ) - if engine_type not in SUPPORTED_ENGINES: - raise ValueError( - f"Unsupported engine {engine_type}, supported engines " - f"are {SUPPORTED_ENGINES}" - ) - if task == "question-answering" and batch_size != 1: - raise ValueError( - f"{task} pipeline only supports batch_size 1. " - f"Supplied batch_size = {batch_size}" - ) - task_info = SUPPORTED_TASKS[task] - - model_path = model_path or _get_default_model_path(task_info) - model_name = model_name or task_info.default_model_name - - onnx_path, config_path, tokenizer_path = get_onnx_path_and_configs(model_path) - - # default the tokenizer and config to file in model directory or given model name - config = config or config_path or model_name - tokenizer = tokenizer or tokenizer_path or model_name - - # create model - model, input_names = _create_model( - onnx_path, - engine_type, - num_cores, - max_length, - scheduler=scheduler, - batch_size=batch_size, - ) - - # Instantiate tokenizer if needed - if isinstance(tokenizer, (str, tuple)): - if isinstance(tokenizer, tuple): - # For tuple we have (tokenizer name, {kwargs}) - tokenizer_kwargs = tokenizer[1] - tokenizer_kwargs["model_max_length"] = max_length - tokenizer = AutoTokenizer.from_pretrained(tokenizer[0], **tokenizer[1]) - else: - tokenizer = AutoTokenizer.from_pretrained( - tokenizer, model_max_length=max_length - ) - - # Instantiate config if needed - if config is not None and isinstance(config, str): - config = AutoConfig.from_pretrained(config, finetuning_task=task) - - return task_info.pipeline_constructor( - model=model, - tokenizer=tokenizer, - config=config, - engine_type=engine_type, - max_length=max_length, - input_names=input_names, - **kwargs, - ) - - -def _get_default_model_path(task_info: TaskInfo) -> str: - if cpu.cpu_vnni_compatible() and task_info.default_quant_stub: - return task_info.default_quant_stub - return task_info.default_pruned_stub or task_info.base_stub - - -def _create_model( - model_path: str, - engine_type: str, - num_cores: Optional[int], - max_length: int = 128, - scheduler: Optional[str] = None, - batch_size: int = 1, -) -> Tuple[Union[Engine, "onnxruntime.InferenceSession"], List[str]]: - onnx_path, input_names, _ = overwrite_transformer_onnx_model_inputs( - model_path, max_length=max_length - ) - - if engine_type == DEEPSPARSE_ENGINE: - model = compile_model( - onnx_path, - batch_size=batch_size, - num_cores=num_cores, - scheduler=scheduler, - ) - elif engine_type == ORT_ENGINE: - _validate_ort_import() - sess_options = onnxruntime.SessionOptions() - if num_cores is not None: - sess_options.intra_op_num_threads = num_cores - sess_options.log_severity_level = 3 - sess_options.graph_optimization_level = ( - onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL - ) - - model = onnxruntime.InferenceSession(onnx_path, sess_options=sess_options) - - return model, input_names - - -def _validate_ort_import(): - if ort_import_error is not None: - raise ImportError( - "An exception occurred when importing onxxruntime. Please verify that " - "onnxruntime is installed in order to use the onnxruntime inference " - f"engine. \n\nException info: {ort_import_error}" - ) - - -def process_dataset( - pipeline_object: Callable, - data_path: str, - batch_size: int, - task: str, - output_path: str, -) -> None: - """ - :param pipeline_object: An instantiated pipeline Callable object - :param data_path: Path to input file, supports csv, json and text files - :param batch_size: batch_size to use for inference - :param task: The task pipeline is instantiated for - :param output_path: Path to a json file to output inference results to - """ - batch_loader = get_batch_loader( - data_file=data_path, - batch_size=batch_size, - task=task, - ) - # Wraps pipeline object to make numpy types serializable - pipeline_object = fix_numpy_types(pipeline_object) - with open(output_path, "a") as output_file: - for batch in batch_loader: - batch_output = pipeline_object(**batch) - json.dump(batch_output, output_file) - output_file.write("\n") diff --git a/src/deepsparse/transformers/pipelines/__init__.py b/src/deepsparse/transformers/pipelines/__init__.py new file mode 100644 index 0000000000..9986181a2a --- /dev/null +++ b/src/deepsparse/transformers/pipelines/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +# flake8: noqa + +from .pipeline import * +from .question_answering import * +from .text_classification import * +from .token_classification import * diff --git a/src/deepsparse/transformers/pipelines/pipeline.py b/src/deepsparse/transformers/pipelines/pipeline.py new file mode 100644 index 0000000000..2fdcd27236 --- /dev/null +++ b/src/deepsparse/transformers/pipelines/pipeline.py @@ -0,0 +1,219 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Base Pipeline class for transformers inference pipeline +""" + + +import warnings +from typing import Any, List, Mapping, Optional + +import numpy +from transformers.models.auto import AutoConfig, AutoTokenizer + +from deepsparse import Pipeline +from deepsparse.transformers.helpers import ( + get_onnx_path_and_configs, + overwrite_transformer_onnx_model_inputs, +) + + +__all__ = [ + "TransformersPipeline", + "pipeline", +] + + +class TransformersPipeline(Pipeline): + """ + Base deepsparse.Pipeline class for transformers model loading. This class handles + the parsing of deepsparse-transformers files and model inputs, supporting loading + from sparsezoo, a directory containing a model.onnx, tokenizer, and model config, + or just an ONNX file with the ability to load a tokenizer and model config from + a default huggingface-transformers model. + + Note, when implementing child tasks in deepsparse.transformers.pipelines, + in addition to registering task names with Pipeline.register, task names should + be added to the supported nlp tasks in deepsparse.tasks so they can be properly + imported at runtime. + + :param model_path: sparsezoo stub to a transformers model, an ONNX file, or + (preferred) a directory containing a model.onnx, tokenizer config, and model + config. If no tokenizer and/or model config(s) are found, then they will be + loaded from huggingface transformers using the `default_model_name` key + :param engine_type: inference engine to use. Currently supported values include + 'deepsparse' and 'onnxruntime'. Default is 'deepsparse' + :param batch_size: static batch size to use for inference. Default is 1 + :param num_cores: number of CPU cores to allocate for inference engine. None + specifies all available cores. Default is None + :param scheduler: (deepsparse only) kind of scheduler to execute with. + Pass None for the default + :param input_shapes: list of shapes to set ONNX the inputs to. Pass None + to use model as-is. Default is None + :param alias: optional name to give this pipeline instance, useful when + inferencing with multiple models. Default is None + :param sequence_length: static sequence length to use for inference + :param default_model_name: huggingface transformers model name to use to + load a tokenizer and model config when none are provided in the `model_path`. + Default is 'bert-base-uncased' + """ + + def __init__( + self, + *, + sequence_length: int = 128, + default_model_name: str = "bert-base-uncased", + **kwargs, + ): + + self._sequence_length = sequence_length + self._default_model_name = default_model_name + + self.config = None + self.tokenizer = None + self.onnx_input_names = None + + self._temp_model_directory = None + + super().__init__(**kwargs) + + @property + def sequence_length(self) -> int: + """ + :return: static sequence length to use for inference + """ + return self._sequence_length + + @property + def default_model_name(self) -> str: + """ + :return: huggingface transformers model name to use to + load a tokenizer and model config when none are provided in the + `model_path` + """ + return self._default_model_name + + def setup_onnx_file_path(self) -> str: + """ + Parses ONNX, tokenizer, and config file paths from the given `model_path`. + Supports sparsezoo stubs. If a tokenizer and/or config file are not found, + they will be defaulted to the default_model_name in the transformers repo + + :return: file path to the processed ONNX file for the engine to compile + """ + onnx_path, config_path, tokenizer_path = get_onnx_path_and_configs( + self.model_path + ) + + # default config + tokenizer if necessary + config_path = config_path or self.default_model_name + tokenizer_path = tokenizer_path or self.default_model_name + + self.config = AutoConfig.from_pretrained( + config_path, finetuning_task=self.task if hasattr(self, "task") else None + ) + self.tokenizer = AutoTokenizer.from_pretrained( + tokenizer_path, model_max_length=self.sequence_length + ) + + # overwrite onnx graph to given required input shape + ( + onnx_path, + self.onnx_input_names, + self._temp_model_directory, + ) = overwrite_transformer_onnx_model_inputs( + onnx_path, max_length=self.sequence_length + ) + + return onnx_path + + def tokens_to_engine_input( + self, tokens: Mapping[Any, numpy.ndarray] + ) -> List[numpy.ndarray]: + """ + :param tokens: outputs of the pipeline tokenizer + :return: list of numpy arrays in expected order for model input + """ + if not all(name in tokens for name in self.onnx_input_names): + raise ValueError( + f"pipeline expected arrays with names {self.onnx_input_names}, " + f"received inputs: {list(tokens.keys())}" + ) + + return [tokens[name] for name in self.onnx_input_names] + + +def pipeline( + task: str, + model_name: Optional[str] = None, + model_path: Optional[str] = None, + engine_type: str = "deepsparse", + config: Optional[str] = None, + tokenizer: Optional[str] = None, + max_length: int = 128, + num_cores: Optional[int] = None, + scheduler: Optional[str] = None, + batch_size: Optional[int] = 1, + **kwargs, +) -> Pipeline: + """ + [DEPRECATED] - deepsparse.transformers.pipeline is deprecated to craete DeepSparse + pipelines for tranformers tasks use deepsparse.Pipeline.create(task, ...) + + Utility factory method to build a Pipeline + + :param task: name of the task to define which pipeline to create. Currently + supported task - "question-answering" + :param model_name: canonical name of the hugging face model this model is based on + :param model_path: path to model directory containing `model.onnx`, `config.json`, + and `tokenizer.json` files, ONNX model file, or SparseZoo stub + :param engine_type: inference engine name to use. Options are 'deepsparse' + and 'onnxruntime'. Default is 'deepsparse' + :param config: huggingface model config, if none provided, default will be used + which will be from the model name or sparsezoo stub if given for model path + :param tokenizer: huggingface tokenizer, if none provided, default will be used + :param max_length: maximum sequence length of model inputs. default is 128 + :param num_cores: number of CPU cores to run engine with. Default is the maximum + available + :param scheduler: The scheduler to use for the engine. Can be None, single or multi + :param batch_size: The batch_size to use for the pipeline. Defaults to 1 + Note: `question-answering` pipeline only supports a batch_size of 1. + :param kwargs: additional key word arguments for task specific pipeline constructor + :return: Pipeline object for the given taks and model + """ + warnings.warn( + "[DEPRECATED] - deepsparse.transformers.pipeline is deprecated to craete " + "DeepSparse pipelines for tranformers tasks use deepsparse.Pipeline.create()" + ) + + if config is not None or tokenizer is not None: + raise ValueError( + "Directly passing in a config or tokenizer to DeepSparse transformers " + "pipelines is no longer supported. config and tokenizer objects should " + "be specified by including config.json and tokenizer.json files in the " + "model directory respectively" + ) + + return Pipeline.create( + task=task, + model_path=model_path, + engine_type=engine_type, + batch_size=batch_size, + num_cores=num_cores, + scheduler=scheduler, + sequence_length=max_length, + default_model_name=model_name, + **kwargs, + ) diff --git a/src/deepsparse/transformers/pipelines/question_answering.py b/src/deepsparse/transformers/pipelines/question_answering.py new file mode 100644 index 0000000000..ba57117dad --- /dev/null +++ b/src/deepsparse/transformers/pipelines/question_answering.py @@ -0,0 +1,409 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +# postprocessing adapted from huggingface/transformers + +# Copyright 2021 The HuggingFace Team. 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. + +""" +Pipeline implementation and pydantic models for question answering transformers +tasks +""" + + +from typing import Any, Dict, List, Tuple, Type + +import numpy +from pydantic import BaseModel, Field +from transformers.data import ( + SquadExample, + SquadFeatures, + squad_convert_examples_to_features, +) +from transformers.tokenization_utils_base import PaddingStrategy + +from deepsparse import Pipeline +from deepsparse.transformers.pipelines import TransformersPipeline + + +__all__ = [ + "QuestionAnsweringInput", + "QuestionAnsweringOutput", + "QuestionAnsweringPipeline", +] + + +class QuestionAnsweringInput(BaseModel): + """ + Schema for inputs to question_answering pipelines + """ + + question: str = Field(description="String question to be answered") + context: str = Field(description="String representing context for answer") + + +class QuestionAnsweringOutput(BaseModel): + """ + Schema for question_answering pipeline output. Values are in batch order + """ + + score: float = Field(description="confidence score for prediction") + answer: str = Field(description="predicted answer") + start: int = Field(description="start index of the answer") + end: int = Field(description="end index of the answer") + + +@Pipeline.register( + task="question_answering", + task_aliases=["qa"], + default_model_path=( + "zoo:nlp/question_answering/bert-base/pytorch/huggingface/" + "squad/12layer_pruned80_quant-none-vnni" + ), +) +class QuestionAnsweringPipeline(TransformersPipeline): + """ + transformers question_answering pipeline + + example instantiation: + ```python + question_answering = Pipeline.create( + task="question_answering", + model_path="question_answering_model_dir/", + ) + ``` + + :param model_path: sparsezoo stub to a transformers model, an ONNX file, or + (preferred) a directory containing a model.onnx, tokenizer config, and model + config. If no tokenizer and/or model config(s) are found, then they will be + loaded from huggingface transformers using the `default_model_name` key + :param engine_type: inference engine to use. Currently supported values include + 'deepsparse' and 'onnxruntime'. Default is 'deepsparse' + :param batch_size: static batch size to use for inference. Default is 1 + :param num_cores: number of CPU cores to allocate for inference engine. None + specifies all available cores. Default is None + :param scheduler: (deepsparse only) kind of scheduler to execute with. + Pass None for the default + :param input_shapes: list of shapes to set ONNX the inputs to. Pass None + to use model as-is. Default is None + :param alias: optional name to give this pipeline instance, useful when + inferencing with multiple models. Default is None + :param sequence_length: sequence length to compile model and tokenizer for. + Default is 128 + :param default_model_name: huggingface transformers model name to use to + load a tokenizer and model config when none are provided in the `model_path`. + Default is 'bert-base-uncased' + :param doc_stride: if the context is too long to fit with the question for the + model, it will be split in several chunks with some overlap. This argument + controls the size of that overlap. Currently, only reading the first span + is supported (everything after doc_stride will be truncated). Default + is 128 + :param max_question_len: maximum length of the question after tokenization. + It will be truncated if needed. Default is 64 + :param max_answer_len: maximum length of answer after decoding. Default is 15 + """ + + def __init__( + self, + *, + doc_stride: int = 128, + max_question_length: int = 64, + max_answer_length: int = 15, + **kwargs, + ): + + if kwargs.get("batch_size") and kwargs["batch_size"] > 1: + raise ValueError( + f"{self.__class__.__name__} currently only supports batch size 1, " + f"batch size set to {kwargs['batch_size']}" + ) + + self._doc_stride = doc_stride + self._max_question_length = max_question_length + self._max_answer_length = max_answer_length + + super().__init__(**kwargs) + + @property + def doc_stride(self) -> int: + """ + :return: if the context is too long to fit with the question for the + model, it will be split in several chunks with some overlap. This argument + controls the size of that overlap. Currently, only reading the first span + is supported (everything after doc_stride will be truncated) + """ + return self._doc_stride + + @property + def max_answer_length(self) -> int: + """ + :return: maximum length of answer after decoding + """ + return self._max_answer_length + + @property + def max_question_length(self) -> int: + """ + :return: maximum length of the question after tokenization. + It will be truncated if needed + """ + return self._max_question_length + + @property + def input_schema(self) -> Type[BaseModel]: + """ + :return: pydantic model class that inputs to this pipeline must comply to + """ + return QuestionAnsweringInput + + @property + def output_schema(self) -> Type[BaseModel]: + """ + :return: pydantic model class that outputs of this pipeline must comply to + """ + return QuestionAnsweringOutput + + def process_inputs( + self, + inputs: QuestionAnsweringInput, + ) -> Tuple[List[numpy.ndarray], Dict[str, Any]]: + """ + :param inputs: inputs to the pipeline. Must be the type of the + QuestionAnsweringInput + :return: inputs of this model processed into a list of numpy arrays that + can be directly passed into the forward pass of the pipeline engine and + dictionary of parsed features and original extracted example + """ + squad_example = SquadExample( + None, inputs.question, inputs.context, None, None, None + ) + features = self._tokenize(squad_example) + tokens = features.__dict__ + + engine_inputs = self.tokens_to_engine_input(tokens) + # add batch dimension, assuming batch size 1 + engine_inputs = [numpy.expand_dims(inp, axis=0) for inp in engine_inputs] + + return engine_inputs, dict( + features=features, + example=squad_example, + ) + + def process_engine_outputs( + self, engine_outputs: List[numpy.ndarray], **kwargs + ) -> BaseModel: + """ + :param engine_outputs: list of numpy arrays that are the output of the engine + forward pass + :return: outputs of engine post-processed into an object in the `output_schema` + format of this pipeline + """ + features = kwargs["features"] + example = kwargs["example"] + start_vals, end_vals = engine_outputs[:2] + + # assuming batch size 0 + start = start_vals[0] + end = end_vals[0] + + # Ensure padded tokens & question tokens cannot belong + undesired_tokens = ( + numpy.abs(numpy.array(features.p_mask) - 1) & features.attention_mask + ) + + # Generate mask + undesired_tokens_mask = undesired_tokens == 0.0 + + # Make sure non-context indexes cannot contribute to the softmax + start = numpy.where(undesired_tokens_mask, -10000.0, start) + end = numpy.where(undesired_tokens_mask, -10000.0, end) + + # Normalize logits and spans to retrieve the answer + start = numpy.exp( + start - numpy.log(numpy.sum(numpy.exp(start), axis=-1, keepdims=True)) + ) + end = numpy.exp( + end - numpy.log(numpy.sum(numpy.exp(end), axis=-1, keepdims=True)) + ) + + # Mask CLS + start[0] = 0.0 + end[0] = 0.0 + + ans_start, ans_end, scores = self._decode(start, end) + # assuming one stride, so grab first idx + ans_start = ans_start[0] + ans_end = ans_end[0] + score = scores[0] + + # decode start, end idx into text + if not self.tokenizer.is_fast: + char_to_word = numpy.array(example.char_to_word_offset) + return self.output_schema( + score=score.item(), + start=numpy.where( + char_to_word == features.token_to_orig_map[ans_start] + )[0][0].item(), + end=numpy.where(char_to_word == features.token_to_orig_map[ans_end])[0][ + -1 + ].item(), + answer=" ".join( + example.doc_tokens[ + features.token_to_orig_map[ + ans_start + ] : features.token_to_orig_map[ans_end] + + 1 + ] + ), + ) + else: + question_first = bool(self.tokenizer.padding_side == "right") + + # Sometimes the max probability token is in the middle of a word so: + # we start by finding the right word containing the token with + # `token_to_word` then we convert this word in a character span + return self.output_schema( + score=score.item(), + start=features.encoding.word_to_chars( + features.encoding.token_to_word(ans_start), + sequence_index=1 if question_first else 0, + )[0], + end=features.encoding.word_to_chars( + features.encoding.token_to_word(ans_end), + sequence_index=1 if question_first else 0, + )[1], + answer=example.context_text[ + features.encoding.word_to_chars( + features.encoding.token_to_word(ans_start), + sequence_index=1 if question_first else 0, + )[0] : features.encoding.word_to_chars( + features.encoding.token_to_word(ans_end), + sequence_index=1 if question_first else 0, + )[ + 1 + ] + ], + ) + + def _tokenize(self, example: SquadExample): + if not self.tokenizer.is_fast: + features = squad_convert_examples_to_features( + examples=[example], + tokenizer=self.tokenizer, + max_set_length=self.sequence_length, + doc_stride=self.doc_stride, + max_query_length=self.max_question_length, + padding_strategy=PaddingStrategy.MAX_LENGTH.value, + is_training=False, + tqdm_enabled=False, + ) + # only 1 span supported so taking only the first element of features + # to add support for num_spans switch to features = features[:num_spans] + # not included for now due to static batch requirements in production + features = features[0] + else: + question_first = bool(self.tokenizer.padding_side == "right") + encoded_inputs = self.tokenizer( + text=example.question_text if question_first else example.context_text, + text_pair=( + example.context_text if question_first else example.question_text + ), + padding=PaddingStrategy.MAX_LENGTH.value, + truncation="only_second" if question_first else "only_first", + max_length=self.sequence_length, + stride=self.doc_stride, + return_tensors="np", + return_token_type_ids=True, + return_overflowing_tokens=True, + return_offsets_mapping=True, + return_special_tokens_mask=True, + ) + + # only 1 span supported so taking only the first element of features + # to add support for num_spans switch hardcoded 0 idx lookups to loop + # over values in num_spans + + # p_mask: mask with 1 for token than cannot be in the answer + # We put 0 on the tokens from the context and 1 everywhere else + p_mask = numpy.asarray( + [ + [ + tok != 1 if question_first else 0 + for tok in encoded_inputs.sequence_ids(0) + ] + ] + ) + + # keep the cls_token unmasked + if self.tokenizer.cls_token_id is not None: + cls_index = numpy.nonzero( + encoded_inputs["input_ids"][0] == self.tokenizer.cls_token_id + ) + p_mask[cls_index] = 0 + + features = SquadFeatures( + input_ids=encoded_inputs["input_ids"][0], + attention_mask=encoded_inputs["attention_mask"][0], + token_type_ids=encoded_inputs["token_type_ids"][0], + p_mask=p_mask[0].tolist(), + encoding=encoded_inputs[0], + # the following values are unused for fast tokenizers + cls_index=None, + token_to_orig_map={}, + example_index=0, + unique_id=0, + paragraph_len=0, + token_is_max_context=0, + tokens=[], + start_position=0, + end_position=0, + is_impossible=False, + qas_id=None, + ) + + return features + + def _decode(self, start: numpy.ndarray, end: numpy.ndarray) -> Tuple: + # Ensure we have batch axis + if start.ndim == 1: + start = start[None] + + if end.ndim == 1: + end = end[None] + + # Compute the score of each tuple(start, end) to be the real answer + outer = numpy.matmul(numpy.expand_dims(start, -1), numpy.expand_dims(end, 1)) + + # Remove candidate with end < start and end - start > max_answer_len + candidates = numpy.tril(numpy.triu(outer), self.max_answer_length - 1) + + # Inspired by Chen & al. (https://github.com/facebookresearch/DrQA) + scores_flat = candidates.flatten() + # only returning best result, use argsort for topk support + idx_sort = [numpy.argmax(scores_flat)] + + start, end = numpy.unravel_index(idx_sort, candidates.shape)[1:] + return start, end, candidates[0, start, end] diff --git a/src/deepsparse/transformers/pipelines/text_classification.py b/src/deepsparse/transformers/pipelines/text_classification.py new file mode 100644 index 0000000000..0df9ba2b59 --- /dev/null +++ b/src/deepsparse/transformers/pipelines/text_classification.py @@ -0,0 +1,221 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +# postprocessing adapted from huggingface/transformers + +# Copyright 2021 The HuggingFace Team. 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. + + +""" +Pipeline implementation and pydantic models for text classification transformers +tasks +""" + + +from typing import List, Type, Union + +import numpy +from pydantic import BaseModel, Field +from transformers.tokenization_utils_base import PaddingStrategy, TruncationStrategy + +from deepsparse import Pipeline +from deepsparse.transformers.pipelines import TransformersPipeline + + +__all__ = [ + "TextClassificationInput", + "TextClassificationOutput", + "TextClassificationPipeline", +] + + +class TextClassificationInput(BaseModel): + """ + Schema for inputs to text_classification pipelines + """ + + sequences: Union[List[List[str]], List[str], str] = Field( + description="A string or List of strings representing input to" + "text_classification task" + ) + + +class TextClassificationOutput(BaseModel): + """ + Schema for text_classification pipeline output. Values are in batch order + """ + + labels: List[str] = Field(description="The predicted labels in batch order") + scores: List[float] = Field( + description="The corresponding probability for each label in the batch" + ) + + +@Pipeline.register( + task="text_classification", + task_aliases=["glue", "sentiment_analysis"], + default_model_path=( + "zoo:nlp/sentiment_analysis/bert-base/pytorch/huggingface/" + "sst2/12layer_pruned80_quant-none-vnni" + ), +) +class TextClassificationPipeline(TransformersPipeline): + """ + transformers text classification pipeline + + example instantiation: + ```python + text_classifier = Pipeline.create( + task="text_classification", + model_path="text_classification_model_dir/", + batch_size=BATCH_SIZE, + ) + ``` + + example batch size 1, single text inputs (ie sentiment analysis): + ```python + sentiment = text_classifier("the food tastes great") + sentiment = text_classifier(["the food tastes great"]) + sentiment = text_classifier([["the food tastes great"]]) + ``` + + example batch size 1, multi text input (ie QQP like tasks): + ```python + prediction = text_classifier([["how is the food?", "what is the food?"]]) + ``` + + example batch size n, single text inputs: + ```python + sentiments = text_classifier(["the food tastes great", "the food tastes bad"]) + sentiments = text_classifier([["the food tastes great"], ["the food tastes bad"]]) + ``` + + :param model_path: sparsezoo stub to a transformers model, an ONNX file, or + (preferred) a directory containing a model.onnx, tokenizer config, and model + config. If no tokenizer and/or model config(s) are found, then they will be + loaded from huggingface transformers using the `default_model_name` key + :param engine_type: inference engine to use. Currently supported values include + 'deepsparse' and 'onnxruntime'. Default is 'deepsparse' + :param batch_size: static batch size to use for inference. Default is 1 + :param num_cores: number of CPU cores to allocate for inference engine. None + specifies all available cores. Default is None + :param scheduler: (deepsparse only) kind of scheduler to execute with. + Pass None for the default + :param input_shapes: list of shapes to set ONNX the inputs to. Pass None + to use model as-is. Default is None + :param alias: optional name to give this pipeline instance, useful when + inferencing with multiple models. Default is None + :param sequence_length: sequence length to compile model and tokenizer for. + Default is 128 + :param default_model_name: huggingface transformers model name to use to + load a tokenizer and model config when none are provided in the `model_path`. + Default is 'bert-base-uncased' + """ + + @property + def input_schema(self) -> Type[BaseModel]: + """ + :return: pydantic model class that inputs to this pipeline must comply to + """ + return TextClassificationInput + + @property + def output_schema(self) -> Type[BaseModel]: + """ + :return: pydantic model class that outputs of this pipeline must comply to + """ + return TextClassificationOutput + + def parse_inputs(self, *args, **kwargs) -> BaseModel: + """ + :param args: ordered arguments to pipeline, only an input_schema object + is supported as an arg for this function + :param kwargs: keyword arguments to pipeline + :return: pipeline arguments parsed into the given `input_schema` + schema if necessary. If an instance of the `input_schema` is provided + it will be returned + """ + if args and kwargs: + raise ValueError( + f"{self.__class__} only support args OR kwargs. Found " + f" {len(args)} args and {len(kwargs)} kwargs" + ) + + if args: + if len(args) == 1: + # passed input_schema schema directly + if isinstance(args[0], self.input_schema): + return args[0] + return self.input_schema(sequences=args[0]) + else: + return self.input_schema(sequences=args) + + return self.input_schema(**kwargs) + + def process_inputs(self, inputs: TextClassificationInput) -> List[numpy.ndarray]: + """ + :param inputs: inputs to the pipeline. Must be the type of the + TextClassificationInput + :return: inputs of this model processed into a list of numpy arrays that + can be directly passed into the forward pass of the pipeline engine + """ + tokens = self.tokenizer( + inputs.sequences, + add_special_tokens=True, + return_tensors="np", + padding=PaddingStrategy.MAX_LENGTH.value, + truncation=TruncationStrategy.LONGEST_FIRST.value, + ) + return self.tokens_to_engine_input(tokens) + + def process_engine_outputs(self, engine_outputs: List[numpy.ndarray]) -> BaseModel: + """ + :param engine_outputs: list of numpy arrays that are the output of the engine + forward pass + :return: outputs of engine post-processed into an object in the `output_schema` + format of this pipeline + """ + outputs = engine_outputs + if isinstance(outputs, list): + outputs = outputs[0] + + scores = ( + 1.0 / (1.0 + numpy.exp(-outputs)) + if self.config.num_labels == 1 + else numpy.exp(outputs) / numpy.exp(outputs).sum(-1, keepdims=True) + ) + + labels = [] + label_scores = [] + + for score in scores: + labels.append(self.config.id2label[score.argmax()]) + label_scores.append(score.max().item()) + + return self.output_schema( + labels=labels, + scores=label_scores, + ) diff --git a/src/deepsparse/transformers/pipelines/token_classification.py b/src/deepsparse/transformers/pipelines/token_classification.py new file mode 100644 index 0000000000..6485df668e --- /dev/null +++ b/src/deepsparse/transformers/pipelines/token_classification.py @@ -0,0 +1,499 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +# postprocessing adapted from huggingface/transformers + +# Copyright 2021 The HuggingFace Team. 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. + + +""" +Pipeline implementation and pydantic models for token classification transformers +tasks +""" +from typing import Any, Dict, List, Optional, Tuple, Type, Union + +import numpy +from pydantic import BaseModel, Field +from transformers.file_utils import ExplicitEnum +from transformers.tokenization_utils_base import PaddingStrategy, TruncationStrategy + +from deepsparse import Pipeline +from deepsparse.transformers.pipelines import TransformersPipeline + + +__all__ = [ + "AggregationStrategy", + "TokenClassificationInput", + "TokenClassificationResult", + "TokenClassificationOutput", + "TokenClassificationPipeline", +] + + +class AggregationStrategy(ExplicitEnum): + """ + Valid aggregation strategies for postprocessing in the TokenClassificationPipeline + """ + + NONE = "none" + SIMPLE = "simple" + FIRST = "first" + AVERAGE = "average" + MAX = "max" + + +class TokenClassificationInput(BaseModel): + """ + Schema for inputs to token_classification pipelines + """ + + inputs: Union[List[str], str] = Field( + description=( + "A string or List of batch of strings representing input(s) to" + "a token_classification task" + ) + ) + + +class TokenClassificationResult(BaseModel): + """ + Schema for a classification of a single token + """ + + entity: str = Field(description="entity predicted for that token/word") + score: float = Field(description="The corresponding probability for `entity`") + index: int = Field(description="index of the corresponding token in the sentence") + word: str = Field(description="token/word classified") + start: Optional[int] = Field( + description=( + "index of the start of the corresponding entity in the sentence. " + "Only exists if the offsets are available within the tokenizer" + ) + ) + end: Optional[int] = Field( + description=( + "index of the end of the corresponding entity in the sentence. " + "Only exists if the offsets are available within the tokenizer" + ) + ) + is_grouped: bool = Field( + default=False, + description="True if this result is part of an entity group", + ) + + +class TokenClassificationOutput(BaseModel): + """ + Schema for results of TokenClassificationPipeline inference. Classifications of each + token stored in a list of lists of batch[sentence[token]] + """ + + predictions: List[List[TokenClassificationResult]] = Field( + description=( + "list of list of results of token classification pipeline. Outer list " + "has one item for each sequence in the batch. Inner list has one " + "TokenClassificationResult item per token in the given sequence" + ) + ) + + +@Pipeline.register( + task="token_classification", + task_aliases=["ner"], + default_model_path=( + "zoo:nlp/token_classification/bert-base/pytorch/huggingface/" + "conll2003/12layer_pruned80_quant-none-vnni" + ), +) +class TokenClassificationPipeline(TransformersPipeline): + """ + transformers token classification pipeline + + example instantiation: + ```python + token_classifier = Pipeline.create( + task="token_classification", + model_path="token_classification_model_dir/", + batch_size=BATCH_SIZE, + ) + ``` + + :param model_path: sparsezoo stub to a transformers model, an ONNX file, or + (preferred) a directory containing a model.onnx, tokenizer config, and model + config. If no tokenizer and/or model config(s) are found, then they will be + loaded from huggingface transformers using the `default_model_name` key + :param engine_type: inference engine to use. Currently supported values include + 'deepsparse' and 'onnxruntime'. Default is 'deepsparse' + :param batch_size: static batch size to use for inference. Default is 1 + :param num_cores: number of CPU cores to allocate for inference engine. None + specifies all available cores. Default is None + :param scheduler: (deepsparse only) kind of scheduler to execute with. + Pass None for the default + :param input_shapes: list of shapes to set ONNX the inputs to. Pass None + to use model as-is. Default is None + :param alias: optional name to give this pipeline instance, useful when + inferencing with multiple models. Default is None + :param sequence_length: sequence length to compile model and tokenizer for. + Default is 128 + :param default_model_name: huggingface transformers model name to use to + load a tokenizer and model config when none are provided in the `model_path`. + Default is 'bert-base-uncased' + :param aggregation_strategy: how to aggregate tokens in postprocessing. Options + include 'none', 'simple', 'first', 'average', and 'max'. Default is None + :param ignore_labels: list of label names to ignore in output. Default is + ['0'] which ignores the default known class label + """ + + def __init__( + self, + *, + aggregation_strategy: AggregationStrategy = AggregationStrategy.NONE, + ignore_labels: List[str] = None, + **kwargs, + ): + + if isinstance(aggregation_strategy, str): + aggregation_strategy = aggregation_strategy.strip().lower() + self._aggregation_strategy = AggregationStrategy(aggregation_strategy) + self._ignore_labels = ["0"] if ignore_labels is None else ignore_labels + + super().__init__(**kwargs) + + @property + def aggregation_strategy(self) -> str: + """ + :return: how to aggregate tokens in postprocessing. Options + include 'none', 'simple', 'first', 'average', and 'max' + """ + return self._aggregation_strategy.value + + @property + def ignore_labels(self) -> List[str]: + """ + :return: list of label names to ignore in output. Default is + ['0'] which ignores the default known class label + """ + return self._ignore_labels + + @property + def input_schema(self) -> Type[BaseModel]: + """ + :return: pydantic model class that inputs to this pipeline must comply to + """ + return TokenClassificationInput + + @property + def output_schema(self) -> Type[BaseModel]: + """ + :return: pydantic model class that outputs of this pipeline must comply to + """ + return TokenClassificationOutput + + def parse_inputs(self, *args, **kwargs) -> BaseModel: + """ + :param args: ordered arguments to pipeline, only an input_schema object + is supported as an arg for this function + :param kwargs: keyword arguments to pipeline + :return: pipeline arguments parsed into the given `input_schema` + schema if necessary. If an instance of the `input_schema` is provided + it will be returned + """ + if args and kwargs: + raise ValueError( + f"{self.__class__} only support args OR kwargs. Found " + f" {len(args)} args and {len(kwargs)} kwargs" + ) + + if args: + if len(args) == 1: + # passed input_schema schema directly + if isinstance(args[0], self.input_schema): + return args[0] + return self.input_schema(inputs=args[0]) + else: + return self.input_schema(inputs=args) + + return self.input_schema(**kwargs) + + def process_inputs( + self, + inputs: TokenClassificationInput, + ) -> Tuple[List[numpy.ndarray], Dict[str, Any]]: + """ + :param inputs: inputs to the pipeline. Must be the type of the + TokenClassificationInput + :return: inputs of this model processed into a list of numpy arrays that + can be directly passed into the forward pass of the pipeline engine + and dictionary containing offset mappings and special tokens mask to + be used during postprocessing + """ + tokens = self.tokenizer( + inputs.inputs, + return_tensors="np", + truncation=TruncationStrategy.LONGEST_FIRST.value, + padding=PaddingStrategy.MAX_LENGTH.value, + return_special_tokens_mask=True, + return_offsets_mapping=self.tokenizer.is_fast, + ) + + offset_mapping = ( + tokens.pop("offset_mapping") + if self.tokenizer.is_fast + else [None] * len(inputs.inputs) + ) + special_tokens_mask = tokens.pop("special_tokens_mask") + postprocessing_kwargs = dict( + inputs=inputs, + tokens=tokens, + offset_mapping=offset_mapping, + special_tokens_mask=special_tokens_mask, + ) + + return self.tokens_to_engine_input(tokens), postprocessing_kwargs + + def process_engine_outputs( + self, + engine_outputs: List[numpy.ndarray], + **kwargs, + ) -> BaseModel: + """ + :param engine_outputs: list of numpy arrays that are the output of the engine + forward pass + :return: outputs of engine post-processed into an object in the `output_schema` + format of this pipeline + """ + inputs = kwargs["inputs"] + tokens = kwargs["tokens"] + offset_mapping = kwargs["offset_mapping"] + special_tokens_mask = kwargs["special_tokens_mask"] + + predictions = [] # type: List[List[TokenClassificationResult]] + + for entities_index, current_entities in enumerate(engine_outputs[0]): + input_ids = tokens["input_ids"][entities_index] + + scores = numpy.exp(current_entities) / numpy.exp(current_entities).sum( + -1, keepdims=True + ) + pre_entities = self._gather_pre_entities( + inputs.inputs[entities_index], + input_ids, + scores, + offset_mapping[entities_index], + special_tokens_mask[entities_index], + ) + grouped_entities = self._aggregate(pre_entities) + # Filter anything that is in self.ignore_labels + current_results = [] # type: List[TokenClassificationResult] + for entity in grouped_entities: + if entity.get("entity") in self.ignore_labels or ( + entity.get("entity_group") in self.ignore_labels + ): + continue + if entity.get("entity_group"): + entity["entity"] = entity["entity_group"] + entity["is_grouped"] = True + del entity["entity_group"] + current_results.append(TokenClassificationResult(**entity)) + predictions.append(current_results) + + return self.output_schema(predictions=predictions) + + # utilities below adapted from transformers + + def _gather_pre_entities( + self, + sentence: str, + input_ids: numpy.ndarray, + scores: numpy.ndarray, + offset_mapping: Optional[List[Tuple[int, int]]], + special_tokens_mask: numpy.ndarray, + ) -> List[dict]: + pre_entities = [] + for idx, token_scores in enumerate(scores): + # Filter special_tokens, they should only occur + # at the sentence boundaries since we're not encoding pairs of + # sentences so we don't have to keep track of those. + if special_tokens_mask[idx]: + continue + + word = self.tokenizer.convert_ids_to_tokens(int(input_ids[idx])) + if offset_mapping is not None: + start_ind, end_ind = offset_mapping[idx] + word_ref = sentence[start_ind:end_ind] + is_subword = len(word_ref) != len(word) + + if int(input_ids[idx]) == self.tokenizer.unk_token_id: + word = word_ref + is_subword = False + else: + start_ind = None + end_ind = None + is_subword = False + + pre_entity = { + "word": word, + "scores": token_scores, + "start": start_ind, + "end": end_ind, + "index": idx, + "is_subword": is_subword, + } + pre_entities.append(pre_entity) + return pre_entities + + def _aggregate(self, pre_entities: List[dict]) -> List[dict]: + if self._aggregation_strategy in { + AggregationStrategy.NONE, + AggregationStrategy.SIMPLE, + }: + entities = [] + for pre_entity in pre_entities: + entity_idx = pre_entity["scores"].argmax() + score = pre_entity["scores"][entity_idx] + entity = { + "entity": self.config.id2label[entity_idx], + "score": score, + "index": pre_entity["index"], + "word": pre_entity["word"], + "start": pre_entity["start"], + "end": pre_entity["end"], + } + entities.append(entity) + else: + entities = self._aggregate_words(pre_entities) + + if self._aggregation_strategy == AggregationStrategy.NONE: + return entities + return self._group_entities(entities) + + def _aggregate_word(self, entities: List[dict]) -> dict: + word = self.tokenizer.convert_tokens_to_string( + [entity["word"] for entity in entities] + ) + if self._aggregation_strategy == AggregationStrategy.FIRST: + scores = entities[0]["scores"] + idx = scores.argmax() + score = scores[idx] + entity = self.config.id2label[idx] + elif self._aggregation_strategy == AggregationStrategy.MAX: + max_entity = max(entities, key=lambda entity: entity["scores"].max()) + scores = max_entity["scores"] + idx = scores.argmax() + score = scores[idx] + entity = self.config.id2label[idx] + elif self._aggregation_strategy == AggregationStrategy.AVERAGE: + scores = numpy.stack([entity["scores"] for entity in entities]) + average_scores = numpy.nanmean(scores, axis=0) + entity_idx = average_scores.argmax() + entity = self.config.id2label[entity_idx] + score = average_scores[entity_idx] + else: + raise ValueError( + f"Invalid aggregation_strategy: {self._aggregation_strategy}" + ) + new_entity = { + "entity": entity, + "score": score, + "word": word, + "start": entities[0]["start"], + "end": entities[-1]["end"], + } + return new_entity + + def _aggregate_words(self, entities: List[dict]) -> List[dict]: + word_entities = [] + word_group = None + for entity in entities: + if word_group is None: + word_group = [entity] + elif entity["is_subword"]: + word_group.append(entity) + else: + word_entities.append(self._aggregate_word(word_group)) + word_group = [entity] + # Last item + word_entities.append(self._aggregate_word(word_group)) + return word_entities + + def _group_sub_entities(self, entities: List[dict]) -> dict: + # Get the first entity in the entity group + entity = entities[0]["entity"].split("-")[-1] + scores = numpy.nanmean([entity["score"] for entity in entities]) + tokens = [entity["word"] for entity in entities] + + entity_group = { + "entity_group": entity, + "score": numpy.mean(scores), + "word": self.tokenizer.convert_tokens_to_string(tokens), + "start": entities[0]["start"], + "end": entities[-1]["end"], + } + return entity_group + + def _get_tag(self, entity_name: str) -> Tuple[str, str]: + if entity_name.startswith("B-"): + bi = "B" + tag = entity_name[2:] + elif entity_name.startswith("I-"): + bi = "I" + tag = entity_name[2:] + else: + # It's not in B-, I- format + bi = "B" + tag = entity_name + return bi, tag + + def _group_entities(self, entities: List[dict]) -> List[dict]: + + entity_groups = [] + entity_group_disagg = [] + + for entity in entities: + if not entity_group_disagg: + entity_group_disagg.append(entity) + continue + + # If the current entity is similar and adjacent to the previous entity, + # append it to the disaggregated entity group + # The split is meant to account for the "B" and "I" prefixes + # Shouldn't merge if both entities are B-type + bi, tag = self._get_tag(entity["entity"]) + last_bi, last_tag = self._get_tag(entity_group_disagg[-1]["entity"]) + + if tag == last_tag and bi != "B": + # Modify subword type to be previous_type + entity_group_disagg.append(entity) + else: + # If the current entity is different from the previous entity + # aggregate the disaggregated entity group + entity_groups.append(self._group_sub_entities(entity_group_disagg)) + entity_group_disagg = [entity] + if entity_group_disagg: + # it's the last entity, add it to the entity groups + entity_groups.append(self._group_sub_entities(entity_group_disagg)) + + return entity_groups diff --git a/src/deepsparse/transformers/server.py b/src/deepsparse/transformers/server.py deleted file mode 100644 index 59035dba80..0000000000 --- a/src/deepsparse/transformers/server.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. - -""" -Specs, schemas, and pipelines for use when serving transformers models -""" - -from typing import Any, Dict, List, Optional, Tuple, Union - -from deepsparse.tasks import SupportedTasks -from deepsparse.transformers.pipelines import Pipeline, pipeline - - -try: - from deepsparse.server.config import ServeModelConfig - - deepsparse_server_err = None -except Exception as _err: - deepsparse_server_err = _err - ServeModelConfig = object - -try: - from pydantic import BaseModel, Field - - pydantic_import_err = None -except Exception as _err: - pydantic_import_err = _err - BaseModel = object - Field = dict - - -__all__ = [ - "create_pipeline_definitions", - "QuestionAnsweringRequest", - "QuestionAnsweringResponse", - "TextClassificationRequest", - "TextClassificationResponse", - "TokenClassificationRequest", - "TokenClassificationResponse", -] - - -def create_pipeline_definitions( - model_config: ServeModelConfig, -) -> Tuple[Pipeline, Any, Any, Dict]: - """ - Create a pipeline definition and the supporting files for a given model config - to use for serving in the DeepSparse inference server - - :param model_config: the server model config describing the model and params - :return: a tuple containing (the pipeline to use for inference, - the expected request body, the expected response body, - any additional keyword args for use with the server) - """ - if deepsparse_server_err: - raise deepsparse_server_err - - if pydantic_import_err: - raise pydantic_import_err - - if SupportedTasks.nlp.question_answering.matches(model_config.task): - request_model = QuestionAnsweringRequest - response_model = Union[ - List[QuestionAnsweringResponse], - QuestionAnsweringResponse, - ] - kwargs = {} - elif SupportedTasks.nlp.text_classification.matches(model_config.task): - request_model = TextClassificationRequest - response_model = Union[ - List[TextClassificationResponse], List[List[TextClassificationResponse]] - ] - kwargs = {} - elif SupportedTasks.nlp.token_classification.matches(model_config.task): - request_model = TokenClassificationRequest - response_model = Union[ - List[TokenClassificationResponse], List[List[TokenClassificationResponse]] - ] - kwargs = {} - else: - raise ValueError( - f"unrecognized task given of {model_config.task} for config {model_config}" - ) - - pipeline_instance: Pipeline = pipeline( - task=model_config.task.lower().replace("_", "-"), - model_path=model_config.model_path, - engine_type=model_config.engine, - num_cores=model_config.num_cores, - scheduler=model_config.scheduler, - batch_size=model_config.batch_size, - **model_config.kwargs, - ) - - return pipeline_instance, request_model, response_model, kwargs - - -class QuestionAnsweringRequest(BaseModel): - """ - The request model for Question Answering Task - """ - - question: Union[List[str], str] = Field( - description="Either a string or a List of string questions to answer" - ) - context: Union[List[str], str] = Field( - description="Either a string or List of strings representing the context " - "for each question" - ) - - -class TokenClassificationRequest(BaseModel): - """ - Schema for TokenClassificationPipeline Request - """ - - inputs: Union[List[str], str] = Field( - description="A string or List of strings representing input to" - "TokenClassificationPipeline task" - ) - - -class TextClassificationRequest(BaseModel): - """ - Schema for TextClassificationPipeline Request - """ - - sequences: Union[List[str], str] = Field( - description="A string or List of strings representing input to" - "TextClassificationPipeline task" - ) - - -class QuestionAnsweringResponse(BaseModel): - """ - Schema for a result from Question Answering Task - """ - - score: float = Field(description="confidence score for prediction") - start: int = Field(description="The start index of the answer") - end: int = Field(description="The end index of the answer") - answer: str = Field(description="The predicted answer") - - -class TokenClassificationResponse(BaseModel): - """ - Schema for TokenClassificationPipeline Response - """ - - entity: str = Field( - description="The entity predicted for that token/word (it is named" - "`entity_group` when `aggregation_strategy` is not `none`." - ) - score: float = Field(description="The corresponding probability for `entity`.") - index: int = Field( - description="The index of the corresponding token in the sentence." - ) - word: str = Field(description="The token/word classified.") - start: Optional[int] = Field( - description="The index of the start of the corresponding entity in the " - "sentence. Only exists if the offsets are available within the tokenizer" - ) - end: Optional[int] = Field( - description="The index of the end of the corresponding entity in the sentence. " - "Only exists if the offsets are available within the tokenizer" - ) - - -class TextClassificationResponse(BaseModel): - """ - Schema for TextClassificationPipeline Response - """ - - label: str = Field(description="The label predicted.") - score: float = Field(description="The corresponding probability.") diff --git a/src/deepsparse/version.py b/src/deepsparse/version.py index aa01f06582..fb7df95188 100644 --- a/src/deepsparse/version.py +++ b/src/deepsparse/version.py @@ -38,7 +38,7 @@ from deepsparse.generated_version import is_release, splash, version except Exception: # otherwise, fall back to version info in this file - version = "0.12.0" + version = "0.12.1" is_release = False splash = ( "DeepSparse Engine, Copyright 2021-present / Neuralmagic, Inc. " diff --git a/src/deepsparse/yolo/__init__.py b/src/deepsparse/yolo/__init__.py new file mode 100644 index 0000000000..0c44f887a4 --- /dev/null +++ b/src/deepsparse/yolo/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. diff --git a/src/deepsparse/yolo/annotate.py b/src/deepsparse/yolo/annotate.py new file mode 100644 index 0000000000..72f7770934 --- /dev/null +++ b/src/deepsparse/yolo/annotate.py @@ -0,0 +1,232 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +""" +Usage: deepsparse.object_detection.annotate [OPTIONS] + + Annotation Script for YOLO with DeepSparse + +Options: + --model_filepath, --model-filepath TEXT + Path/SparseZoo stub to the model file to be + used for annotation [default: zoo:cv/detect + ion/yolov5-s/pytorch/ultralytics/coco/pruned + -aggressive_96] + --source TEXT File path to image or directory of .jpg + files, a .mp4 video, or an integer (i.e. 0) + for webcam [required] + --engine [deepsparse|onnxruntime|torch] + Inference engine backend to run on. Choices + are 'deepsparse', 'onnxruntime', and + 'torch'. Default is 'deepsparse' + --image_shape, --image_shape INTEGER... + Image shape to use for inference, must be + two integers [default: 640, 640] + --num_cores, --num-cores INTEGER + The number of physical cores to run the + annotations with, defaults to using all + physical cores available on the system. For + DeepSparse benchmarks, this value is the + number of cores per socket + --save_dir, --save-dir DIRECTORY + The path to the directory for saving results + [default: annotation-results] + --name TEXT Name of directory in save-dir to write + results to. defaults to + {engine}-annotations-{run_number} + --target_fps, --target-fps FLOAT + Target FPS when writing video files. Frames + will be dropped to closely match target FPS. + --source must be a video file and if target- + fps is greater than the source video fps + then it will be ignored + --no_save, --no-save Set flag when source is from webcam to not + save results.Not supported for non-webcam + sources [default: False] + --help Show this message and exit. + +####### +Examples: + +1) deepsparse.object_detection.annotate --source PATH/TO/IMAGE.jpg +2) deepsparse.object_detection.annotate --source PATH/TO/VIDEO.mp4 +3) deepsparse.object_detection.annotate --source 0 +4) deepsparse.object_detection.annotate --source PATH/TO/IMAGE_DIR +""" +import logging +from typing import Optional + +import click + +import cv2 +from deepsparse.pipeline import Pipeline +from deepsparse.yolo import utils +from deepsparse.yolo.utils.cli_helpers import create_dir_callback + + +yolo_v5_default_stub = ( + "zoo:cv/detection/yolov5-s/pytorch/ultralytics/coco/" "pruned-aggressive_96" +) + +DEEPSPARSE_ENGINE = "deepsparse" +ORT_ENGINE = "onnxruntime" +TORCH_ENGINE = "torch" + +_LOGGER = logging.getLogger(__name__) + + +@click.command() +@click.option( + "--model_filepath", + "--model-filepath", + type=str, + default=yolo_v5_default_stub, + help="Path/SparseZoo stub to the model file to be used for annotation", + show_default=True, +) +@click.option( + "--source", + type=str, + required=True, + help="File path to image or directory of .jpg files, a .mp4 video, " + "or an integer (i.e. 0) for webcam", +) +@click.option( + "--engine", + type=click.Choice([DEEPSPARSE_ENGINE, ORT_ENGINE, TORCH_ENGINE]), + default=DEEPSPARSE_ENGINE, + help="Inference engine backend to run on. Choices are 'deepsparse', " + "'onnxruntime', and 'torch'. Default is 'deepsparse'", +) +@click.option( + "--image_shape", + "--image_shape", + type=int, + nargs=2, + default=(640, 640), + help="Image shape to use for inference, must be two integers", + show_default=True, +) +@click.option( + "--num_cores", + "--num-cores", + type=int, + default=None, + help="The number of physical cores to run the annotations with, " + "defaults to using all physical cores available on the system." + " For DeepSparse benchmarks, this value is the number of cores " + "per socket", + show_default=True, +) +@click.option( + "--save_dir", + "--save-dir", + type=click.Path(dir_okay=True, file_okay=False), + default="annotation-results", + callback=create_dir_callback, + help="The path to the directory for saving results", + show_default=True, +) +@click.option( + "--name", + type=str, + default=None, + help="Name of directory in save-dir to write results to. defaults to " + "{engine}-annotations-{run_number}", +) +@click.option( + "--target_fps", + "--target-fps", + type=float, + default=None, + help="Target FPS when writing video files. Frames will be dropped to " + "closely match target FPS. --source must be a video file and if " + "target-fps is greater than the source video fps then it " + "will be ignored", + show_default=True, +) +@click.option( + "--no_save", + "--no-save", + is_flag=True, + help="Set flag when source is from webcam to not save results." + "Not supported for non-webcam sources", + show_default=True, +) +def main( + model_filepath: str, + source: str, + engine: str, + image_shape: tuple, + num_cores: Optional[int], + save_dir: str, + name: Optional[str], + target_fps: Optional[float], + no_save: bool, +) -> None: + """ + Annotation Script for YOLO with DeepSparse + """ + save_dir = utils.get_annotations_save_dir( + initial_save_dir=save_dir, + tag=name, + engine=engine, + ) + + loader, saver, is_video = utils.get_yolo_loader_and_saver( + path=source, + save_dir=save_dir, + image_shape=image_shape, + target_fps=target_fps, + no_save=no_save, + ) + + is_webcam = source.isnumeric() + yolo_pipeline = Pipeline.create( + task="yolo", + model_path=model_filepath, + class_names="coco", + engine_type=engine, + num_cores=num_cores, + ) + + for iteration, (input_image, source_image) in enumerate(loader): + + # annotate + annotated_images = utils.annotate( + pipeline=yolo_pipeline, + image_batch=input_image, + target_fps=target_fps, + calc_fps=is_video, + original_images=[source_image], + ) + + for annotated_image in annotated_images: + # display + if is_webcam: + cv2.imshow("annotated", annotated_image) + cv2.waitKey(1) + + # save + if saver: + saver.save_frame(annotated_image) + + if saver: + saver.close() + + _LOGGER.info(f"Results saved to {save_dir}") + + +if __name__ == "__main__": + main() diff --git a/src/deepsparse/yolo/pipelines.py b/src/deepsparse/yolo/pipelines.py new file mode 100644 index 0000000000..2398313c31 --- /dev/null +++ b/src/deepsparse/yolo/pipelines.py @@ -0,0 +1,248 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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 json +from typing import Dict, List, Optional, Tuple, Type, Union + +import numpy +import onnx + +from deepsparse.pipeline import Pipeline +from deepsparse.utils import model_to_path +from deepsparse.yolo.schemas import YOLOInput, YOLOOutput +from deepsparse.yolo.utils import COCO_CLASSES, YoloPostprocessor, postprocess_nms + + +try: + import cv2 + + cv2_error = None +except ModuleNotFoundError as cv2_import_error: + cv2 = None + cv2_error = cv2_import_error + + +@Pipeline.register( + task="yolo", + default_model_path=( + "zoo:cv/detection/yolov5-l/pytorch/ultralytics/coco/pruned_quant-aggressive_95" + ), +) +class YOLOPipeline(Pipeline): + """ + Image Segmentation YOLO pipeline for DeepSparse + + :param model_path: path on local system or SparseZoo stub to load the model from + :param engine_type: inference engine to use. Currently supported values include + 'deepsparse' and 'onnxruntime'. Default is 'deepsparse' + :param batch_size: static batch size to use for inference. Default is 1 + :param num_cores: number of CPU cores to allocate for inference engine. None + specifies all available cores. Default is None + :param scheduler: (deepsparse only) kind of scheduler to execute with. + Pass None for the default + :param input_shapes: list of shapes to set ONNX the inputs to. Pass None + to use model as-is. Default is None + :param alias: optional name to give this pipeline instance, useful when + inferencing with multiple models. Default is None + :param class_names: Optional string identifier, dict, or json file of + class names to use for mapping class ids to class labels. Default is + `coco` + """ + + def __init__( + self, + *, + class_names: Optional[Union[str, Dict[str, str]]] = "coco", + model_config: Optional[str] = None, + **kwargs, + ): + super().__init__( + **kwargs, + ) + + if isinstance(class_names, str): + if class_names.endswith(".json"): + class_names = json.load(open(class_names)) + elif class_names == "coco": + class_names = COCO_CLASSES + else: + raise ValueError(f"Unknown class_names: {class_names}") + + if isinstance(class_names, dict): + self._class_names = class_names + elif isinstance(class_names, list): + self._class_names = { + str(index): class_name for index, class_name in enumerate(class_names) + } + else: + raise ValueError( + "class_names must be a str identifier, dict, json file, or " + f"list of class names got {type(class_names)}" + ) + + onnx_model = onnx.load(self.onnx_file_path) + self.has_postprocessing = self.model_has_postprocessing( + loaded_onnx_model=onnx_model, + ) + self.input_shape = self._infer_image_shape(onnx_model=onnx_model) + self.is_quantized = self.model_is_quantized(onnx_model=onnx_model) + self.postprocessor = ( + None + if self.has_postprocessing + else YoloPostprocessor( + image_size=self.input_shape, + cfg=model_config, + ) + ) + self._model_config = model_config + + @property + def model_config(self) -> str: + return self._model_config + + @property + def class_names(self) -> Optional[Dict[str, str]]: + return self._class_names + + @property + def input_schema(self) -> Type[YOLOInput]: + """ + :return: pydantic model class that inputs to this pipeline must comply to + """ + return YOLOInput + + @property + def output_schema(self) -> Type[YOLOOutput]: + """ + :return: pydantic model class that outputs of this pipeline must comply to + """ + return YOLOOutput + + def setup_onnx_file_path(self) -> str: + """ + Performs any setup to unwrap and process the given `model_path` and other + class properties into an inference ready onnx file to be compiled by the + engine of the pipeline + + :return: file path to the ONNX file for the engine to compile + """ + return model_to_path(self.model_path) + + def process_inputs(self, inputs: YOLOInput) -> List[numpy.ndarray]: + """ + :param inputs: inputs to the pipeline. Must be the type of the `input_schema` + of this pipeline + :return: inputs of this model processed into a list of numpy arrays that + can be directly passed into the forward pass of the pipeline engine + """ + image_batch = [] + + if isinstance(inputs.images, str): + inputs.images = [inputs.images] + + for image in inputs.images: + if isinstance(image, str): + image = cv2.imread(image) + image = cv2.resize(image, dsize=self.input_shape) + image = image[:, :, ::-1].transpose(2, 0, 1) + + image_batch.append(image) + + image_batch = numpy.stack(image_batch, axis=0) + image_batch = numpy.ascontiguousarray( + image_batch, + dtype=numpy.int8 if self.is_quantized else numpy.float32, + ) + image_batch /= 255 + + return [image_batch] + + def process_engine_outputs( + self, + engine_outputs: List[numpy.ndarray], + ) -> YOLOOutput: + """ + :param engine_outputs: list of numpy arrays that are the output of the engine + forward pass + :return: outputs of engine post-processed into an object in the `output_schema` + format of this pipeline + """ + + # post-processing + if self.postprocessor: + batch_output = self.postprocessor.pre_nms_postprocess(engine_outputs) + else: + batch_output = engine_outputs[ + 0 + ] # post-processed values stored in first output + + # NMS + batch_output = postprocess_nms(batch_output) + + batch_predictions, batch_boxes, batch_scores, batch_labels = [], [], [], [] + + for image_output in batch_output: + batch_predictions.append(image_output.tolist()) + batch_boxes.append(image_output[:, 0:4].tolist()) + batch_scores.append(image_output[:, 4].tolist()) + batch_labels.append( + [ + self.class_names[str(class_ids)] + for class_ids in image_output[:, 5].astype(int) + ] + ) + + return YOLOOutput( + predictions=batch_predictions, + boxes=batch_boxes, + scores=batch_scores, + labels=batch_labels, + ) + + def _infer_image_shape(self, onnx_model) -> Tuple[int, ...]: + """ + Infer and return the expected shape of the input tensor + + :return: The expected shape of the input tensor from onnx graph + """ + input_tensor = onnx_model.graph.input[0] + return ( + input_tensor.type.tensor_type.shape.dim[2].dim_value, + input_tensor.type.tensor_type.shape.dim[3].dim_value, + ) + + def model_has_postprocessing(self, loaded_onnx_model) -> bool: + """ + :return: True if loaded_onnx_model has postprocessing, False otherwise + """ + # get number of dimensions in each output + outputs_num_dims = [ + len(output.type.tensor_type.shape.dim) + for output in loaded_onnx_model.graph.output + ] + + # assume if only one output, then it is post-processed + if len(outputs_num_dims) == 1: + return True + + return all(num_dims > outputs_num_dims[0] for num_dims in outputs_num_dims[1:]) + + def model_is_quantized(self, onnx_model) -> bool: + """ + :return: True if loaded_onnx_model is quantized, False otherwise + """ + return ( + onnx_model.graph.input[0].type.tensor_type.elem_type + == onnx.TensorProto.UINT8 + ) diff --git a/src/deepsparse/yolo/schemas.py b/src/deepsparse/yolo/schemas.py new file mode 100644 index 0000000000..f60357dfb5 --- /dev/null +++ b/src/deepsparse/yolo/schemas.py @@ -0,0 +1,70 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + + +""" +Input/Output Schemas for Image Segmentation with YOLO +""" +from collections import namedtuple +from typing import List, Union + +import numpy +from pydantic import BaseModel + + +__all__ = [ + "YOLOOutput", + "YOLOInput", +] + +_YOLOImageOutput = namedtuple( + "_YOLOImageOutput", ["predictions", "boxes", "scores", "labels"] +) + + +class YOLOInput(BaseModel): + """ + Input model for image classification + """ + + images: Union[str, List[numpy.ndarray], List[str]] + + class Config: + arbitrary_types_allowed = True + + +class YOLOOutput(BaseModel): + """ + Output model for image classification + """ + + predictions: List[List[List[float]]] + boxes: List[List[List[float]]] + scores: List[List[float]] + labels: List[List[str]] + + def __getitem__(self, index): + if index >= len(self.predictions): + raise IndexError("Index out of range") + + return _YOLOImageOutput( + self.predictions[index], + self.boxes[index], + self.scores[index], + self.labels[index], + ) + + def __iter__(self): + for index in range(len(self.predictions)): + yield self[index] diff --git a/src/deepsparse/yolo/utils/__init__.py b/src/deepsparse/yolo/utils/__init__.py new file mode 100644 index 0000000000..5344738df6 --- /dev/null +++ b/src/deepsparse/yolo/utils/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +# flake8: noqa + +from .coco_classes import * +from .utils import * diff --git a/src/deepsparse/yolo/utils/cli_helpers.py b/src/deepsparse/yolo/utils/cli_helpers.py new file mode 100644 index 0000000000..ccd366236f --- /dev/null +++ b/src/deepsparse/yolo/utils/cli_helpers.py @@ -0,0 +1,46 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from typing import Optional, Union + + +def parse_device( + ctx, + params, + value: Optional[Union[str, int]], +) -> Optional[Union[str, int]]: + """ + :param ctx: The click context + :param params: The click params + :param value: The device value to parse + :return: The correct inferred device + """ + try: + return int(value) + except (ValueError, TypeError): + return value + + +def create_dir_callback(ctx, params, value: str): + """ + Create and return directory if it doesn't exist. + + :param ctx: The click context + :param params: The click params + :param value: The value to create the directory from + :returns: The directory path + """ + os.makedirs(value, exist_ok=True) + return value diff --git a/src/deepsparse/yolo/utils/coco_classes.py b/src/deepsparse/yolo/utils/coco_classes.py new file mode 100644 index 0000000000..5e67829d8f --- /dev/null +++ b/src/deepsparse/yolo/utils/coco_classes.py @@ -0,0 +1,96 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +COCO_CLASSES = [ + "person", + "bicycle", + "car", + "motorcycle", + "airplane", + "bus", + "train", + "truck", + "boat", + "traffic light", + "fire hydrant", + "stop sign", + "parking meter", + "bench", + "bird", + "cat", + "dog", + "horse", + "sheep", + "cow", + "elephant", + "bear", + "zebra", + "giraffe", + "backpack", + "umbrella", + "handbag", + "tie", + "suitcase", + "frisbee", + "skis", + "snowboard", + "sports ball", + "kite", + "baseball bat", + "baseball glove", + "skateboard", + "surfboard", + "tennis racket", + "bottle", + "wine glass", + "cup", + "fork", + "knife", + "spoon", + "bowl", + "banana", + "apple", + "sandwich", + "orange", + "broccoli", + "carrot", + "hot dog", + "pizza", + "donut", + "cake", + "chair", + "couch", + "potted plant", + "bed", + "dining table", + "toilet", + "tv", + "laptop", + "mouse", + "remote", + "keyboard", + "cell phone", + "microwave", + "oven", + "toaster", + "sink", + "refrigerator", + "book", + "clock", + "vase", + "scissors", + "teddy bear", + "hair drier", + "toothbrush", +] diff --git a/src/deepsparse/yolo/utils/utils.py b/src/deepsparse/yolo/utils/utils.py new file mode 100644 index 0000000000..0e14aad9fe --- /dev/null +++ b/src/deepsparse/yolo/utils/utils.py @@ -0,0 +1,795 @@ +# Copyright (c) 2021 - present / Neuralmagic, Inc. 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. + +""" +Helpers and Utilities for YOLO +""" +import functools +import glob +import itertools +import logging +import os +import random +import shutil +import time +from pathlib import Path +from typing import Any, Iterable, Iterator, List, Optional, Tuple, Union + +import numpy +import onnx +import yaml + +import torch +import torchvision +from sparsezoo.utils import create_dirs + + +try: + import cv2 + + cv2_error = None +except ModuleNotFoundError as cv2_import_error: + cv2 = None + cv2_error = cv2_import_error + +_YOLO_CLASS_COLORS = list(itertools.product([0, 255, 128, 64, 192], repeat=3)) +_YOLO_CLASS_COLORS.remove((255, 255, 255)) # remove white from possible colors +_LOGGER = logging.getLogger(__name__) + +# Default YOLO anchor grids +_YOLO_DEFAULT_ANCHORS = [ + torch.Tensor([[10, 13], [16, 30], [33, 23]]), + torch.Tensor([[30, 61], [62, 45], [59, 119]]), + torch.Tensor([[116, 90], [156, 198], [373, 326]]), +] +_YOLO_DEFAULT_ANCHOR_GRIDS = [ + t.clone().view(1, -1, 1, 1, 2) for t in _YOLO_DEFAULT_ANCHORS +] + + +@functools.lru_cache(maxsize=None) +def _get_color(label): + # cache color lookups + return random.choice(_YOLO_CLASS_COLORS) + + +class YoloPostprocessor: + """ + Class for performing post-processing of YOLO model predictions + + :param image_size: size of input image to model. used to calculate stride based on + output shapes + """ + + def __init__( + self, image_size: Tuple[int, int] = (640, 640), cfg: Optional[str] = None + ): + self._image_size = image_size + self._anchor_grids = ( + self._load_cfg_anchor_grid(cfg) if cfg else _YOLO_DEFAULT_ANCHOR_GRIDS + ) + self._grids = {} # Dict[Tuple[int], torch.Tensor] + + def pre_nms_postprocess(self, outputs: List[numpy.ndarray]) -> torch.Tensor: + """ + :param outputs: raw outputs of a YOLO model before anchor grid processing + :return: post-processed model outputs without NMS. + """ + # postprocess and transform raw outputs into single torch tensor + processed_outputs = [] + for idx, pred in enumerate(outputs): + pred = torch.from_numpy(pred) + pred = pred.sigmoid() + + # get grid and stride + grid_shape = pred.shape[2:4] + grid = self._get_grid(grid_shape) + stride = self._image_size[0] / grid_shape[0] + + # decode xywh box values + pred[..., 0:2] = (pred[..., 0:2] * 2.0 - 0.5 + grid) * stride + pred[..., 2:4] = (pred[..., 2:4] * 2) ** 2 * self._anchor_grids[idx] + # flatten anchor and grid dimensions -> + # (bs, num_predictions, num_classes + 5) + processed_outputs.append(pred.view(pred.size(0), -1, pred.size(-1))) + return torch.cat(processed_outputs, 1) + + def _get_grid(self, grid_shape: Tuple[int, int]) -> torch.Tensor: + if grid_shape not in self._grids: + # adapted from yolov5.yolo.Detect._make_grid + coords_y, coords_x = torch.meshgrid( + [torch.arange(grid_shape[0]), torch.arange(grid_shape[1])] + ) + grid = torch.stack((coords_x, coords_y), 2) + self._grids[grid_shape] = grid.view( + 1, 1, grid_shape[0], grid_shape[1], 2 + ).float() + return self._grids[grid_shape] + + @staticmethod + def _load_cfg_anchor_grid(cfg: str) -> List[torch.Tensor]: + with open(cfg) as f: + anchors = yaml.safe_load(f)["anchors"] + + def _split_to_coords(coords_list): + return [ + [coords_list[idx], coords_list[idx + 1]] + for idx in range(0, len(coords_list), 2) + ] + + anchors = [torch.Tensor(_split_to_coords(coords)) for coords in anchors] + return [t.clone().view(1, -1, 1, 1, 2) for t in anchors] + + +def postprocess_nms(outputs: Union[torch.Tensor, numpy.ndarray]) -> List[numpy.ndarray]: + """ + :param outputs: Tensor of post-processed model outputs + :return: List of numpy arrays of NMS predictions for each image in the batch + """ + # run nms in PyTorch, only post-process first output + if isinstance(outputs, numpy.ndarray): + outputs = torch.from_numpy(outputs) + nms_outputs = _non_max_suppression(outputs) + return [output.cpu().numpy() for output in nms_outputs] + + +def _non_max_suppression( + prediction, + conf_thres=0.25, + iou_thres=0.45, + classes=None, + agnostic=False, + multi_label=False, + labels=(), +): + # Ported from ultralytics/yolov5 + + nc = prediction.shape[2] - 5 # number of classes + xc = prediction[..., 4] > conf_thres # candidates + + # Checks + assert 0 <= conf_thres <= 1, ( + f"Invalid Confidence threshold {conf_thres}, " + "valid values are between 0.0 and 1.0" + ) + assert ( + 0 <= iou_thres <= 1 + ), f"Invalid IoU {iou_thres}, valid values are between 0.0 and 1.0" + + # Settings + _, max_wh = 2, 4096 # (pixels) minimum and maximum box width and height + max_det = 300 # maximum number of detections per image + max_nms = 30000 # maximum number of boxes into torchvision.ops.nms() + time_limit = 10.0 # seconds to quit after + redundant = True # require redundant detections + multi_label &= nc > 1 # multiple labels per box (adds 0.5ms/img) + merge = False # use merge-NMS + + t = time.time() + output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0] + for xi, x in enumerate(prediction): # image index, image inference + # Apply constraints + # x[((x[..., 2:4] < min_wh) | (x[..., 2:4] > max_wh)).any(1), 4] = 0 + x = x[xc[xi]] # confidence + + # Cat apriori labels if autolabelling + if labels and len(labels[xi]): + label_ = labels[xi] + v = torch.zeros((len(label_), nc + 5), device=x.device) + v[:, :4] = label_[:, 1:5] # box + v[:, 4] = 1.0 # conf + v[range(len(label_)), label_[:, 0].long() + 5] = 1.0 # cls + x = torch.cat((x, v), 0) + + # If none remain process next image + if not x.shape[0]: + continue + + # Compute conf + x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf + + # Box (center x, center y, width, height) to (x1, y1, x2, y2) + box = _xywh2xyxy(x[:, :4]) + + # Detections matrix nx6 (xyxy, conf, cls) + if multi_label: + i, j = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T + x = torch.cat((box[i], x[i, j + 5, None], j[:, None].float()), 1) + else: # best class only + conf, j = x[:, 5:].max(1, keepdim=True) + x = torch.cat((box, conf, j.float()), 1)[conf.view(-1) > conf_thres] + + # Filter by class + if classes is not None: + x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)] + + # Apply finite constraint + # if not torch.isfinite(x).all(): + # x = x[torch.isfinite(x).all(1)] + + # Check shape + n = x.shape[0] # number of boxes + if not n: # no boxes + continue + elif n > max_nms: # excess boxes + x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence + + # Batched NMS + c = x[:, 5:6] * (0 if agnostic else max_wh) # classes + boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores + i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS + if i.shape[0] > max_det: # limit detections + i = i[:max_det] + if merge and (1 < n < 3e3): # Merge NMS (boxes merged using weighted mean) + # update boxes as boxes(i,4) = weights(i,n) * boxes(n,4) + iou = _box_iou(boxes[i], boxes) > iou_thres # iou matrix + weights = iou * scores[None] # box weights + x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum( + 1, keepdim=True + ) # merged boxes + if redundant: + i = i[iou.sum(1) > 1] # require redundancy + + output[xi] = x[i] + if (time.time() - t) > time_limit: + print(f"WARNING: NMS time limit {time_limit}s exceeded") + break # time limit exceeded + + return output + + +def _xywh2xyxy( + x: Union[torch.Tensor, numpy.ndarray] +) -> Union[torch.Tensor, numpy.ndarray]: + # ported from ultralytics/yolov5 + # Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] + # where xy1=top-left, xy2=bottom-right + y = x.clone() if isinstance(x, torch.Tensor) else numpy.copy(x) + y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x + y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y + y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x + y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y + return y + + +def _box_iou(box1: torch.Tensor, box2: torch.Tensor) -> torch.Tensor: + # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py + """ + Return intersection-over-union (Jaccard index) of boxes. + Both sets of boxes are expected to be in (x1, y1, x2, y2) format. + Arguments: + box1 (Tensor[N, 4]) + box2 (Tensor[M, 4]) + Returns: + iou (Tensor[N, M]): the NxM matrix containing the pairwise + IoU values for every element in boxes1 and boxes2 + """ + + def box_area(box): + # box = 4xn + return (box[2] - box[0]) * (box[3] - box[1]) + + area1 = box_area(box1.T) + area2 = box_area(box2.T) + + # inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2) + inter = ( + ( + torch.min(box1[:, None, 2:], box2[:, 2:]) + - torch.max(box1[:, None, :2], box2[:, :2]) + ) + .clamp(0) + .prod(2) + ) + return inter / ( + area1[:, None] + area2 - inter + ) # iou = inter / (area1 + area2 - inter) + + +def yolo_onnx_has_postprocessing(model_path: str) -> bool: + """ + :param model_path: file path to YOLO ONNX model + :return: True if YOLO postprocessing (pre-nms) is included in the ONNX graph, + this is assumed to be when the first output of the model has fewer dimensions + than the other outputs as the grid dimensions have been flattened + """ + model = onnx.load(model_path) + + # get number of dimensions in each output + outputs_num_dims = [ + len(output.type.tensor_type.shape.dim) for output in model.graph.output + ] + + # assume if only one output, then it is post-processed + if len(outputs_num_dims) == 1: + return True + + return all(num_dims > outputs_num_dims[0] for num_dims in outputs_num_dims[1:]) + + +def annotate( + pipeline: "YOLOPipeline", # noqa: F821 + image_batch: Union[List[numpy.ndarray], List[str]], + target_fps: float = None, + calc_fps: bool = False, + original_images: Optional[Union[List[numpy.ndarray], numpy.ndarray]] = None, +) -> List[numpy.ndarray]: + """ + Annotated and return image_batch with bounding boxes and labels + + :param pipeline: A YOLOPipeline object + :param image_batch: A list of image files, or batch of numpy image_batch + :param target_fps: If not None, then the pipeline will be run at this target + :param calc_fps: If True, and target_fps is None then the pipeline will + calculate the FPS + :param original_images: images from input_batch before any processing + :return: A list of annotated images + + """ + + if not isinstance(image_batch, list): + image_batch = [image_batch] + + if not original_images: + original_images = image_batch + + batch_size = len(image_batch) + if image_batch and isinstance(image_batch[0], str): + original_images = [cv2.imread(image) for image in image_batch] + + if target_fps is None and calc_fps: + start = time.time() + + pipeline_outputs = pipeline(images=image_batch) + + if target_fps is None and calc_fps: + target_fps = float(batch_size) / (time.time() - start) + + annotated_images = [] + for index, image_output in enumerate(pipeline_outputs): + image = original_images[index] + result = _annotate_image( + img=image, + boxes=image_output.boxes, + labels=image_output.labels, + scores=image_output.scores, + model_input_size=pipeline.input_shape, + images_per_sec=target_fps, + ) + annotated_images.append(result) + + return annotated_images + + +def _annotate_image( + img: numpy.ndarray, + boxes: List[List[float]], + scores: List[float], + labels: List[str], + score_threshold: float = 0.35, + model_input_size: Tuple[int, int] = None, + images_per_sec: Optional[float] = None, +) -> numpy.ndarray: + """ + Draws bounding boxes on predictions of a detection model + + :param img: Original image to annotate (no pre-processing needed) + :param boxes: List of bounding boxes (x1, y1, x2, y2) + :param scores: List of scores for each bounding box + :param labels: List of labels for each bounding box + :param score_threshold: minimum score a detection should have to be annotated + on the image. Default is 0.35 + :param model_input_size: 2-tuple of expected input size for the given model to + be used for bounding box scaling with original image. Scaling will not + be applied if model_input_size is None. Default is None + :param images_per_sec: optional image_batch per second to annotate the left corner + of the image with + :return: the original image annotated with the given bounding boxes + """ + img_res = numpy.copy(img) + + scale_y = img.shape[0] / (1.0 * model_input_size[0]) if model_input_size else 1.0 + scale_x = img.shape[1] / (1.0 * model_input_size[1]) if model_input_size else 1.0 + + for idx in range(len(boxes)): + label = labels[idx] + if scores[idx] > score_threshold: + annotation_text = f"{label}: {scores[idx]:.0%}" + + # bounding box points + left = boxes[idx][0] * scale_x + top = boxes[idx][1] * scale_y + right = boxes[idx][2] * scale_x + bottom = boxes[idx][3] * scale_y + + # calculate text size + (text_width, text_height), text_baseline = cv2.getTextSize( + annotation_text, + cv2.FONT_HERSHEY_SIMPLEX, + 0.9, # font scale + 2, # thickness + ) + text_height += text_baseline + + # make solid background for annotation text + cv2.rectangle( + img_res, + (int(left), int(top) - 33), + (int(left) + text_width, int(top) - 28 + text_height), + _get_color(label), + thickness=-1, # filled solid + ) + + # add white annotation text + cv2.putText( + img_res, + annotation_text, + (int(left), int(top) - 10), + cv2.FONT_HERSHEY_SIMPLEX, + 0.9, # font scale + (255, 255, 255), # white text + 2, # thickness + cv2.LINE_AA, + ) + + # draw bounding box + cv2.rectangle( + img_res, + (int(left), int(top)), + (int(right), int(bottom)), + _get_color(label), + thickness=2, + ) + + if images_per_sec is not None: + cv2.putText( + img_res, + f"images_per_sec: {int(images_per_sec)}", + (50, 50), + cv2.FONT_HERSHEY_SIMPLEX, + 2.0, # font scale + (245, 46, 6), # color + 2, # thickness + cv2.LINE_AA, + ) + return img_res + + +def get_yolo_loader_and_saver( + path: str, + save_dir: str, + image_shape: Tuple[int, int] = (640, 640), + target_fps: Optional[float] = None, + no_save: bool = False, +) -> Union[Iterable, Any, bool]: + """ + + :param path: file path to image or directory of .jpg files, a .mp4 video, + or an integer (i.e. 0) for web-cam + :param save_dir: path of directory to save to + :param image_shape: size of input image_batch to model + :param target_fps: fps to save potential video at + :param no_save: set true if not saving results of processing + :return: image loader iterable, result saver objects + image_batch, video, or web-cam based on path given, and a boolean value + that is True is the returned objects load videos + """ + # video + if path.endswith(".mp4"): + loader = YoloVideoLoader(path, image_shape) + saver = VideoSaver( + save_dir, + loader.original_fps, + loader.original_frame_size, + target_fps, + ) + return loader, saver, True + # webcam + if path.isnumeric(): + loader = YoloWebcamLoader(int(path), image_shape) + saver = ( + VideoSaver(save_dir, 30, loader.original_frame_size, None) + if not no_save + else None + ) + return loader, saver, True + # image file(s) + return YoloImageLoader(path, image_shape), ImagesSaver(save_dir), False + + +class YoloImageLoader: + """ + Class for pre-processing and iterating over image_batch to be used as input for YOLO + models + + :param path: Filepath to single image file or directory of image files to load, + glob paths also valid + :param image_size: size of input image_batch to model + """ + + def __init__(self, path: str, image_size: Tuple[int, int] = (640, 640)): + self._path = path + self._image_size = image_size + + if os.path.isdir(path): + self._image_file_paths = [ + os.path.join(path, file_name) for file_name in os.listdir(path) + ] + elif "*" in path: + self._image_file_paths = glob.glob(path) + elif os.path.isfile(path): + # single file + self._image_file_paths = [path] + else: + raise ValueError(f"{path} is not a file, glob, or directory") + + def __iter__(self) -> Iterator[Tuple[numpy.ndarray, numpy.ndarray]]: + for image_path in self._image_file_paths: + yield load_image(image_path, image_size=self._image_size) + + +class YoloVideoLoader: + """ + Class for pre-processing and iterating over video frames to be used as input for + YOLO models + + :param path: Filepath to single video file + :param image_size: size of input image_batch to model + """ + + def __init__(self, path: str, image_size: Tuple[int, int] = (640, 640)): + self._path = path + self._image_size = image_size + self._vid = cv2.VideoCapture(self._path) + self._total_frames = int(self._vid.get(cv2.CAP_PROP_FRAME_COUNT)) + self._fps = self._vid.get(cv2.CAP_PROP_FPS) + + def __iter__(self) -> Iterator[Tuple[numpy.ndarray, numpy.ndarray]]: + for _ in range(self._total_frames): + loaded, frame = self._vid.read() + if not loaded: + break + yield load_image(frame, image_size=self._image_size) + self._vid.release() + + @property + def original_fps(self) -> float: + """ + :return: the frames per second of the video this object reads + """ + return self._fps + + @property + def original_frame_size(self) -> Tuple[int, int]: + """ + :return: the original size of frames in the video this object reads + """ + return ( + int(self._vid.get(cv2.CAP_PROP_FRAME_WIDTH)), + int(self._vid.get(cv2.CAP_PROP_FRAME_HEIGHT)), + ) + + @property + def total_frames(self) -> int: + """ + :return: the total number of frames this object may laod from the video + """ + return self._total_frames + + +class YoloWebcamLoader: + """ + Class for pre-processing and iterating over webcam frames to be used as input for + YOLO models. + + Adapted from: https://github.com/ultralytics/yolov5/blob/master/utils/datasets.py + + :param camera: Webcam index + :param image_size: size of input image_batch to model + """ + + def __init__(self, camera: int, image_size: Tuple[int, int] = (640, 640)): + + self._camera = camera + self._image_size = image_size + self._stream = cv2.VideoCapture(self._camera) + self._stream.set(cv2.CAP_PROP_BUFFERSIZE, 3) + + def __iter__(self) -> Iterator[Tuple[numpy.ndarray, numpy.ndarray]]: + while True: + if cv2.waitKey(1) == ord("q"): # q to quit + self._stream.release() + cv2.destroyAllWindows() + break + loaded, frame = self._stream.read() + + assert loaded, f"Could not load image from webcam {self._camera}" + + frame = cv2.flip(frame, 1) # flip left-right + yield load_image(frame, image_size=self._image_size) + + @property + def original_frame_size(self) -> Tuple[int, int]: + """ + :return: the original size of frames in the stream this object reads + """ + return ( + int(self._stream.get(cv2.CAP_PROP_FRAME_WIDTH)), + int(self._stream.get(cv2.CAP_PROP_FRAME_HEIGHT)), + ) + + +class ImagesSaver: + """ + Base class for saving YOLO model outputs. Saves each image as an individual file in + the given directory + + :param save_dir: path to directory to write to + """ + + def __init__(self, save_dir: str): + self._save_dir = save_dir + self._idx = 0 + + create_dirs(save_dir) + + def save_frame(self, image: numpy.ndarray): + """ + :param image: numpy array of image to save + """ + output_path = os.path.join(self._save_dir, f"result-{self._idx}.jpg") + cv2.imwrite(output_path, image) + self._idx += 1 + + def close(self): + """ + perform any clean-up tasks + """ + pass + + +class VideoSaver(ImagesSaver): + """ + Class for saving YOLO model outputs as a VideoFile + + :param save_dir: path to directory to write to + :param original_fps: frames per second to save video with + :param output_frame_size: size of frames to write + :param target_fps: fps target for output video. if present, video + will be written with a certain number of the original frames + evenly dropped to match the target FPS. + """ + + def __init__( + self, + save_dir: str, + original_fps: float, + output_frame_size: Tuple[int, int], + target_fps: Optional[float] = None, + ): + super().__init__(save_dir) + + self._output_frame_size = output_frame_size + self._original_fps = original_fps + + if target_fps is not None and target_fps >= original_fps: + print( + f"target_fps {target_fps} is greater than source_fps " + f"{original_fps}. target fps file will not be invoked" + ) + self._target_fps = target_fps + + self._file_path = os.path.join(self._save_dir, "results.mp4") + self._writer = cv2.VideoWriter( + self._file_path, + cv2.VideoWriter_fourcc(*"mp4v"), + original_fps, + self._output_frame_size, + ) + self._n_frames = 0 + + def save_frame(self, image: numpy.ndarray): + """ + :param image: numpy array of image to save + """ + self._writer.write(image) + self._n_frames += 1 + + def close(self): + """ + perform any clean-up tasks + """ + self._writer.release() + if self._target_fps is not None and self._target_fps < self._original_fps: + self._write_target_fps_video() + + def _write_target_fps_video(self): + assert self._target_fps is not None + num_frames_to_keep = int( + self._n_frames * (self._target_fps / self._original_fps) + ) + # adjust target fps so we can keep the same video duration + adjusted_target_fps = num_frames_to_keep * (self._original_fps / self._n_frames) + + # select num_frames_to_keep evenly spaced frame idxs + frame_idxs_to_keep = set( + numpy.round(numpy.linspace(0, self._n_frames, num_frames_to_keep)) + .astype(int) + .tolist() + ) + + # create new video writer for adjusted video + vid_path = os.path.join( + self._save_dir, f"_results-{adjusted_target_fps:.2f}fps.mp4" + ) + fps_writer = cv2.VideoWriter( + vid_path, + cv2.VideoWriter_fourcc(*"mp4v"), + adjusted_target_fps, + self._output_frame_size, + ) + + # read from original video and write to FPS adjusted video + saved_vid = cv2.VideoCapture(self._file_path) + for idx in range(self._n_frames): + _, frame = saved_vid.read() + if idx in frame_idxs_to_keep: + fps_writer.write(frame) + + saved_vid.release() + fps_writer.release() + shutil.move(vid_path, self._file_path) # overwrite original file + + +def load_image( + img: Union[str, numpy.ndarray], image_size: Tuple[int, int] = (640, 640) +) -> Tuple[List[numpy.ndarray], List[numpy.ndarray]]: + """ + :param img: file path to image or raw image array + :param image_size: target shape for image + :return: Image loaded into numpy and reshaped to the given shape and the original + image + """ + img = cv2.imread(img) if isinstance(img, str) else img + img_resized = cv2.resize(img, image_size) + img_transposed = img_resized[:, :, ::-1].transpose(2, 0, 1) + + return img_transposed, img + + +def get_annotations_save_dir( + initial_save_dir: str, + tag: Optional[str] = None, + engine: Optional[str] = None, +) -> str: + """ + Returns the directory to save annotations to. If directory exists and is + non-empty, a number is appended to the end of the directory name. + + :param initial_save_dir: Initial directory to save annotations to + :param tag: A tag under which to save the annotations inside `save_dir` + :param engine: Used to generate a unique tag if it is not provided. + :return: A new unique dir path to save annotations to + """ + name = tag or f"{engine}-annotations" + initial_save_dir = os.path.join(initial_save_dir, name) + counter = 0 + new_save_dir = initial_save_dir + while Path(new_save_dir).exists() and any(Path(new_save_dir).iterdir()): + counter += 1 + new_save_dir = os.path.join(initial_save_dir, f"{name}-{counter:03d}") + + _LOGGER.info(f"Results will be saved to {new_save_dir}") + Path(new_save_dir).mkdir(parents=True, exist_ok=True) + return new_save_dir