Fix save_pretrained_merged for full-finetuned models (#4755)
* Fix save_pretrained_merged for full-finetuned models save_pretrained_merged and push_to_hub_merged silently do nothing when the model is not a PeftModel (i.e. full finetuning without LoRA). merge_and_overwrite_lora returns None immediately for non-PeftModel, and unsloth_generic_save does not check the return value. Add a non-PeftModel branch in unsloth_generic_save that falls back to model.save_pretrained / model.push_to_hub. When save_method contains "16bit", cast weights to bfloat16 (or float16) via a state_dict copy to honor the user's intent without mutating the live model. The existing PeftModel (LoRA) code path is unchanged. * Forward create_pr and revision to tokenizer.push_to_hub The tokenizer push_to_hub call was missing create_pr and revision, which could cause the tokenizer to push to the wrong branch or bypass PR creation when the model push uses them. * Honor merged_16bit dtype contract for full-finetuned models Cast state_dict to bfloat16/float16 when save_method contains "16bit" to match the documented behavior of save_pretrained_merged. Also pass state_dict and save kwargs consistently to both save_pretrained and push_to_hub paths. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Address review feedback for PR #4755 - Simplify PeftModel isinstance check (PeftModelForCausalLM inherits from PeftModel) - Add is_main_process guard for distributed training - Forward variant to save_pretrained - Set tokenizer padding_side to "left" before saving (matches other save paths) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
77e1a9edc9
commit
5d7d882ce6
1 changed files with 73 additions and 13 deletions
|
|
@ -2777,19 +2777,79 @@ def unsloth_generic_save(
|
|||
elif save_method == "merged_4bit_forced":
|
||||
save_method = "merged_4bit"
|
||||
|
||||
merge_and_overwrite_lora(
|
||||
get_model_name,
|
||||
model = model,
|
||||
tokenizer = tokenizer,
|
||||
save_directory = save_directory,
|
||||
push_to_hub = push_to_hub,
|
||||
private = private,
|
||||
token = token,
|
||||
save_method = save_method,
|
||||
output_dtype = None,
|
||||
low_disk_space_usage = True,
|
||||
use_temp_file = False,
|
||||
)
|
||||
# Full-finetuned models (no LoRA) cannot use merge_and_overwrite_lora
|
||||
# since there are no adapters to merge. Fall back to save_pretrained.
|
||||
# This mirrors the non-PeftModel handling in save_pretrained_torchao
|
||||
# and the GGUF save path.
|
||||
_is_peft = isinstance(model, PeftModel)
|
||||
if not _is_peft:
|
||||
if not is_main_process:
|
||||
return
|
||||
|
||||
# Honor merged_16bit by casting to the target dtype if needed
|
||||
_save_kwargs = dict(
|
||||
safe_serialization = safe_serialization,
|
||||
max_shard_size = max_shard_size,
|
||||
variant = variant,
|
||||
)
|
||||
if "16bit" in save_method:
|
||||
_target_dtype = (
|
||||
torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
|
||||
)
|
||||
_save_kwargs["state_dict"] = {
|
||||
k: v.to(dtype = _target_dtype) if v.is_floating_point() else v
|
||||
for k, v in model.state_dict().items()
|
||||
}
|
||||
|
||||
if push_to_hub:
|
||||
print(f"Unsloth: Pushing full fine-tuned model to '{save_directory}' ...")
|
||||
model.push_to_hub(
|
||||
repo_id = save_directory,
|
||||
token = token,
|
||||
private = private,
|
||||
commit_message = commit_message,
|
||||
create_pr = create_pr,
|
||||
revision = revision,
|
||||
commit_description = commit_description,
|
||||
tags = tags,
|
||||
**_save_kwargs,
|
||||
)
|
||||
if tokenizer is not None:
|
||||
old_padding_side = tokenizer.padding_side
|
||||
tokenizer.padding_side = "left"
|
||||
tokenizer.push_to_hub(
|
||||
save_directory,
|
||||
token = token,
|
||||
private = private,
|
||||
commit_message = commit_message,
|
||||
create_pr = create_pr,
|
||||
revision = revision,
|
||||
)
|
||||
tokenizer.padding_side = old_padding_side
|
||||
else:
|
||||
print(f"Unsloth: Saving full fine-tuned model to '{save_directory}' ...")
|
||||
model.save_pretrained(save_directory, **_save_kwargs)
|
||||
if tokenizer is not None:
|
||||
old_padding_side = tokenizer.padding_side
|
||||
tokenizer.padding_side = "left"
|
||||
tokenizer.save_pretrained(save_directory)
|
||||
tokenizer.padding_side = old_padding_side
|
||||
|
||||
print(f"Unsloth: Model saved successfully to '{save_directory}'")
|
||||
else:
|
||||
merge_and_overwrite_lora(
|
||||
get_model_name,
|
||||
model = model,
|
||||
tokenizer = tokenizer,
|
||||
save_directory = save_directory,
|
||||
push_to_hub = push_to_hub,
|
||||
private = private,
|
||||
token = token,
|
||||
save_method = save_method,
|
||||
output_dtype = None,
|
||||
low_disk_space_usage = True,
|
||||
use_temp_file = False,
|
||||
)
|
||||
|
||||
if push_to_hub and datasets:
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue