From e7ae52a55a7dce5abc26a5f72ea0487d1231b514 Mon Sep 17 00:00:00 2001 From: Ella Charlaix Date: Wed, 27 Sep 2023 11:38:15 +0200 Subject: [PATCH 1/4] add openvino export CLI --- optimum/commands/export/openvino.py | 84 +++++++++++++++++++ .../commands/register/register_openvino.py | 19 +++++ 2 files changed, 103 insertions(+) create mode 100644 optimum/commands/export/openvino.py create mode 100644 optimum/commands/register/register_openvino.py diff --git a/optimum/commands/export/openvino.py b/optimum/commands/export/openvino.py new file mode 100644 index 0000000000..37bcb77c4f --- /dev/null +++ b/optimum/commands/export/openvino.py @@ -0,0 +1,84 @@ +# Copyright 2023 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. +"""Defines the command line for the export with OpenVINO.""" + +import subprocess +import sys +from pathlib import Path +from typing import TYPE_CHECKING, Optional + +from ...exporters import TasksManager +from ..base import BaseOptimumCLICommand, CommandInfo + + +if TYPE_CHECKING: + from argparse import ArgumentParser, Namespace, _SubParsersAction + + +def parse_args_openvino(parser: "ArgumentParser"): + required_group = parser.add_argument_group("Required arguments") + required_group.add_argument( + "-m", "--model", type=str, required=True, help="Model ID on huggingface.co or path on disk to load model from." + ) + required_group.add_argument( + "output", type=Path, help="Path indicating the directory where to store the generated OV model." + ) + optional_group = parser.add_argument_group("Optional arguments") + optional_group.add_argument( + "--task", + default="auto", + help=( + "The task to export the model for. If not specified, the task will be auto-inferred based on the model. Available tasks depend on the model, but are among:" + f" {str(TasksManager.get_all_tasks())}. For decoder models, use `xxx-with-past` to export the model using past key values in the decoder." + ), + ) + optional_group.add_argument("--cache_dir", type=str, default=None, help="Path indicating where to store cache.") + + +class OVExportCommand(BaseOptimumCLICommand): + COMMAND = CommandInfo(name="openvino", help="Export PyTorch models to OpenVINO IR.") + + def __init__( + self, + subparsers: "_SubParsersAction", + args: Optional["Namespace"] = None, + command: Optional["CommandInfo"] = None, + from_defaults_factory: bool = False, + parser: Optional["ArgumentParser"] = None, + ): + super().__init__( + subparsers, args=args, command=command, from_defaults_factory=from_defaults_factory, parser=parser + ) + self.args_string = " ".join(sys.argv[3:]) + + @staticmethod + def parse_args(parser: "ArgumentParser"): + return parse_args_openvino(parser) + + + def run(self): + from ...exporters.openvino.__main__ import main_export + + main_export( + model_name_or_path=self.args.model, + output=self.args.output, + task=self.args.task, + # fp16=self.args.fp16, + #no_post_process=self.args.no_post_process, + # framework=self.args.framework, + cache_dir=self.args.cache_dir, + # trust_remote_code=self.args.trust_remote_code, + # pad_token_id=self.args.pad_token_id, + # **input_shapes, + ) diff --git a/optimum/commands/register/register_openvino.py b/optimum/commands/register/register_openvino.py new file mode 100644 index 0000000000..7ae12fa75b --- /dev/null +++ b/optimum/commands/register/register_openvino.py @@ -0,0 +1,19 @@ +# Copyright 2023 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. + +# from ..openvino.base import OVCommand +from ..export.openvino import OVExportCommand + + +REGISTER_COMMANDS = [OVExportCommand] From 66e70069cf1c1b6dc8068f76899d4912c2a9e210 Mon Sep 17 00:00:00 2001 From: Ella Charlaix Date: Wed, 27 Sep 2023 11:50:29 +0200 Subject: [PATCH 2/4] fix --- optimum/commands/export/openvino.py | 4 +--- optimum/commands/register/register_openvino.py | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/optimum/commands/export/openvino.py b/optimum/commands/export/openvino.py index 37bcb77c4f..0eaddddef4 100644 --- a/optimum/commands/export/openvino.py +++ b/optimum/commands/export/openvino.py @@ -13,7 +13,6 @@ # limitations under the License. """Defines the command line for the export with OpenVINO.""" -import subprocess import sys from pathlib import Path from typing import TYPE_CHECKING, Optional @@ -66,7 +65,6 @@ def __init__( def parse_args(parser: "ArgumentParser"): return parse_args_openvino(parser) - def run(self): from ...exporters.openvino.__main__ import main_export @@ -75,7 +73,7 @@ def run(self): output=self.args.output, task=self.args.task, # fp16=self.args.fp16, - #no_post_process=self.args.no_post_process, + # no_post_process=self.args.no_post_process, # framework=self.args.framework, cache_dir=self.args.cache_dir, # trust_remote_code=self.args.trust_remote_code, diff --git a/optimum/commands/register/register_openvino.py b/optimum/commands/register/register_openvino.py index 7ae12fa75b..a1a74abaca 100644 --- a/optimum/commands/register/register_openvino.py +++ b/optimum/commands/register/register_openvino.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# from ..openvino.base import OVCommand +from ..export import ExportCommand from ..export.openvino import OVExportCommand -REGISTER_COMMANDS = [OVExportCommand] +REGISTER_COMMANDS = [(OVExportCommand, ExportCommand)] From 101c924a4374e3a7869ebc3ae859c945531f8964 Mon Sep 17 00:00:00 2001 From: Ella Charlaix Date: Wed, 27 Sep 2023 12:58:02 +0200 Subject: [PATCH 3/4] add test --- optimum/commands/export/openvino.py | 36 +++++++++++++--- tests/openvino/test_exporters_cli.py | 61 ++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 tests/openvino/test_exporters_cli.py diff --git a/optimum/commands/export/openvino.py b/optimum/commands/export/openvino.py index 0eaddddef4..ade989857d 100644 --- a/optimum/commands/export/openvino.py +++ b/optimum/commands/export/openvino.py @@ -43,7 +43,32 @@ def parse_args_openvino(parser: "ArgumentParser"): ), ) optional_group.add_argument("--cache_dir", type=str, default=None, help="Path indicating where to store cache.") + optional_group.add_argument( + "--framework", + type=str, + choices=["pt", "tf"], + default=None, + help=( + "The framework to use for the export. If not provided, will attempt to use the local checkpoint's original framework or what is available in the environment." + ), + ) + optional_group.add_argument( + "--trust-remote-code", + action="store_true", + help=( + "Allows to use custom code for the modeling hosted in the model repository. This option should only be set for repositories you trust and in which " + "you have read the code, as it will execute on your local machine arbitrary code present in the model repository." + ), + ) + optional_group.add_argument( + "--pad-token-id", + type=int, + default=None, + help=( + "This is needed by some models, for some tasks. If not provided, will attempt to use the tokenizer to guess it." + ), + ) class OVExportCommand(BaseOptimumCLICommand): COMMAND = CommandInfo(name="openvino", help="Export PyTorch models to OpenVINO IR.") @@ -68,15 +93,14 @@ def parse_args(parser: "ArgumentParser"): def run(self): from ...exporters.openvino.__main__ import main_export + # TODO : add input shapes main_export( model_name_or_path=self.args.model, output=self.args.output, task=self.args.task, - # fp16=self.args.fp16, - # no_post_process=self.args.no_post_process, - # framework=self.args.framework, + framework=self.args.framework, cache_dir=self.args.cache_dir, - # trust_remote_code=self.args.trust_remote_code, - # pad_token_id=self.args.pad_token_id, + trust_remote_code=self.args.trust_remote_code, + pad_token_id=self.args.pad_token_id, # **input_shapes, - ) + ) \ No newline at end of file diff --git a/tests/openvino/test_exporters_cli.py b/tests/openvino/test_exporters_cli.py new file mode 100644 index 0000000000..e100c2ffe4 --- /dev/null +++ b/tests/openvino/test_exporters_cli.py @@ -0,0 +1,61 @@ +# Copyright 2023 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. +import subprocess +import unittest +from tempfile import TemporaryDirectory +from typing import Dict + +from parameterized import parameterized +from utils_tests import MODEL_NAMES + +from optimum.exporters.openvino.__main__ import main_export +from optimum.exporters.tasks import TasksManager + + +class OVCLIExportTestCase(unittest.TestCase): + """ + Integration tests ensuring supported models are correctly exported. + """ + + SUPPORTED_ARCHITECTURES = ( + ["causal-lm", "gpt2"], + ["causal-lm-with-past", "gpt2"], + ["seq2seq-lm", "t5"], + ["seq2seq-lm-with-past", "t5"], + ["sequence-classification", "bert"], + ["question-answering", "distilbert"], + ["masked-lm", "bert"], + ["default", "blenderbot"], + ["default-with-past", "blenderbot"], + ["stable-diffusion", "stable-diffusion"], + ["stable-diffusion-xl", "stable-diffusion-xl"], + ["stable-diffusion-xl", "stable-diffusion-xl-refiner"], + ) + + def _openvino_export(self, model_name: str, task: str): + with TemporaryDirectory() as tmpdir: + main_export(model_name_or_path=model_name, output=tmpdir, task=task) + + @parameterized.expand(SUPPORTED_ARCHITECTURES) + def test_export(self, task: str, model_type: str): + self._openvino_export(MODEL_NAMES[model_type], task) + + @parameterized.expand(SUPPORTED_ARCHITECTURES) + def test_exporters_cli(self, task: str, model_type: str): + with TemporaryDirectory() as tmpdirname: + subprocess.run( + f"optimum-cli export openvino --model {MODEL_NAMES[model_type]} --task {task} {tmpdirname}", + shell=True, + check=True, + ) From 51fff5c0675ae7d058591e57ac1cd630f272efa8 Mon Sep 17 00:00:00 2001 From: Ella Charlaix Date: Wed, 27 Sep 2023 12:58:23 +0200 Subject: [PATCH 4/4] format --- optimum/commands/export/openvino.py | 4 ++-- tests/openvino/test_exporters_cli.py | 26 ++++++++++++-------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/optimum/commands/export/openvino.py b/optimum/commands/export/openvino.py index ade989857d..b2d33e7647 100644 --- a/optimum/commands/export/openvino.py +++ b/optimum/commands/export/openvino.py @@ -58,7 +58,6 @@ def parse_args_openvino(parser: "ArgumentParser"): help=( "Allows to use custom code for the modeling hosted in the model repository. This option should only be set for repositories you trust and in which " "you have read the code, as it will execute on your local machine arbitrary code present in the model repository." - ), ) optional_group.add_argument( @@ -70,6 +69,7 @@ def parse_args_openvino(parser: "ArgumentParser"): ), ) + class OVExportCommand(BaseOptimumCLICommand): COMMAND = CommandInfo(name="openvino", help="Export PyTorch models to OpenVINO IR.") @@ -103,4 +103,4 @@ def run(self): trust_remote_code=self.args.trust_remote_code, pad_token_id=self.args.pad_token_id, # **input_shapes, - ) \ No newline at end of file + ) diff --git a/tests/openvino/test_exporters_cli.py b/tests/openvino/test_exporters_cli.py index e100c2ffe4..6b1ba249ab 100644 --- a/tests/openvino/test_exporters_cli.py +++ b/tests/openvino/test_exporters_cli.py @@ -14,13 +14,11 @@ import subprocess import unittest from tempfile import TemporaryDirectory -from typing import Dict from parameterized import parameterized from utils_tests import MODEL_NAMES from optimum.exporters.openvino.__main__ import main_export -from optimum.exporters.tasks import TasksManager class OVCLIExportTestCase(unittest.TestCase): @@ -29,18 +27,18 @@ class OVCLIExportTestCase(unittest.TestCase): """ SUPPORTED_ARCHITECTURES = ( - ["causal-lm", "gpt2"], - ["causal-lm-with-past", "gpt2"], - ["seq2seq-lm", "t5"], - ["seq2seq-lm-with-past", "t5"], - ["sequence-classification", "bert"], - ["question-answering", "distilbert"], - ["masked-lm", "bert"], - ["default", "blenderbot"], - ["default-with-past", "blenderbot"], - ["stable-diffusion", "stable-diffusion"], - ["stable-diffusion-xl", "stable-diffusion-xl"], - ["stable-diffusion-xl", "stable-diffusion-xl-refiner"], + ["causal-lm", "gpt2"], + ["causal-lm-with-past", "gpt2"], + ["seq2seq-lm", "t5"], + ["seq2seq-lm-with-past", "t5"], + ["sequence-classification", "bert"], + ["question-answering", "distilbert"], + ["masked-lm", "bert"], + ["default", "blenderbot"], + ["default-with-past", "blenderbot"], + ["stable-diffusion", "stable-diffusion"], + ["stable-diffusion-xl", "stable-diffusion-xl"], + ["stable-diffusion-xl", "stable-diffusion-xl-refiner"], ) def _openvino_export(self, model_name: str, task: str):