Skip to content

Respect user quantization_config#3835

Merged
danielhanchen merged 3 commits into
mainfrom
quant-config-respect
Jan 4, 2026
Merged

Respect user quantization_config#3835
danielhanchen merged 3 commits into
mainfrom
quant-config-respect

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Summary

  • Respect user-provided quantization_config in FastLanguageModel and FastModel.
  • Align dtype with bnb_4bit_compute_dtype when dtype is not set.
  • Avoid overriding quantization_config in vision loading paths when a user config is provided.

Motivation

User-supplied BitsAndBytesConfig was being overwritten with NF4 defaults, which caused unexpected quantization and dtype mismatches. This change preserves explicit user intent without changing defaults.

Testing

  • Local smoke run of sentence-transformer training script with the fix applied.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @danielhanchen, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an issue where user-defined quantization configurations were being inadvertently overwritten by default settings during model loading. The changes ensure that explicit user intent regarding quantization (e.g., BitsAndBytesConfig) and data types is fully respected across FastLanguageModel, FastModel, and vision model loading paths, preventing unexpected behavior and improving configuration reliability.

Highlights

  • User-provided Quantization Config: The system now prioritizes and respects 'quantization_config' objects supplied by the user, preventing them from being overwritten by default settings in 'FastLanguageModel' and 'FastModel' loading processes.
  • Dtype Alignment: The 'dtype' parameter will now automatically align with the 'bnb_4bit_compute_dtype' specified within a user's 'quantization_config' if 'dtype' is not explicitly set, ensuring consistency.
  • Vision Model Loading: Default 'quantization_config' settings will no longer override a user-provided 'quantization_config' during the loading of vision models, preserving explicit user intent.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request does a good job of respecting user-provided quantization_config, which is a great improvement for usability. The changes in FastLanguageModel, FastModel, and the vision loading paths are all aligned with this goal. I've identified a critical bug where using a dictionary for quantization_config would cause a crash, and I've provided suggestions to fix it. I also noted some code duplication that could be refactored to improve maintainability. Overall, these are valuable changes.

Comment thread unsloth/models/loader.py Outdated
Comment on lines +157 to +162
if getattr(quantization_config, "load_in_4bit", False):
load_in_4bit = True
load_in_8bit = False
if getattr(quantization_config, "load_in_8bit", False):
load_in_8bit = True
load_in_4bit = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

getattr will raise an AttributeError if quantization_config is a dictionary, which is a valid way to pass this configuration. This will cause a crash. The logic should handle both dict and object types, similar to how bnb_4bit_compute_dtype is handled later in the code.

Suggested change
if getattr(quantization_config, "load_in_4bit", False):
load_in_4bit = True
load_in_8bit = False
if getattr(quantization_config, "load_in_8bit", False):
load_in_8bit = True
load_in_4bit = False
if isinstance(quantization_config, dict):
if quantization_config.get("load_in_4bit", False):
load_in_4bit = True
load_in_8bit = False
if quantization_config.get("load_in_8bit", False):
load_in_8bit = True
load_in_4bit = False
else:
if getattr(quantization_config, "load_in_4bit", False):
load_in_4bit = True
load_in_8bit = False
if getattr(quantization_config, "load_in_8bit", False):
load_in_8bit = True
load_in_4bit = False

Comment thread unsloth/models/loader.py Outdated
Comment on lines +737 to +742
if getattr(quantization_config, "load_in_4bit", False):
load_in_4bit = True
load_in_8bit = False
if getattr(quantization_config, "load_in_8bit", False):
load_in_8bit = True
load_in_4bit = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Similar to the FastLanguageModel, getattr will raise an AttributeError if quantization_config is a dictionary. This will cause a crash. The logic should handle both dict and object types to prevent this.

