| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790 |
- from torch import Tensor
- import torch
- import torch.nn as nn
- from torch.nn import functional as F
- from trl.trainer.kto_trainer import (Any, AutoModelForCausalLM, BaseImageProcessor, Callable, DPODataCollatorWithPadding, DataCollator, DataLoader, Dataset, EvalLoopOutput, F, FeatureExtractionMixin, KTOConfig, KTOTrainer, Literal, Optional, PartialState, PeftModel, PreTrainedModel, PreTrainedModelWrapper, PreTrainedTokenizerBase, ProcessorMixin, SequentialSampler, Trainer, TrainerCallback, TrainingArguments, Union, _get_kl_dataset, _process_tokens, _tokenize, amp, concatenate_datasets, contextmanager, create_reference_model, deepcopy, defaultdict, disable_dropout_in_model, generate_model_card, get_comet_experiment_url, has_length, inspect, is_comet_available, is_peft_available, is_wandb_available, itemgetter, log_table_to_comet_experiment, maybe_apply_chat_template, maybe_extract_prompt, maybe_unpair_preference_dataset, nn, np, nullcontext, os, pad_to_length, pd, peft_module_casting_to_bf16, prepare_model_for_kbit_training, random, textwrap, torch, tqdm, transformers, version, warnings)
- import os
- from typing import *
- from dataclasses import dataclass, field
- from packaging.version import Version
- import torch
- import numpy as np
- from contextlib import nullcontext
- from torch.nn import functional as F
- torch_compile_options = {
- "epilogue_fusion" : True,
- "max_autotune" : False,
- "shape_padding" : True,
- "trace.enabled" : False,
- "triton.cudagraphs" : False,
- }
- @torch.compile(dynamic = True, fullgraph = True, options = torch_compile_options,)
- def selective_log_softmax(logits, index):
- logits = logits.to(torch.float32)
- selected_logits = torch.gather(logits, dim = -1, index = index.unsqueeze(-1)).squeeze(-1)
- # loop to reduce peak mem consumption
- # logsumexp_values = torch.stack([torch.logsumexp(lg, dim=-1) for lg in logits])
- logsumexp_values = torch.logsumexp(logits, dim = -1)
- per_token_logps = selected_logits - logsumexp_values # log_softmax(x_i) = x_i - logsumexp(x)
- return per_token_logps
- @dataclass
- class UnslothKTOConfig(KTOConfig):
- """
-
- Configuration class for the [`KTOTrainer`].
- Using [`~transformers.HfArgumentParser`] we can turn this class into
- [argparse](https://docs.python.org/3/library/argparse#module-argparse) arguments that can be specified on the
- command line.
- Parameters:
- learning_rate (`float`, *optional*, defaults to `5e-7`):
- Initial learning rate for [`AdamW`] optimizer. The default value replaces that of
- [`~transformers.TrainingArguments`].
- max_length (`int` or `None`, *optional*, defaults to `1024`):
- Maximum length of the sequences (prompt + completion) in the batch. This argument is required if you want
- to use the default data collator.
- max_prompt_length (`int` or `None`, *optional*, defaults to `512`):
- Maximum length of the prompt. This argument is required if you want to use the default data collator.
- max_completion_length (`int` or `None`, *optional*, defaults to `None`):
- Maximum length of the completion. This argument is required if you want to use the default data collator
- and your model is an encoder-decoder.
- beta (`float`, *optional*, defaults to `0.1`):
- Parameter controlling the deviation from the reference model. Higher β means less deviation from the
- reference model.
- loss_type (`str`, *optional*, defaults to `"kto"`):
- Type of loss to use. Possible values are:
- - `"kto"`: KTO loss from the [KTO](https://huggingface.co/papers/2402.01306) paper.
- - `"apo_zero_unpaired"`: Unpaired variant of APO-zero loss from the [APO](https://huggingface.co/papers/2408.06266) paper.
- desirable_weight (`float`, *optional*, defaults to `1.0`):
- Desirable losses are weighed by this factor to counter unequal number of desirable and undesirable paris.
- undesirable_weight (`float`, *optional*, defaults to `1.0`):
- Undesirable losses are weighed by this factor to counter unequal number of desirable and undesirable pairs.
- label_pad_token_id (`int`, *optional*, defaults to `-100`):
- Label pad token id. This argument is required if you want to use the default data collator.
- padding_value (`int` or `None`, *optional*, defaults to `None`):
- Padding value to use. If `None`, the padding value of the tokenizer is used.
- truncation_mode (`str`, *optional*, defaults to `"keep_end"`):
- Truncation mode to use when the prompt is too long. Possible values are `"keep_end"` or `"keep_start"`.
- This argument is required if you want to use the default data collator.
- generate_during_eval (`bool`, *optional*, defaults to `False`):
- If `True`, generates and logs completions from both the model and the reference model to W&B or Comet during
- evaluation.
- is_encoder_decoder (`bool` or `None`, *optional*, defaults to `None`):
- When using the `model_init` argument (callable) to instantiate the model instead of the `model` argument,
- you need to specify if the model returned by the callable is an encoder-decoder model.
- precompute_ref_log_probs (`bool`, *optional*, defaults to `False`):
- Whether to precompute reference model log probabilities for training and evaluation datasets. This is
- useful when training without the reference model to reduce the total GPU memory needed.
- model_init_kwargs (`dict[str, Any]` or `None`, *optional*, defaults to `None`):
- Keyword arguments to pass to `AutoModelForCausalLM.from_pretrained` when instantiating the model from a
- string.
- ref_model_init_kwargs (`dict[str, Any]` or `None`, *optional*, defaults to `None`):
- Keyword arguments to pass to `AutoModelForCausalLM.from_pretrained` when instantiating the reference model
- from a string.
- dataset_num_proc: (`int` or `None`, *optional*, defaults to `None`):
- Number of processes to use for processing the dataset.
- disable_dropout (`bool`, *optional*, defaults to `True`):
- Whether to disable dropout in the model and reference model.
-
- """
- vllm_sampling_params: Optional[Any] = field(
- default = None,
- metadata = {'help': 'vLLM SamplingParams'},
- )
- unsloth_num_chunks : Optional[int] = field(
- default = -1,
- metadata = {'help': 'Chunk size to reduce memory usage. -1 is most efficient.'},
- )
- def __init__(
- self,
- output_dir = None,
- overwrite_output_dir = None,
- do_train = False,
- do_eval = False,
- do_predict = False,
- eval_strategy = 'no',
- prediction_loss_only = False,
- per_device_train_batch_size = 4,
- per_device_eval_batch_size = 4,
- per_gpu_train_batch_size = None,
- per_gpu_eval_batch_size = None,
- gradient_accumulation_steps = 2,
- eval_accumulation_steps = 2,
- eval_delay = 0,
- torch_empty_cache_steps = 250,
- learning_rate = 5e-05,
- weight_decay = 0.01,
- adam_beta1 = 0.9,
- adam_beta2 = 0.999,
- adam_epsilon = 1e-08,
- max_grad_norm = 1.0,
- num_train_epochs = 3.0,
- max_steps = -1,
- lr_scheduler_type = 'linear',
- warmup_ratio = 0.1,
- warmup_steps = 0,
- log_level = 'passive',
- log_level_replica = 'warning',
- log_on_each_node = True,
- logging_dir = None,
- logging_strategy = 'steps',
- logging_first_step = False,
- logging_steps = 1,
- logging_nan_inf_filter = False,
- save_strategy = 'steps',
- save_steps = 500,
- save_total_limit = None,
- save_safetensors = True,
- save_on_each_node = False,
- save_only_model = False,
- restore_callback_states_from_checkpoint = False,
- no_cuda = False,
- use_cpu = False,
- use_mps_device = False,
- seed = 3407,
- data_seed = 3407,
- jit_mode_eval = False,
- use_ipex = False,
- bf16 = False,
- fp16 = False,
- fp16_opt_level = 'O1',
- half_precision_backend = 'auto',
- bf16_full_eval = False,
- fp16_full_eval = False,
- tf32 = None,
- local_rank = -1,
- ddp_backend = None,
- tpu_num_cores = None,
- tpu_metrics_debug = False,
- debug = '',
- dataloader_drop_last = False,
- eval_steps = None,
- dataloader_num_workers = 0,
- dataloader_prefetch_factor = None,
- past_index = -1,
- run_name = None,
- disable_tqdm = None,
- remove_unused_columns = True,
- label_names = None,
- load_best_model_at_end = False,
- metric_for_best_model = None,
- greater_is_better = None,
- ignore_data_skip = False,
- fsdp = '',
- fsdp_min_num_params = 0,
- fsdp_config = None,
- fsdp_transformer_layer_cls_to_wrap = None,
- accelerator_config = None,
- deepspeed = None,
- label_smoothing_factor = 0.0,
- optim = 'adamw_8bit',
- optim_args = None,
- adafactor = False,
- group_by_length = False,
- length_column_name = 'length',
- report_to = None,
- ddp_find_unused_parameters = None,
- ddp_bucket_cap_mb = None,
- ddp_broadcast_buffers = None,
- dataloader_pin_memory = True,
- dataloader_persistent_workers = False,
- skip_memory_metrics = True,
- use_legacy_prediction_loop = False,
- push_to_hub = False,
- resume_from_checkpoint = None,
- hub_model_id = None,
- hub_strategy = 'every_save',
- hub_token = None,
- hub_private_repo = None,
- hub_always_push = False,
- gradient_checkpointing = False,
- gradient_checkpointing_kwargs = None,
- include_inputs_for_metrics = False,
- eval_do_concat_batches = True,
- fp16_backend = 'auto',
- evaluation_strategy = None,
- push_to_hub_model_id = None,
- push_to_hub_organization = None,
- push_to_hub_token = None,
- mp_parameters = '',
- auto_find_batch_size = False,
- full_determinism = False,
- torchdynamo = None,
- ray_scope = 'last',
- ddp_timeout = 1800,
- torch_compile = False,
- torch_compile_backend = None,
- torch_compile_mode = None,
- dispatch_batches = None,
- split_batches = None,
- include_tokens_per_second = False,
- include_num_input_tokens_seen = False,
- neftune_noise_alpha = None,
- optim_target_modules = None,
- batch_eval_metrics = False,
- eval_on_start = False,
- use_liger_kernel = False,
- eval_use_gather_object = False,
- average_tokens_across_devices = False,
- max_length = 1024,
- max_prompt_length = 512,
- max_completion_length = None,
- beta = 0.1,
- loss_type = 'kto',
- desirable_weight = 1.0,
- undesirable_weight = 1.0,
- label_pad_token_id = -100,
- padding_value = None,
- truncation_mode = 'keep_end',
- generate_during_eval = False,
- is_encoder_decoder = None,
- disable_dropout = True,
- precompute_ref_log_probs = False,
- model_init_kwargs = None,
- ref_model_init_kwargs = None,
- dataset_num_proc = None,
- vllm_sampling_params = None,
- unsloth_num_chunks = -1,
- **kwargs,
- ):
- if learning_rate < 1e-7: raise FloatingPointError(f'Unsloth: Your learning rate of `{learning_rate}` is too small and less than 1e-7! Consider increasing it, otherwise gradient updates will be close to 0!')
- if learning_rate > 1: raise OverflowError(f'Unsloth: Your learning rate of `{learning_rate}` is way too larger > 1! Consider decreasing it to 1e-1, otherwise gradient updates will explode!')
- if output_dir is None and save_strategy == 'steps' and save_steps == 500:
- output_dir = 'unsloth_training_checkpoints'
- save_strategy = 'no'
- if dataset_num_proc is None:
- from multiprocessing import cpu_count
- dataset_num_proc = cpu_count()
-
- super().__init__(
- output_dir = output_dir,
- overwrite_output_dir = overwrite_output_dir,
- do_train = do_train,
- do_eval = do_eval,
- do_predict = do_predict,
- eval_strategy = eval_strategy,
- prediction_loss_only = prediction_loss_only,
- per_device_train_batch_size = per_device_train_batch_size,
- per_device_eval_batch_size = per_device_eval_batch_size,
- per_gpu_train_batch_size = per_gpu_train_batch_size,
- per_gpu_eval_batch_size = per_gpu_eval_batch_size,
- gradient_accumulation_steps = gradient_accumulation_steps,
- eval_accumulation_steps = eval_accumulation_steps,
- eval_delay = eval_delay,
- torch_empty_cache_steps = torch_empty_cache_steps,
- learning_rate = learning_rate,
- weight_decay = weight_decay,
- adam_beta1 = adam_beta1,
- adam_beta2 = adam_beta2,
- adam_epsilon = adam_epsilon,
- max_grad_norm = max_grad_norm,
- num_train_epochs = num_train_epochs,
- max_steps = max_steps,
- lr_scheduler_type = lr_scheduler_type,
- warmup_ratio = warmup_ratio,
- warmup_steps = warmup_steps,
- log_level = log_level,
- log_level_replica = log_level_replica,
- log_on_each_node = log_on_each_node,
- logging_dir = logging_dir,
- logging_strategy = logging_strategy,
- logging_first_step = logging_first_step,
- logging_steps = logging_steps,
- logging_nan_inf_filter = logging_nan_inf_filter,
- save_strategy = save_strategy,
- save_steps = save_steps,
- save_total_limit = save_total_limit,
- save_safetensors = save_safetensors,
- save_on_each_node = save_on_each_node,
- save_only_model = save_only_model,
- restore_callback_states_from_checkpoint = restore_callback_states_from_checkpoint,
- no_cuda = no_cuda,
- use_cpu = use_cpu,
- use_mps_device = use_mps_device,
- seed = seed,
- data_seed = data_seed,
- jit_mode_eval = jit_mode_eval,
- use_ipex = use_ipex,
- bf16 = bf16,
- fp16 = fp16,
- fp16_opt_level = fp16_opt_level,
- half_precision_backend = half_precision_backend,
- bf16_full_eval = bf16_full_eval,
- fp16_full_eval = fp16_full_eval,
- tf32 = tf32,
- local_rank = local_rank,
- ddp_backend = ddp_backend,
- tpu_num_cores = tpu_num_cores,
- tpu_metrics_debug = tpu_metrics_debug,
- debug = debug,
- dataloader_drop_last = dataloader_drop_last,
- eval_steps = eval_steps,
- dataloader_num_workers = dataloader_num_workers,
- dataloader_prefetch_factor = dataloader_prefetch_factor,
- past_index = past_index,
- run_name = run_name,
- disable_tqdm = disable_tqdm,
- remove_unused_columns = remove_unused_columns,
- label_names = label_names,
- load_best_model_at_end = load_best_model_at_end,
- metric_for_best_model = metric_for_best_model,
- greater_is_better = greater_is_better,
- ignore_data_skip = ignore_data_skip,
- fsdp = fsdp,
- fsdp_min_num_params = fsdp_min_num_params,
- fsdp_config = fsdp_config,
- fsdp_transformer_layer_cls_to_wrap = fsdp_transformer_layer_cls_to_wrap,
- accelerator_config = accelerator_config,
- deepspeed = deepspeed,
- label_smoothing_factor = label_smoothing_factor,
- optim = optim,
- optim_args = optim_args,
- adafactor = adafactor,
- group_by_length = group_by_length,
- length_column_name = length_column_name,
- report_to = report_to,
- ddp_find_unused_parameters = ddp_find_unused_parameters,
- ddp_bucket_cap_mb = ddp_bucket_cap_mb,
- ddp_broadcast_buffers = ddp_broadcast_buffers,
- dataloader_pin_memory = dataloader_pin_memory,
- dataloader_persistent_workers = dataloader_persistent_workers,
- skip_memory_metrics = skip_memory_metrics,
- use_legacy_prediction_loop = use_legacy_prediction_loop,
- push_to_hub = push_to_hub,
- resume_from_checkpoint = resume_from_checkpoint,
- hub_model_id = hub_model_id,
- hub_strategy = hub_strategy,
- hub_token = hub_token,
- hub_private_repo = hub_private_repo,
- hub_always_push = hub_always_push,
- gradient_checkpointing = gradient_checkpointing,
- gradient_checkpointing_kwargs = gradient_checkpointing_kwargs,
- include_inputs_for_metrics = include_inputs_for_metrics,
- eval_do_concat_batches = eval_do_concat_batches,
- fp16_backend = fp16_backend,
- evaluation_strategy = evaluation_strategy,
- push_to_hub_model_id = push_to_hub_model_id,
- push_to_hub_organization = push_to_hub_organization,
- push_to_hub_token = push_to_hub_token,
- mp_parameters = mp_parameters,
- auto_find_batch_size = auto_find_batch_size,
- full_determinism = full_determinism,
- torchdynamo = torchdynamo,
- ray_scope = ray_scope,
- ddp_timeout = ddp_timeout,
- torch_compile = torch_compile,
- torch_compile_backend = torch_compile_backend,
- torch_compile_mode = torch_compile_mode,
- dispatch_batches = dispatch_batches,
- split_batches = split_batches,
- include_tokens_per_second = include_tokens_per_second,
- include_num_input_tokens_seen = include_num_input_tokens_seen,
- neftune_noise_alpha = neftune_noise_alpha,
- optim_target_modules = optim_target_modules,
- batch_eval_metrics = batch_eval_metrics,
- eval_on_start = eval_on_start,
- use_liger_kernel = use_liger_kernel,
- eval_use_gather_object = eval_use_gather_object,
- average_tokens_across_devices = average_tokens_across_devices,
- max_length = max_length,
- max_prompt_length = max_prompt_length,
- max_completion_length = max_completion_length,
- beta = beta,
- loss_type = loss_type,
- desirable_weight = desirable_weight,
- undesirable_weight = undesirable_weight,
- label_pad_token_id = label_pad_token_id,
- padding_value = padding_value,
- truncation_mode = truncation_mode,
- generate_during_eval = generate_during_eval,
- is_encoder_decoder = is_encoder_decoder,
- disable_dropout = disable_dropout,
- precompute_ref_log_probs = precompute_ref_log_probs,
- model_init_kwargs = model_init_kwargs,
- ref_model_init_kwargs = ref_model_init_kwargs,
- dataset_num_proc = dataset_num_proc,**kwargs)
- self.vllm_sampling_params = vllm_sampling_params
- self.unsloth_num_chunks = unsloth_num_chunks
- pass
- class _UnslothKTOTrainer(Trainer):
- r""""""
- _tag_names = ["trl", "kto"]
- def __init__(
- self,
- model: Union[PreTrainedModel, nn.Module, str] = None,
- ref_model: Optional[Union[PreTrainedModel, nn.Module, str]] = None,
- args: KTOConfig = None,
- train_dataset: Optional[Dataset] = None,
- eval_dataset: Optional[Union[Dataset, dict[str, Dataset]]] = None,
- processing_class: Optional[
- Union[PreTrainedTokenizerBase, BaseImageProcessor, FeatureExtractionMixin, ProcessorMixin]
- ] = None,
- data_collator: Optional[DataCollator] = None,
- model_init: Optional[Callable[[], PreTrainedModel]] = None,
- callbacks: Optional[list[TrainerCallback]] = None,
- optimizers: tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (None, None),
- preprocess_logits_for_metrics: Optional[Callable[[torch.Tensor, torch.Tensor], torch.Tensor]] = None,
- peft_config: Optional[dict] = None,
- compute_metrics: Optional[Callable[[EvalLoopOutput], dict]] = None,
- model_adapter_name: Optional[str] = None,
- ref_adapter_name: Optional[str] = None,
- ):
- if type(args) is TrainingArguments:
- raise ValueError("Please use `KTOConfig` instead TrainingArguments.")
- if not isinstance(model, str) and ref_model is model:
- raise ValueError(
- "`model` and `ref_model` cannot be the same object. If you want `ref_model` to be the "
- "same as `model`, you must mass a copy of it, or `None` if you use peft."
- )
- if args.model_init_kwargs is None:
- model_init_kwargs = {}
- elif not isinstance(model, str):
- raise ValueError("You passed model_kwargs to the KTOTrainer. But your model is already instantiated.")
- else:
- model_init_kwargs = args.model_init_kwargs
- torch_dtype = model_init_kwargs.get("torch_dtype")
- if torch_dtype is not None:
- # Convert to `torch.dtype` if an str is passed
- if isinstance(torch_dtype, str) and torch_dtype != "auto":
- torch_dtype = getattr(torch, torch_dtype)
- if torch_dtype != "auto" and not isinstance(torch_dtype, torch.dtype):
- raise ValueError(
- f"Invalid `torch_dtype` passed to the KTOConfig. Expected a string with either `torch.dtype` or 'auto', but got {torch_dtype}."
- )
- model_init_kwargs["torch_dtype"] = torch_dtype
- if args.ref_model_init_kwargs is None:
- ref_model_init_kwargs = {}
- elif not isinstance(ref_model, str):
- raise ValueError(
- "You passed ref_model_kwargs to the KTOTrainer. But your ref_model is already instantiated."
- )
- else:
- ref_model_init_kwargs = args.ref_model_init_kwargs
- torch_dtype = ref_model_init_kwargs.get("torch_dtype")
- if torch_dtype is not None:
- # Convert to `torch.dtype` if an str is passed
- if isinstance(torch_dtype, str) and torch_dtype != "auto":
- torch_dtype = getattr(torch, torch_dtype)
- if torch_dtype != "auto" and not isinstance(torch_dtype, torch.dtype):
- raise ValueError(
- f"Invalid `torch_dtype` passed to the KTOConfig. Expected a string with either `torch.dtype` or 'auto', but got {torch_dtype}."
- )
- ref_model_init_kwargs["torch_dtype"] = torch_dtype
- if isinstance(model, str):
- model = AutoModelForCausalLM.from_pretrained(model, **model_init_kwargs)
- if isinstance(ref_model, str):
- ref_model = AutoModelForCausalLM.from_pretrained(ref_model, **ref_model_init_kwargs)
- # Initialize this variable to False. This helps tracking the case when `peft_module_casting_to_bf16`
- # has been called in order to properly call autocast if needed.
- self._peft_has_been_casted_to_bf16 = False
- if not is_peft_available() and peft_config is not None:
- raise ValueError(
- "PEFT is not installed and you passed a `peft_config` in the trainer's kwargs, please install it with `pip install peft` to use the PEFT models"
- )
- elif is_peft_available() and peft_config is not None:
- # if model is a peft model and we have a peft_config, we merge and unload it first
- if isinstance(model, PeftModel):
- model = model.merge_and_unload()
- if getattr(model, "is_loaded_in_8bit", False) or getattr(model, "is_loaded_in_4bit", False):
- _support_gc_kwargs = hasattr(
- args, "gradient_checkpointing_kwargs"
- ) and "gradient_checkpointing_kwargs" in list(
- inspect.signature(prepare_model_for_kbit_training).parameters
- )
- prepare_model_kwargs = {"use_gradient_checkpointing": args.gradient_checkpointing}
- if _support_gc_kwargs:
- prepare_model_kwargs["gradient_checkpointing_kwargs"] = args.gradient_checkpointing_kwargs
- model = prepare_model_for_kbit_training(model, **prepare_model_kwargs)
- elif getattr(args, "gradient_checkpointing", False):
- # For backward compatibility with older versions of transformers
- if hasattr(model, "enable_input_require_grads"):
- model.enable_input_require_grads()
- else:
- def make_inputs_require_grad(module, input, output):
- output.requires_grad_(True)
- model.get_input_embeddings().register_forward_hook(make_inputs_require_grad)
- # get peft model with the given config
- model = model
- if args.bf16 and getattr(model, "is_loaded_in_4bit", False):
- peft_module_casting_to_bf16(model)
- # If args.bf16 we need to explicitly call `generate` with torch amp autocast context manager
- self._peft_has_been_casted_to_bf16 = True
- # For models that use gradient_checkpointing, we need to attach a hook that enables input
- # to explicitly have `requires_grad=True`, otherwise training will either silently
- # fail or completely fail.
- elif getattr(args, "gradient_checkpointing", False):
- # For backward compatibility with older versions of transformers
- if hasattr(model, "enable_input_require_grads"):
- model.enable_input_require_grads()
- else:
- def make_inputs_require_grad(module, input, output):
- output.requires_grad_(True)
- model.get_input_embeddings().register_forward_hook(make_inputs_require_grad)
- if args.generate_during_eval and not (is_wandb_available() or is_comet_available()):
- raise ValueError(
- "`generate_during_eval=True` requires Weights and Biases or Comet to be installed."
- " Please install `wandb` or `comet-ml` to resolve."
- )
- if model is not None:
- self.is_encoder_decoder = model.config.is_encoder_decoder
- elif args.is_encoder_decoder is None:
- raise ValueError("When no model is provided, you need to pass the parameter is_encoder_decoder.")
- else:
- self.is_encoder_decoder = args.is_encoder_decoder
- self.is_peft_model = is_peft_available() and isinstance(model, PeftModel)
- self.model_adapter_name = model_adapter_name
- self.ref_adapter_name = ref_adapter_name
- if ref_model:
- self.ref_model = ref_model
- elif self.is_peft_model or args.precompute_ref_log_probs:
- # The `model` with adapters turned off will be used as the reference model
- self.ref_model = None
- else:
- self.ref_model = create_reference_model(model)
- if processing_class is None:
- raise ValueError(
- "max_length or a processing_class must be specified when using the default DPODataCollatorWithPadding"
- )
- if args.max_length is None:
- warnings.warn(
- "When using DPODataCollatorWithPadding, you should set `max_length` in the KTOTrainer's init"
- " it will be set to `512` by default, but you should do it yourself in the future.",
- UserWarning,
- )
- max_length = 512
- if args.max_length is not None:
- max_length = args.max_length
- if args.max_prompt_length is None:
- warnings.warn(
- "When using DPODataCollatorWithPadding, you should set `max_prompt_length` in the KTOTrainer's init"
- " it will be set to `128` by default, but you should do it yourself in the future.",
- UserWarning,
- )
- max_prompt_length = 128
- if args.max_prompt_length is not None:
- max_prompt_length = args.max_prompt_length
- max_completion_length = None
- if args.max_completion_length is None and self.is_encoder_decoder:
- warnings.warn(
- "When using DPODataCollatorWithPadding with an encoder decoder architecture, you should set `max_completion_length` in the KTOTrainer's init"
- " it will be set to `128` by default, but you should do it yourself in the future.",
- UserWarning,
- )
- max_completion_length = 128
- if args.max_completion_length is not None and self.is_encoder_decoder:
- max_completion_length = args.max_completion_length
- if data_collator is None:
- data_collator = DPODataCollatorWithPadding(
- pad_token_id=processing_class.pad_token_id,
- label_pad_token_id=args.label_pad_token_id,
- is_encoder_decoder=self.is_encoder_decoder,
- )
- if args.remove_unused_columns:
- args.remove_unused_columns = False
- # warn users
- warnings.warn(
- "When using DPODataCollatorWithPadding, you should set `remove_unused_columns=False` in your KTOConfig"
- " we have set it for you, but you should do it yourself in the future.",
- UserWarning,
- )
- self.use_dpo_data_collator = True
- else:
- self.use_dpo_data_collator = False
- # Disable dropout in the model and reference model
- if args.disable_dropout:
- disable_dropout_in_model(model)
- if self.ref_model is not None:
- disable_dropout_in_model(self.ref_model)
- self.loss_type = args.loss_type
- self.max_length = max_length
- self.generate_during_eval = args.generate_during_eval
- self.label_pad_token_id = args.label_pad_token_id
- self.padding_value = args.padding_value if args.padding_value is not None else processing_class.pad_token_id
- self.max_prompt_length = max_prompt_length
- self.truncation_mode = args.truncation_mode
- self.max_completion_length = max_completion_length
- self.processing_class = processing_class
- self.precompute_ref_log_probs = args.precompute_ref_log_probs
- # Not all losses require a KL calculation
- self.calculate_KL = True
- if self.loss_type in ["apo_zero_unpaired"]:
- self.calculate_KL = False
- # Since ref_logs are precomputed on the first call to get_train/eval_dataloader
- # keep track of first called to avoid computation of future calls
- self._precomputed_train_ref_log_probs = False
- self._precomputed_eval_ref_log_probs = False
- # metric
- self._stored_metrics = defaultdict(lambda: defaultdict(list))
- # KTO parameter
- self.beta = args.beta
- self.desirable_weight = args.desirable_weight
- self.undesirable_weight = args.undesirable_weight
- self.aux_loss_enabled = getattr(model.config, "output_router_logits", False)
- self.aux_loss_coef = getattr(model.config, "router_aux_loss_coef", 0.0)
- if self.aux_loss_enabled and self.aux_loss_coef == 0.0:
- warnings.warn(
- "You set `output_router_logits` to `True` in the model config, but `router_aux_loss_coef` is set to "
- "`0.0`, meaning the auxiliary loss will not be used. Either set `router_aux_loss_coef` to a value "
- "greater than `0.0`, or set `output_router_logits` to `False` if you don't want to use the auxiliary "
- "loss.",
- UserWarning,
- )
- # The trainer estimates the number of FLOPs (floating-point operations) using the number of elements in the
- # input tensor associated with the key "input_ids". However, in KTO, the sampled data does not include the
- # "input_ids" key. Instead, the available keys are "prompt_input_ids" and "completion_input_ids". As a result,
- # the trainer issues the warning: "Could not estimate the number of tokens of the input, floating-point
- # operations will not be computed." To suppress this warning, we set the "estimate_tokens" key in the model's
- # "warnings_issued" dictionary to True. This acts as a flag to indicate that the warning has already been
- # issued.
- model.warnings_issued["estimate_tokens"] = True
- # Compute that only on the main process for faster data processing.
- # see: https://github.com/huggingface/trl/pull/1255
- with PartialState().local_main_process_first():
- # Extract the prompt if needed
- train_dataset = train_dataset.map(
- maybe_extract_prompt, num_proc=args.dataset_num_proc, desc="Extracting prompt from train dataset"
- )
- # Unpair the dataset if needed
- train_dataset = maybe_unpair_preference_dataset(
- train_dataset, args.dataset_num_proc, desc="Unpairing train dataset"
- )
- # Apply the chat template if needed
- train_dataset = train_dataset.map(
- maybe_apply_chat_template,
- fn_kwargs={"tokenizer": processing_class},
- num_proc=args.dataset_num_proc,
- desc="Applying chat template to train dataset",
- )
- if eval_dataset is not None:
- eval_dataset = eval_dataset.map(
- maybe_extract_prompt, num_proc=args.dataset_num_proc, desc="Extracting prompt from eval dataset"
- )
- eval_dataset = maybe_unpair_preference_dataset(
- eval_dataset, args.dataset_num_proc, desc="Unpairing eval dataset"
- )
- eval_dataset = eval_dataset.map(
- maybe_apply_chat_template,
- fn_kwargs={"tokenizer": processing_class},
- num_proc=args.dataset_num_proc,
- desc="Applying chat template to eval dataset",
- )
- # Tokenize and prepare the training datasets
- train_dataset = train_dataset.map(
- _tokenize,
- batched=True,
- fn_kwargs={"tokenizer": self.processing_class},
- num_proc=args.dataset_num_proc,
- desc="Tokenizing train dataset",
- )
- fn_kwargs = {
- "prefix": "",
- "is_encoder_decoder": self.is_encoder_decoder,
- "tokenizer": self.processing_class,
- "max_length": self.max_length,
- "truncation_mode": self.truncation_mode,
- "label_pad_token_id": self.label_pad_token_id,
- "max_prompt_length": self.max_prompt_length,
- "max_completion_length": self.max_completion_length,
- }
- train_dataset = train_dataset.map(
- _process_tokens,
- fn_kwargs=fn_kwargs,
- num_proc=args.dataset_num_proc,
- desc="Processing tokenized train dataset",
- )
- # Tokenize and prepare the eval datasets
- if eval_dataset is not None:
- eval_dataset = eval_dataset.map(
- _tokenize,
- fn_kwargs={"tokenizer": self.processing_class},
- batched=True,
- num_proc=args.dataset_num_proc,
- desc="Tokenizing eval dataset",
- )
- eval_dataset = eval_dataset.map(
- _process_tokens,
- fn_kwargs=fn_kwargs,
- num_proc=args.dataset_num_proc,
- desc="Processing tokenized eval dataset",
- )
- # Get KL datasets if needed
- if self.calculate_KL:
- if args.per_device_train_batch_size <= 1:
- raise ValueError(
- "Actual (not effective) batch size must be > 1. KTO will not work properly because the KL term will be equivalent to the implied reward."
- )
- # create pairs for estimating the KL term by flipping the matched pairs in each batch of size total_batch_size
- # i.e., (x_1, y_1), ..., (x_n, y_n) --> (x_1, y_n), ..., (x_n, y_1) = (x'_1, y'_1), ..., (x'_n, y'_n)
- train_kl_dataset = train_dataset.map(
- _get_kl_dataset,
- batched=True,
- batch_size=args.per_device_train_batch_size,
- num_proc=args.dataset_num_proc,
- desc="Extracting KL train dataset",
- )
- fn_kwargs["prefix"] = "KL_"
- train_kl_dataset = train_kl_dataset.map(
- _process_tokens,
- fn_kwargs=fn_kwargs,
- num_proc=args.dataset_num_proc,
- remove_columns=[c for c in train_kl_dataset.column_names if c in train_dataset.column_names],
- desc="Processing tokenized train KL dataset",
- )
- # merge the datasets
- train_dataset = concatenate_datasets([train_dataset, train_kl_dataset], axis=1)
- if eval_dataset is not None:
- # Get KL dataset
- eval_kl_dataset = eval_dataset.map(
- _get_kl_dataset,
- batched=True,
- batch_size=args.per_device_train_batch_size,
- num_proc=args.dataset_num_proc,
- desc="Extracting eval KL dataset",
- )
- eval_kl_dataset = eval_kl_dataset.map(
- _process_tokens,
- fn_kwargs=fn_kwargs,
- num_proc=args.dataset_num_proc,
- remove_columns=[c for c in eval_kl_dataset.column_names if c in eval_dataset.column_names],
- desc="Processing tokenized eval KL dataset",
- )
- # merge the datasets
- eval_dataset = concatenate_datasets([eval_dataset, eval_kl_dataset], axis=1)
- # calculate dataset desirability balance
- num_desirable = max(sum(train_dataset["label"]), 1)
- num_undesirable = max(len(train_dataset["label"]) - num_desirable, 1) # "label" is binary
- if num_desirable != num_undesirable:
- # The lower and upper bounds come from Eq. (8) of https://huggingface.co/papers/2402.01306
- des_weight_lower_bound = round((num_undesirable * self.undesirable_weight / num_desirable) * 1, 2)
- des_weight_upper_bound = round((num_undesirable * self.undesirable_weight / num_desirable) * 1.33, 2)
- und_weight_lower_bound = round((num_desirable * self.desirable_weight / num_undesirable) / 1.33, 2)
- und_weight_upper_bound = round((num_desirable * self.desirable_weight / num_undesirable) / 1, 2)
- des_weight_in_range = des_weight_lower_bound <= self.desirable_weight <= des_weight_upper_bound
- und_weight_in_range = und_weight_lower_bound <= self.undesirable_weight <= und_weight_upper_bound
- if not (des_weight_in_range or und_weight_in_range):
- warnings.warn(
- "You have different amounts of desirable/positive and undesirable/negative examples but the "
- "weights on the desirable and undesirable losses don't seem to be in an ideal range. Based "
- f"on your data, we recommend EITHER "
- f"desirable_weight in [{des_weight_lower_bound}, {des_weight_upper_bound}] or "
- f"undesirable_weight in [{und_weight_lower_bound}, {und_weight_upper_bound}] (but NOT BOTH). "
- "See the documentation on how to optimally set these weights.",
- UserWarning,
- )
- super().__init__(
- model=model,
- args=args,
- data_collator=data_collator,
- train_dataset=train_dataset,
- eval_dataset=eval_dataset,
- processing_class=processing_class,
- model_init=model_init,
- compute_metrics=compute_metrics,
- callbacks=callbacks,
- optimizers=optimizers,
- preprocess_logits_for_metrics=preprocess_logits_for_metrics,
- )
- # Gradient accumulation requires scaled loss. Normally, loss scaling in the parent class depends on whether the
- # model accepts loss-related kwargs. Since we compute our own loss, this check is irrelevant. We set
- # self.model_accepts_loss_kwargs to False to enable scaling.
- self.model_accepts_loss_kwargs = False
- # Add tags for models that have been loaded with the correct transformers version
- if hasattr(self.model, "add_model_tags"):
- self.model.add_model_tags(self._tag_names)
- if not hasattr(self, "accelerator"):
- raise AttributeError(
- "Your `Trainer` does not have an `accelerator` object. Consider upgrading `transformers`."
- )
- # Deepspeed Zero-3 does not support precompute_ref_log_probs
- if self.is_deepspeed_enabled:
- if self.accelerator.state.deepspeed_plugin.zero_stage == 3 and self.precompute_ref_log_probs:
- raise ValueError(
- "You cannot use `precompute_ref_log_probs=True` with Deepspeed ZeRO-3. Please set `precompute_ref_log_probs=False`."
- )
- if self.ref_model is None:
- if not (self.is_peft_model or self.precompute_ref_log_probs):
- raise ValueError(
- "No reference model and model is not a Peft model. Try setting `precompute_ref_log_probs=True`"
- )
- else:
- if self.is_deepspeed_enabled:
- self.ref_model = self._prepare_deepspeed(self.ref_model)
- else:
- self.ref_model = self.accelerator.prepare_model(self.ref_model, evaluation_mode=True)
- def _prepare_deepspeed(self, model: PreTrainedModelWrapper):
- # Adapted from accelerate: https://github.com/huggingface/accelerate/blob/739b135f8367becb67ffaada12fe76e3aa60fefd/src/accelerate/accelerator.py#L1473
- deepspeed_plugin = self.accelerator.state.deepspeed_plugin
- config_kwargs = deepcopy(deepspeed_plugin.deepspeed_config)
- if model is not None:
- if hasattr(model, "config"):
- hidden_size = (
- max(model.config.hidden_sizes)
- if getattr(model.config, "hidden_sizes", None)
- else getattr(model.config, "hidden_size", None)
- )
- if hidden_size is not None and config_kwargs["zero_optimization"]["stage"] == 3:
- # Note that `stage3_prefetch_bucket_size` can produce DeepSpeed messages like: `Invalidate trace cache @ step 0: expected module 1, but got module 0`
- # This is expected and is not an error, see: https://github.com/microsoft/DeepSpeed/discussions/4081
- config_kwargs.update(
- {
- "zero_optimization.reduce_bucket_size": hidden_size * hidden_size,
- "zero_optimization.stage3_param_persistence_threshold": 10 * hidden_size,
- "zero_optimization.stage3_prefetch_bucket_size": 0.9 * hidden_size * hidden_size,
- }
- )
- # If ZeRO-3 is used, we shard both the active and reference model.
- # Otherwise, we assume the reference model fits in memory and is initialized on each device with ZeRO disabled (stage 0)
- if config_kwargs["zero_optimization"]["stage"] != 3:
- config_kwargs["zero_optimization"]["stage"] = 0
- model, *_ = deepspeed.initialize(model=model, config=config_kwargs)
- model.eval()
- return model
- @contextmanager
- def null_ref_context(self):
- """Context manager for handling null reference model (that is, peft adapter manipulation)."""
- with (
- self.accelerator.unwrap_model(self.model).disable_adapter()
- if self.is_peft_model and not self.ref_adapter_name
- else nullcontext()
- ):
- if self.ref_adapter_name:
- self.model.set_adapter(self.ref_adapter_name)
- yield
- if self.ref_adapter_name:
- self.model.set_adapter(self.model_adapter_name or "default")
- def get_train_dataloader(self) -> DataLoader:
- """
- Returns the training [`~torch.utils.data.DataLoader`].
- Subclass of transformers.src.transformers.trainer.get_train_dataloader to precompute `ref_log_probs`.
- """
- if self.precompute_ref_log_probs and not self._precomputed_train_ref_log_probs:
- dataloader_params = {
- "batch_size": self.args.per_device_train_batch_size,
- "collate_fn": self.data_collator,
- "num_workers": self.args.dataloader_num_workers,
- "pin_memory": self.args.dataloader_pin_memory,
- "shuffle": False,
- }
- # prepare dataloader
- data_loader = self.accelerator.prepare(DataLoader(self.train_dataset, **dataloader_params))
- reference_completion_logps = []
- reference_KL_logps = []
- for padded_batch in tqdm(iterable=data_loader, desc="Train dataset reference log probs"):
- reference_completion_logp, reference_KL_logp = self.compute_reference_log_probs(padded_batch)
- reference_completion_logp = self.accelerator.gather_for_metrics(reference_completion_logp)
- reference_completion_logps.append(reference_completion_logp.cpu())
- if self.calculate_KL:
- reference_KL_logp = self.accelerator.gather_for_metrics(reference_KL_logp)
- reference_KL_logps.append(reference_KL_logp.cpu())
- self.train_dataset = self.train_dataset.add_column(
- name="reference_logps", column=torch.cat(reference_completion_logps).float().numpy()
- )
- if self.calculate_KL:
- self.train_dataset = self.train_dataset.add_column(
- name="reference_KL_logps", column=torch.cat(reference_KL_logps).float().numpy()
- )
- self._precomputed_train_ref_log_probs = True
- return super().get_train_dataloader()
- def get_eval_dataloader(self, eval_dataset: Optional[Dataset] = None) -> DataLoader:
- """
- Returns the evaluation [`~torch.utils.data.DataLoader`].
- Subclass of transformers.src.transformers.trainer.get_eval_dataloader to precompute `ref_log_probs`.
- Args:
- eval_dataset (`torch.utils.data.Dataset`, *optional*):
- If provided, will override `self.eval_dataset`. If it is a [`~datasets.Dataset`], columns not accepted
- by the `model.forward()` method are automatically removed. It must implement `__len__`.
- """
- if eval_dataset is None and self.eval_dataset is None:
- raise ValueError("Trainer: evaluation requires an eval_dataset.")
- eval_dataset = eval_dataset if eval_dataset is not None else self.eval_dataset
- if self.precompute_ref_log_probs and not self._precomputed_eval_ref_log_probs:
- dataloader_params = {
- "batch_size": self.args.per_device_eval_batch_size,
- "collate_fn": self.data_collator,
- "num_workers": self.args.dataloader_num_workers,
- "pin_memory": self.args.dataloader_pin_memory,
- "shuffle": False,
- }
- # prepare dataloader
- data_loader = self.accelerator.prepare(DataLoader(eval_dataset, **dataloader_params))
- reference_completion_logps = []
- reference_KL_logps = []
- for padded_batch in tqdm(iterable=data_loader, desc="Eval dataset reference log probs"):
- reference_completion_logp, reference_KL_logp = self.compute_reference_log_probs(padded_batch)
- reference_completion_logp = self.accelerator.gather_for_metrics(reference_completion_logp)
- reference_completion_logps.append(reference_completion_logp.cpu())
- if self.calculate_KL:
- reference_KL_logp = self.accelerator.gather_for_metrics(reference_KL_logp)
- reference_KL_logps.append(reference_KL_logp.cpu())
- eval_dataset = eval_dataset.add_column(
- name="reference_logps", column=torch.cat(reference_completion_logps).float().numpy()
- )
- if self.calculate_KL:
- eval_dataset = eval_dataset.add_column(
- name="reference_KL_logps", column=torch.cat(reference_KL_logps).float().numpy()
- )
- # Save calculated reference_chosen_logps and reference_rejected_logps to the eval_dataset for subsequent runs
- if self.eval_dataset is not None:
- self.eval_dataset = eval_dataset
- self._precomputed_eval_ref_log_probs = True
- return super().get_eval_dataloader(eval_dataset=eval_dataset)
- def compute_reference_log_probs(self, padded_batch: dict) -> dict:
- """Computes log probabilities of the reference model for a single padded batch of a KTO specific dataset."""
- with torch.no_grad():
- if self.ref_model is None:
- with self.null_ref_context():
- if self.is_encoder_decoder:
- completion_logits = self.model(
- padded_batch["prompt_input_ids"],
- attention_mask=padded_batch["prompt_attention_mask"],
- decoder_input_ids=padded_batch.get("completion_decoder_input_ids"),
- labels=padded_batch["completion_labels"],
- ).logits
- if self.calculate_KL:
- KL_logits = self.model(
- padded_batch["KL_prompt_input_ids"],
- attention_mask=padded_batch["KL_prompt_attention_mask"],
- decoder_input_ids=padded_batch.get("KL_completion_decoder_input_ids"),
- labels=padded_batch["KL_completion_labels"],
- ).logits
- else:
- completion_logits = self.model(
- padded_batch["completion_input_ids"],
- attention_mask=padded_batch["completion_attention_mask"],
- ).logits
- if self.calculate_KL:
- KL_logits = self.model(
- padded_batch["KL_completion_input_ids"],
- attention_mask=padded_batch["KL_completion_attention_mask"],
- ).logits
- else:
- if self.is_encoder_decoder:
- completion_logits = self.ref_model(
- padded_batch["prompt_input_ids"],
- attention_mask=padded_batch["prompt_attention_mask"],
- decoder_input_ids=padded_batch.get("completion_decoder_input_ids"),
- labels=padded_batch["completion_labels"],
- ).logits
- if self.calculate_KL:
- KL_logits = self.ref_model(
- padded_batch["KL_prompt_input_ids"],
- attention_mask=padded_batch["KL_prompt_attention_mask"],
- decoder_input_ids=padded_batch.get("KL_completion_decoder_input_ids"),
- labels=padded_batch["KL_completion_labels"],
- ).logits
- else:
- completion_logits = self.ref_model(
- padded_batch["completion_input_ids"], attention_mask=padded_batch["completion_attention_mask"]
- ).logits
- if self.calculate_KL:
- KL_logits = self.ref_model(
- padded_batch["KL_completion_input_ids"],
- attention_mask=padded_batch["KL_completion_attention_mask"],
- ).logits
- completion_logps = self.get_batch_logps(
- completion_logits,
- padded_batch["completion_labels"],
- average_log_prob=False,
- is_encoder_decoder=self.is_encoder_decoder,
- label_pad_token_id=self.label_pad_token_id,
- )
- if self.calculate_KL:
- KL_logps = self.get_batch_logps(
- KL_logits,
- padded_batch["KL_completion_labels"],
- average_log_prob=False,
- is_encoder_decoder=self.is_encoder_decoder,
- label_pad_token_id=self.label_pad_token_id,
- )
- else:
- KL_logps = None
- return completion_logps, KL_logps
- @staticmethod
- def get_batch_logps(
- logits: torch.FloatTensor,
- labels: torch.LongTensor,
- average_log_prob: bool = False,
- label_pad_token_id: int = -100,
- is_encoder_decoder: bool = False,
- ) -> torch.FloatTensor:
- """Compute the log probabilities of the given labels under the given logits.
- Args:
- logits: Logits of the model (unnormalized). Shape: (batch_size, sequence_length, vocab_size)
- labels: Labels for which to compute the log probabilities. Label tokens with a value of label_pad_token_id are ignored. Shape: (batch_size, sequence_length)
- average_log_prob: If True, return the average log probability per (non-masked) token. Otherwise, return the sum of the log probabilities of the (non-masked) tokens.
- Returns:
- A tensor of shape (batch_size,) containing the average/sum log probabilities of the given labels under the given logits.
- """
- if logits.shape[:-1] != labels.shape:
- raise ValueError("Logits (batch and sequence length dim) and labels must have the same shape.")
- if not is_encoder_decoder:
- labels = labels[:, 1:].clone()
- logits = logits[:, :-1, :]
- else:
- # Fixes end-dec RuntimeError
- labels = labels.clone()
- loss_mask = labels != label_pad_token_id
- # dummy token; we'll ignore the losses on these tokens later
- labels[labels == label_pad_token_id] = 0
- per_token_logps = selective_log_softmax(logits, labels)
- if average_log_prob:
- return (per_token_logps * loss_mask).sum(-1) / loss_mask.sum(-1)
- else:
- return (per_token_logps * loss_mask).sum(-1)
- def forward(
- self, model: nn.Module, batch: dict[str, Union[list, torch.LongTensor]]
- ) -> tuple[torch.FloatTensor, torch.FloatTensor, torch.FloatTensor, torch.FloatTensor]:
- if self.calculate_KL:
- KL_logps = None
- KL_model_kwargs = (
- {
- "input_ids": batch["KL_prompt_input_ids"],
- "attention_mask": batch["KL_prompt_attention_mask"],
- "labels": batch["KL_completion_labels"],
- "decoder_input_ids": batch.get("KL_completion_decoder_input_ids"),
- }
- if self.is_encoder_decoder
- else {
- "input_ids": batch["KL_completion_input_ids"],
- "attention_mask": batch["KL_completion_attention_mask"],
- }
- )
- with torch.no_grad():
- KL_logits = model(
- **KL_model_kwargs,
- ).logits
- KL_logps = self.get_batch_logps(
- KL_logits,
- batch["KL_completion_labels"],
- average_log_prob=False,
- is_encoder_decoder=self.is_encoder_decoder,
- label_pad_token_id=self.label_pad_token_id,
- )
- else:
- KL_logps = None
- model_kwargs = (
- {
- "labels": batch["completion_labels"],
- "decoder_input_ids": batch.get("completion_decoder_input_ids"),
- }
- if self.is_encoder_decoder
- else {}
- )
- if self.aux_loss_enabled:
- model_kwargs["output_router_logits"] = True
- outputs = model(
- batch["completion_input_ids"],
- attention_mask=batch["completion_attention_mask"],
- **model_kwargs,
- )
- completion_logits = outputs.logits
- completion_logps = self.get_batch_logps(
- completion_logits,
- batch["completion_labels"],
- average_log_prob=False,
- is_encoder_decoder=self.is_encoder_decoder,
- label_pad_token_id=self.label_pad_token_id,
- )
- if completion_logps.shape[0] != len(batch["label"]):
- raise ValueError(
- "There is a mismatch between the number of examples in this batch and the number of "
- "examples for which an output sequence was predicted."
- )
- chosen_idx = [i for i in range(completion_logps.shape[0]) if batch["label"][i] is True]
- rejected_idx = [i for i in range(completion_logps.shape[0]) if batch["label"][i] is False]
- chosen_logps = completion_logps[chosen_idx, ...]
- rejected_logps = completion_logps[rejected_idx, ...]
- chosen_logits = completion_logits[chosen_idx, ...]
- rejected_logits = completion_logits[rejected_idx, ...]
- if self.aux_loss_enabled:
- return (chosen_logps, rejected_logps, chosen_logits, rejected_logits, KL_logps, outputs.aux_loss)
- else:
- return (chosen_logps, rejected_logps, chosen_logits, rejected_logits, KL_logps)
- def kto_loss(
- self,
- policy_chosen_logps: torch.FloatTensor,
- policy_rejected_logps: torch.FloatTensor,
- policy_KL_logps: torch.FloatTensor,
- reference_chosen_logps: torch.FloatTensor,
- reference_rejected_logps: torch.FloatTensor,
- reference_KL_logps: torch.FloatTensor,
- ) -> tuple[torch.FloatTensor, torch.FloatTensor, torch.FloatTensor, torch.FloatTensor]:
- """Compute the KTO loss for a batch of policy and reference model log probabilities.
- Args:
- policy_chosen_logps: Log probabilities of the policy model for the chosen responses. Shape: (num(chosen) in batch_size,)
- policy_rejected_logps: Log probabilities of the policy model for the rejected responses. Shape: (num(rejected) in batch_size,)
- policy_KL_logps: Log probabilities of the policy model for the KL responses. Shape: (batch_size,)
- reference_chosen_logps: Log probabilities of the reference model for the chosen responses. Shape: (num(chosen) in batch_size,)
- reference_rejected_logps: Log probabilities of the reference model for the rejected responses. Shape: (num(rejected) in batch_size,)
- reference_KL_logps: Log probabilities of the reference model for the KL responses. Shape: (batch_size,)
- Returns:
- A tuple of four tensors: (losses, chosen_rewards, rejected_rewards, KL).
- The losses tensor contains the KTO loss for each example in the batch.
- The chosen_rewards and rejected_rewards tensors contain the rewards for the chosen and rejected responses, respectively.
- The KL tensor contains the detached KL divergence estimate between the policy and reference models.
- """
- if self.calculate_KL:
- kl = (policy_KL_logps - reference_KL_logps).mean().detach()
- kl = self.accelerator.gather_for_metrics(kl).mean().clamp(min=0)
- else:
- kl = torch.zeros(1).to(policy_chosen_logps.device)
- # Chosen losses
- if policy_chosen_logps.shape[0] != 0 or reference_chosen_logps.shape[0] != 0:
- chosen_logratios = policy_chosen_logps - reference_chosen_logps
- if self.loss_type == "kto":
- # Eqn (7) of the KTO paper (https://huggingface.co/papers/2402.01306)
- chosen_losses = 1 - F.sigmoid(self.beta * (chosen_logratios - kl))
- elif self.loss_type == "apo_zero_unpaired":
- # Unpaired variant of Eqn (7) of the APO paper (https://huggingface.co/papers/2408.06266)
- # Use this loss when you believe the chosen outputs are better than your model's default output
- chosen_losses = 1 - F.sigmoid(self.beta * chosen_logratios)
- chosen_rewards = self.beta * chosen_logratios.detach()
- else:
- # lists can't be empty -- if they are, then accelerate.gather will hang
- chosen_losses = torch.Tensor([]).to(self.accelerator.device)
- chosen_rewards = torch.Tensor([]).to(self.accelerator.device)
- # Rejected losses
- if policy_rejected_logps.shape[0] != 0 or reference_rejected_logps.shape[0] != 0:
- rejected_logratios = policy_rejected_logps - reference_rejected_logps
- if self.loss_type == "kto":
- rejected_losses = 1 - F.sigmoid(self.beta * (kl - rejected_logratios))
- elif self.loss_type == "apo_zero_unpaired":
- rejected_losses = F.sigmoid(self.beta * rejected_logratios)
- rejected_rewards = self.beta * rejected_logratios.detach()
- else:
- # lists can't be empty -- if they are, then accelerate.gather will hang
- rejected_losses = torch.Tensor([]).to(self.accelerator.device)
- rejected_rewards = torch.Tensor([]).to(self.accelerator.device)
- losses = torch.cat(
- (self.desirable_weight * chosen_losses, self.undesirable_weight * rejected_losses),
- 0,
- )
- return losses, chosen_rewards, rejected_rewards, kl
- def get_batch_loss_metrics(
- self,
- model,
- batch: dict[str, Union[list, torch.LongTensor]],
- ):
- """Compute the KTO loss and other metrics for the given batch of inputs for train or test."""
- metrics = {}
- batch = {k: (v.to(self.accelerator.device) if isinstance(v, torch.Tensor) else v) for k, v in batch.items()}
- forward_output = self.forward(model, batch)
- (
- policy_chosen_logps,
- policy_rejected_logps,
- policy_chosen_logits,
- policy_rejected_logits,
- policy_KL_logps,
- ) = forward_output[:5]
- if self.aux_loss_enabled:
- aux_loss = forward_output[5]
- # if reference_logps in batch use them, otherwise use the reference model
- if "reference_logps" in batch:
- chosen_idx = [i for i in range(batch["reference_logps"].shape[0]) if batch["label"][i] is True]
- rejected_idx = [i for i in range(batch["reference_logps"].shape[0]) if batch["label"][i] is False]
- reference_chosen_logps = batch["reference_logps"][chosen_idx, ...]
- reference_rejected_logps = batch["reference_logps"][rejected_idx, ...]
- if self.calculate_KL:
- reference_KL_logps = batch["reference_KL_logps"]
- else:
- reference_KL_logps = None
- else:
- with torch.no_grad():
- if self.ref_model is None:
- with self.null_ref_context():
- (
- reference_chosen_logps,
- reference_rejected_logps,
- _,
- _,
- reference_KL_logps,
- ) = self.forward(self.model, batch)[:5]
- else:
- (
- reference_chosen_logps,
- reference_rejected_logps,
- _,
- _,
- reference_KL_logps,
- ) = self.forward(self.ref_model, batch)[:5]
- losses, chosen_rewards, rejected_rewards, kl = self.kto_loss(
- policy_chosen_logps,
- policy_rejected_logps,
- policy_KL_logps,
- reference_chosen_logps,
- reference_rejected_logps,
- reference_KL_logps,
- )
- metrics["kl"] = kl.item()
- num_chosen = torch.Tensor([len(chosen_rewards)]).to(self.accelerator.device)
- num_rejected = torch.Tensor([len(rejected_rewards)]).to(self.accelerator.device)
- all_num_chosen = self.accelerator.gather_for_metrics(num_chosen).sum().item()
- all_num_rejected = self.accelerator.gather_for_metrics(num_rejected).sum().item()
- if all_num_chosen > 0:
- metrics["rewards/chosen_sum"] = (
- self.accelerator.gather_for_metrics(chosen_rewards.nansum()).nansum().item()
- )
- metrics["logps/chosen_sum"] = (
- self.accelerator.gather_for_metrics(policy_chosen_logps.nansum()).nansum().item()
- )
- metrics["logits/chosen_sum"] = (
- self.accelerator.gather_for_metrics(policy_chosen_logits.nansum()).nansum().item()
- )
- metrics["count/chosen"] = all_num_chosen
- if all_num_rejected > 0:
- metrics["rewards/rejected_sum"] = (
- self.accelerator.gather_for_metrics(rejected_rewards.nansum()).nansum().item()
- )
- metrics["logps/rejected_sum"] = (
- self.accelerator.gather_for_metrics(policy_rejected_logps.nansum()).nansum().item()
- )
- metrics["logits/rejected_sum"] = (
- self.accelerator.gather_for_metrics(policy_rejected_logits.nansum()).nansum().item()
- )
- metrics["count/rejected"] = all_num_rejected
- loss = losses.nanmean()
- if self.aux_loss_enabled:
- loss += self.aux_loss_coef * aux_loss
- return loss, metrics
- def compute_loss(
- self,
- model: Union[PreTrainedModel, nn.Module],
- inputs: dict[str, Union[torch.Tensor, Any]],
- return_outputs=False,
- num_items_in_batch=None,
- ) -> Union[torch.Tensor, tuple[torch.Tensor, dict[str, torch.Tensor]]]:
- compute_loss_context_manager = amp.autocast("cuda") if self._peft_has_been_casted_to_bf16 else nullcontext()
- with compute_loss_context_manager:
- loss, metrics = self.get_batch_loss_metrics(model, inputs)
- # Make sure to move the loss to the device the original accumulating loss is at back in the `Trainer` class:
- loss = loss.to(self.args.device)
- # force log the metrics
- if self.accelerator.is_main_process:
- self.store_metrics(metrics, train_eval="train")
- if return_outputs:
- return (loss, metrics)
- return loss
- def store_metrics(self, metrics: dict[str, float], train_eval: Literal["train", "eval"] = "train") -> None:
- for key, value in metrics.items():
- self._stored_metrics[train_eval][key].append(value)
- def _get_train_sampler(self) -> Optional[torch.utils.data.Sampler]:
- if self.train_dataset is None or not has_length(self.train_dataset):
- return None
- return SequentialSampler(self.train_dataset)
- def generate_from_model_and_ref(self, model, batch: dict[str, torch.LongTensor]) -> tuple[str, str]:
- """Generate samples from the model and reference model for the given batch of inputs."""
- # If one uses `generate_during_eval` with peft + bf16, we need to explicitly call generate with
- # the torch cuda amp context manager as some hidden states are silently casted to full precision.
- generate_context_manager = amp.autocast("cuda") if self._peft_has_been_casted_to_bf16 else nullcontext()
- with generate_context_manager:
- policy_output = model.generate(
- input_ids=batch["prompt_input_ids"],
- attention_mask=batch["prompt_attention_mask"],
- max_length=self.max_length,
- do_sample=True,
- pad_token_id=self.processing_class.pad_token_id,
- )
- # if reference_output in batch use that otherwise use the reference model
- if "reference_output" in batch:
- reference_output = batch["reference_output"]
- else:
- if self.ref_model is None:
- with self.null_ref_context():
- reference_output = self.model.generate(
- input_ids=batch["prompt_input_ids"],
- attention_mask=batch["prompt_attention_mask"],
- max_length=self.max_length,
- do_sample=True,
- pad_token_id=self.processing_class.pad_token_id,
- )
- else:
- reference_output = self.ref_model.generate(
- input_ids=batch["prompt_input_ids"],
- attention_mask=batch["prompt_attention_mask"],
- max_length=self.max_length,
- do_sample=True,
- pad_token_id=self.processing_class.pad_token_id,
- )
- policy_output = pad_to_length(policy_output, self.max_length, self.processing_class.pad_token_id)
- policy_output_decoded = self.processing_class.batch_decode(policy_output, skip_special_tokens=True)
- reference_output = pad_to_length(reference_output, self.max_length, self.processing_class.pad_token_id)
- reference_output_decoded = self.processing_class.batch_decode(reference_output, skip_special_tokens=True)
- return policy_output_decoded, reference_output_decoded
- def prediction_step(
- self,
- model: Union[PreTrainedModel, nn.Module],
- inputs: dict[str, Union[torch.Tensor, Any]],
- prediction_loss_only: bool,
- ignore_keys: Optional[list[str]] = None,
- ):
- if ignore_keys is None:
- if hasattr(model, "config"):
- ignore_keys = getattr(model.config, "keys_to_ignore_at_inference", [])
- else:
- ignore_keys = []
- prediction_context_manager = amp.autocast("cuda") if self._peft_has_been_casted_to_bf16 else nullcontext()
- with torch.no_grad(), prediction_context_manager:
- loss, metrics = self.get_batch_loss_metrics(model, inputs)
- # force log the metrics
- if self.accelerator.is_main_process:
- self.store_metrics(metrics, train_eval="eval")
- if prediction_loss_only:
- return (loss.detach(), None, None)
- # logits for the chosen and rejected samples from model
- logits_dict = {
- "eval_logits/chosen": metrics["logits/chosen"],
- "eval_logits/rejected": metrics["logits/rejected"],
- }
- logits = torch.tensor(
- [v for k, v in logits_dict.items() if k not in ignore_keys], device=self.accelerator.device
- )
- labels = torch.zeros(logits.shape[0], device=self.accelerator.device)
- return (loss.detach(), logits, labels)
- def evaluation_loop(
- self,
- dataloader: DataLoader,
- description: str,
- prediction_loss_only: Optional[bool] = None,
- ignore_keys: Optional[list[str]] = None,
- metric_key_prefix: str = "eval",
- ) -> EvalLoopOutput:
- """
- Overriding built-in evaluation loop to store metrics for each batch.
- Prediction/evaluation loop, shared by `Trainer.evaluate()` and `Trainer.predict()`.
- Works both with or without labels.
- """
- # Sample and save to game log if requested (for one batch to save time)
- if self.generate_during_eval:
- # Generate random indices within the range of the total number of samples
- num_samples = len(dataloader.dataset)
- random_indices = random.sample(range(num_samples), k=self.args.eval_batch_size)
- # Use dataloader.dataset.select to get the random batch without iterating over the DataLoader
- random_batch_dataset = dataloader.dataset.select(random_indices)
- random_batch = self.data_collator(random_batch_dataset)
- random_batch = self._prepare_inputs(random_batch)
- target_indicies = [i for i in range(len(random_batch["label"])) if random_batch["label"][i] is False]
- target_batch = {
- "prompt_input_ids": random_batch["prompt_input_ids"][target_indicies],
- "prompt_attention_mask": random_batch["prompt_attention_mask"][target_indicies],
- "prompt": itemgetter(*target_indicies)(random_batch["prompt"]),
- }
- policy_output_decoded, ref_output_decoded = self.generate_from_model_and_ref(self.model, target_batch)
- table = pd.DataFrame(
- columns=["Prompt", "Policy", "Ref Model"],
- data=[
- [prompt, pol[len(prompt) :], ref[len(prompt) :]]
- for prompt, pol, ref in zip(target_batch["prompt"], policy_output_decoded, ref_output_decoded)
- ],
- )
- if "wandb" in self.args.report_to:
- wandb.log({"game_log": wandb.Table(data=table)})
- if "comet_ml" in self.args.report_to:
- log_table_to_comet_experiment(
- name="game_log.csv",
- table=table,
- )
- # Base evaluation
- initial_output = super().evaluation_loop(
- dataloader, description, prediction_loss_only, ignore_keys, metric_key_prefix
- )
- return initial_output
- def log(self, logs: dict[str, float], start_time: Optional[float] = None) -> None:
- """
- Log `logs` on the various objects watching training, including stored metrics.
- Args:
- logs (`dict[str, float]`):
- The values to log.
- start_time (`float` or `None`, *optional*, defaults to `None`):
- Start time of the training.
- """
- # logs either has 'loss' or 'eval_loss'
- train_eval = "train" if "loss" in logs else "eval"
- # train metrics should have no prefix, eval should have 'eval_'
- prefix = "eval_" if train_eval == "eval" else ""
- # accumulate average metrics from sums and lengths
- for split in ["chosen", "rejected"]:
- if f"count/{split}" in self._stored_metrics[train_eval]:
- count_sum = torch.Tensor(self._stored_metrics[train_eval][f"count/{split}"]).sum().item()
- for metric in ["rewards", "logps", "logits"]:
- logs[f"{prefix}{metric}/{split}"] = (
- torch.Tensor(self._stored_metrics[train_eval][f"{metric}/{split}_sum"]).sum().item()
- / count_sum
- )
- # delete obsolete metric
- del self._stored_metrics[train_eval][f"{metric}/{split}_sum"]
- del self._stored_metrics[train_eval][f"count/{split}"]
- # calculate reward margin
- if f"{prefix}rewards/chosen" in logs and f"{prefix}rewards/rejected" in logs:
- logs[f"{prefix}rewards/margins"] = logs[f"{prefix}rewards/chosen"] - logs[f"{prefix}rewards/rejected"]
- # Add averaged stored metrics to logs
- for key, metrics in self._stored_metrics[train_eval].items():
- logs[f"{prefix}{key}"] = torch.Tensor(metrics).mean().item()
- del self._stored_metrics[train_eval]
- if version.parse(transformers.__version__) >= version.parse("4.47.0.dev0"):
- return super().log(logs, start_time)
- else: # transformers<=4.46
- return super().log(logs)
- def create_model_card(
- self,
- model_name: Optional[str] = None,
- dataset_name: Optional[str] = None,
- tags: Union[str, list[str], None] = None,
- ):
- """
- Creates a draft of a model card using the information available to the `Trainer`.
- Args:
- model_name (`str` or `None`, *optional*, defaults to `None`):
- Name of the model.
- dataset_name (`str` or `None`, *optional*, defaults to `None`):
- Name of the dataset used for training.
- tags (`str`, `list[str]` or `None`, *optional*, defaults to `None`):
- Tags to be associated with the model card.
- """
- if not self.is_world_process_zero():
- return
- if hasattr(self.model.config, "_name_or_path") and not os.path.isdir(self.model.config._name_or_path):
- base_model = self.model.config._name_or_path
- else:
- base_model = None
- tags = tags or []
- if isinstance(tags, str):
- tags = [tags]
- if hasattr(self.model.config, "unsloth_version"):
- tags.append("unsloth")
- citation = textwrap.dedent("""\
- @article{ethayarajh2024kto,
- title = {{KTO: Model Alignment as Prospect Theoretic Optimization}},
- author = {Kawin Ethayarajh and Winnie Xu and Niklas Muennighoff and Dan Jurafsky and Douwe Kiela},
- year = 2024,
- eprint = {arXiv:2402.01306},
- }""")
- model_card = generate_model_card(
- base_model=base_model,
- model_name=model_name,
- hub_model_id=self.hub_model_id,
- dataset_name=dataset_name,
- tags=tags,
- wandb_url=wandb.run.get_url() if is_wandb_available() and wandb.run is not None else None,
- comet_url=get_comet_experiment_url(),
- trainer_name="KTO",
- trainer_citation=citation,
- paper_title="KTO: Model Alignment as Prospect Theoretic Optimization",
- paper_id="2402.01306",
- )
- model_card.save(os.path.join(self.args.output_dir, "README.md"))
- class UnslothKTOTrainer(_UnslothKTOTrainer):
- """
-
- Initialize KTOTrainer.
- Args:
- model (`transformers.PreTrainedModel`):
- The model to train, preferably an `AutoModelForSequenceClassification`.
- ref_model (`PreTrainedModelWrapper`):
- Hugging Face transformer model with a casual language modelling head. Used for implicit reward computation and loss. If no
- reference model is provided, the trainer will create a reference model with the same architecture as the model to be optimized.
- args (`KTOConfig`):
- The arguments to use for training.
- train_dataset (`datasets.Dataset`):
- The dataset to use for training.
- eval_dataset (`datasets.Dataset`):
- The dataset to use for evaluation.
- processing_class (`PreTrainedTokenizerBase` or `BaseImageProcessor` or `FeatureExtractionMixin` or `ProcessorMixin`, *optional*):
- Processing class used to process the data. If provided, will be used to automatically process the inputs
- for the model, and it will be saved along the model to make it easier to rerun an interrupted training or
- reuse the fine-tuned model.
- data_collator (`transformers.DataCollator`, *optional*, defaults to `None`):
- The data collator to use for training. If None is specified, the default data collator (`DPODataCollatorWithPadding`) will be used
- which will pad the sequences to the maximum length of the sequences in the batch, given a dataset of paired sequences.
- model_init (`Callable[[], transformers.PreTrainedModel]`):
- The model initializer to use for training. If None is specified, the default model initializer will be used.
- callbacks (`list[transformers.TrainerCallback]`):
- The callbacks to use for training.
- optimizers (`tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR]`):
- The optimizer and scheduler to use for training.
- preprocess_logits_for_metrics (`Callable[[torch.Tensor, torch.Tensor], torch.Tensor]`):
- The function to use to preprocess the logits before computing the metrics.
- peft_config (`dict`, defaults to `None`):
- The PEFT configuration to use for training. If you pass a PEFT configuration, the model will be wrapped in a PEFT model.
- compute_metrics (`Callable[[EvalPrediction], dict]`, *optional*):
- The function to use to compute the metrics. Must take a `EvalPrediction` and return
- a dictionary string to metric values.
- model_adapter_name (`str`, defaults to `None`):
- Name of the train target PEFT adapter, when using LoRA with multiple adapters.
- ref_adapter_name (`str`, defaults to `None`):
- Name of the reference PEFT adapter, when using LoRA with multiple adapters.
-
- """
- def __init__(
- self,
- model = None,
- ref_model = None,
- args = None,
- train_dataset = None,
- eval_dataset = None,
- processing_class = None,
- data_collator = None,
- model_init = None,
- callbacks = None,
- preprocess_logits_for_metrics = None,
- peft_config = None,
- compute_metrics = None,
- model_adapter_name = None,
- ref_adapter_name = None,
- **kwargs
- ):
- if args is None: args = UnslothKTOConfig()
- use_bf16 = getattr(args, 'bf16', False)
- use_fp16 = getattr(args, 'fp16', False)
- dtype = getattr(model.config, 'torch_dtype', None)
- if dtype is None: dtype = model.get_input_embeddings().dtype
- from unsloth_zoo.utils import _get_dtype
- dtype = _get_dtype(dtype)
- float16 = dtype == torch.float16
- if float16 and use_bf16: raise TypeError('Unsloth: Model is in float16 precision but you want to use bfloat16 precision. Set fp16 to `True` and bf16 to `False`')
- if not float16 and use_fp16: raise TypeError('Unsloth: Model is in bfloat16 precision but you want to use float16 precision. Set fp16 to `False` and bf16 to `True`')
- if not use_bf16 and not use_fp16:
- args.fp16 = float16
- args.bf16 = not float16
- os.environ['ACCELERATE_MIXED_PRECISION'] = 'fp16' if float16 else 'bf16'
- if getattr(args, 'eval_dataset', None) is not None and getattr(args, 'eval_strategy', 'no') == 'no':
- args.eval_strategy = 'steps'
- if getattr(args, 'eval_steps', None) is None: args.eval_steps = 0.1
- ga_steps = getattr(args, 'gradient_accumulation_steps', None)
- if ga_steps is not None and ga_steps > 1:
- from transformers import __version__ as transformers_version
- if Version(transformers_version) <= Version('4.45.2'):
- print('**** Unsloth: Please use our fixed gradient_accumulation_steps by updating transformers, TRL and Unsloth!\n'
- '`pip install --upgrade --no-cache-dir --force-reinstall --no-deps unsloth transformers trl unsloth_zoo`')
- if getattr(args, 'eval_strategy', 'no') != 'no':
- eval_bsz = getattr(args, 'per_device_eval_batch_size', 8)
- if eval_bsz == 8 and args.per_device_train_batch_size < eval_bsz: args.per_device_eval_batch_size = args.per_device_train_batch_size
- if getattr(args, 'eval_accumulation_steps', None) is None and ga_steps is not None: args.eval_accumulation_steps = ga_steps
- fp16_full_eval = getattr(args, 'fp16_full_eval', False)
- bf16_full_eval = getattr(args, 'bf16_full_eval', False)
- if args.fp16 and bf16_full_eval: args.bf16_full_eval = False; args.fp16_full_eval = True
- if args.bf16 and fp16_full_eval: args.bf16_full_eval = True; args.fp16_full_eval = False
- if not bf16_full_eval and not fp16_full_eval: args.bf16_full_eval = args.bf16; args.fp16_full_eval = args.fp16
- if 'max_seq_length' not in locals() and not hasattr(args, 'max_seq_length'):
- pass
- else:
- model_max_seq_length = getattr(model, 'max_seq_length', None)
- args_max_seq_length = getattr(args, 'max_seq_length', None)
- if args_max_seq_length is None and model_max_seq_length is not None:
- max_seq_length = model.max_seq_length
- if hasattr(args, 'max_seq_length'): args.max_seq_length = max_seq_length
- if model is not None and hasattr(model, 'for_training'):
- model.for_training()
- if 'tokenizer' in locals() and hasattr(tokenizer, 'padding_side'): tokenizer.padding_side = 'right'
- if 'processing_class' in locals():
- if hasattr(processing_class, 'padding_side'): processing_class.padding_side = 'right'
- if hasattr(processing_class, 'tokenizer') and hasattr(processing_class.tokenizer, 'padding_side'): processing_class.tokenizer.padding_side = 'right'
- other_metrics = []
-
- from unsloth_zoo.logging_utils import PatchRLStatistics
- PatchRLStatistics('kto_trainer', other_metrics)
-
- super().__init__(
- model = model,
- ref_model = ref_model,
- args = args,
- train_dataset = train_dataset,
- eval_dataset = eval_dataset,
- processing_class = processing_class,
- data_collator = data_collator,
- model_init = model_init,
- callbacks = callbacks,
- preprocess_logits_for_metrics = preprocess_logits_for_metrics,
- peft_config = peft_config,
- compute_metrics = compute_metrics,
- model_adapter_name = model_adapter_name,
- ref_adapter_name = ref_adapter_name,**kwargs)
- if hasattr(self, 'neftune_hook_handle'):
- self.neftune_hook_handle.remove()
- if hasattr(self, 'neftune_hook_handle'): del self.neftune_hook_handle
- if getattr(args, 'neftune_noise_alpha', None) is not None:
- model.get_input_embeddings().neftune_noise_alpha = self.neftune_noise_alpha
- pass
-
- pass
|