



'''simple docstring'''







import subprocess
import sys
import warnings
from argparse import ArgumentParser
from pathlib import Path

from packaging import version

from .. import AutoFeatureExtractor, AutoImageProcessor, AutoProcessor, AutoTokenizer
from ..utils import logging
from ..utils.import_utils import is_optimum_available
from .convert import export, validate_model_outputs
from .features import FeaturesManager
from .utils import get_preprocessor


lowercase__     :  List[Any]    					=			'1.5.0'

lowercase__     :  str    					=			['vision-encoder-decoder']





def 						snake_case__ (lowerCAmelCase_	):
    if is_optimum_available():
        from optimum.version import __version__ as optimum_version

        UpperCamelCase__      =			version.parse(lowerCAmelCase_	)
        if parsed_optimum_version < version.parse(lowerCAmelCase_	):
            raise RuntimeError(
                F'transformers.onnx requires optimum >= {MIN_OPTIMUM_VERSION} but {optimum_version} is installed. You '
                "can upgrade optimum by running: pip install -U optimum[exporters]"	)
    else:
        raise RuntimeError(
            "transformers.onnx requires optimum to run, you can install the library by running: pip install "
            "optimum[exporters]"	)
    UpperCamelCase__      =			[
        sys.executable,
        "-m",
        "optimum.exporters.onnx",
        F'--model {args.model}',
        F'--task {args.feature}',
        F'--framework {args.framework}' if args.framework is not None else "",
        F'{args.output}',
    ]
    UpperCamelCase__      =			subprocess.Popen(" ".join(lowerCAmelCase_	)					,  stdout=subprocess.PIPE					,  shell=lowerCAmelCase_	)
    proc.wait()

    logger.info(
        "The export was done by optimum.exporters.onnx. We recommend using to use this package directly in future, as "
        "transformers.onnx is deprecated, and will be removed in v5. You can find more information here: "
        "https://huggingface.co/docs/optimum/exporters/onnx/usage_guides/export_a_model."	)





