From 966c8c2df5eb0fee88151cfa58dfab31c1ab3fe9 Mon Sep 17 00:00:00 2001 From: Ekaterina Aidova Date: Tue, 22 Oct 2024 22:38:56 +0400 Subject: [PATCH] fix tmp dir saving (#959) --- optimum/exporters/openvino/convert.py | 3 + optimum/intel/openvino/modeling_base.py | 3 +- .../intel/openvino/modeling_base_seq2seq.py | 2 +- optimum/intel/openvino/modeling_decoder.py | 2 +- optimum/intel/openvino/modeling_diffusion.py | 3 +- optimum/intel/openvino/modeling_open_clip.py | 2 +- .../openvino/modeling_visual_language.py | 2 +- optimum/intel/openvino/utils.py | 276 ++++++++++++++++++ tests/openvino/test_export.py | 2 +- tests/openvino/test_exporters_cli.py | 3 +- tests/openvino/test_modeling.py | 28 +- .../test_modeling_sentence_transformers.py | 4 +- tests/openvino/test_quantization.py | 30 +- tests/openvino/test_training_examples.py | 9 +- 14 files changed, 324 insertions(+), 45 deletions(-) diff --git a/optimum/exporters/openvino/convert.py b/optimum/exporters/openvino/convert.py index 59cc2bb7c..4e6503b5b 100644 --- a/optimum/exporters/openvino/convert.py +++ b/optimum/exporters/openvino/convert.py @@ -206,6 +206,7 @@ def export_tensorflow( ov_config=ov_config, library_name=library_name, ) + del ov_model return input_names, output_names, True @@ -268,6 +269,7 @@ def export_pytorch_via_onnx( ov_config=ov_config, library_name=library_name, ) + del ov_model return input_names, output_names, True @@ -442,6 +444,7 @@ def ts_patched_forward(*args, **kwargs): library_name=library_name, ) clear_class_registry() + del ov_model del model gc.collect() return input_names, output_names, False diff --git a/optimum/intel/openvino/modeling_base.py b/optimum/intel/openvino/modeling_base.py index 353416ff5..ed3cdadb5 100644 --- a/optimum/intel/openvino/modeling_base.py +++ b/optimum/intel/openvino/modeling_base.py @@ -16,7 +16,7 @@ import os import warnings from pathlib import Path -from tempfile import TemporaryDirectory, gettempdir +from tempfile import gettempdir from typing import Dict, Optional, Union import openvino @@ -41,6 +41,7 @@ ONNX_WEIGHTS_NAME, OV_TO_PT_TYPE, OV_XML_FILE_NAME, + TemporaryDirectory, _print_compiled_model_properties, model_has_dynamic_inputs, ) diff --git a/optimum/intel/openvino/modeling_base_seq2seq.py b/optimum/intel/openvino/modeling_base_seq2seq.py index 763dd2b50..06c601148 100644 --- a/optimum/intel/openvino/modeling_base_seq2seq.py +++ b/optimum/intel/openvino/modeling_base_seq2seq.py @@ -15,7 +15,6 @@ import logging import os from pathlib import Path -from tempfile import TemporaryDirectory from typing import Dict, Optional, Union import openvino @@ -36,6 +35,7 @@ OV_DECODER_NAME, OV_DECODER_WITH_PAST_NAME, OV_ENCODER_NAME, + TemporaryDirectory, ) diff --git a/optimum/intel/openvino/modeling_decoder.py b/optimum/intel/openvino/modeling_decoder.py index 56b7a1c5a..7c0bde8cd 100644 --- a/optimum/intel/openvino/modeling_decoder.py +++ b/optimum/intel/openvino/modeling_decoder.py @@ -15,7 +15,6 @@ import logging import os from pathlib import Path -from tempfile import TemporaryDirectory from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union import numpy as np @@ -50,6 +49,7 @@ ONNX_WEIGHTS_NAME, OV_XML_FILE_NAME, STR_TO_OV_TYPE, + TemporaryDirectory, get_export_transformers_version, model_has_dynamic_inputs, ) diff --git a/optimum/intel/openvino/modeling_diffusion.py b/optimum/intel/openvino/modeling_diffusion.py index 6c9063df7..68dc31bc9 100644 --- a/optimum/intel/openvino/modeling_diffusion.py +++ b/optimum/intel/openvino/modeling_diffusion.py @@ -21,7 +21,7 @@ from collections import OrderedDict from copy import deepcopy from pathlib import Path -from tempfile import TemporaryDirectory, gettempdir +from tempfile import gettempdir from typing import Any, Dict, Optional, Union import numpy as np @@ -70,6 +70,7 @@ ONNX_WEIGHTS_NAME, OV_TO_PT_TYPE, OV_XML_FILE_NAME, + TemporaryDirectory, _print_compiled_model_properties, model_has_dynamic_inputs, np_to_pt_generators, diff --git a/optimum/intel/openvino/modeling_open_clip.py b/optimum/intel/openvino/modeling_open_clip.py index 4a3cb0fca..ef00c182e 100644 --- a/optimum/intel/openvino/modeling_open_clip.py +++ b/optimum/intel/openvino/modeling_open_clip.py @@ -16,7 +16,6 @@ import logging import os from pathlib import Path -from tempfile import TemporaryDirectory from typing import Dict, Optional, Union import numpy as np @@ -39,6 +38,7 @@ from ..utils.modeling_utils import _find_files_matching_pattern, _OpenClipForZeroShotImageClassification from .configuration import OVConfig, OVWeightQuantizationConfig from .modeling import MODEL_START_DOCSTRING, OVModel +from .utils import TemporaryDirectory logger = logging.getLogger(__name__) diff --git a/optimum/intel/openvino/modeling_visual_language.py b/optimum/intel/openvino/modeling_visual_language.py index cf6aee7b1..141abeb87 100644 --- a/optimum/intel/openvino/modeling_visual_language.py +++ b/optimum/intel/openvino/modeling_visual_language.py @@ -2,7 +2,6 @@ import os import warnings from pathlib import Path -from tempfile import TemporaryDirectory from typing import Dict, Optional, Tuple, Union import numpy as np @@ -19,6 +18,7 @@ from .configuration import OVConfig, OVWeightQuantizationConfig from .modeling_base import OVBaseModel, OVModelPart from .modeling_decoder import CausalLMOutputWithPast, OVModelForCausalLM +from .utils import TemporaryDirectory logger = logging.getLogger(__name__) diff --git a/optimum/intel/openvino/utils.py b/optimum/intel/openvino/utils.py index 279a24818..fcc6944e9 100644 --- a/optimum/intel/openvino/utils.py +++ b/optimum/intel/openvino/utils.py @@ -16,8 +16,12 @@ import json import logging import os +import stat +import warnings +import weakref from glob import glob from pathlib import Path +from tempfile import TemporaryDirectory as OrigTemporaryDirectory from typing import Tuple, Type, Union import numpy as np @@ -260,3 +264,275 @@ def model_has_dynamic_inputs(model): if is_dynamic: return is_dynamic return is_dynamic + + +# adopted from https://github.com/python/cpython/blob/3.12/Lib/shutil.py for compatibility with python<3.10 +def _rmtree(path, ignore_errors=False, onerror=None, *, onexc=None, dir_fd=None): + """Recursively delete a directory tree. + + If dir_fd is not None, it should be a file descriptor open to a directory; + path will then be relative to that directory. + dir_fd may not be implemented on your platform. + If it is unavailable, using it will raise a NotImplementedError. + + If ignore_errors is set, errors are ignored; otherwise, if onexc or + onerror is set, it is called to handle the error with arguments (func, + path, exc_info) where func is platform and implementation dependent; + path is the argument to that function that caused it to fail; and + the value of exc_info describes the exception. For onexc it is the + exception instance, and for onerror it is a tuple as returned by + sys.exc_info(). If ignore_errors is false and both onexc and + onerror are None, the exception is reraised. + + onerror is deprecated and only remains for backwards compatibility. + If both onerror and onexc are set, onerror is ignored and onexc is used. + """ + _use_fd_functions = ( + {os.open, os.stat, os.unlink, os.rmdir} <= os.supports_dir_fd + and os.scandir in os.supports_fd + and os.stat in os.supports_follow_symlinks + ) + + if hasattr(os.stat_result, "st_file_attributes"): + + def _rmtree_islink(path): + try: + st = os.lstat(path) + return stat.S_ISLNK(st.st_mode) or ( + st.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT + and st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT + ) + except OSError: + return False + + else: + + def _rmtree_islink(path): + return os.path.islink(path) + + def _rmtree_safe_fd(stack, onexc): + # Each stack item has four elements: + # * func: The first operation to perform: os.lstat, os.close or os.rmdir. + # Walking a directory starts with an os.lstat() to detect symlinks; in + # this case, func is updated before subsequent operations and passed to + # onexc() if an error occurs. + # * dirfd: Open file descriptor, or None if we're processing the top-level + # directory given to rmtree() and the user didn't supply dir_fd. + # * path: Path of file to operate upon. This is passed to onexc() if an + # error occurs. + # * orig_entry: os.DirEntry, or None if we're processing the top-level + # directory given to rmtree(). We used the cached stat() of the entry to + # save a call to os.lstat() when walking subdirectories. + func, dirfd, path, orig_entry = stack.pop() + name = path if orig_entry is None else orig_entry.name + try: + if func is os.close: + os.close(dirfd) + return + if func is os.rmdir: + os.rmdir(name, dir_fd=dirfd) + return + + # Note: To guard against symlink races, we use the standard + # lstat()/open()/fstat() trick. + assert func is os.lstat + if orig_entry is None: + orig_st = os.lstat(name, dir_fd=dirfd) + else: + orig_st = orig_entry.stat(follow_symlinks=False) + + func = os.open # For error reporting. + topfd = os.open(name, os.O_RDONLY | os.O_NONBLOCK, dir_fd=dirfd) + + func = os.path.islink # For error reporting. + try: + if not os.path.samestat(orig_st, os.fstat(topfd)): + # Symlinks to directories are forbidden, see GH-46010. + raise OSError("Cannot call rmtree on a symbolic link") + stack.append((os.rmdir, dirfd, path, orig_entry)) + finally: + stack.append((os.close, topfd, path, orig_entry)) + + func = os.scandir # For error reporting. + with os.scandir(topfd) as scandir_it: + entries = list(scandir_it) + for entry in entries: + fullname = os.path.join(path, entry.name) + try: + if entry.is_dir(follow_symlinks=False): + # Traverse into sub-directory. + stack.append((os.lstat, topfd, fullname, entry)) + continue + except OSError: + pass + try: + os.unlink(entry.name, dir_fd=topfd) + except OSError as err: + onexc(os.unlink, fullname, err) + except OSError as err: + err.filename = path + onexc(func, path, err) + + def _rmtree_unsafe(path, onexc): + def onerror(err): + onexc(os.scandir, err.filename, err) + + results = os.walk(path, topdown=False, onerror=onerror, followlinks=hasattr(os, "_walk_symlinks_as_files")) + for dirpath, dirnames, filenames in results: + for name in dirnames: + fullname = os.path.join(dirpath, name) + try: + os.rmdir(fullname) + except OSError as err: + onexc(os.rmdir, fullname, err) + for name in filenames: + fullname = os.path.join(dirpath, name) + try: + os.unlink(fullname) + except OSError as err: + onexc(os.unlink, fullname, err) + try: + os.rmdir(path) + except OSError as err: + onexc(os.rmdir, path, err) + + if ignore_errors: + + def onexc(*args): + pass + + elif onerror is None and onexc is None: + + def onexc(*args): + raise + + elif onexc is None: + if onerror is None: + + def onexc(*args): + raise + + else: + # delegate to onerror + def onexc(*args): + func, path, exc = args + if exc is None: + exc_info = None, None, None + else: + exc_info = type(exc), exc, exc.__traceback__ + return onerror(func, path, exc_info) + + if _use_fd_functions: + # While the unsafe rmtree works fine on bytes, the fd based does not. + if isinstance(path, bytes): + path = os.fsdecode(path) + stack = [(os.lstat, dir_fd, path, None)] + try: + while stack: + _rmtree_safe_fd(stack, onexc) + finally: + # Close any file descriptors still on the stack. + while stack: + func, fd, path, entry = stack.pop() + if func is not os.close: + continue + try: + os.close(fd) + except OSError as err: + onexc(os.close, path, err) + else: + if dir_fd is not None: + raise NotImplementedError("dir_fd unavailable on this platform") + try: + if _rmtree_islink(path): + # symlinks to directories are forbidden, see bug #1669 + raise OSError("Cannot call rmtree on a symbolic link") + except OSError as err: + onexc(os.path.islink, path, err) + # can't continue even if onexc hook returns + return + return _rmtree_unsafe(path, onexc) + + +# copied https://github.com/python/cpython/blob/3.12/Lib/tempfile.py +# to add behaviour that available only for python3.10+ for older python version +class TemporaryDirectory(OrigTemporaryDirectory): + def __init__(self, suffix=None, prefix=None, dir=None, ignore_cleanup_errors=True, *, delete=True): + super().__init__(suffix=suffix, prefix=prefix, dir=dir) + self._ignore_cleanup_errors = ignore_cleanup_errors + self._delete = delete + self._finalizer = weakref.finalize( + self, + self._cleanup, + self.name, + warn_message="Implicitly cleaning up {!r}".format(self), + ignore_errors=self._ignore_cleanup_errors, + delete=self._delete, + ) + + @classmethod + def _cleanup(cls, name, warn_message, ignore_errors=False, delete=True): + if delete: + cls._rmtree(name, ignore_errors=ignore_errors) + warnings.warn(warn_message, ResourceWarning) + + @classmethod + def _rmtree(cls, name, ignore_errors=False, repeated=False): + def _dont_follow_symlinks(func, path, *args): + # Pass follow_symlinks=False, unless not supported on this platform. + if func in os.supports_follow_symlinks: + func(path, *args, follow_symlinks=False) + elif os.name == "nt" or not os.path.islink(path): + func(path, *args) + + def _resetperms(path): + try: + chflags = os.chflags + except AttributeError: + pass + else: + _dont_follow_symlinks(chflags, path, 0) + _dont_follow_symlinks(os.chmod, path, 0o700) + + def onexc(func, path, exc): + if isinstance(exc, PermissionError): + if repeated and path == name: + if ignore_errors: + return + raise + + try: + if path != name: + _resetperms(os.path.dirname(path)) + _resetperms(path) + + try: + os.unlink(path) + except IsADirectoryError: + cls._rmtree(path, ignore_errors=ignore_errors) + except PermissionError: + # The PermissionError handler was originally added for + # FreeBSD in directories, but it seems that it is raised + # on Windows too. + # bpo-43153: Calling _rmtree again may + # raise NotADirectoryError and mask the PermissionError. + # So we must re-raise the current PermissionError if + # path is not a directory. + if not os.path.isdir(path) or os.path.isjunction(path): + if ignore_errors: + return + raise + cls._rmtree(path, ignore_errors=ignore_errors, repeated=(path == name)) + except FileNotFoundError: + pass + elif isinstance(exc, FileNotFoundError): + pass + else: + if not ignore_errors: + raise + + _rmtree(name, onexc=onexc) + + def cleanup(self): + if self._finalizer.detach() or os.path.exists(self.name): + self._rmtree(self.name, ignore_errors=self._ignore_cleanup_errors) diff --git a/tests/openvino/test_export.py b/tests/openvino/test_export.py index d48e86fe2..43c535e67 100644 --- a/tests/openvino/test_export.py +++ b/tests/openvino/test_export.py @@ -15,7 +15,6 @@ import unittest from pathlib import Path -from tempfile import TemporaryDirectory import torch from parameterized import parameterized @@ -46,6 +45,7 @@ OVStableDiffusionXLPipeline, ) from optimum.intel.openvino.modeling_base import OVBaseModel +from optimum.intel.openvino.utils import TemporaryDirectory from optimum.intel.utils.import_utils import _transformers_version from optimum.utils.save_utils import maybe_load_preprocessors diff --git a/tests/openvino/test_exporters_cli.py b/tests/openvino/test_exporters_cli.py index 04e6fc601..8443f95b3 100644 --- a/tests/openvino/test_exporters_cli.py +++ b/tests/openvino/test_exporters_cli.py @@ -14,7 +14,6 @@ import subprocess import unittest from pathlib import Path -from tempfile import TemporaryDirectory from parameterized import parameterized from transformers import AutoModelForCausalLM @@ -44,7 +43,7 @@ OVStableDiffusionXLPipeline, ) from optimum.intel.openvino.configuration import _DEFAULT_4BIT_CONFIGS -from optimum.intel.openvino.utils import _HEAD_TO_AUTOMODELS +from optimum.intel.openvino.utils import _HEAD_TO_AUTOMODELS, TemporaryDirectory from optimum.intel.utils.import_utils import ( compare_versions, is_openvino_tokenizers_available, diff --git a/tests/openvino/test_modeling.py b/tests/openvino/test_modeling.py index 2eb6c1e84..119e00403 100644 --- a/tests/openvino/test_modeling.py +++ b/tests/openvino/test_modeling.py @@ -94,7 +94,7 @@ OVModelWithEmbedForCausalLM, OVVisionEmbedding, ) -from optimum.intel.openvino.utils import _print_compiled_model_properties +from optimum.intel.openvino.utils import TemporaryDirectory, _print_compiled_model_properties from optimum.intel.pipelines import pipeline as optimum_pipeline from optimum.intel.utils.import_utils import is_openvino_version, is_transformers_version from optimum.intel.utils.modeling_utils import _find_files_matching_pattern @@ -171,7 +171,7 @@ def test_load_from_hub_and_save_model(self): self.assertTrue(torch.equal(loaded_model_outputs.logits, outputs.logits)) del compile_only_model - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: loaded_model.save_pretrained(tmpdirname) folder_contents = os.listdir(tmpdirname) self.assertTrue(OV_XML_FILE_NAME in folder_contents) @@ -200,7 +200,7 @@ def test_load_from_hub_and_save_decoder_model(self, use_cache): self.assertEqual(loaded_model.request.get_compiled_model().get_property("PERFORMANCE_HINT"), "LATENCY") loaded_model_outputs = loaded_model(**tokens) - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: loaded_model.save_pretrained(tmpdirname) folder_contents = os.listdir(tmpdirname) self.assertTrue(OV_XML_FILE_NAME in folder_contents) @@ -234,7 +234,7 @@ def test_load_from_hub_and_save_seq2seq_model(self): loaded_model_outputs = loaded_model.generate(**tokens) - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: loaded_model.save_pretrained(tmpdirname) folder_contents = os.listdir(tmpdirname) self.assertTrue(OV_ENCODER_NAME in folder_contents) @@ -278,7 +278,7 @@ def test_load_from_hub_and_save_stable_diffusion_model(self): pipeline_outputs = loaded_pipeline(**inputs).images self.assertEqual(pipeline_outputs.shape, (batch_size, height, width, 3)) - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: loaded_pipeline.save_pretrained(tmpdirname) pipeline = OVStableDiffusionPipeline.from_pretrained(tmpdirname) folder_contents = os.listdir(tmpdirname) @@ -333,7 +333,7 @@ def test_loading_with_config_in_root(self, subfolder): OVModelForFeatureExtraction.from_pretrained(model_id, subfolder=subfolder, export=export) # local model api = HfApi() - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: local_dir = Path(tmpdirname) / "model" api.snapshot_download(repo_id=model_id, local_dir=local_dir) OVModelForFeatureExtraction.from_pretrained(local_dir, subfolder=subfolder, export=export) @@ -341,7 +341,7 @@ def test_loading_with_config_in_root(self, subfolder): def test_infer_export_when_loading(self): model_id = MODEL_NAMES["phi"] model = AutoModelForCausalLM.from_pretrained(model_id) - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: model.save_pretrained(Path(tmpdirname) / "original") # Load original model and convert model = OVModelForCausalLM.from_pretrained(Path(tmpdirname) / "original") @@ -363,7 +363,7 @@ def test_find_files_matching_pattern(self): # local model api = HfApi() - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: for revision in ("main", "ov", "itrex"): local_dir = Path(tmpdirname) / revision api.snapshot_download(repo_id=model_id, local_dir=local_dir, revision=revision) @@ -382,7 +382,7 @@ def test_find_files_matching_pattern_sd(self, model_arch): # local model api = HfApi() - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: local_dir = Path(tmpdirname) / "model" api.snapshot_download(repo_id=model_id, local_dir=local_dir) ov_files = _find_files_matching_pattern(local_dir, pattern=pattern) @@ -416,7 +416,7 @@ def test_load_model_from_hub(self): self.assertIsInstance(ov_exported_pipe.model, OVBaseModel) self.assertIsInstance(ov_pipe.model, OVBaseModel) - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: ov_exported_pipe.save_pretrained(tmpdirname) folder_contents = os.listdir(tmpdirname) self.assertTrue(OV_XML_FILE_NAME in folder_contents) @@ -436,7 +436,7 @@ def test_seq2seq_load_from_hub(self): self.assertIsInstance(ov_exported_pipe.model, OVBaseModel) self.assertIsInstance(ov_pipe.model, OVBaseModel) - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: ov_exported_pipe.save_pretrained(tmpdirname) folder_contents = os.listdir(tmpdirname) self.assertTrue(OV_DECODER_WITH_PAST_NAME in folder_contents) @@ -752,7 +752,7 @@ def test_sentence_transformers_pipeline(self, model_arch): from Sentence Transformers then an appropriate exception raises. """ model_id = MODEL_NAMES[model_arch] - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: save_dir = str(tmp_dir) OVSentenceTransformer.from_pretrained(model_id, export=True).save_pretrained(save_dir) with self.assertRaises(Exception) as context: @@ -1389,7 +1389,7 @@ def test_compare_to_timm(self, model_id): @parameterized.expand(TIMM_MODELS) def test_timm_save_and_infer(self, model_id): ov_model = OVModelForImageClassification.from_pretrained(model_id, export=True) - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: model_save_path = os.path.join(tmpdirname, "timm_ov_model") ov_model.save_pretrained(model_save_path) model = OVModelForImageClassification.from_pretrained(model_save_path) @@ -2258,7 +2258,7 @@ def test_load_from_hub_and_save_model(self): loaded_model_outputs = loaded_model(tokens, processed_image) - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: loaded_model.save_pretrained(tmpdirname) folder_contents = os.listdir(tmpdirname) self.assertTrue(loaded_model.text_model._xml_model_name in folder_contents) diff --git a/tests/openvino/test_modeling_sentence_transformers.py b/tests/openvino/test_modeling_sentence_transformers.py index acda04512..0ddd60ea0 100644 --- a/tests/openvino/test_modeling_sentence_transformers.py +++ b/tests/openvino/test_modeling_sentence_transformers.py @@ -14,7 +14,6 @@ import gc import os -import tempfile import unittest import numpy as np @@ -26,6 +25,7 @@ ) from optimum.intel import OVSentenceTransformer +from optimum.intel.openvino.utils import TemporaryDirectory SEED = 42 @@ -65,7 +65,7 @@ def test_compare_to_transformers(self, model_arch): def test_sentence_transformers_save_and_infer(self, model_arch): model_id = MODEL_NAMES[model_arch] ov_model = OVSentenceTransformer.from_pretrained(model_id, export=True, ov_config=F32_CONFIG) - with tempfile.TemporaryDirectory() as tmpdirname: + with TemporaryDirectory() as tmpdirname: model_save_path = os.path.join(tmpdirname, "sentence_transformers_ov_model") ov_model.save_pretrained(model_save_path) model = OVSentenceTransformer.from_pretrained(model_save_path) diff --git a/tests/openvino/test_quantization.py b/tests/openvino/test_quantization.py index 719509738..b294e3e22 100644 --- a/tests/openvino/test_quantization.py +++ b/tests/openvino/test_quantization.py @@ -17,7 +17,6 @@ import itertools import logging -import tempfile import unittest from collections import defaultdict from enum import Enum @@ -70,6 +69,7 @@ _DEFAULT_4BIT_CONFIGS, _DEFAULT_4BIT_CONFIG, ) +from optimum.intel.openvino.utils import TemporaryDirectory from copy import deepcopy from optimum.intel.openvino.quantization import InferRequestWrapper @@ -102,7 +102,7 @@ def test_automodel_static_quantization(self, model_cls, model_name, expected_fak def preprocess_function(examples, tokenizer): return tokenizer(examples[column_name], padding="max_length", max_length=128, truncation=True) - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: transformers_model = model_cls.auto_model_class.from_pretrained(model_id) tokenizer = AutoTokenizer.from_pretrained(model_id) if tokenizer.pad_token is None: @@ -146,7 +146,7 @@ def test_ovmodel_static_quantization(self, model_cls, model_name, expected_fake_ def preprocess_function(examples, tokenizer): return tokenizer(examples[column_name], padding="max_length", max_length=128, truncation=True) - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: ov_model = model_cls.from_pretrained(model_id, export=True) tokenizer = AutoTokenizer.from_pretrained(model_id) if tokenizer.pad_token is None: @@ -315,7 +315,7 @@ def test_automodel_weight_compression(self, model_cls, model_name, expected_pt_i task = model_cls.export_feature model_id = MODEL_NAMES[model_name] - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: transformers_model = model_cls.auto_model_class.from_pretrained(model_id) tokenizer = AutoTokenizer.from_pretrained(model_id) if tokenizer.pad_token is None: @@ -346,7 +346,7 @@ def test_ovmodel_8bit_weight_compression(self, model_cls, model_name, expected_p task = model_cls.export_feature model_id = MODEL_NAMES[model_name] - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: transformers_model = model_cls.from_pretrained(model_id, export=True) tokenizer = AutoTokenizer.from_pretrained(model_id) if tokenizer.pad_token is None: @@ -371,7 +371,7 @@ def test_ovmodel_8bit_weight_compression(self, model_cls, model_name, expected_p def test_ovmodel_4bit_weight_compression(self, model_cls, model_name, expected_int8, expected_int4): task = model_cls.export_feature model_id = MODEL_NAMES[model_name] - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: transformers_model = model_cls.from_pretrained(model_id, export=True, stateful=False) tokenizer = AutoTokenizer.from_pretrained(model_id) if tokenizer.pad_token is None: @@ -398,7 +398,7 @@ def test_ovmodel_4bit_weight_compression(self, model_cls, model_name, expected_i def test_ovmodel_8bit_weight_compression_stateful(self, model_cls, model_name, expected_pt_int8, expected_ov_int8): task = model_cls.export_feature model_id = MODEL_NAMES[model_name] - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: transformers_model = model_cls.from_pretrained(model_id, export=True, stateful=True) tokenizer = AutoTokenizer.from_pretrained(model_id) if tokenizer.pad_token is None: @@ -451,7 +451,7 @@ def test_ovmodel_load_with_compressed_weights(self, model_cls, model_type): def test_ovmodel_hybrid_quantization(self, model_cls, model_type, expected_num_fake_quantize, expected_ov_int8): model_id = MODEL_NAMES[model_type] quantization_config = OVWeightQuantizationConfig(bits=8, dataset="conceptual_captions", num_samples=2) - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: model = model_cls.from_pretrained(model_id, export=True, quantization_config=quantization_config) num_fake_quantize, num_weight_nodes = get_num_quantized_nodes(model.unet) @@ -497,7 +497,7 @@ def test_ovmodel_hybrid_quantization_with_custom_dataset( "optimum.intel.openvino.configuration._DEFAULT_4BIT_CONFIGS", {"facebook/opt-125m": DEFAULT_INT4_CONFIG} ) def test_ovmodel_4bit_auto_compression(self, model_cls, model_type, expected_ov_int8, expected_ov_int4): - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: model_id = MODEL_NAMES[model_type] model = model_cls.from_pretrained(model_id, export=True, quantization_config={"bits": 4}) tokenizer = AutoTokenizer.from_pretrained(model_id) @@ -521,7 +521,7 @@ def test_ovmodel_4bit_auto_compression_with_config( self, model_cls, model_name, quantization_config, expected_num_weight_nodes ): model_id = MODEL_NAMES[model_name] - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: quantization_config = OVWeightQuantizationConfig.from_dict(quantization_config) model = model_cls.from_pretrained(model_id, export=True, quantization_config=quantization_config) if quantization_config.quant_method.lower() == "awq": @@ -658,7 +658,7 @@ def test_ovmodel_4bit_dynamic_with_config( self, model_cls, model_name, quantization_config, expected_num_weight_nodes ): model_id = MODEL_NAMES[model_name] - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: group_size = quantization_config.pop("group_size", 32) quantization_config = OVDynamicQuantizationConfig( weights_group_size=group_size, activations_group_size=group_size, **quantization_config @@ -693,7 +693,7 @@ def preprocess_function(examples, tokenizer): examples["question"], examples["context"], padding="max_length", max_length=64, truncation=True ) - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: transformers_model = AutoModelForQuestionAnswering.from_pretrained(model_name) tokenizer = AutoTokenizer.from_pretrained(model_name) quantizer = OVQuantizer.from_pretrained(transformers_model) @@ -734,7 +734,7 @@ def preprocess_function(examples, tokenizer): examples["question"], examples["context"], padding="max_length", max_length=64, truncation=True ) - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: transformers_model = OVModelForQuestionAnswering.from_pretrained(model_name, export=True) tokenizer = AutoTokenizer.from_pretrained(model_name) quantizer = OVQuantizer.from_pretrained(transformers_model) @@ -787,7 +787,7 @@ def test_aware_training_quantization(self, model_name, expected_fake_quantize, e def compute_metrics(p): return metric.compute(predictions=np.argmax(p.predictions, axis=1), references=p.label_ids) - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: trainer = OVTrainer( model=model, ov_config=ov_config, @@ -916,7 +916,7 @@ def get_default_configurations() -> dict: @parameterized.expand(QUANTIZATION_CONFIGS) def test_config_serialization(self, quantization_config: OVQuantizationConfigBase): ov_config = OVConfig(quantization_config=quantization_config) - with tempfile.TemporaryDirectory() as tmp_dir: + with TemporaryDirectory() as tmp_dir: ov_config.save_pretrained(tmp_dir) loaded_ov_config = OVConfig.from_pretrained(tmp_dir) diff --git a/tests/openvino/test_training_examples.py b/tests/openvino/test_training_examples.py index 8a33ba42e..023f9df7b 100644 --- a/tests/openvino/test_training_examples.py +++ b/tests/openvino/test_training_examples.py @@ -15,7 +15,6 @@ import os import subprocess import sys -import tempfile import unittest from dataclasses import dataclass from pathlib import Path @@ -25,7 +24,7 @@ import torch.cuda from parameterized import parameterized -from optimum.intel.openvino.utils import OV_XML_FILE_NAME +from optimum.intel.openvino.utils import OV_XML_FILE_NAME, TemporaryDirectory PROJECT_ROOT = Path(__file__).parents[2] @@ -148,7 +147,7 @@ def test_single_card_training(self, _, desc: TrainingExampleDescriptor): self.skipTest("No enough cuda devices.") self.env[CUDA_VISIBLE_DEVICES] = str(self.available_cuda_device_ids[0]) - with tempfile.TemporaryDirectory() as output_dir: + with TemporaryDirectory() as output_dir: args = ["torchrun", "--nproc_per_node=1", desc.filename, *desc.get_args_with_output_dir(output_dir)] proc = subprocess.Popen( args=args, @@ -165,7 +164,7 @@ def test_data_parallel_training(self, _, desc: TrainingExampleDescriptor): self.skipTest("No enough cuda devices.") self.env[CUDA_VISIBLE_DEVICES] = ",".join(map(str, self.available_cuda_device_ids[:2])) - with tempfile.TemporaryDirectory() as output_dir: + with TemporaryDirectory() as output_dir: args = [sys.executable, desc.filename, *desc.get_args_with_output_dir(output_dir)] proc = subprocess.Popen( args=args, @@ -182,7 +181,7 @@ def test_distributed_data_parallel_training(self, _, desc: TrainingExampleDescri self.skipTest("No enough cuda devices.") self.env[CUDA_VISIBLE_DEVICES] = ",".join(map(str, self.available_cuda_device_ids[:2])) - with tempfile.TemporaryDirectory() as output_dir: + with TemporaryDirectory() as output_dir: args = [ "torchrun", "--rdzv_backend=c10d",