





import argparse
import dataclasses
import json
import logging
import os
import shutil
from typing import List, Optional

import datasets
from accelerate import Accelerator
from datasets import load_dataset
from finetuning import finetune
from tqdm.auto import tqdm

import transformers
from transformers import AutoConfig, set_seed
from transformers.trainer_utils import IntervalStrategy


lowercase_					     =	logging.getLogger(__name__)

lowercase_					     =	"""pytorch_model.bin"""
@dataclasses.dataclass
class        SCREAMING_SNAKE_CASE     :
   _UpperCamelCase					:	str				      = dataclasses.field(
       metadata={'help': 'Path to pretrained model or model identifier from huggingface.co/models.'}   )
   _UpperCamelCase					:	Optional[str]				      = dataclasses.field(
       default=UpperCAmelCase    ,			metadata={'help': 'Where do you want to store the pretrained models downloaded from huggingface.co.'}    ,			)






@dataclasses.dataclass
class        SCREAMING_SNAKE_CASE     :
   _UpperCamelCase					:	str				      = dataclasses.field(metadata={'help': 'A csv or a json file containing the training data.'}   )
   _UpperCamelCase					:	str				      = dataclasses.field(metadata={'help': 'A csv or a json file containing the data to predict on.'}   )
   _UpperCamelCase					:	Optional[str]				      = dataclasses.field(
       default=UpperCAmelCase    ,			metadata={'help': 'A csv or a json file containing the validation data.'}   )
   _UpperCamelCase					:	Optional[str]				      = dataclasses.field(
       default=UpperCAmelCase    ,			metadata={'help': 'The name of the task to train on.'}    ,			)
   _UpperCamelCase					:	Optional[List[str]]				      = dataclasses.field(
       default=UpperCAmelCase    ,			metadata={'help': 'The list of labels for the task.'}   )






@dataclasses.dataclass
class        SCREAMING_SNAKE_CASE     :
   _UpperCamelCase					:	str				      = dataclasses.field(
       metadata={'help': 'The output directory where the model predictions and checkpoints will be written.'}   )
   _UpperCamelCase					:	Optional[str]				      = dataclasses.field(
       default='accuracy'    ,			metadata={'help': 'The evaluation metric used for the task.'}   )
   _UpperCamelCase					:	Optional[str]				      = dataclasses.field(
       default='no'    ,			metadata={
           'help': 'The evaluation strategy to adopt during training. Possible values are: ["no", "step", "epoch]'
       }    ,			)
   _UpperCamelCase					:	Optional[int]				      = dataclasses.field(
       default=10    ,			metadata={'help': 'Number of evaluation calls with no improvement after which training will be stopped.'}    ,			)
   _UpperCamelCase					:	Optional[float]				      = dataclasses.field(
       default=0.0    ,			metadata={
           'help': 'How much the specified evaluation metric must improve to satisfy early stopping conditions.'
       }    ,			)
   _UpperCamelCase					:	Optional[bool]				      = dataclasses.field(
       default=UpperCAmelCase    ,			metadata={'help': 'Whether to filter the pseudo-labeled data based on the confidence score.'}    ,			)
   _UpperCamelCase					:	Optional[bool]				      = dataclasses.field(
       default=UpperCAmelCase    ,			metadata={'help': 'Whether to filter the pseudo-labeled data based on the validation performance.'}    ,			)
   _UpperCamelCase					:	Optional[bool]				      = dataclasses.field(
       default=UpperCAmelCase    ,			metadata={'help': 'Whether to fine-tune on labeled data after pseudo training.'}    ,			)
   _UpperCamelCase					:	Optional[float]				      = dataclasses.field(
       default=0.0    ,			metadata={'help': 'Confidence threshold for pseudo-labeled data filtering.'}    ,			)
   _UpperCamelCase					:	Optional[int]				      = dataclasses.field(
       default=1_00    ,			metadata={'help': 'Number of evaluation calls with no improvement after which training will be stopped.'}    ,			)
   _UpperCamelCase					:	Optional[int]				      = dataclasses.field(
       default=UpperCAmelCase    ,			metadata={'help': 'Random seed for initialization.'}    ,			)