def 						snake_case__ (lowerCAmelCase_	):
    UpperCamelCase__      =			args.output if args.output.is_file() else args.output.joinpath("model.onnx"	)
    if not args.output.parent.exists():
        args.output.parent.mkdir(parents=lowerCAmelCase_	)

    # Allocate the model
    UpperCamelCase__      =			FeaturesManager.get_model_from_feature(
        args.feature					,  args.model					,  framework=args.framework					,  cache_dir=args.cache_dir	)

    UpperCamelCase__    ,  UpperCamelCase__      =			FeaturesManager.check_supported_model_or_raise(lowerCAmelCase_					,  feature=args.feature	)
    UpperCamelCase__      =			model_onnx_config(model.config	)

    if model_kind in ENCODER_DECODER_MODELS:
        UpperCamelCase__      =			model.get_encoder()
        UpperCamelCase__      =			model.get_decoder()

        UpperCamelCase__      =			onnx_config.get_encoder_config(encoder_model.config	)
        UpperCamelCase__      =			onnx_config.get_decoder_config(
            encoder_model.config					,  decoder_model.config					,  feature=args.feature	)

        if args.opset is None:
            UpperCamelCase__      =			max(encoder_onnx_config.default_onnx_opset					,  decoder_onnx_config.default_onnx_opset	)

        if args.opset < min(encoder_onnx_config.default_onnx_opset					,  decoder_onnx_config.default_onnx_opset	):
            raise ValueError(
                F'Opset {args.opset} is not sufficient to export {model_kind}. At least '
                F' {min(encoder_onnx_config.default_onnx_opset					,  decoder_onnx_config.default_onnx_opset	)} is required.'	)

        UpperCamelCase__      =			AutoFeatureExtractor.from_pretrained(args.model	)

        UpperCamelCase__    ,  UpperCamelCase__      =			export(
            lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  args.opset					,  args.output.parent.joinpath("encoder_model.onnx"	)					,  )

        validate_model_outputs(
            lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  args.output.parent.joinpath("encoder_model.onnx"	)					,  lowerCAmelCase_					,  args.atol if args.atol else encoder_onnx_config.atol_for_validation					,  )

        UpperCamelCase__      =			AutoTokenizer.from_pretrained(args.model	)

        UpperCamelCase__    ,  UpperCamelCase__      =			export(
            lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  args.opset					,  args.output.parent.joinpath("decoder_model.onnx"	)					,  )

        validate_model_outputs(
            lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  args.output.parent.joinpath("decoder_model.onnx"	)					,  lowerCAmelCase_					,  args.atol if args.atol else decoder_onnx_config.atol_for_validation					,  )
        logger.info(
            F'All good, model saved at: {args.output.parent.joinpath("encoder_model.onnx"	).as_posix()},'
            F' {args.output.parent.joinpath("decoder_model.onnx"	).as_posix()}'	)

    else:
        # Instantiate the appropriate preprocessor
        if args.preprocessor == "auto":
            UpperCamelCase__      =			get_preprocessor(args.model	)
        elif args.preprocessor == "tokenizer":
            UpperCamelCase__      =			AutoTokenizer.from_pretrained(args.model	)
        elif args.preprocessor == "image_processor":
            UpperCamelCase__      =			AutoImageProcessor.from_pretrained(args.model	)
        elif args.preprocessor == "feature_extractor":
            UpperCamelCase__      =			AutoFeatureExtractor.from_pretrained(args.model	)
        elif args.preprocessor == "processor":
            UpperCamelCase__      =			AutoProcessor.from_pretrained(args.model	)
        else:
            raise ValueError(F'Unknown preprocessor type \'{args.preprocessor}\''	)

        # Ensure the requested opset is sufficient
        if args.opset is None:
            UpperCamelCase__      =			onnx_config.default_onnx_opset

        if args.opset < onnx_config.default_onnx_opset:
            raise ValueError(
                F'Opset {args.opset} is not sufficient to export {model_kind}. '
                F'At least  {onnx_config.default_onnx_opset} is required.'	)

        UpperCamelCase__    ,  UpperCamelCase__      =			export(
            lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  args.opset					,  args.output					,  )

        if args.atol is None:
            UpperCamelCase__      =			onnx_config.atol_for_validation

        validate_model_outputs(lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  args.output					,  lowerCAmelCase_					,  args.atol	)
        logger.info(F'All good, model saved at: {args.output.as_posix()}'	)
        warnings.warn(
            "The export was done by transformers.onnx which is deprecated and will be removed in v5. We recommend"
            " using optimum.exporters.onnx in future. You can find more information here:"
            " https://huggingface.co/docs/optimum/exporters/onnx/usage_guides/export_a_model."					,  lowerCAmelCase_					,  )





def 						snake_case__ ():
    UpperCamelCase__      =			ArgumentParser("Hugging Face Transformers ONNX exporter"	)
    parser.add_argument(
        "-m"					,  "--model"					,  type=lowerCAmelCase_					,  required=lowerCAmelCase_					,  help="Model ID on huggingface.co or path on disk to load model from."	)
    parser.add_argument(
        "--feature"					,  default="default"					,  help="The type of features to export the model with."					,  )
    parser.add_argument("--opset"					,  type=lowerCAmelCase_					,  default=lowerCAmelCase_					,  help="ONNX opset version to export the model with."	)
    parser.add_argument(
        "--atol"					,  type=lowerCAmelCase_					,  default=lowerCAmelCase_					,  help="Absolute difference tolerance when validating the model."	)
    parser.add_argument(
        "--framework"					,  type=lowerCAmelCase_					,  choices=["pt", "tf"]					,  default=lowerCAmelCase_					,  help=(
            "The framework to use for the ONNX export."
            " If not provided, will attempt to use the local checkpoint's original framework"
            " or what is available in the environment."
        )					,  )
    parser.add_argument("output"					,  type=lowerCAmelCase_					,  help="Path indicating where to store generated ONNX model."	)
    parser.add_argument("--cache_dir"					,  type=lowerCAmelCase_					,  default=lowerCAmelCase_					,  help="Path indicating where to store cache."	)
    parser.add_argument(
        "--preprocessor"					,  type=lowerCAmelCase_					,  choices=["auto", "tokenizer", "feature_extractor", "image_processor", "processor"]					,  default="auto"					,  help="Which type of preprocessor to use. 'auto' tries to automatically detect it."					,  )
    parser.add_argument(
        "--export_with_transformers"					,  action="store_true"					,  help=(
            "Whether to use transformers.onnx instead of optimum.exporters.onnx to perform the ONNX export. It can be "
            "useful when exporting a model supported in transformers but not in optimum, otherwise it is not "
            "recommended."
        )					,  )

    UpperCamelCase__      =			parser.parse_args()
    if args.export_with_transformers or not is_optimum_available():
        export_with_transformers(lowerCAmelCase_	)
    else:
        export_with_optimum(lowerCAmelCase_	)


