From f35d5977d639645c85f2ed2dd270af5f011878fb Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Wed, 12 Mar 2025 01:23:34 -0700 Subject: [PATCH] Gemma 3 (#1986) * Update llama.py * GRPO optimized * Update rl.py * Update rl_replacements.py * Update rl_replacements.py * Update rl.py * Update rl.py * Update rl.py * Update rl.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Selective Log softmax * Fix GRPO bsz * Update rl.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Fix TRL * Metrics GRPO * Update rl_replacements.py * Update rl_replacements.py * No compile * Update rl.py * Remove docs * Update rl.py * Update rl.py * Update rl.py * Update rl.py * Update rl_replacements.py * Update rl.py * Update rl.py * Update rl_replacements.py * Update rl_replacements.py * llama-quantize on WINDOWS WSL error fix - edit save.py (gguf saving breaks) (#1649) * edit save.py to fix gguf saving breaks. * add check for .exe or not exe file extension for linux and windows * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update llama.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update llama.py * Update llama.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl.py * Update rl.py * Update rl_replacements.py * Update rl.py * Update rl.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * unsloth_num_chunks * Update rl.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl.py * Update rl.py * Update rl.py * Update rl.py * Update rl.py * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py (#1754) Fix typo in comment: know -> now. This was printed when running the Llama3.1_(8B)-GRPO.ipynb example notebook, so I'd expect others to run into it as well. * Optional logits * Update rl.py * Update rl.py * Update rl.py * Update rl.py * Update rl.py * Update rl.py * Update rl.py * Update rl.py * Update rl_replacements.py * Update rl.py * Update rl.py * Update rl.py * Update rl.py * fix an import error (#1767) * fix an import error * Delete .gitignore * Update loader.py * Update save.py --------- Co-authored-by: Daniel Han * SamplingParams * Convert mask to float (#1762) * [Windows Support] Add latest `xformers` wheels to pyproject.toml (#1753) * Add latest xformers * Add a couple of lines to docs * vLLMSamplingParams * Update __init__.py * default num_chunks == -1 * Versioning * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update _utils.py * Update rl_replacements.py * Update rl_replacements.py * Update pyproject.toml * Update pyproject.toml * Export Model to ollama.com (#1648) * Ollama Export Model to ollama.com Signed-off-by: Jyotin Goel * Check for model_name Signed-off-by: Jyotin Goel * subprocess use instead of requests | added check for ollama server Signed-off-by: Jyotin Goel * create_ollama_model Signed-off-by: Jyotin Goel * create_ollama_model | fix Signed-off-by: Jyotin Goel * Push to Ollama Signed-off-by: Jyotin Goel --------- Signed-off-by: Jyotin Goel * Update cross_entropy_loss.py * torch_cuda_device * Update utils.py * Update utils.py * Update utils.py * device * device * Update loader.py * Update llama.py * Update README.md * Update llama.py * Update llama.py * Update _utils.py * Update utils.py * Update utils.py * Update utils.py * Update utils.py * Update utils.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update utils.py * Update utils.py * Update utils.py * Update utils.py * __version__ * Update rl.py * Bug fixes * Bug fixes * Update llama.py * Update _utils.py * _wrap_fast_inference * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update _utils.py * SFT dataset prepare * Update pyproject.toml * Update rl_replacements.py * Update rl_replacements.py * Update rl_replacements.py * Update rl.py * Update llama.py * Update llama.py * Update utils.py * bug fix * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update llama.py * Update __init__.py * Update _utils.py * Update _utils.py * Update _utils.py * Update _utils.py * Update _utils.py * Update rl.py * Update rl.py * Update rl.py * Update _utils.py * Update __init__.py * Update _utils.py * Version * versioning * Update _utils.py * Update llama.py * Update llama.py * Bug fixes * FastModel * __doc__ * Update vision.py * Update loader.py * Update loader.py * Update loader.py * version * move use_modelscope to _utils (#1938) * move use_modelscope to _utils * Update _utils.py * Update loader.py --------- Co-authored-by: Daniel Han * Don't use revision when loading model_config and is_peft=True (#1949) * More syntax warnings (#1944) * move use_modelscope to _utils * fix * Update _utils.py * Update loader.py --------- Co-authored-by: Daniel Han * Update loader.py * Full finetuning and other fixes * UNSLOTH_ENABLE_FULL_FINETUNING * Update loader.py * Update loader.py * Update loader.py * Update vision.py * Update vision.py * full finetuning * Update loader.py * Update loader.py * Update loader.py * Update _utils.py * max_seq_length * Update rl.py * Update rl.py * Update rl.py * Update pyproject.toml * AutoModelForImageTextToText * Update mapper.py * Update pyproject.toml * Update _utils.py * Update _utils.py * Update _utils.py * Batch samples * Update loader.py * Update loader.py * Update loader.py * Update loader.py * Update _utils.py * Update loader.py * Update vision.py * Update loader.py * Update vision.py * Update vision.py * Update vision.py * Update mapper.py --------- Signed-off-by: Jyotin Goel Co-authored-by: Gennadii Manzhos <105049664+everythingisc00l@users.noreply.github.com> Co-authored-by: Seth Weidman Co-authored-by: Nino Risteski <95188570+NinoRisteski@users.noreply.github.com> Co-authored-by: Edd <68678137+Erland366@users.noreply.github.com> Co-authored-by: Ben <6579034+versipellis@users.noreply.github.com> Co-authored-by: Jyotin Goel <120490013+gjyotin305@users.noreply.github.com> Co-authored-by: Kareem <81531392+KareemMusleh@users.noreply.github.com> Co-authored-by: Wilson Wu <140025193+wiwu2390@users.noreply.github.com> --- pyproject.toml | 4 +- unsloth/__init__.py | 17 +++-- unsloth/models/_utils.py | 130 +++++++++---------------------------- unsloth/models/llama.py | 14 ++-- unsloth/models/loader.py | 129 +++++++++++++++++++++++++++++------- unsloth/models/mapper.py | 44 +++++++++++++ unsloth/models/rl.py | 32 ++++++++- unsloth/models/vision.py | 120 +++++++++++++++++++++++++++------- unsloth/tokenizer_utils.py | 2 +- 9 files changed, 329 insertions(+), 163 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5b9dc8bb57..667901e76f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ triton = [ ] huggingface = [ - "unsloth_zoo>=2025.3.8", + "unsloth_zoo>=2025.3.9", "packaging", "tyro", "transformers>=4.46.1,!=4.47.0", @@ -354,7 +354,7 @@ colab-ampere-torch220 = [ "flash-attn>=2.6.3", ] colab-new = [ - "unsloth_zoo>=2025.3.8", + "unsloth_zoo>=2025.3.9", "packaging", "tyro", "transformers>=4.46.1,!=4.47.0", diff --git a/unsloth/__init__.py b/unsloth/__init__.py index 5bbb85d520..9bcdd5cf64 100644 --- a/unsloth/__init__.py +++ b/unsloth/__init__.py @@ -198,14 +198,19 @@ pass # Check for unsloth_zoo try: unsloth_zoo_version = importlib_version("unsloth_zoo") - if Version(unsloth_zoo_version) < Version("2025.3.8"): - try: - os.system("pip install --upgrade --no-cache-dir --no-deps unsloth_zoo") - except: + if Version(unsloth_zoo_version) < Version("2025.3.9"): + print( + "Unsloth: Updating Unsloth-Zoo utilies to the latest version.\n"\ + "To disable this, set os.environ['UNSLOTH_DISABLE_AUTO_UPDATES'] = '1'" + ) + if os.environ.get("UNSLOTH_DISABLE_AUTO_UPDATES", "0") == "0": try: - os.system("pip install --upgrade --no-cache-dir --no-deps --user unsloth_zoo") + os.system("pip install --upgrade --no-cache-dir --no-deps unsloth_zoo") except: - raise ImportError("Unsloth: Please update unsloth_zoo via `pip install --upgrade --no-cache-dir --no-deps unsloth_zoo`") + try: + os.system("pip install --upgrade --no-cache-dir --no-deps --user unsloth_zoo") + except: + raise ImportError("Unsloth: Please update unsloth_zoo via `pip install --upgrade --no-cache-dir --no-deps unsloth_zoo`") import unsloth_zoo except: raise ImportError("Unsloth: Please install unsloth_zoo via `pip install unsloth_zoo`") diff --git a/unsloth/models/_utils.py b/unsloth/models/_utils.py index 03eb21f4eb..c79d702b15 100644 --- a/unsloth/models/_utils.py +++ b/unsloth/models/_utils.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2025.3.9" +__version__ = "2025.3.10" __all__ = [ "SUPPORTS_BFLOAT16", @@ -25,6 +25,7 @@ __all__ = [ "__version__", "HAS_FLASH_ATTENTION", "HAS_FLASH_ATTENTION_SOFTCAPPING", + "USE_MODELSCOPE", "platform_system", "patch_tokenizer", "get_statistics", @@ -100,6 +101,7 @@ from unsloth_zoo.gradient_checkpointing import ( from unsloth_zoo.loss_utils import ( HAS_CUT_CROSS_ENTROPY, fused_linear_cross_entropy, + _unsloth_get_batch_samples, ) from unsloth_zoo.vision_utils import ( process_vision_info, @@ -108,6 +110,9 @@ from unsloth_zoo.compiler import ( get_transformers_model_type, unsloth_compile_transformers as _unsloth_compile_transformers, ) +from unsloth_zoo.training_utils import ( + prepare_model_for_training, +) # ============================================= # Disable some warnings which can get annoying @@ -508,67 +513,16 @@ def prepare_model_for_kbit_training( use_gradient_checkpointing : Optional = True, use_reentrant : Optional[bool] = True, ) -> Any: - """ - Calculates where to place the gradient checkpoints given n_layers. - We also freeze all other layers's gradients - - Args: - model: Any LlamaModel with layers. - use_gradient_checkpointing (`bool`, *optional*): - Default enabled. Provides memory savings by not saving all activations, - but only some. - use_reentrant (`bool`, *optional*): - https://github.com/pytorch/pytorch/blob/main/torch/utils/checkpoint.py#L354 - Optimal gradient checkpointing algorithm which will be the default in - future Pytorch versions. - """ - - # Freeze all parameters except LoRA - with torch.no_grad(): - for name, param in model.named_parameters(): - if ".lora_A." in name or ".lora_B." in name or ".lora_magnitude_vector" in name: - param.requires_grad_(True) - # Also must be in float32! - if param.dtype != torch.float32: - name = name.replace("base_model", "model", 1) - layer_number = re.search(r"\.[\d]{1,}\.", name).group(0) - name = name.replace(layer_number, f"[{layer_number[1:-1]}].") - name = name.replace(".weight", "", 1) - exec(f"{name}.to(torch.float32)") - pass - else: - param.requires_grad_(False) - pass - pass - - # Gradient checkpointing! - if use_gradient_checkpointing == "unsloth": - - # Saves VRAM! - original_model = model - while hasattr(original_model, "model"): - original_model._offloaded_gradient_checkpointing = True - original_model = original_model.model - pass - original_model._offloaded_gradient_checkpointing = True - - model.gradient_checkpointing_enable() - - elif use_gradient_checkpointing == True: - model.gradient_checkpointing_enable() - pass - - # If use_reentrant = True which is the Pytorch default, we just make the input requires_grad. - if use_reentrant: - 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) - pass - - return model + return prepare_model_for_training( + model = model, + use_gradient_checkpointing = use_gradient_checkpointing, + use_reentrant = use_reentrant, + full_finetuning = False, + train_layernorms = False, + train_embedding = False, + train_lm_head = False, + float32_mixed_precision = True, + ) pass # ============================================= @@ -999,44 +953,6 @@ def test_mask_creation(): pass -def _unsloth_get_batch_samples(self, epoch_iterator, num_batches): - batch_samples = [] - num_items_in_batch = None - - # Check if model allows **kwargs - model = self.model - f = model.base_model.model.forward if hasattr(model, "base_model") else model.forward - has_kwargs = tuple(inspect.signature(f).parameters.values())[-1].kind == inspect._VAR_KEYWORD - - # Iterate to find all batches - for _ in range(num_batches): - try: - batch_samples += [next(epoch_iterator)] - except StopIteration: - break - pass - - # Get num_items_in_batch - if has_kwargs and len(batch_samples) > 0 and "labels" in batch_samples[0]: - try: - num_items_in_batch = sum( - [(x["labels"][..., 1:] != -100).sum() for x in batch_samples] - ) - - if self.args.average_tokens_across_devices: - num_items_in_batch = self.accelerator.gather(num_items_in_batch).sum().item() - - if torch.is_tensor(num_items_in_batch): - num_items_in_batch = num_items_in_batch.item() - - except Exception as exception: - logger.warning_once(exception) - pass - - return batch_samples, num_items_in_batch -pass - - def _unsloth_pre_compute_loss(self, model, inputs, *args, **kwargs): num_items_in_batch = None @@ -1053,7 +969,12 @@ def _unsloth_pre_compute_loss(self, model, inputs, *args, **kwargs): # Get gradient accumulation steps if possible if num_items_in_batch is None and \ getattr(getattr(self, "args", self), "gradient_accumulation_steps", 1) != 1: - name = (model.base_model.model if hasattr(model, "base_model") else model).__class__.__name__ + + inner_model = model + if hasattr(inner_model, "base_model"): inner_model = inner_model. base_model + if hasattr(inner_model, "model"): inner_model = inner_model.model + name = inner_model.__class__.__name__ + logger.warning_once( f"Unsloth: Not an error, but {name} does not accept `num_items_in_batch`.\n"\ "Using gradient accumulation will be very slightly less accurate.\n"\ @@ -1271,3 +1192,10 @@ for j, function in enumerate(functions): try: exec(f"EMPTY_LOGITS.{function} = raise_{j}", globals(), locals()) except: continue pass + +USE_MODELSCOPE = os.environ.get("UNSLOTH_USE_MODELSCOPE", "0") == "1" +if USE_MODELSCOPE: + if importlib.util.find_spec("modelscope") is None: + raise ImportError(f'You are using the modelscope hub, please install modelscope by `pip install modelscope -U`') + pass +pass diff --git a/unsloth/models/llama.py b/unsloth/models/llama.py index 3504037b66..7ae6e92d11 100644 --- a/unsloth/models/llama.py +++ b/unsloth/models/llama.py @@ -1913,12 +1913,12 @@ class FastLlamaModel: # Save max_seq_length model.max_seq_length = max_seq_length - internal_model = model - while hasattr(internal_model, "model"): - internal_model.max_seq_length = max_seq_length - internal_model = internal_model.model + m = model + while hasattr(m, "model"): + m.max_seq_length = max_seq_length + m = m.model pass - internal_model.max_seq_length = max_seq_length + m.max_seq_length = max_seq_length # We check the tokenizer first for errors if fix_tokenizer: @@ -2016,6 +2016,10 @@ class FastLlamaModel: temporary_location = "_unsloth_temporary_saved_buffers", **kwargs, ): + if os.environ.get("UNSLOTH_ENABLE_FULL_FINETUNING", "0") == "1": + print("Unsloth: Full finetuning is enabled, so .get_peft_model has no effect") + return model + pass transformers_set_seed(random_state) if use_gradient_checkpointing == "unsloth": diff --git a/unsloth/models/loader.py b/unsloth/models/loader.py index 800c016cc8..92a166f69a 100644 --- a/unsloth/models/loader.py +++ b/unsloth/models/loader.py @@ -12,7 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ._utils import is_bfloat16_supported, HAS_FLASH_ATTENTION, HAS_FLASH_ATTENTION_SOFTCAPPING +from ._utils import ( + is_bfloat16_supported, + HAS_FLASH_ATTENTION, + HAS_FLASH_ATTENTION_SOFTCAPPING, + USE_MODELSCOPE, +) from .granite import FastGraniteModel from .llama import FastLlamaModel, logger from .mistral import FastMistralModel @@ -36,14 +41,6 @@ pass from huggingface_hub import HfFileSystem import importlib.util -# [TODO] Move USE_MODELSCOPE to utils -USE_MODELSCOPE = os.environ.get("UNSLOTH_USE_MODELSCOPE", "0") == "1" -if USE_MODELSCOPE: - if importlib.util.find_spec("modelscope") is None: - raise ImportError(f'You are using the modelscope hub, please install modelscope by `pip install modelscope -U`') - pass -pass - # https://github.com/huggingface/transformers/pull/26037 allows 4 bit loading! from unsloth_zoo.utils import Version, _get_dtype transformers_version = Version(transformers_version) @@ -76,6 +73,8 @@ class FastLanguageModel(FastLlamaModel): max_seq_length = None, dtype = None, load_in_4bit = True, + load_in_8bit = False, + full_finetuning = False, token = None, device_map = "sequential", rope_scaling = None, @@ -94,6 +93,29 @@ class FastLanguageModel(FastLlamaModel): disable_log_stats = True, *args, **kwargs, ): + if load_in_8bit or full_finetuning: + return FastModel.from_pretrained( + model_name = model_name, + max_seq_length = max_seq_length, # [TODO] No effect + dtype = dtype, + load_in_4bit = load_in_4bit, + load_in_8bit = load_in_8bit, + full_finetuning = full_finetuning, + token = token, + device_map = device_map, + rope_scaling = rope_scaling, # [TODO] No effect + fix_tokenizer = fix_tokenizer, # [TODO] No effect + trust_remote_code = trust_remote_code, + use_gradient_checkpointing = use_gradient_checkpointing, + resize_model_vocab = resize_model_vocab, # [TODO] No effect + revision = revision, + return_logits = False, # Return logits + fullgraph = True, # No graph breaks + use_exact_model_name = use_exact_model_name, + *args, **kwargs, + ) + pass + if token is None: token = get_token() assert (dtype is None or dtype == torch.float16 or dtype == torch.bfloat16) @@ -153,7 +175,7 @@ class FastLanguageModel(FastLlamaModel): # Old transformers versions check both_exist = (is_model and is_peft) and not SUPPORTS_LLAMA32 - + # New transformers need to check manually. if SUPPORTS_LLAMA32: # Check if folder exists locally @@ -202,7 +224,6 @@ class FastLanguageModel(FastLlamaModel): model_config = AutoConfig.from_pretrained( model_name, token = token, - revision = revision, trust_remote_code = trust_remote_code, ) pass @@ -265,15 +286,32 @@ class FastLanguageModel(FastLlamaModel): dispatch_model = FastGemma2Model elif model_type == "qwen2": dispatch_model = FastQwen2Model - elif model_type == "cohere": - dispatch_model = FastCohereModel - elif model_type == "granite": - dispatch_model = FastGraniteModel + # Temporary disable optimized Cohere until errors match + # elif model_type == "cohere": + # dispatch_model = FastCohereModel + # Temporary disable optimized Granite until errors match + # elif model_type == "granite": + # dispatch_model = FastGraniteModel else: - raise NotImplementedError( - f"Unsloth: {model_name} not supported yet!\n"\ - "Maybe you're doing vision finetuning? Please use FastVisionModel instead!\n"\ - "Otherwise, make an issue to https://github.com/unslothai/unsloth!", + return FastModel.from_pretrained( + model_name = model_name, + max_seq_length = max_seq_length, # [TODO] No effect + dtype = dtype, + load_in_4bit = load_in_4bit, + load_in_8bit = load_in_8bit, + full_finetuning = full_finetuning, + token = token, + device_map = device_map, + rope_scaling = rope_scaling, # [TODO] No effect + fix_tokenizer = fix_tokenizer, # [TODO] No effect + trust_remote_code = trust_remote_code, + use_gradient_checkpointing = use_gradient_checkpointing, + resize_model_vocab = resize_model_vocab, # [TODO] No effect + revision = revision, + return_logits = False, # Return logits + fullgraph = True, # No graph breaks + use_exact_model_name = use_exact_model_name, + *args, **kwargs, ) pass @@ -288,6 +326,11 @@ class FastLanguageModel(FastLlamaModel): pass if fast_inference: + import platform + if platform.system().lower() == 'windows': + print("Unsloth: vLLM does not work in Windows! Will use Unsloth inference!") + fast_inference = False + pass from unsloth_zoo.vllm_utils import ( patch_vllm, vllm_dynamic_quant_supported, @@ -385,9 +428,15 @@ from ..kernels import ( ) from .vision import FastBaseModel from transformers import ( - AutoModelForVision2Seq, AutoModelForCausalLM, ) +try: + from transformers import AutoModelForImageTextToText + AutoModelForVision2Seq = AutoModelForImageTextToText +except: + from transformers import AutoModelForVision2Seq +pass + class FastModel(FastBaseModel): @staticmethod @@ -396,6 +445,8 @@ class FastModel(FastBaseModel): max_seq_length = None, # [TODO] No effect dtype = None, load_in_4bit = True, + load_in_8bit = False, + full_finetuning = False, token = None, device_map = "sequential", rope_scaling = None, # [TODO] No effect @@ -417,10 +468,40 @@ class FastModel(FastBaseModel): if use_gradient_checkpointing == "unsloth": patch_unsloth_smart_gradient_checkpointing(dtype = dtype) + if full_finetuning and (load_in_4bit or load_in_8bit): + print("Unsloth: You selected full finetuning support, but 4bit / 8bit is enabled - disabling LoRA / QLoRA.") + load_in_4bit = False + load_in_8bit = False + pass + + if load_in_4bit and load_in_8bit: + raise RuntimeError( + "Unsloth: Can only load in 4bit or 8bit, not both!\n"\ + "Also, we by default set `load_in_4bit = True`.\n"\ + "If you want 8bit finetuning, set both `load_in_4bit = False` and `load_in_8bit = True`" + ) + if load_in_4bit: pass + elif load_in_8bit: pass + elif not load_in_4bit and not load_in_8bit and not full_finetuning: + print("Unsloth: LoRA, QLoRA and full finetuning all not selected. Switching to QLoRA.") + load_in_4bit = True + pass + old_model_name = model_name if not use_exact_model_name: model_name = get_model_name(model_name, load_in_4bit) + # Check versions + LATEST = '\nPlease use transformers via `pip install --no-deps git+https://github.com/huggingface/transformers.git`' + NIGHTLY = '\nPlease use nightly transformers via pip install --upgrade "transformers>=4.49.0"`' + if "pixtral" in model_name.lower() and transformers_version < Version("4.49.0"): + raise RuntimeError("Unsloth: Pixtral only works on transformers >= 4.49.0." + LATEST) + elif "qwen2.5" in model_name.lower() and transformers_version < Version("4.49.0"): + raise RuntimeError("Unsloth: Qwen 2.5 only works on transformers >= 4.49.0." + LATEST) + elif "aya-vision" in model_name.lower() and transformers_version < Version("4.50.0.dev0"): + raise RuntimeError("Unsloth: Aya Vision only works on transformers >= 4.50.0." + NIGHTLY) + pass + if USE_MODELSCOPE and not os.path.exists(model_name): from modelscope import snapshot_download model_name = snapshot_download(model_name) @@ -510,7 +591,6 @@ class FastModel(FastBaseModel): model_config = AutoConfig.from_pretrained( model_name, token = token, - revision = revision, trust_remote_code = trust_remote_code, ) pass @@ -565,7 +645,7 @@ class FastModel(FastBaseModel): pass # Check if VLM - is_vlm = (x.endswith("ForConditionalGeneration") for x in model_config.architectures) + is_vlm = any(x.endswith("ForConditionalGeneration") for x in model_config.architectures) is_vlm = is_vlm or hasattr(model_config, "vision_config") auto_model = AutoModelForVision2Seq if is_vlm else AutoModelForCausalLM @@ -574,6 +654,8 @@ class FastModel(FastBaseModel): max_seq_length = max_seq_length, dtype = _get_dtype(dtype), load_in_4bit = load_in_4bit, + load_in_8bit = load_in_8bit, + full_finetuning = full_finetuning, token = token, device_map = device_map, trust_remote_code = trust_remote_code, @@ -581,6 +663,7 @@ class FastModel(FastBaseModel): model_types = model_types, tokenizer_name = tokenizer_name, auto_model = auto_model, + use_gradient_checkpointing = use_gradient_checkpointing, *args, **kwargs, ) @@ -628,7 +711,7 @@ class FastModel(FastBaseModel): trust_remote_code = trust_remote_code, ) # Patch it as well! - model = FastBaseModel.patch_peft_model(model, use_gradient_checkpointing) + model = FastBaseModel.post_patch_model(model, use_gradient_checkpointing) pass return model, tokenizer pass diff --git a/unsloth/models/mapper.py b/unsloth/models/mapper.py index a2e609f203..47dbb325ef 100644 --- a/unsloth/models/mapper.py +++ b/unsloth/models/mapper.py @@ -492,6 +492,18 @@ __INT_TO_FLOAT_MAPPER = \ "unsloth/Qwen2-VL-72B-Instruct", "Qwen/Qwen2-VL-72B-Instruct", ), + "unsloth/Qwen2-VL-2B-bnb-4bit" : ( + "unsloth/Qwen2-VL-2B", + "Qwen/Qwen2-VL-2B", + ), + "unsloth/Qwen2-VL-7B-bnb-4bit" : ( + "unsloth/Qwen2-VL-7B", + "Qwen/Qwen2-VL-7B", + ), + "unsloth/Qwen2-VL-72B-bnb-4bit" : ( + "unsloth/Qwen2-VL-72B", + "Qwen/Qwen2-VL-72B", + ), "unsloth/Llama-3.2-11B-Vision-Instruct-unsloth-bnb-4bit" : ( "unsloth/Llama-3.2-11B-Vision-Instruct", "meta-llama/Llama-3.2-11B-Vision-Instruct", @@ -626,6 +638,38 @@ __INT_TO_FLOAT_MAPPER = \ "Qwen/QwQ-32B", "unsloth/QwQ-32B-bnb-4bit", ), + "unsloth/gemma-3-1b-it" : ( + "unsloth/gemma-3-1b-it", + "google/gemma-3-1b-it", + ), + "unsloth/gemma-3-4b-it" : ( + "unsloth/gemma-3-4b-it", + "google/gemma-3-4b-it", + ), + "unsloth/gemma-3-12b-it" : ( + "unsloth/gemma-3-12b-it", + "google/gemma-3-12b-it", + ), + "unsloth/gemma-3-27b-it" : ( + "unsloth/gemma-3-27b-it", + "google/gemma-3-27b-it", + ), + "unsloth/gemma-3-1b-pt" : ( + "unsloth/gemma-3-1b-pt", + "google/gemma-3-1b-pt", + ), + "unsloth/gemma-3-4b-pt" : ( + "unsloth/gemma-3-4b-pt", + "google/gemma-3-4b-pt", + ), + "unsloth/gemma-3-12b-pt" : ( + "unsloth/gemma-3-12b-pt", + "google/gemma-3-12b-pt", + ), + "unsloth/gemma-3-27b-pt" : ( + "unsloth/gemma-3-27b-pt", + "google/gemma-3-27b-pt", + ), } INT_TO_FLOAT_MAPPER = {} diff --git a/unsloth/models/rl.py b/unsloth/models/rl.py index cf9c16514e..86a174ebfe 100644 --- a/unsloth/models/rl.py +++ b/unsloth/models/rl.py @@ -106,6 +106,8 @@ import torch import numpy as np from contextlib import nullcontext from torch.nn import functional as F +from transformers import DataCollatorForSeq2Seq, DataCollatorForLanguageModeling + torch_compile_options = {{ "epilogue_fusion" : True, "max_autotune" : False, @@ -234,6 +236,7 @@ def _patch_trl_rl_trainers(trainer_file = "grpo_trainer"): mixed_precision = \ "use_bf16 = getattr(args, 'bf16', False)\n"\ "use_fp16 = getattr(args, 'fp16', False)\n"\ + "mixed_precision_dtype = os.environ.get('UNSLOTH_MIXED_PRECISION', 'float32')\n"\ "dtype = getattr(model.config, 'torch_dtype', None)\n"\ "if dtype is None: dtype = model.get_input_embeddings().dtype\n"\ "from unsloth_zoo.utils import _get_dtype\n"\ @@ -241,10 +244,14 @@ def _patch_trl_rl_trainers(trainer_file = "grpo_trainer"): "float16 = dtype == torch.float16\n"\ "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`')\n"\ "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`')\n"\ - "if not use_bf16 and not use_fp16:\n"\ + "if (not use_bf16 and not use_fp16) and mixed_precision_dtype == 'float32':\n"\ " args.fp16 = float16\n"\ " args.bf16 = not float16\n"\ " os.environ['ACCELERATE_MIXED_PRECISION'] = 'fp16' if float16 else 'bf16'\n" + "elif mixed_precision_dtype == 'bfloat16':\n"\ + " args.fp16 = False\n"\ + " args.bf16 = False\n"\ + " os.environ['ACCELERATE_MIXED_PRECISION'] = 'no'\n" extra_args += mixed_precision pass @@ -280,7 +287,12 @@ def _patch_trl_rl_trainers(trainer_file = "grpo_trainer"): "bf16_full_eval = getattr(args, 'bf16_full_eval', False)\n"\ "if args.fp16 and bf16_full_eval: args.bf16_full_eval = False; args.fp16_full_eval = True\n"\ "if args.bf16 and fp16_full_eval: args.bf16_full_eval = True; args.fp16_full_eval = False\n"\ - "if not bf16_full_eval and not fp16_full_eval: args.bf16_full_eval = args.bf16; args.fp16_full_eval = args.fp16\n" + "if os.environ.get('UNSLOTH_MIXED_PRECISION', 'float32') == 'bfloat16':\n"\ + " args.bf16_full_eval = True\n"\ + " args.fp16_full_eval = False\n"\ + "elif not bf16_full_eval and not fp16_full_eval:\n"\ + " args.bf16_full_eval = args.bf16\n"\ + " args.fp16_full_eval = args.fp16\n" extra_args += eval_changes pass @@ -327,6 +339,20 @@ def _patch_trl_rl_trainers(trainer_file = "grpo_trainer"): extra_args += training_check pass + # Check data collator if it's correct! + if "data_collator" in call_args and "train_dataset" in call_args: + data_collator_check = \ + "if isinstance(data_collator, DataCollatorForSeq2Seq) and 'labels' not in train_dataset.column_names:\n"\ + " print('Unsloth: Changing data collator to `DataCollatorForLanguageModeling` since `labels` not found.')\n"\ + " data_collator = DataCollatorForLanguageModeling("\ + "tokenizer = processing_class if 'processing_class' in locals() else tokenizer, mlm = False)\n"\ + "elif isinstance(data_collator, DataCollatorForLanguageModeling) and 'labels' in train_dataset.column_names:\n"\ + " print('Unsloth: Changing data collator to `DataCollatorForSeq2Seq` since `labels` found.')\n"\ + " data_collator = DataCollatorForSeq2Seq("\ + "tokenizer = processing_class if 'processing_class' in locals() else tokenizer)\n" + extra_args += data_collator_check + pass + # Check NEFTune if "model" in call_args: neftune_check = \ @@ -536,7 +562,7 @@ def patch_functions(RLTrainer, trainer_file, RLTrainer_name, all_imports, import if "args.use_vllm" in init and "model" in init and "args" in init: # .*? matches first match. .+? matches final match. replacer = re.findall( - "def __init__\(.*?\).*?\:\n", + r"def __init__\(.*?\).*?\:\n", init, flags = re.MULTILINE | re.DOTALL, ) diff --git a/unsloth/models/vision.py b/unsloth/models/vision.py index ff07ef6917..fa5547ec55 100644 --- a/unsloth/models/vision.py +++ b/unsloth/models/vision.py @@ -15,17 +15,22 @@ import torch from transformers import ( BitsAndBytesConfig, - AutoModelForVision2Seq, AutoProcessor, AutoTokenizer, AutoModelForCausalLM, ) +try: + from transformers import AutoModelForImageTextToText + AutoModelForVision2Seq = AutoModelForImageTextToText +except: + from transformers import AutoModelForVision2Seq +pass from .llama import * from ..kernels import ( post_patch_loss_function, ) from ._utils import __version__ -from peft import LoraConfig, TaskType, get_peft_model +from peft import LoraConfig, TaskType, get_peft_model as _get_peft_model from transformers import set_seed as transformers_set_seed from unsloth_zoo.peft_utils import ( get_peft_regex, @@ -35,6 +40,7 @@ from unsloth_zoo.peft_utils import ( from triton import __version__ as triton_version from unsloth_zoo.utils import _get_dtype from unsloth_zoo.patching_utils import patch_model_and_tokenizer +from unsloth_zoo.training_utils import prepare_model_for_training import types import functools @@ -52,14 +58,21 @@ def unsloth_base_fast_generate( dtype = _get_dtype(self.config.torch_dtype) # Check if VLM - is_vlm = (x.endswith("ForConditionalGeneration") for x in self.config.architectures) + is_vlm = ( + x.endswith(("ForConditionalGeneration", "ForVisionText2Text")) + for x in self.config.architectures + ) is_vlm = is_vlm or hasattr(self.config, "vision_config") # Remove token_type_ids kwargs.pop("token_type_ids", None) # VLMs do not allow logits_to_keep - if not is_vlm: kwargs["logits_to_keep"] = 1 + if not is_vlm: + kwargs["logits_to_keep"] = 1 + else: + kwargs.pop("logits_to_keep", None) + kwargs.pop("num_logits_to_keep", None) # Check pad_token model_eos_token_id = getattr(self.config, "eos_token_id", None) @@ -90,12 +103,15 @@ class FastBaseModel: max_seq_length = None, dtype = None, load_in_4bit = True, + load_in_8bit = False, + full_finetuning = False, token = None, device_map = "sequential", trust_remote_code = False, model_types = None, tokenizer_name = None, auto_model = AutoModelForVision2Seq, + use_gradient_checkpointing = "unsloth", **kwargs, ): if trust_remote_code: @@ -141,6 +157,14 @@ class FastBaseModel: assert(dtype == torch.float16 or dtype == torch.bfloat16 or dtype == torch.float32) bnb_config = None + if full_finetuning and (load_in_4bit or load_in_8bit): + print("Unsloth: You selected full finetuning support, but 4bit / 8bit is enabled - disabling LoRA / QLoRA.") + load_in_4bit = False + load_in_8bit = False + pass + + if load_in_4bit and load_in_8bit: + raise RuntimeError("Unsloth: Can only load in 4bit or 8bit, not both!") if load_in_4bit: bnb_config = BitsAndBytesConfig( load_in_4bit = True, @@ -149,6 +173,31 @@ class FastBaseModel: bnb_4bit_compute_dtype = dtype, llm_int8_skip_modules = SKIP_QUANTIZATION_MODULES, ) + elif load_in_8bit: + bnb_config = BitsAndBytesConfig( + load_in_8bit = True, + llm_int8_skip_modules = SKIP_QUANTIZATION_MODULES, + ) + elif not load_in_4bit and not load_in_8bit and not full_finetuning: + print("Unsloth: LoRA, QLoRA and full finetuning all not selected. Switching to QLoRA.") + load_in_4bit = True + bnb_config = BitsAndBytesConfig( + load_in_4bit = True, + bnb_4bit_use_double_quant = True, + bnb_4bit_quant_type = "nf4", + bnb_4bit_compute_dtype = dtype, + llm_int8_skip_modules = SKIP_QUANTIZATION_MODULES, + ) + pass + + if full_finetuning: + os.environ["UNSLOTH_ENABLE_FULL_FINETUNING"] = "1" + if dtype == torch.bfloat16: + print("Unsloth: Using bfloat16 full finetuning which cuts memory usage by 50%.") + else: + print("Unsloth: Float16 full finetuning uses more memory since we upcast weights to float32.") + else: + os.environ["UNSLOTH_ENABLE_FULL_FINETUNING"] = "0" pass kwargs.pop("attn_implementation", None); # No need since we auto call it @@ -204,23 +253,37 @@ class FastBaseModel: # Save tokenizer for inference purposes tokenizer.padding_side = "left" # Force inference - tokenizer.tokenizer.padding_side = "left" # Force inference + if hasattr(tokenizer, "tokenizer"): + tokenizer.tokenizer.padding_side = "left" # Force inference m = model while hasattr(m, "model"): + m.max_seq_length = max_seq_length m._saved_temp_tokenizer = tokenizer # Also set is_loaded_in_8bit to disable incorrect DDP - m.is_loaded_in_8bit = True + m.is_loaded_in_8bit = True if not full_finetuning else False m = m.model pass + m.max_seq_length = max_seq_length m._saved_temp_tokenizer = tokenizer # Also set is_loaded_in_8bit to disable incorrect DDP - m.is_loaded_in_8bit = True + m.is_loaded_in_8bit = True if not full_finetuning else False # Patch generate if model.generate.__name__ != "unsloth_base_fast_generate": model._old_generate = model.generate unsloth_base_fast_generate.__doc__ = model._old_generate.__doc__ model.generate = types.MethodType(unsloth_base_fast_generate, model) + + # Post patches + model = FastBaseModel.post_patch_model( + model, + use_gradient_checkpointing = use_gradient_checkpointing, + ) + # Clear deleted GPU items + for _ in range(3): + gc.collect() + torch.cuda.empty_cache() + pass return model, tokenizer pass @@ -249,6 +312,10 @@ class FastBaseModel: temporary_location = "_unsloth_temporary_saved_buffers", **kwargs, ): + if os.environ.get("UNSLOTH_ENABLE_FULL_FINETUNING", "0") == "1": + print("Unsloth: Full finetuning is enabled, so .get_peft_model has no effect") + return model + pass transformers_set_seed(random_state) if type(r) is not int: @@ -282,7 +349,7 @@ class FastBaseModel: gc.collect() torch.cuda.empty_cache() pass - + max_seq_length = model.max_seq_length lora_config = LoraConfig( r = r, lora_alpha = lora_alpha, @@ -295,11 +362,12 @@ class FastBaseModel: model, use_gradient_checkpointing = use_gradient_checkpointing, ) - model = get_peft_model(model, lora_config) + model = _get_peft_model(model, lora_config) # Enable gradients on modules which are trainable requires_grad_for_gradient_checkpointing(model) - model = FastBaseModel.patch_peft_model(model, use_gradient_checkpointing) + model = FastBaseModel.post_patch_model(model, use_gradient_checkpointing) + model.max_seq_length = max_seq_length # Clear deleted GPU items for _ in range(3): @@ -316,20 +384,26 @@ class FastBaseModel: @staticmethod - def patch_peft_model( + def post_patch_model( model, use_gradient_checkpointing = True, ): - if not isinstance(model, PeftModelForCausalLM): - raise TypeError( - "Unsloth: Your model needs to call `.get_peft_model` first!" - ) - pass + full_finetuning = os.environ.get("UNSLOTH_ENABLE_FULL_FINETUNING", "0") == "1" - model = prepare_model_for_kbit_training( + float32_mixed_precision = True + if _get_dtype(model.config.torch_dtype) == torch.bfloat16: + # Use bfloat16 precision for full finetuning + float32_mixed_precision = False + + model = prepare_model_for_training( model, use_gradient_checkpointing = use_gradient_checkpointing, - use_reentrant = True, + use_reentrant = True, + full_finetuning = full_finetuning, + train_layernorms = full_finetuning, + train_embedding = full_finetuning, + train_lm_head = full_finetuning, + float32_mixed_precision = float32_mixed_precision, ) from transformers.trainer import Trainer @@ -347,17 +421,19 @@ class FastBaseModel: m = model while hasattr(m, "model"): if hasattr(m, "_saved_temp_tokenizer"): - m._saved_temp_tokenizer.tokenizer.padding_side = "right" + if hasattr(m._saved_temp_tokenizer, "tokenizer"): + m._saved_temp_tokenizer.tokenizer.padding_side = "right" pass # Also set is_loaded_in_8bit to disable incorrect DDP - m.is_loaded_in_8bit = True + m.is_loaded_in_8bit = True if not full_finetuning else False m = m.model pass if hasattr(m, "_saved_temp_tokenizer"): - m._saved_temp_tokenizer.tokenizer.padding_side = "right" + if hasattr(m._saved_temp_tokenizer, "tokenizer"): + m._saved_temp_tokenizer.tokenizer.padding_side = "right" pass # Also set is_loaded_in_8bit to disable incorrect DDP - m.is_loaded_in_8bit = True + m.is_loaded_in_8bit = True if not full_finetuning else False # Clear deleted GPU items for _ in range(3): diff --git a/unsloth/tokenizer_utils.py b/unsloth/tokenizer_utils.py index 91bb0202ff..26669127d7 100644 --- a/unsloth/tokenizer_utils.py +++ b/unsloth/tokenizer_utils.py @@ -945,7 +945,7 @@ def patch_sft_trainer_tokenizer(): if replacer is None: # .*? matches first match. .+? matches final match. replacer = re.findall( - f"def {function_name}\(.*?\).*?\:\n", + f"def {function_name}" + r"\(.*?\).*?\:\n", function, flags = re.MULTILINE | re.DOTALL, )