def     __UpperCamelCase						(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	)       ->     Optional[int]:
  lowercase__			    =						datasets.concatenate_datasets([infer_input, infer_output]	,		axis=1	)

  if args.do_filter_by_confidence:
    lowercase__			    =						dataset.filter(lambda _SCREAMING_SNAKE_CASE	: example["probability"] > args.confidence_threshold	)

  if args.do_filter_by_val_performance:
    assert eval_result >= 0.0 and eval_result <= 1.0
    lowercase__			    =						int(eval_result * len(_SCREAMING_SNAKE_CASE	)	)
    print(_SCREAMING_SNAKE_CASE	)
    lowercase__			    =						dataset.sort('probability'	,		reverse=_SCREAMING_SNAKE_CASE	)
    lowercase__			    =						dataset.select(range(_SCREAMING_SNAKE_CASE	)	)

  lowercase__			    =						dataset.remove_columns(['label', 'probability']	)
  lowercase__			    =						dataset.rename_column('prediction'	,		'label'	)
  lowercase__			    =						dataset.map(lambda _SCREAMING_SNAKE_CASE	: {"label": idalabel[example["label"]]}	)
  lowercase__			    =						dataset.shuffle(seed=args.seed	)

  lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		F"""train_pseudo.{args.data_file_extension}"""	)
  if args.data_file_extension == "csv":
    dataset.to_csv(_SCREAMING_SNAKE_CASE	,		index=_SCREAMING_SNAKE_CASE	)
  else:
    dataset.to_json(_SCREAMING_SNAKE_CASE	)