if __name__ == "__main__":
      lowercase__     :  Optional[int]    					=			logging.get_logger('transformers.onnx')  # pylint: disable=invalid-name
      logger.setLevel(logging.INFO)
      main()









'''simple docstring'''







import importlib.util
import weakref
from functools import partialmethod

from .dependency_versions_check import dep_version_check
from .utils import is_accelerate_available, is_torch_available, logging


if is_torch_available():
      import torch

lowercase__     :  List[Any]    					=			logging.get_logger(__name__)





def 						snake_case__ ():
    return importlib.util.find_spec("deepspeed"	) is not None


if is_accelerate_available() and is_deepspeed_available():
      from accelerate.utils.deepspeed import HfDeepSpeedConfig as DeepSpeedConfig
else:
      # Inherits from a dummy `object` if accelerate is not available, so that python succeeds to import this file.
      # Deepspeed glue code will never inherit this dummy object as it checks if accelerate is available.
      from builtins import object as DeepSpeedConfig

class       lowerCamelCase_							(	lowerCAmelCase_  ):






   '''simple docstring'''

   def __init__(  self     ,						__UpperCamelCase ):
       # set global weakref object
       set_hf_deepspeed_config(self )
       dep_version_check("accelerate" )
       dep_version_check("deepspeed" )
       super().__init__(__UpperCamelCase )

