




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


A      = logging.getLogger(__name__)

A      = "pytorch_model.bin"


@dataclasses.dataclass
class     lowercase__  :
  A__=  dataclasses.field(
      metadata={'help': 'Path to pretrained model or model identifier from huggingface.co/models.'}		)
  A__=  dataclasses.field(
      default=__SCREAMING_SNAKE_CASE					,							metadata={'help': 'Where do you want to store the pretrained models downloaded from huggingface.co.'}					,							)




@dataclasses.dataclass
class     lowercase__  :
  A__=  dataclasses.field(metadata={'help': 'A csv or a json file containing the training data.'}		)
  A__=  dataclasses.field(metadata={'help': 'A csv or a json file containing the data to predict on.'}		)
  A__=  dataclasses.field(
      default=__SCREAMING_SNAKE_CASE					,							metadata={'help': 'A csv or a json file containing the validation data.'}		)
  A__=  dataclasses.field(
      default=__SCREAMING_SNAKE_CASE					,							metadata={'help': 'The name of the task to train on.'}					,							)
  A__=  dataclasses.field(
      default=__SCREAMING_SNAKE_CASE					,							metadata={'help': 'The list of labels for the task.'}		)




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





def __UpperCAmelCase       (		__A ,						__A ,						__A ,						__A ,						__A ,						__A			)						->					List[str]:



       '''simple docstring'''





       UpperCAmelCase__										=     datasets.concatenate_datasets([infer_input, infer_output] ,						axis=1			)

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

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

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

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