def     __UpperCamelCase						(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		**_SCREAMING_SNAKE_CASE	)       ->     Optional[Any]:
  lowercase__			    =						Accelerator()
  # Make one log on every process with the configuration for debugging.
  logging.basicConfig(
      format='%(asctime)s - %(levelname)s - %(name)s - %(message)s'	,		datefmt='%m/%d/%Y %H:%M:%S'	,		level=logging.INFO	,		)
  logger.info(accelerator.state	)

  # Setup logging, we only want one process per machine to log things on the
  # screen. accelerator.is_local_main_process is only True for one process per
  # machine.
  logger.setLevel(logging.INFO if accelerator.is_local_main_process else logging.ERROR	)

  if accelerator.is_local_main_process:
    datasets.utils.logging.set_verbosity_warning()
    transformers.utils.logging.set_verbosity_info()
  else:
    datasets.utils.logging.set_verbosity_error()
    transformers.utils.logging.set_verbosity_error()

  lowercase__			    =						STModelArguments(model_name_or_path=_SCREAMING_SNAKE_CASE	)
  lowercase__			    =						STDataArguments(train_file=_SCREAMING_SNAKE_CASE	,		infer_file=_SCREAMING_SNAKE_CASE	)
  lowercase__			    =						STTrainingArguments(output_dir=_SCREAMING_SNAKE_CASE	)
  lowercase__			    =						argparse.Namespace()

  for arg_class in (model_args, data_args, training_args):
    for key, value in vars(_SCREAMING_SNAKE_CASE	).items():
      setattr(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	)

  for key, value in kwargs.items():
    if hasattr(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	):
      setattr(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	)

    # Sanity checks
  lowercase__			    =						{}
  lowercase__			    =						None

  # You need to provide the training data and the data to predict on
  assert args.train_file is not None
  assert args.infer_file is not None
  lowercase__			    =						args.train_file
  lowercase__			    =						args.infer_file

  if args.evaluation_strategy != IntervalStrategy.NO.value:
    assert args.eval_file is not None
    lowercase__			    =						args.eval_file

  for key in data_files:
    lowercase__			    =						data_files[key].split('.'	)[-1]
    assert extension in ["csv", "json"], F"""`{key}_file` should be a csv or a json file."""
    if args.data_file_extension is None:
      lowercase__			    =						extension
    else:
      assert extension == args.data_file_extension, F"""`{key}_file` should be a {args.data_file_extension} file`."""

  assert (
      args.eval_metric in datasets.list_metrics()
  ), F"""{args.eval_metric} not in the list of supported metrics {datasets.list_metrics()}."""

  # If passed along, set the training seed now.
  if args.seed is not None:
    set_seed(args.seed	)

  logger.info('Creating the initial data directory for self-training...'	)
  lowercase__			    =						F"""{args.output_dir}/self-train_iter-{{}}""".format
  lowercase__			    =						data_dir_format(0	)

  if accelerator.is_main_process:
    if args.output_dir is not None:
      os.makedirs(args.output_dir	,		exist_ok=_SCREAMING_SNAKE_CASE	)
      os.makedirs(_SCREAMING_SNAKE_CASE	,		exist_ok=_SCREAMING_SNAKE_CASE	)
  accelerator.wait_for_everyone()

  lowercase__			    =						None
  lowercase__			    =						None
  lowercase__			    =						0
  lowercase__			    =						False
  # Show the progress bar
  lowercase__			    =						tqdm(range(args.max_selftrain_iterations	)	,		disable=not accelerator.is_local_main_process	)

  # Self-train
  for iteration in range(0	,		int(args.max_selftrain_iterations	)	):
    lowercase__			    =						data_dir_format(_SCREAMING_SNAKE_CASE	)
    assert os.path.exists(_SCREAMING_SNAKE_CASE	)

    # Stage 1: initial fine-tuning for iteration = 0 or pseudo-training for
    # iteration > 0
    lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		'stage-1'	)
    lowercase__			    =						{
        'accelerator': accelerator,
        'model_name_or_path': args.model_name_or_path,
        'cache_dir': args.cache_dir,
        'do_train': True,
        'train_file': data_files['train'] if iteration == 0 else data_files['train_pseudo'],
        'do_eval': True if args.eval_file is not None else False,
        'eval_file': data_files['eval'],
        'do_predict': True,
        'infer_file': data_files['infer'],
        'task_name': args.task_name,
        'label_list': args.label_list,
        'output_dir': current_output_dir,
        'eval_metric': args.eval_metric,
        'evaluation_strategy': args.evaluation_strategy,
        'early_stopping_patience': args.early_stopping_patience,
        'early_stopping_threshold': args.early_stopping_threshold,
        'seed': args.seed,
    }
    # Add additional training arguments
    for key, value in kwargs.items():
      if key not in arguments_dict and not hasattr(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	):
        arguments_dict.update({key: value}	)

    lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		'best-checkpoint'	,		_SCREAMING_SNAKE_CASE	)
    if os.path.exists(_SCREAMING_SNAKE_CASE	):
      logger.info(
          'Found existing model checkpoint at %s. Skipping self-training: iteration: %d, stage: 1.'	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		)
    else:
      logger.info('***** Running self-training: iteration: %d, stage: 1 *****'	,		_SCREAMING_SNAKE_CASE	)
      finetune(**_SCREAMING_SNAKE_CASE	)
      accelerator.wait_for_everyone()
      assert os.path.exists(_SCREAMING_SNAKE_CASE	)
      logger.info('Self-training job completed: iteration: %d, stage: 1.'	,		_SCREAMING_SNAKE_CASE	)

    if iteration > 0 and args.finetune_on_labeled_data:
      # Stage 2 (optional): fine-tuning on the original labeled data
      lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		'best-checkpoint'	)
      lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		'stage-2'	)
      # Update arguments_dict
      lowercase__			    =						model_path
      lowercase__			    =						data_files['train']
      lowercase__			    =						current_output_dir

      lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		'best-checkpoint'	,		_SCREAMING_SNAKE_CASE	)
      if os.path.exists(_SCREAMING_SNAKE_CASE	):
        logger.info(
            'Found existing model checkpoint at %s. Skipping self-training: iteration: %d, stage: 2.'	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		)
      else:
        logger.info('***** Running self-training: iteration: %d, stage: 2 *****'	,		_SCREAMING_SNAKE_CASE	)
        finetune(**_SCREAMING_SNAKE_CASE	)
        accelerator.wait_for_everyone()
        assert os.path.exists(_SCREAMING_SNAKE_CASE	)
        logger.info('Self-training job completed: iteration: %d, stage: 2.'	,		_SCREAMING_SNAKE_CASE	)

    lowercase__			    =						iteration
    lowercase__			    =						data_dir_format(iteration + 1	)

    lowercase__			    =						AutoConfig.from_pretrained(os.path.join(_SCREAMING_SNAKE_CASE	,		'best-checkpoint'	)	)
    lowercase__			    =						config.idalabel
    lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		'eval_results_best-checkpoint.json'	)
    lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		'test_results_best-checkpoint.json'	)
    assert os.path.exists(_SCREAMING_SNAKE_CASE	)

    with open(_SCREAMING_SNAKE_CASE	,		'r'	) as f:
      lowercase__			    =						float(json.load(_SCREAMING_SNAKE_CASE	)[args.eval_metric]	)
    lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		'infer_output_best-checkpoint.csv'	)
    assert os.path.exists(_SCREAMING_SNAKE_CASE	)
    # Loading the dataset from local csv or json files.
    lowercase__			    =						load_dataset(args.data_file_extension	,		data_files={'data': data_files['infer']}	)['data']
    lowercase__			    =						load_dataset('csv'	,		data_files={'data': infer_output_file}	)['data']

    if accelerator.is_main_process:
      os.makedirs(_SCREAMING_SNAKE_CASE	,		exist_ok=_SCREAMING_SNAKE_CASE	)
      shutil.copy(_SCREAMING_SNAKE_CASE	,		os.path.join(_SCREAMING_SNAKE_CASE	,		F"""eval_results_iter-{iteration}.json"""	)	)
      if os.path.exists(_SCREAMING_SNAKE_CASE	):
        shutil.copy(_SCREAMING_SNAKE_CASE	,		os.path.join(_SCREAMING_SNAKE_CASE	,		F"""test_results_iter-{iteration}.json"""	)	)
      create_pseudo_labeled_data(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	)
    accelerator.wait_for_everyone()

    lowercase__			    =						os.path.join(_SCREAMING_SNAKE_CASE	,		F"""train_pseudo.{args.data_file_extension}"""	)

    if args.evaluation_strategy != IntervalStrategy.NO.value:
      lowercase__			    =						eval_result

      if best_iteration is None:
        lowercase__			    =						new_iteration
        lowercase__			    =						new_eval_result
      else:
        if new_eval_result - best_eval_result > args.early_stopping_threshold:
          lowercase__			    =						new_iteration
          lowercase__			    =						new_eval_result
          lowercase__			    =						0
        else:
          if new_eval_result == best_eval_result:
            lowercase__			    =						new_iteration
            lowercase__			    =						new_eval_result
          early_stopping_patience_counter += 1

        if early_stopping_patience_counter >= args.early_stopping_patience:
          lowercase__			    =						True

    progress_bar.update(1	)

    if should_training_stop:
      break

  if best_iteration is not None:
    # Save the best iteration
    logger.info('Best iteration: %d'	,		_SCREAMING_SNAKE_CASE	)
    logger.info('Best evaluation result: %s = %f'	,		args.eval_metric	,		_SCREAMING_SNAKE_CASE	)
    accelerator.wait_for_everyone()
    if accelerator.is_main_process:
      shutil.copy(
          os.path.join(_SCREAMING_SNAKE_CASE	,		F"""eval_results_iter-{iteration}.json"""	)	,		os.path.join(_SCREAMING_SNAKE_CASE	,		'eval_results_best-iteration.json'	)	,		)
  else:
    # Assume that the last iteration is the best
    logger.info('Best iteration: %d'	,		args.max_selftrain_iterations - 1	)
    logger.info('Best evaluation result: %s = %f'	,		args.eval_metric	,		_SCREAMING_SNAKE_CASE	)
    accelerator.wait_for_everyone()
    if accelerator.is_main_process:
      shutil.copy(
          os.path.join(_SCREAMING_SNAKE_CASE	,		F"""eval_results_iter-{args.max_selftrain_iterations - 1}.json"""	)	,		os.path.join(_SCREAMING_SNAKE_CASE	,		'eval_results_best-iteration.json'	)	,		)