class       lowerCamelCase_							(	lowerCAmelCase_  ):






   '''simple docstring'''

   def __init__(  self     ,						__UpperCamelCase ):
       super().__init__(__UpperCamelCase )
       UpperCamelCase__      =			None
       UpperCamelCase__      =			[]


   def 	_UpperCamelCase       (  self ):
       if self._dtype is None:
           raise ValueError("trainer_config_process() wasn't called yet to tell dtype" )
       return self._dtype


   def 	_UpperCamelCase       (  self     ,						__UpperCamelCase ):
       UpperCamelCase__      =			self.get_value(__UpperCamelCase )
       if val is None:
           return False
       else:
           return val == "auto"


   def 	_UpperCamelCase       (  self     ,						__UpperCamelCase     ,						__UpperCamelCase     ,						__UpperCamelCase=None     ,						__UpperCamelCase=True ):
       UpperCamelCase__    ,  UpperCamelCase__      =			self.find_config_node(__UpperCamelCase )
       if config is None:
           return

       if config.get(__UpperCamelCase ) == "auto":
           UpperCamelCase__      =			hf_val
           return

       if not must_match:
           return

       UpperCamelCase__      =			config.get(__UpperCamelCase )
       if ds_val is not None and ds_val != hf_val:
           self.mismatches.append(f'- ds {ds_key_long}={ds_val} vs hf {hf_key}={hf_val}' )

   A :     Any				=	partialmethod(lowerCAmelCase_					,    must_match=lowerCAmelCase_  )


   def 	_UpperCamelCase       (  self     ,						__UpperCamelCase ):
       UpperCamelCase__      =			args.world_size * args.per_device_train_batch_size * args.gradient_accumulation_steps
       self.fill_match(
           "train_micro_batch_size_per_gpu"     ,						args.per_device_train_batch_size     ,						"per_device_train_batch_size" )
       self.fill_match("gradient_accumulation_steps"     ,						args.gradient_accumulation_steps     ,						"gradient_accumulation_steps" )
       self.fill_match("train_batch_size"     ,						__UpperCamelCase     ,						"train_batch_size (calculated)" )
       self.fill_match("gradient_clipping"     ,						args.max_grad_norm     ,						"max_grad_norm" )

       self.fill_match("optimizer.params.lr"     ,						args.learning_rate     ,						"learning_rate" )
       self.fill_match("optimizer.params.betas"     ,						[args.adam_betaa, args.adam_betaa]     ,						"adam_beta1+adam_beta2" )
       self.fill_match("optimizer.params.eps"     ,						args.adam_epsilon     ,						"adam_epsilon" )
       self.fill_match("optimizer.params.weight_decay"     ,						args.weight_decay     ,						"weight_decay" )

       self.fill_only("scheduler.params.warmup_min_lr"     ,						0 )  # not a trainer arg
       self.fill_match("scheduler.params.warmup_max_lr"     ,						args.learning_rate     ,						"learning_rate" )
       # total_num_steps - will get set in trainer_config_finalize

       # fp16
       if args.fpaa or args.fpaa_full_eval:
           UpperCamelCase__      =			"apex" if args.fpaa_backend == "apex" else "amp"
       else:
           UpperCamelCase__      =			None

       if args.save_on_each_node:
           # deepspeed uses shared storage by default. Let's override this setting if save_on_each_node == True
           UpperCamelCase__      =			self.config.get("checkpoint"     ,						{} )
           UpperCamelCase__      =			args.save_on_each_node

       # amp: similar to the pytorch native amp - it has a bunch of optional params but we won't set
       # any here unless the user did the work
       self.fill_match(
           "fp16.enabled"     ,						((args.fpaa or args.fpaa_full_eval) and fpaa_backend == "amp")     ,						"fp16|fp16_full_eval+fp16_backend(amp)"     ,						)

       # apex: delegates amp work to apex (which needs to be available), but it cannot be used with any
       # ZeRO features
       self.fill_match("amp.enabled"     ,						fpaa_backend == "apex"     ,						"fp16+fp16_backend(apex)" )
       self.fill_match("amp.opt_level"     ,						args.fpaa_opt_level     ,						"fp16_opt_level" )

       self.fill_match("bf16.enabled"     ,						(args.bfaa or args.bfaa_full_eval)     ,						"bf16|bf16_full_eval" )

       # deepspeed's default mode is fp16 unless there is a config that says differently
       if self.is_true("bf16.enabled" ):
           UpperCamelCase__      =			torch.bfloataa
       elif self.is_false("fp16.enabled" ):
           UpperCamelCase__      =			torch.floataa
       else:
           UpperCamelCase__      =			torch.floataa







   def 	_UpperCamelCase       (  self     ,						__UpperCamelCase     ,						__UpperCamelCase     ,						__UpperCamelCase ):
       UpperCamelCase__      =			[
           "zero_optimization.reduce_bucket_size",
           "zero_optimization.stage3_prefetch_bucket_size",
           "zero_optimization.stage3_param_persistence_threshold",
       ]
       UpperCamelCase__      =			[x for x in hidden_size_based_keys if self.is_auto(__UpperCamelCase )]

       if len(__UpperCamelCase ) > 0:
           if hasattr(model.config     ,						"hidden_size" ):
               UpperCamelCase__      =			model.config.hidden_size
           elif hasattr(model.config     ,						"hidden_sizes" ):
               # if there are many hidden sizes pick the largest one
               UpperCamelCase__      =			max(model.config.hidden_sizes )
           else:
               raise ValueError(
                   "The model's config file has neither `hidden_size` nor `hidden_sizes` entry, "
                   "therefore it's not possible to automatically fill out the following `auto` entries "
                   f'in the DeepSpeed config file: {hidden_size_auto_keys}. You can fix that by replacing '
                   "`auto` values for these keys with an integer value of your choice." )

           self.fill_only("zero_optimization.reduce_bucket_size"     ,						hidden_size * hidden_size )
           if self.is_zeroa():
               # automatically assign the optimal config values based on model config
               self.fill_only("zero_optimization.stage3_prefetch_bucket_size"     ,						0.9 * hidden_size * hidden_size )
               self.fill_only("zero_optimization.stage3_param_persistence_threshold"     ,						10 * hidden_size )

        # scheduler
       self.fill_match("scheduler.params.total_num_steps"     ,						__UpperCamelCase     ,						"num_training_steps (calculated)" )
       self.fill_match("scheduler.params.warmup_num_steps"     ,						args.get_warmup_steps(__UpperCamelCase )     ,						"warmup_steps" )

       if len(self.mismatches ) > 0:
           UpperCamelCase__      =			"\n".join(self.mismatches )
           raise ValueError(
               "Please correct the following DeepSpeed config values that mismatch TrainingArguments"
               f' values:\n{mismatches}\nThe easiest method is to set these DeepSpeed config values to \'auto\'.' )

