optimum-intel
200 строк · 8.6 Кб
1# Copyright 2023 The HuggingFace Team. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14import subprocess
15import unittest
16from pathlib import Path
17from tempfile import TemporaryDirectory
18
19from parameterized import parameterized
20from utils_tests import (
21_ARCHITECTURES_TO_EXPECTED_INT4_INT8,
22_ARCHITECTURES_TO_EXPECTED_INT8,
23MODEL_NAMES,
24get_num_quantized_nodes,
25)
26
27from optimum.exporters.openvino.__main__ import main_export
28from optimum.intel import ( # noqa
29OVModelForAudioClassification,
30OVModelForCausalLM,
31OVModelForFeatureExtraction,
32OVModelForImageClassification,
33OVModelForMaskedLM,
34OVModelForQuestionAnswering,
35OVModelForSeq2SeqLM,
36OVModelForSequenceClassification,
37OVModelForTokenClassification,
38OVStableDiffusionPipeline,
39OVStableDiffusionXLPipeline,
40)
41from optimum.intel.openvino.utils import _HEAD_TO_AUTOMODELS
42from optimum.intel.utils.import_utils import is_openvino_tokenizers_available
43
44
45class OVCLIExportTestCase(unittest.TestCase):
46"""
47Integration tests ensuring supported models are correctly exported.
48"""
49
50SUPPORTED_ARCHITECTURES = (
51("text-generation", "gpt2"),
52("text-generation-with-past", "gpt2"),
53("text2text-generation", "t5"),
54("text2text-generation-with-past", "t5"),
55("text-classification", "albert"),
56("question-answering", "distilbert"),
57("token-classification", "roberta"),
58("image-classification", "vit"),
59("audio-classification", "wav2vec2"),
60("fill-mask", "bert"),
61("feature-extraction", "blenderbot"),
62("stable-diffusion", "stable-diffusion"),
63("stable-diffusion-xl", "stable-diffusion-xl"),
64("stable-diffusion-xl", "stable-diffusion-xl-refiner"),
65)
66EXPECTED_NUMBER_OF_TOKENIZER_MODELS = {
67"gpt2": 2,
68"t5": 0, # failed internal sentencepiece check - no <s> token in the vocab
69"albert": 0, # not supported yet
70"distilbert": 1, # no detokenizer
71"roberta": 2,
72"vit": 0, # no tokenizer for image model
73"wav2vec2": 0, # no tokenizer
74"bert": 1, # no detokenizer
75"blenderbot": 2,
76"stable-diffusion": 0, # not supported
77"stable-diffusion-xl": 0, # not supported
78}
79
80SUPPORTED_4BIT_ARCHITECTURES = (("text-generation-with-past", "opt125m"),)
81
82SUPPORTED_4BIT_OPTIONS = ["int4_sym_g128", "int4_asym_g128", "int4_sym_g64", "int4_asym_g64"]
83
84TEST_4BIT_CONFIGURATONS = []
85for arch in SUPPORTED_4BIT_ARCHITECTURES:
86for option in SUPPORTED_4BIT_OPTIONS:
87TEST_4BIT_CONFIGURATONS.append([arch[0], arch[1], option])
88
89def _openvino_export(
90self, model_name: str, task: str, compression_option: str = None, compression_ratio: float = None
91):
92with TemporaryDirectory() as tmpdir:
93main_export(
94model_name_or_path=model_name,
95output=tmpdir,
96task=task,
97compression_option=compression_option,
98compression_ratio=compression_ratio,
99)
100
101@parameterized.expand(SUPPORTED_ARCHITECTURES)
102def test_export(self, task: str, model_type: str):
103self._openvino_export(MODEL_NAMES[model_type], task)
104
105@parameterized.expand(SUPPORTED_ARCHITECTURES)
106def test_exporters_cli(self, task: str, model_type: str):
107with TemporaryDirectory() as tmpdir:
108subprocess.run(
109f"optimum-cli export openvino --model {MODEL_NAMES[model_type]} --task {task} {tmpdir}",
110shell=True,
111check=True,
112)
113model_kwargs = {"use_cache": task.endswith("with-past")} if "generation" in task else {}
114eval(_HEAD_TO_AUTOMODELS[task.replace("-with-past", "")]).from_pretrained(tmpdir, **model_kwargs)
115
116@parameterized.expand(
117arch
118for arch in SUPPORTED_ARCHITECTURES
119if not arch[0].endswith("-with-past") and not arch[1].endswith("-refiner")
120)
121@unittest.skipIf(not is_openvino_tokenizers_available(), reason="OpenVINO Tokenizers not available")
122def test_exporters_cli_tokenizers(self, task: str, model_type: str):
123with TemporaryDirectory() as tmpdir:
124output = subprocess.check_output(
125f"optimum-cli export openvino --model {MODEL_NAMES[model_type]} --convert-tokenizer --task {task} {tmpdir}",
126shell=True,
127stderr=subprocess.STDOUT,
128).decode()
129save_dir = Path(tmpdir)
130number_of_tokenizers = sum("tokenizer" in file for file in map(str, save_dir.rglob("*.xml")))
131self.assertEqual(
132self.EXPECTED_NUMBER_OF_TOKENIZER_MODELS[model_type],
133number_of_tokenizers,
134f"OVT: {is_openvino_tokenizers_available() }",
135)
136
137if number_of_tokenizers == 1:
138self.assertTrue("Detokenizer is not supported, convert tokenizer only." in output, output)
139elif number_of_tokenizers == 0 and task not in ("image-classification", "audio-classification"):
140self.assertTrue(("OpenVINO Tokenizer export for" in output and "is not supported." in output), output)
141
142@parameterized.expand(SUPPORTED_ARCHITECTURES)
143def test_exporters_cli_fp16(self, task: str, model_type: str):
144with TemporaryDirectory() as tmpdir:
145subprocess.run(
146f"optimum-cli export openvino --model {MODEL_NAMES[model_type]} --task {task} --weight-format fp16 {tmpdir}",
147shell=True,
148check=True,
149)
150model_kwargs = {"use_cache": task.endswith("with-past")} if "generation" in task else {}
151eval(_HEAD_TO_AUTOMODELS[task.replace("-with-past", "")]).from_pretrained(tmpdir, **model_kwargs)
152
153@parameterized.expand(SUPPORTED_ARCHITECTURES)
154def test_exporters_cli_int8(self, task: str, model_type: str):
155with TemporaryDirectory() as tmpdir:
156subprocess.run(
157f"optimum-cli export openvino --model {MODEL_NAMES[model_type]} --task {task} --weight-format int8 {tmpdir}",
158shell=True,
159check=True,
160)
161model_kwargs = {"use_cache": task.endswith("with-past")} if "generation" in task else {}
162model = eval(_HEAD_TO_AUTOMODELS[task.replace("-with-past", "")]).from_pretrained(tmpdir, **model_kwargs)
163
164if task.startswith("text2text-generation"):
165models = [model.encoder, model.decoder]
166if task.endswith("with-past"):
167models.append(model.decoder_with_past)
168elif task.startswith("stable-diffusion"):
169models = [model.unet, model.vae_encoder, model.vae_decoder]
170models.append(model.text_encoder if task == "stable-diffusion" else model.text_encoder_2)
171else:
172models = [model]
173
174expected_int8 = _ARCHITECTURES_TO_EXPECTED_INT8[model_type]
175for i, model in enumerate(models):
176_, num_int8, _ = get_num_quantized_nodes(model)
177self.assertEqual(expected_int8[i], num_int8)
178
179@parameterized.expand(TEST_4BIT_CONFIGURATONS)
180def test_exporters_cli_int4(self, task: str, model_type: str, option: str):
181with TemporaryDirectory() as tmpdir:
182subprocess.run(
183f"optimum-cli export openvino --model {MODEL_NAMES[model_type]} --task {task} --weight-format {option} {tmpdir}",
184shell=True,
185check=True,
186)
187model_kwargs = {"use_cache": task.endswith("with-past")} if "generation" in task else {}
188model = eval(_HEAD_TO_AUTOMODELS[task.replace("-with-past", "")]).from_pretrained(tmpdir, **model_kwargs)
189
190expected_int8, expected_int4 = _ARCHITECTURES_TO_EXPECTED_INT4_INT8[model_type]
191_, num_int8, num_int4 = get_num_quantized_nodes(model)
192self.assertEqual(expected_int8, num_int8)
193self.assertEqual(expected_int4, num_int4)
194
195def test_exporters_cli_help(self):
196subprocess.run(
197"optimum-cli export openvino --help",
198shell=True,
199check=True,
200)
201