from string import ascii_uppercase

lowercase_					     =	{str(ord(c) - 55): c for c in ascii_uppercase}




def     __UpperCamelCase						(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	)       ->     str:
  if isinstance(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	):
    raise TypeError('int() can\'t convert non-string with explicit base'	)
  if num < 0:
    raise ValueError('parameter must be positive int'	)
  if isinstance(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	):
    raise TypeError('\'str\' object cannot be interpreted as an integer'	)
  if isinstance(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	):
    raise TypeError('\'float\' object cannot be interpreted as an integer'	)
  if base in (0, 1):
    raise ValueError('base must be >= 2'	)
  if base > 36:
    raise ValueError('base must be <= 36'	)
  lowercase__			    =						''
  lowercase__			    =						0
  lowercase__			    =						0
  while div != 1:
    lowercase__    ,					lowercase__			    =						divmod(_SCREAMING_SNAKE_CASE	,		_SCREAMING_SNAKE_CASE	)
    if base >= 11 and 9 < mod < 36:
      lowercase__			    =						ALPHABET_VALUES[str(_SCREAMING_SNAKE_CASE	)]
    else:
      lowercase__			    =						str(_SCREAMING_SNAKE_CASE	)
    new_value += actual_value
    lowercase__			    =						num // base
    lowercase__			    =						div
    if div == 0:
      return str(new_value[::-1]	)
    elif div == 1:
      new_value += str(_SCREAMING_SNAKE_CASE	)
      return str(new_value[::-1]	)

  return new_value[::-1]


if __name__ == "__main__":
      import doctest

      doctest.testmod()

      for base in range(2, 37):
            for num in range(1_000):
                  assert int(decimal_to_any(num, base), base) == num, (
                      num,
                      base,
                      decimal_to_any(num, base),
                      int(decimal_to_any(num, base), base),
                  )