lowercase__     :  List[str]    					=			None





def 						snake_case__ (lowerCAmelCase_	):
    # this is a special weakref global object to allow us to get to Deepspeed config from APIs
    # that don't have an easy way to get to the Deepspeed config outside of the Trainer domain.
    global _hf_deepspeed_config_weak_ref
    # will go away automatically when HfDeepSpeedConfig is destroyed (when TrainingArguments is destroyed)
    UpperCamelCase__      =			weakref.ref(lowerCAmelCase_	)





def 						snake_case__ ():
    # useful for unit tests to ensure the global state doesn't leak - call from `tearDown` method
    global _hf_deepspeed_config_weak_ref
    UpperCamelCase__      =			None





def 						snake_case__ ():
    if _hf_deepspeed_config_weak_ref is not None and _hf_deepspeed_config_weak_ref() is not None:
        return _hf_deepspeed_config_weak_ref().is_zeroa()
    else:
        return False





def 						snake_case__ ():
    if _hf_deepspeed_config_weak_ref is not None and _hf_deepspeed_config_weak_ref() is not None:
        return _hf_deepspeed_config_weak_ref().config
    else:
        return None





def 						snake_case__ (lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_	):
    from accelerate.utils import DummyOptim, DummyScheduler

    UpperCamelCase__      =			hf_deepspeed_config.config

    # Optimizer + Scheduler
    # Currently supported combos:
    # 1. DS scheduler + DS optimizer: Yes
    # 2. HF scheduler + HF optimizer: Yes
    # 3. DS scheduler + HF optimizer: Yes
    # 4. HF scheduler + DS optimizer: No
    #
    # Unless Offload is enabled in which case it's:
    # 1. DS scheduler + DS optimizer: Yes
    # 2. HF scheduler + HF optimizer: Mostly*
    # 3. DS scheduler + HF optimizer: Mostly*
    # 4. HF scheduler + DS optimizer: No
    #
    # Mostly*: All non-native DeepSpeed optimizers that have both CPU and GPU implementation should work (except LAMB)

    UpperCamelCase__      =			None
    if "optimizer" in config:
        if args.adafactor:
            raise ValueError(
                "--adafactor was passed, but also found `optimizer` configured in the DeepSpeed config. "
                "Only one optimizer can be configured."	)
        UpperCamelCase__      =			DummyOptim(params=lowerCAmelCase_	)
    else:
        if hf_deepspeed_config.is_offload():
            logger.info(
                "Detected ZeRO Offload and non-DeepSpeed optimizers: This combination should work as long as the"
                " custom optimizer has both CPU and GPU implementation (except LAMB)"	)

        # ds supports Adam, OneBitAdam, and Lamb optimizers and can import other optimizers from torch.
        # But trainer uses AdamW by default.
        UpperCamelCase__      =			trainer.create_optimizer()
        # To use other optimizers requires voiding warranty with: `zero_allow_untested_optimizer`
        UpperCamelCase__      =			True

    UpperCamelCase__      =			None
    if "scheduler" in config:
        UpperCamelCase__      =			DummyScheduler(lowerCAmelCase_	)
    else:
        if isinstance(lowerCAmelCase_					,  lowerCAmelCase_	):
            raise ValueError(
                "Found `optimizer` configured in the DeepSpeed config, but no `scheduler`. "
                "Please configure a scheduler in the DeepSpeed config."	)
        UpperCamelCase__      =			trainer.create_scheduler(num_training_steps=lowerCAmelCase_					,  optimizer=lowerCAmelCase_	)

    return optimizer, lr_scheduler