def __UpperCAmelCase       (		__A ,						__A ,						__A ,						__A ,						**__A			)						->					List[str]:



       '''simple docstring'''





       UpperCAmelCase__										=     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()

       UpperCAmelCase__										=     STModelArguments(model_name_or_path=__A			)
       UpperCAmelCase__										=     STDataArguments(train_file=__A ,						infer_file=__A			)
       UpperCAmelCase__										=     STTrainingArguments(output_dir=__A			)
       UpperCAmelCase__										=     argparse.Namespace()

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

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

    # Sanity checks
       UpperCAmelCase__										=     {}
       UpperCAmelCase__										=     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
       UpperCAmelCase__										=     args.train_file
       UpperCAmelCase__										=     args.infer_file

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

       for key in data_files:
              UpperCAmelCase__										=     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:
                     UpperCAmelCase__										=     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..."			)
       UpperCAmelCase__										=     F"""{args.output_dir}/self-train_iter-{{}}""".format
       UpperCAmelCase__										=     data_dir_format(0			)

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

       UpperCAmelCase__										=     None
       UpperCAmelCase__										=     None
       UpperCAmelCase__										=     0
       UpperCAmelCase__										=     False
       # Show the progress bar
       UpperCAmelCase__										=     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			)			):
              UpperCAmelCase__										=     data_dir_format(__A			)
              assert os.path.exists(__A			)

              # Stage 1: initial fine-tuning for iteration = 0 or pseudo-training for
              # iteration > 0
              UpperCAmelCase__										=     os.path.join(__A ,						"stage-1"			)
              UpperCAmelCase__										=     {
                  "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(__A ,						__A			):
                            arguments_dict.update({key: value}			)

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

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

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

              UpperCAmelCase__										=     iteration
              UpperCAmelCase__										=     data_dir_format(iteration + 1			)

              UpperCAmelCase__										=     AutoConfig.from_pretrained(os.path.join(__A ,						"best-checkpoint"			)			)
              UpperCAmelCase__										=     config.idalabel
              UpperCAmelCase__										=     os.path.join(__A ,						"eval_results_best-checkpoint.json"			)
              UpperCAmelCase__										=     os.path.join(__A ,						"test_results_best-checkpoint.json"			)
              assert os.path.exists(__A			)

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

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

              UpperCAmelCase__										=     os.path.join(__A ,						F"""train_pseudo.{args.data_file_extension}"""			)

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

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

                            if early_stopping_patience_counter >= args.early_stopping_patience:
                                   UpperCAmelCase__										=     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" ,						__A			)
              logger.info("Best evaluation result: %s = %f" ,						args.eval_metric ,						__A			)
              accelerator.wait_for_everyone()
              if accelerator.is_main_process:
                     shutil.copy(
                         os.path.join(__A ,						F"""eval_results_iter-{iteration}.json"""			) ,						os.path.join(__A ,						"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 ,						__A			)
              accelerator.wait_for_everyone()
              if accelerator.is_main_process:
                     shutil.copy(
                         os.path.join(__A ,						F"""eval_results_iter-{args.max_selftrain_iterations - 1}.json"""			) ,						os.path.join(__A ,						"eval_results_best-iteration.json"			) ,						)









import uuid
from typing import Any, Dict, List, Optional, Union

from ..utils import add_end_docstrings, is_tf_available, is_torch_available, logging
from .base import PIPELINE_INIT_ARGS, Pipeline


if is_tf_available():
    import tensorflow as tf

if is_torch_available():
    import torch


A      = logging.get_logger(__name__)


class     lowercase__  :






  def __init__(					self      :					List[str]		,						_lowercase      :					str = None		,						_lowercase      :					uuid.UUID = None		,						_lowercase      :					Dict=None		,						_lowercase      :					Any=None ):




         """simple docstring"""







         if not conversation_id:
                UpperCAmelCase__										=     uuid.uuida()
         if past_user_inputs is None:
                UpperCAmelCase__										=     []
         if generated_responses is None:
                UpperCAmelCase__										=     []

         UpperCAmelCase__										=     conversation_id
         UpperCAmelCase__										=     past_user_inputs
         UpperCAmelCase__										=     generated_responses
         UpperCAmelCase__										=     text






  def __eq__(					self      :					Any		,						_lowercase      :					str ):




         """simple docstring"""







         if not isinstance(_lowercase		,						_lowercase ):
                return False
         if self.uuid == other.uuid:
                return True
         return (
             self.new_user_input == other.new_user_input
             and self.past_user_inputs == other.past_user_inputs
             and self.generated_responses == other.generated_responses
         )






  def 			_UpperCAmelCase  (					self      :					Any		,						_lowercase      :					str		,						_lowercase      :					bool = False ):




         """simple docstring"""







         if self.new_user_input:
                if overwrite:
                       logger.warning(
                           F"""User input added while unprocessed input was existing: \"{self.new_user_input}\" was overwritten """
                           F"""with: \"{text}\".""" )
                       UpperCAmelCase__										=     text
                else:
                       logger.warning(
                           F"""User input added while unprocessed input was existing: \"{self.new_user_input}\" new input """
                           F"""ignored: \"{text}\". Set `overwrite` to True to overwrite unprocessed user input""" )
         else:
                UpperCAmelCase__										=     text






  def 			_UpperCAmelCase  (					self      :					int ):




         """simple docstring"""







         if self.new_user_input:
                self.past_user_inputs.append(self.new_user_input )
         UpperCAmelCase__										=     None






  def 			_UpperCAmelCase  (					self      :					List[str]		,						_lowercase      :					str ):




         """simple docstring"""







         self.generated_responses.append(_lowercase )






  def 			_UpperCAmelCase  (					self      :					List[str] ):




         """simple docstring"""







         for user_input, generated_response in zip(self.past_user_inputs		,						self.generated_responses ):
                yield True, user_input
                yield False, generated_response
         if self.new_user_input:
                yield True, self.new_user_input






  def __repr__(					self      :					List[Any] ):




         """simple docstring"""







         UpperCAmelCase__										=     F"""Conversation id: {self.uuid} \n"""
         for is_user, text in self.iter_texts():
                UpperCAmelCase__										=     "user" if is_user else "bot"
                output += F"""{name} >> {text} \n"""
         return output




@add_end_docstrings(
    __SCREAMING_SNAKE_CASE					,							R'\n        min_length_for_response (`int`, *optional*, defaults to 32):\n            The minimum length (in number of tokens) for a response.\n        minimum_tokens (`int`, *optional*, defaults to 10):\n            The minimum length of tokens to leave for a response.\n    '					,							)
class     lowercase__  (					__SCREAMING_SNAKE_CASE		):






  def __init__(					self      :					str		,						*_lowercase      :					Union[str, Any]		,						**_lowercase      :					Any ):




         """simple docstring"""







         super().__init__(*_lowercase		,						**_lowercase )
         if self.tokenizer.pad_token_id is None:
                UpperCAmelCase__										=     self.tokenizer.eos_token






  def 			_UpperCAmelCase  (					self      :					List[str]		,						_lowercase      :					int=None		,						_lowercase      :					Any=None		,						_lowercase      :					List[str]=None		,						**_lowercase      :					Dict ):




         """simple docstring"""







         UpperCAmelCase__										=     {}
         UpperCAmelCase__										=     {}
         UpperCAmelCase__										=     {}

         if min_length_for_response is not None:
                UpperCAmelCase__										=     min_length_for_response
         if minimum_tokens is not None:
                UpperCAmelCase__										=     minimum_tokens

         if "max_length" in generate_kwargs:
                UpperCAmelCase__										=     generate_kwargs["max_length"]
                # self.max_length = generate_kwargs.get("max_length", self.model.config.max_length)
         if clean_up_tokenization_spaces is not None:
                UpperCAmelCase__										=     clean_up_tokenization_spaces

         if generate_kwargs:
                forward_params.update(_lowercase )
         return preprocess_params, forward_params, postprocess_params






  def __call__(					self      :					Dict		,						_lowercase      :					Union[Conversation, List[Conversation]]		,						_lowercase      :					List[str]=0		,						**_lowercase      :					Optional[int] ):




         """simple docstring"""







         UpperCAmelCase__										=     super().__call__(_lowercase		,						num_workers=_lowercase		,						**_lowercase )
         if isinstance(_lowercase		,						_lowercase ) and len(_lowercase ) == 1:
                return outputs[0]
         return outputs






  def 			_UpperCAmelCase  (					self      :					Optional[Any]		,						_lowercase      :					Conversation		,						_lowercase      :					List[Any]=32 ):




         """simple docstring"""







         if not isinstance(_lowercase		,						_lowercase ):
                raise ValueError("ConversationalPipeline, expects Conversation as inputs" )
         if conversation.new_user_input is None:
                raise ValueError(
                    F"""Conversation with UUID {type(conversation.uuid )} does not contain new user input to process. """
                    "Add user inputs with the conversation's `add_user_input` method" )
         if hasattr(self.tokenizer		,						"_build_conversation_input_ids" ):
                UpperCAmelCase__										=     self.tokenizer._build_conversation_input_ids(_lowercase )
         else:
                # If the tokenizer cannot handle conversations, we default to only the old version
                UpperCAmelCase__										=     self._legacy_parse_and_tokenize(_lowercase )

         if self.framework == "pt":
                UpperCAmelCase__										=     torch.LongTensor([input_ids] )
         elif self.framework == "tf":
                UpperCAmelCase__										=     tf.constant([input_ids] )
         return {"input_ids": input_ids, "conversation": conversation}






  def 			_UpperCAmelCase  (					self      :					Optional[Any]		,						_lowercase      :					List[Any]		,						_lowercase      :					List[Any]=10		,						**_lowercase      :					Optional[Any] ):




         """simple docstring"""







         UpperCAmelCase__										=     generate_kwargs.get("max_length"		,						self.model.config.max_length )

         UpperCAmelCase__										=     model_inputs["input_ids"].shape[1]
         if max_length - minimum_tokens < n:
                logger.warning(F"""Conversation input is to long ({n}), trimming it to ({max_length} - {minimum_tokens})""" )
                UpperCAmelCase__										=     max_length - minimum_tokens
                UpperCAmelCase__										=     model_inputs["input_ids"][:, -trim:]
                if "attention_mask" in model_inputs:
                       UpperCAmelCase__										=     model_inputs["attention_mask"][:, -trim:]
         UpperCAmelCase__										=     model_inputs.pop("conversation" )
         UpperCAmelCase__										=     max_length
         UpperCAmelCase__										=     self.model.generate(**_lowercase		,						**_lowercase )
         if self.model.config.is_encoder_decoder:
                UpperCAmelCase__										=     1
         else:
                UpperCAmelCase__										=     n
         return {"output_ids": output_ids[:, start_position:], "conversation": conversation}






  def 			_UpperCAmelCase  (					self      :					Tuple		,						_lowercase      :					Union[str, Any]		,						_lowercase      :					Optional[Any]=True ):




         """simple docstring"""







         UpperCAmelCase__										=     model_outputs["output_ids"]
         UpperCAmelCase__										=     self.tokenizer.decode(
             output_ids[0]		,						skip_special_tokens=_lowercase		,						clean_up_tokenization_spaces=_lowercase		,						)
         UpperCAmelCase__										=     model_outputs["conversation"]
         conversation.mark_processed()
         conversation.append_response(_lowercase )
         return conversation






  def 			_UpperCAmelCase  (					self      :					List[str]		,						_lowercase      :					Conversation ):




         """simple docstring"""







         UpperCAmelCase__										=     self.tokenizer.eos_token_id
         UpperCAmelCase__										=     []
         for is_user, text in conversation.iter_texts():
                if eos_token_id is not None:
                       input_ids.extend(self.tokenizer.encode(_lowercase		,						add_special_tokens=_lowercase ) + [eos_token_id] )
                else:
                       input_ids.extend(self.tokenizer.encode(_lowercase		,						add_special_tokens=_lowercase ) )

         if len(_lowercase ) > self.tokenizer.model_max_length:
                UpperCAmelCase__										=     input_ids[-self.tokenizer.model_max_length :]
         return input_ids





