optimum-quanto

huggingfaceApache-2.0活跃维护

PyTorch 量化后端,接入 Optimum 生态,支持多种量化方案,帮你在推理和训练中压缩模型、降低显存占用。

A pytorch quantization backend for optimum

v0.2.7发布于 2025-03-06 08:10 UTC1,056star102fork同步于 2026-11-10 17:53 UTC

怎么上手

由 README 提炼,执行前请确认
pip install optimum-quanto
硬件要求按 README 说明

README 未说明具体显存要求;量化模型可部署在 CUDA 和 MPS 设备上,CUDA 上提供加速矩阵乘内核。

需要 CUDA
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

PyTorch 量化后端,为 optimum 提供 int2/4/8 与 float8 权重量化

PyTorch 量化后端,接入 Optimum 生态,支持多种量化方案,帮你在推理和训练中压缩模型、降低显存占用。

核心能力

  • eager 模式下自动插入量化与反量化 stub
  • 支持 int2、int4、int8、float8 权重与 int8、float8 激活
  • 量化模型可放到 CUDA、MPS 等任意设备
  • CUDA 上加速 int8-int8、fp16-int4、bf16-int8 等矩阵乘
  • 序列化兼容 PyTorch weight_only 与 safetensors

容易踩的坑

项目已进入维护模式,只接受小修复,新功能建议改用 bitsandbytes 或 torchAO;低层 API 默认动态量化权重,需显式调用 freeze 冻结;激活量化需先用 Calibration 校准,否则大离群值会导致精度严重下降。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v0.2.7
  1. v0.2.7release: 0.2.7最新正式版2025-03-06 · 2025-03-06 08:10 UTC

    What's Changed

    What's new

    Bug fixes

    New Contributors

    Full Changelog: https://github.com/huggingface/optimum-quanto/compare/v0.2.6...v0.2.7

  2. v0.2.6release: 0.2.62024-10-29 · 2024-10-29 17:13 UTC
    查看更新说明

    What's Changed

    New Contributors

    Full Changelog: https://github.com/huggingface/optimum-quanto/compare/v0.2.5...v0.2.6

  3. v0.2.5release: 0.2.52024-10-01 · 2024-10-01 11:52 UTC
    查看更新说明

    New features

    • Load and save models from the Hugging Face hub #263 by @sayakpaul
    • Add support for float8 e4f3mnuz #310 (from #281) by @maktukmak
    • Faster and less memory-intensive requantization #290 by @latentCall145
    • Support torch.equal for QTensor #294 by @dacorvo
    • Add Marlin Float8 kernel #296 (from #241) by @fxmarty
    • Add Whisper for speech recognition example #298 (from #242) by @mattiadg
    • Add ViT classification example #308 by @shovan777

    Bug fixes

    • Fix include patterns in quantize #271 by @kaibioinfo
    • Enable non-strict loading of state dicts #295 by @BenjaminBossan
    • Fix transformers forward error #303 by @dacorvo
    • Fix missing call in transformers models #325 by @dacorvo
    • Fix 8-bit mm calls for 4D inputs #326 by @dacorvo

    Full Changelog: https://github.com/huggingface/optimum-quanto/compare/v0.2.4...v0.2.5

  4. v0.2.4release: 0.2.42024-07-26 · 2024-07-26 09:25 UTC
    查看更新说明

    Bug Fixes:

    • fix import error in optimum-cli when diffusers is not installed by @dacorvo

    Full Changelog: https://github.com/huggingface/optimum-quanto/compare/v0.2.3...v0.2.4

  5. v0.2.3release: 0.2.32024-07-25 · 2024-07-25 15:18 UTC
    查看更新说明

    What's Changed

    Bug fixes

    New Contributors

    Full Changelog: https://github.com/huggingface/optimum-quanto/compare/v0.2.2...v0.2.3

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Optimum Quanto

    This project is currently in maintenance mode. We accept pull requests only for minor bug fixes, documentation improvements, and other maintenance tasks. Major new features or breaking changes are unlikely to be merged. For production-ready quantization features or active development, consider alternative projects such as bitsandbytes or torchAO.

    🤗 Optimum Quanto is a pytorch quantization backend for optimum.

    It has been designed with versatility and simplicity in mind:

    • all features are available in eager mode (works with non-traceable models),
    • quantized models can be placed on any device (including CUDA and MPS),
    • automatically inserts quantization and dequantization stubs,
    • automatically inserts quantized functional operations,
    • automatically inserts quantized modules (see below the list of supported modules),
    • provides a seamless workflow from a float model to a dynamic to a static quantized model,
    • serialization compatible with pytorch weight_only and 🤗 safetensors,
    • accelerated matrix multiplications on CUDA devices (int8-int8, fp16-int4, bf16-int8, bf16-int4),
    • supports int2, int4, int8 and float8 weights,
    • supports int8 and float8 activations.

    Features yet to be implemented:

    • dynamic activations smoothing,
    • kernels for all mixed matrix multiplications on all devices,
    • compatibility with torch compiler (aka dynamo).

    Performances

    In a nutshell:

    • accuracy: models compiled with int8/float8 weights and float8 activations are very close to the full-precision models,
    • latency: whenever optimized kernels are available, the inference of quantized model is comparable with the full-precision models when quantizing only the model weights,
    • device memory: approximately divided by float bits / integer bits.

    The paragraph below is just an example. Please refer to the bench folder for detailed results per use-case of model.

    meta-llama/Meta-Llama-3.1-8B

    Installation

    Optimum Quanto is available as a pip package.

    pip install optimum-quanto
    

    Quantization workflow for Hugging Face models

    optimum-quanto provides helper classes to quantize, save and reload Hugging Face quantized models.

    LLM models

    The first step is to quantize the model

    from transformers import AutoModelForCausalLM
    from optimum.quanto import QuantizedModelForCausalLM, qint4
    
    model = AutoModelForCausalLM.from_pretrained('meta-llama/Meta-Llama-3-8B')
    qmodel = QuantizedModelForCausalLM.quantize(model, weights=qint4, exclude='lm_head')
    

    Note: the model quantized weights will be frozen. If you want to keep them unfrozen to train them you need to use optimum.quanto.quantize directly.

    The quantized model can be saved using save_pretrained:

    qmodel.save_pretrained('./Llama-3-8B-quantized')
    

    It can later be reloaded using from_pretrained:

    from optimum.quanto import QuantizedModelForCausalLM
    
    qmodel = QuantizedModelForCausalLM.from_pretrained('Llama-3-8B-quantized')
    

    Diffusers models

    You can quantize any of the submodels inside a diffusers pipeline and seamlessly include them later in another pipeline.

    Here we quantize the transformer of a Pixart pipeline.

    from diffusers import PixArtTransformer2DModel
    from optimum.quanto import QuantizedPixArtTransformer2DModel, qfloat8
    
    model = PixArtTransformer2DModel.from_pretrained("PixArt-alpha/PixArt-Sigma-XL-2-1024-MS", subfolder="transformer")
    qmodel = QuantizedPixArtTransformer2DModel.quantize(model, weights=qfloat8)
    qmodel.save_pretrained("./pixart-sigma-fp8")
    

    Later, we can reload the quantized model and recreate the pipeline:

    from diffusers import PixArtTransformer2DModel
    from optimum.quanto import QuantizedPixArtTransformer2DModel
    
    transformer = QuantizedPixArtTransformer2DModel.from_pretrained("./pixart-sigma-fp8")
    transformer.to(device="cuda")
    pipe = PixArtSigmaPipeline.from_pretrained(
      "PixArt-alpha/PixArt-Sigma-XL-2-1024-MS",
      transformer=None,
      torch_dtype=torch.float16,
    ).to("cuda")
    pipe.transformer = transformer
    

    Quantization workflow for vanilla pytorch models (low-level API)

    One thing to keep in mind when using the low-level quanto API is that by default models weights are dynamically quantized: an explicit call must be made to 'freeze' the quantized weights.

    A typical quantization workflow would consist of the following steps:

    1. Quantize

    The first step converts a standard float model into a dynamically quantized model.

    from optimum.quanto import quantize, qint8
    
    quantize(model, weights=qint8, activations=qint8)
    

    At this stage, only the inference of the model is modified to dynamically quantize the weights.

    2. Calibrate (optional if activations are not quantized)

    Quanto supports a calibration mode that allows to record the activation ranges while passing representative samples through the quantized model.

    from optimum.quanto import Calibration
    
    with Calibration(momentum=0.9):
        model(samples)
    

    This automatically activates the quantization of the activations in the quantized modules.

    3. Tune, aka Quantization-Aware-Training (optional)

    If the performance of the model degrades too much, one can tune it for a few epochs to recover the float model performance.

    import torch
    
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data).dequantize()
        loss = torch.nn.functional.nll_loss(output, target)
        loss.backward()
        optimizer.step()
    

    4. Freeze integer weights

    When freezing a model, its float weights are replaced by quantized integer weights.

    from optimum.quanto import freeze
    
    freeze(model)
    

    5. Serialize quantized model

    Quantized models weights can be serialized to a state_dict, and saved to a file. Both pickle and safetensors (recommended) are supported.

    from safetensors.torch import save_file
    
    save_file(model.state_dict(), 'model.safetensors')
    

    In order to be able to reload these weights, you also need to store the quantized model quantization map.

    import json
    
    from optimum.quanto import quantization_map
    
    with open('quantization_map.json', 'w') as f:
      json.dump(quantization_map(model), f)
    

    5. Reload a quantized model

    A serialized quantized model can be reloaded from a state_dict and a quantization_map using the requantize helper. Note that you need first to instantiate an empty model.

    import json
    
    from safetensors.torch import load_file
    from optimum.quanto import requantize
    
    state_dict = load_file('model.safetensors')
    with open('quantization_map.json', 'r') as f:
      quantization_map = json.load(f)
    
    # Create an empty model from your modeling code and requantize it
    with torch.device('meta'):
      new_model = ...
    requantize(new_model, state_dict, quantization_map, device=torch.device('cuda'))
    

    Please refer to the examples for instantiations of that workflow.

    Design overview

    Tensors

    At the heart of quanto is a Tensor subclass that corresponds to:

    • the projection of a source Tensor into the optimal range for a given destination type,
    • the mapping of projected values to the destination type.

    For floating-point destination types, the mapping is done by the native pytorch cast (i.e. Tensor.to()).

    For integer destination types, the mapping is a simple rounding operation (i.e. torch.round()).

    The goal of the projection is to increase the accuracy of the conversion by minimizing the number of:

    • saturated values (i.e. mapped to the destination type min/max),
    • zeroed values (because they are below the smallest number that can be represented by the destination type)

    The projection is symmetric per-tensor or per-channel for int8 and float8, and group-wise affine (with a shift or 'zero-point') for lower bitwidth.

    One of the benefits of using a lower-bitwidth representation is that you will be able to take advantage of accelerated operations for the destination type, which is typically faster than their higher precision equivalents.

    Quanto does not support the conversion of a Tensor using mixed destination types.

    Modules

    Quanto provides a generic mechanism to replace torch modules by optimum-quanto modules that are able to process quanto tensors.

    optimum-quanto modules dynamically convert their weights until a model is frozen, which slows down inference a bit but is required if the model needs to be tuned.

    Weights are usually quantized per-channel along the first dimension (output features).

    Biases are not converted to preserve the accuracy of a typical addmm operation.

    Explanation: to be consistent with the unquantized arithmetic operations, biases would need to be quantized with a scale that is equal to the product of the input and weight scales, which leads to a ridiculously small scale, and conversely requires a very high bitwidth to avoid clipping. Typically, with int8 inputs and weights, biases would need to be quantized with at least 12 bits, i.e. in int16. Since most biases are today float16, this is a waste of time.

    Activations are dynamically quantized per-tensor using static scales (defaults to the range [-1, 1]).

    To preserve accuracy, the model needs to be calibrated to evaluate the best activation scales (using a momentum).

    The following modules can be quantized:

    • Linear (QLinear). Weights are always quantized, and biases are not quantized. Inputs and outputs can be quantized.
    • Conv2d (QConv2D). Weights are always quantized, and biases are not quantized. Inputs and outputs can be quantized.
    • LayerNorm, Weights and biases are not quantized. Outputs can be quantized.

    Pitfalls to avoid when quantizing activations

    Activations are always quantized per-tensor because most linear algebra operations in a model graph are not compatible with per-axis inputs: you simply cannot add numbers that are not expressed in the same base (you cannot add apples and oranges).

    Weights involved in matrix multiplications are, on the contrary, always quantized along their first axis, because all output features are evaluated independently from one another.

    The outputs of a quantized matrix multiplication will anyway always be dequantized, even if activations are quantized, because:

    • the resulting accumulated values are expressed with a much higher bitwidth (typically int32 or float32) than the activation bitwidth (typically int8 or float8),
    • they might be combined with a float bias.

    Quantizing activations per-tensor to int8 can lead to serious quantization errors if the corresponding tensors contain large outlier values. Typically, this will lead to quantized tensors with most values set to zero (except the outliers).

    A possible solution to work around that issue is to 'smooth' the activations statically as illustrated by SmoothQuant. You can find a script to smooth some model architectures under external/smoothquant.

    A better option is to represent activations using float8.