def 						snake_case__ (lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_=False	):
    from deepspeed.utils import logger as ds_logger

    UpperCamelCase__      =			trainer.model
    UpperCamelCase__      =			trainer.args

    UpperCamelCase__      =			trainer.accelerator.state.deepspeed_plugin.hf_ds_config

    # resume config update - some bits like `model` and `num_training_steps` only become available during train
    hf_deepspeed_config.trainer_config_finalize(lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_	)

    # set the Deepspeed log level consistent with the Trainer
    ds_logger.setLevel(args.get_process_log_level()	)

    if inference:
        # only Z3 makes sense for the inference
        if not hf_deepspeed_config.is_zeroa():
            raise ValueError("ZeRO inference only makes sense with ZeRO Stage 3 - please adjust your config"	)

        # in case the training config is re-used for inference
        hf_deepspeed_config.del_config_sub_tree("optimizer"	)
        hf_deepspeed_config.del_config_sub_tree("lr_scheduler"	)
        UpperCamelCase__    ,  UpperCamelCase__      =			None, None
        UpperCamelCase__      =			None
    else:
        UpperCamelCase__      =			None  # important for when deepspeed_init is used as re-init
        UpperCamelCase__      =			list(filter(lambda lowerCAmelCase_	: p.requires_grad					,  model.parameters()	)	)
        UpperCamelCase__    ,  UpperCamelCase__      =			deepspeed_optim_sched(
            lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_					,  lowerCAmelCase_	)

    # keep for quick debug:
    # from pprint import pprint; pprint(config)

    return optimizer, lr_scheduler





def 						snake_case__ (lowerCAmelCase_					,  lowerCAmelCase_	):
    # it's possible that the user is trying to resume from model_path, which doesn't necessarily
    # contain a deepspeed checkpoint. e.g. examples just check if the dir exists and assume it's
    # a resume from a checkpoint and not just a local pretrained weight. So we check here if the
    # path contains what looks like a deepspeed checkpoint
    import glob

    UpperCamelCase__      =			sorted(glob.glob(F'{checkpoint_path}/global_step*'	)	)

    if len(lowerCAmelCase_	) > 0:
        logger.info(F'Attempting to resume from {checkpoint_path}'	)
        # this magically updates self.optimizer and self.lr_scheduler
        UpperCamelCase__    ,  UpperCamelCase__      =			deepspeed_engine.load_checkpoint(
            lowerCAmelCase_					,  load_optimizer_states=lowerCAmelCase_					,  load_lr_scheduler_states=lowerCAmelCase_	)
        if load_path is None:
            raise ValueError(F'[deepspeed] failed to resume from checkpoint {checkpoint_path}'	)
    else:
        raise ValueError(F'Can\'t find a valid checkpoint at {checkpoint_path}'	)