Suggested change
if getattr(quantization_config, "load_in_4bit", False):
load_in_4bit = True
load_in_8bit = False
if getattr(quantization_config, "load_in_8bit", False):
load_in_8bit = True
load_in_4bit = False
if isinstance(quantization_config, dict):
if quantization_config.get("load_in_4bit", False):
load_in_4bit = True
load_in_8bit = False
if quantization_config.get("load_in_8bit", False):
load_in_8bit = True
load_in_4bit = False
else:
if getattr(quantization_config, "load_in_4bit", False):
load_in_4bit = True
load_in_8bit = False
if getattr(quantization_config, "load_in_8bit", False):
load_in_8bit = True
load_in_4bit = False

Comment thread unsloth/models/loader.py
Comment on lines +173 to +188
if dtype is None and quantization_config is not None:
bnb_compute_dtype = None
if isinstance(quantization_config, dict):
if quantization_config.get("load_in_4bit", False):
bnb_compute_dtype = quantization_config.get(
"bnb_4bit_compute_dtype", None
)
else:
if getattr(quantization_config, "load_in_4bit", False):
bnb_compute_dtype = getattr(
quantization_config, "bnb_4bit_compute_dtype", None
)
if isinstance(bnb_compute_dtype, str):
bnb_compute_dtype = getattr(torch, bnb_compute_dtype, None)
if isinstance(bnb_compute_dtype, torch.dtype):
dtype = bnb_compute_dtype

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block of code to determine dtype from quantization_config is duplicated in FastModel.from_pretrained (lines 757-772). To improve maintainability and reduce redundancy, consider extracting this logic into a shared helper function. This will make the code easier to maintain and prevent potential inconsistencies in the future.

Comment thread unsloth/models/loader.py
Comment on lines +757 to +772
if dtype is None and quantization_config is not None:
bnb_compute_dtype = None
if isinstance(quantization_config, dict):
if quantization_config.get("load_in_4bit", False):
bnb_compute_dtype = quantization_config.get(
"bnb_4bit_compute_dtype", None
)
else:
if getattr(quantization_config, "load_in_4bit", False):
bnb_compute_dtype = getattr(
quantization_config, "bnb_4bit_compute_dtype", None
)
if isinstance(bnb_compute_dtype, str):
bnb_compute_dtype = getattr(torch, bnb_compute_dtype, None)
if isinstance(bnb_compute_dtype, torch.dtype):
dtype = bnb_compute_dtype

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block of code to determine dtype from quantization_config is duplicated from FastLanguageModel.from_pretrained (lines 173-188). To improve maintainability and reduce redundancy, consider extracting this logic into a shared helper function.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eddc4a8754

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread unsloth/models/loader.py
Comment on lines 579 to +582
model_name = model_name,
max_seq_length = max_seq_length,
dtype = _get_dtype(dtype),
load_in_4bit = load_in_4bit,
load_in_4bit = load_in_4bit_kwargs,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep 4bit flag when forwarding quantization_config to vLLM

When a user provides a quantization_config, this call now forwards load_in_4bit_kwargs (set to False earlier whenever quantization_config is present) into the model loader. In the fast_inference path, FastLlamaModel uses the load_in_4bit flag to decide use_bitsandbytes for vLLM (unsloth/models/llama.py:2340-2351), and the quantization_config itself is ignored there. With fast_inference=True and a 4bit quantization_config, the model will now be loaded unquantized, leading to unexpected full-precision memory use or OOM. The 4bit flag should stay True for vLLM when the user requested 4bit quantization.

Useful? React with 👍 / 👎.

@danielhanchen

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request successfully addresses the issue of user-provided quantization_config being overridden by default settings. The changes ensure that the user's explicit configuration is respected across different model loading paths, including for vision models. This is a good improvement for user experience and predictability.

My main feedback is regarding significant code duplication in unsloth/models/loader.py. The logic for processing quantization_config and aligning dtype is repeated in FastLanguageModel.from_pretrained and FastModel.from_pretrained. This should be refactored into a shared helper function to improve code maintainability and reduce redundancy.

