Repository navigation
Add input-embedding quantization example #2830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
brian-dellabetta
merged 10 commits into
vllm-project:main
from
KKothuri:embedding-quant-example
Jun 15, 2026
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
efd26ab
Add input-embedding quantization example
KKothuri 0da9136
Update examples/quantization_embedding/llama3_example.py
KKothuri 4a8e0f7
Update examples/quantization_embedding/README.md
KKothuri ba7b5ca
Address review: README device_map consistency + gated-model note
KKothuri 622f69c
Wrap model load to satisfy 88-char line limit (ruff E501)
KKothuri 7ac2ab9
Merge branch 'main' into embedding-quant-example
kylesayrs 4a77322
Merge branch 'main' into embedding-quant-example
kylesayrs d6e4d44
Update examples/quantization_embedding/llama3_example.py
kylesayrs b62237e
Update examples/quantization_embedding/README.md
kylesayrs bcff761
Merge branch 'main' into embedding-quant-example
brian-dellabetta File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # Quantizing the Input Embedding | ||
|
|
||
| `llm-compressor` can quantize a model's **input embedding table** (the vocab | ||
| lookup) to weight-only `intN` (`WNA16`), in addition to the linear layers. vLLM | ||
| loads these checkpoints and runs an efficient fused gather + dequant for the | ||
| looked-up rows, so the packed table is never densified. | ||
|
|
||
| Embedding quantization is most useful for **large-vocabulary models**, where the | ||
| embedding table is a meaningful fraction of memory (e.g. a 150k-vocab model has a | ||
| ~1 GB embedding table in fp16). It is **weight-only and data-free** -- no | ||
| calibration dataset is required -- and its accuracy impact is typically | ||
| negligible. | ||
|
|
||
| ## Installation | ||
|
|
||
| ```bash | ||
| git clone https://github.com/vllm-project/llm-compressor.git | ||
| cd llm-compressor | ||
| pip install -e . | ||
| ``` | ||
|
|
||
| ## Quickstart | ||
|
|
||
| ```bash | ||
| python3 llama3_example.py | ||
| ``` | ||
|
|
||
| The resulting model `Meta-Llama-3-8B-Instruct-embedding-W4A16-G64` is ready to be | ||
| loaded into vLLM. | ||
|
|
||
| ## Code Walkthrough | ||
|
|
||
| ### 1) Load the model | ||
|
|
||
| ```python | ||
| from transformers import AutoModelForCausalLM, AutoTokenizer | ||
|
|
||
| model_id = "meta-llama/Meta-Llama-3-8B-Instruct" | ||
| model = AutoModelForCausalLM.from_pretrained(model_id) | ||
| tokenizer = AutoTokenizer.from_pretrained(model_id) | ||
|
KKothuri marked this conversation as resolved.
Resolved
|
||
| ``` | ||
|
|
||
| > Note: `meta-llama/Meta-Llama-3-8B-Instruct` is gated on Hugging Face. Make sure | ||
| > your account has access and that you are authenticated locally (e.g. via | ||
| > `huggingface-cli login`) before running, or substitute an open model. | ||
|
|
||
| ### 2) Apply embedding quantization (data-free) | ||
|
|
||
| Target the `Embedding` module **by class name** rather than by a layer-name | ||
| regex. Class-based matching is portable across architectures and does not depend | ||
| on the module's prefix, so the same recipe works for `model.embed_tokens`, | ||
| `gpt_neox.embed_in`, and so on: | ||
|
|
||
| ```python | ||
| from llmcompressor import oneshot | ||
| from llmcompressor.modifiers.quantization import QuantizationModifier | ||
|
|
||
| recipe = QuantizationModifier( | ||
| config_groups={ | ||
| "embedding": { | ||
| "targets": ["Embedding"], | ||
| "weights": { | ||
| "num_bits": 4, | ||
| "type": "int", | ||
| "symmetric": True, | ||
| "strategy": "group", | ||
| "group_size": 64, | ||
| }, | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| # Weight-only -> data-free; no calibration dataset needed. | ||
| oneshot(model=model, recipe=recipe) | ||
| ``` | ||
|
|
||
| Use `"strategy": "channel"` (and drop `group_size`) for per-row channel scales, | ||
| or `"num_bits": 8` for a more conservative, effectively lossless setting. | ||
|
|
||
| ### 3) Save the compressed model | ||
|
|
||
| ```python | ||
| SAVE_DIR = "Meta-Llama-3-8B-Instruct-embedding-W4A16-G64" | ||
| model.save_pretrained(SAVE_DIR, save_compressed=True) | ||
| tokenizer.save_pretrained(SAVE_DIR) | ||
| ``` | ||
|
|
||
| ## Composing with weight quantization | ||
|
|
||
| Embedding quantization can be combined with linear-weight quantization by adding | ||
| the linear modifier alongside it, e.g.: | ||
|
|
||
| ```python | ||
| from llmcompressor.modifiers.quantization import GPTQModifier | ||
|
|
||
| recipe = [ | ||
| GPTQModifier(targets="Linear", scheme="W4A16", ignore=["lm_head"]), | ||
| QuantizationModifier(config_groups={"embedding": embedding_config}), # where embedding_config is defined as above | ||
| ] | ||
| ``` | ||
|
|
||
| ## Accuracy | ||
|
|
||
| Quantizing only the input embedding is near-lossless. On `pythia-1.4b` | ||
| (`lm-eval`, `wikitext` word perplexity / `arc_easy` acc): | ||
|
|
||
| | scheme | wikitext ppl | arc_easy acc | | ||
| | --- | --- | --- | | ||
| | baseline (fp16) | 14.733 | 0.6048 | | ||
| | embedding W8 channel | 14.732 | 0.6052 | | ||
| | embedding W4 group-64 | 14.752 | 0.6061 | | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| from compressed_tensors.offload import dispatch_model | ||
| from transformers import AutoModelForCausalLM, AutoTokenizer | ||
|
|
||
| from llmcompressor import oneshot | ||
| from llmcompressor.modifiers.quantization import QuantizationModifier | ||
|
|
||
| # Select model and load it. | ||
| model_id = "meta-llama/Meta-Llama-3-8B-Instruct" | ||
| model = AutoModelForCausalLM.from_pretrained(model_id) | ||
| tokenizer = AutoTokenizer.from_pretrained(model_id) | ||
|
|
||
| # Configure the quantization algorithm to run. | ||
| # * quantize the input embedding table to int4 with a group size of 64 | ||
| # * weight-only: there are no input/output activations to quantize for a lookup | ||
| # | ||
| # The target is the `Embedding` module *class* (not a name regex). Matching by | ||
| # class name keeps the recipe portable across architectures and independent of a | ||
| # model's module prefix, so the same recipe works whether the embedding is named | ||
| # `model.embed_tokens`, `gpt_neox.embed_in`, etc. | ||
| recipe = QuantizationModifier( | ||
| config_groups={ | ||
| "embedding": { | ||
| "targets": ["Embedding"], | ||
| "weights": { | ||
| "num_bits": 4, | ||
| "type": "int", | ||
| "symmetric": True, | ||
| "strategy": "group", | ||
| "group_size": 64, | ||
| }, | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| # Apply the quantization algorithm. Embedding quantization is weight-only, so it | ||
| # is data-free -- no calibration dataset is required. | ||
| oneshot(model=model, recipe=recipe) | ||
|
|
||
| # Confirm generations of the quantized model look sane. | ||
| print("\n\n") | ||
| print("========== SAMPLE GENERATION ==============") | ||
| dispatch_model(model) | ||
| sample = tokenizer("Hello my name is", return_tensors="pt") | ||
| sample = {key: value.to(model.device) for key, value in sample.items()} | ||
| output = model.generate(**sample, max_new_tokens=100) | ||
| print(tokenizer.decode(output[0])) | ||
| print("==========================================\n\n") | ||
|
|
||
| # Save to disk compressed. | ||
| SAVE_DIR = model_id.rstrip("/").split("/")[-1] + "-embedding-W4A16-G64" | ||
| model.save_pretrained(SAVE_DIR, save_compressed=True) | ||
| tokenizer.save_pretrained(SAVE_DIR) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.