Comment thread unsloth/models/loader.py
Comment on lines +740 to +778
# Respect user-provided quantization_config (e.g. BitsAndBytesConfig)
quantization_config = kwargs.get("quantization_config", None)
if quantization_config is not None:
if isinstance(quantization_config, dict):
q_load_in_4bit = quantization_config.get("load_in_4bit", False)
q_load_in_8bit = quantization_config.get("load_in_8bit", False)
else:
q_load_in_4bit = getattr(quantization_config, "load_in_4bit", False)
q_load_in_8bit = getattr(quantization_config, "load_in_8bit", False)
if q_load_in_4bit:
load_in_4bit = True
load_in_8bit = False
if q_load_in_8bit:
load_in_8bit = True
load_in_4bit = False

# Login to allow private models
token = hf_login(token)
if whisper_language is not None:
assert type(whisper_language) is str
if whisper_task is not None:
assert type(whisper_task) is str
# Align dtype with bnb_4bit_compute_dtype if provided and dtype is unset.
if dtype is None and quantization_config is not None:
bnb_compute_dtype = None
if isinstance(quantization_config, dict):
if quantization_config.get("load_in_4bit", False):
bnb_compute_dtype = quantization_config.get(
"bnb_4bit_compute_dtype", None
)
else:
if getattr(quantization_config, "load_in_4bit", False):
bnb_compute_dtype = getattr(
quantization_config, "bnb_4bit_compute_dtype", None
)
if isinstance(bnb_compute_dtype, str):
bnb_compute_dtype = getattr(torch, bnb_compute_dtype, None)
if isinstance(bnb_compute_dtype, torch.dtype):
dtype = bnb_compute_dtype

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This block of code for handling quantization_config and aligning dtype is a duplicate of the logic in FastLanguageModel.from_pretrained (lines 154-188). Large blocks of duplicated code make the software harder to maintain, as any changes or bug fixes need to be applied in multiple places.

To improve maintainability, I suggest refactoring this logic into a single helper function. This function could take kwargs, dtype, load_in_4bit, and load_in_8bit as input and return the processed values.

Here's a potential implementation for the helper function:

def _process_user_quantization_config(kwargs, dtype, load_in_4bit, load_in_8bit):
    quantization_config = kwargs.get("quantization_config", None)
    if quantization_config is None:
        return quantization_config, dtype, load_in_4bit, load_in_8bit

    # Respect user-provided quantization_config
    if isinstance(quantization_config, dict):
        q_load_in_4bit = quantization_config.get("load_in_4bit", False)
        q_load_in_8bit = quantization_config.get("load_in_8bit", False)
    else:
        q_load_in_4bit = getattr(quantization_config, "load_in_4bit", False)
        q_load_in_8bit = getattr(quantization_config, "load_in_8bit", False)

    if q_load_in_4bit:
        load_in_4bit = True
        load_in_8bit = False
    elif q_load_in_8bit:
        load_in_8bit = True
        load_in_4bit = False

    # Align dtype with bnb_4bit_compute_dtype if provided and dtype is unset.
    if dtype is None:
        bnb_compute_dtype = None
        if isinstance(quantization_config, dict):
            if quantization_config.get("load_in_4bit", False):
                bnb_compute_dtype = quantization_config.get("bnb_4bit_compute_dtype", None)
        else:
            if getattr(quantization_config, "load_in_4bit", False):
                bnb_compute_dtype = getattr(quantization_config, "bnb_4bit_compute_dtype", None)
        
        if isinstance(bnb_compute_dtype, str):
            import torch
            bnb_compute_dtype = getattr(torch, bnb_compute_dtype, None)
        if isinstance(bnb_compute_dtype, torch.dtype):
            dtype = bnb_compute_dtype
            
    return quantization_config, dtype, load_in_4bit, load_in_8bit

You could then call this helper from both from_pretrained methods. Note the use of elif which makes the precedence explicit if both 4-bit and 8-bit are specified in the config.

@danielhanchen danielhanchen merged commit 50dafa6 into main Jan 4, 2026
4 checks passed
abiswas-realadvice pushed a commit to abiswas-realadvice/unsloth that referenced this pull request May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant