Transformers documentation

Contribuir

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Contribuir

Transformers admite muchos métodos de cuantización, como QLoRA, GPTQ, LLM.int8 y AWQ. Sin embargo, aún existen muchos más enfoques de cuantización que no se han integrado todavía. Para facilitar la adición y el uso de estos métodos de cuantización con Transformers, utilice la clase HfQuantizer. HfQuantizer está diseñada para ser una clase auxiliar interna para agregar un método de cuantización, en lugar de algo que se aplique a cada módulo de PyTorch.

Esta guía le mostrará cómo integrar un nuevo método de cuantización con HfQuantizer.

Requisitos

Antes de integrar un nuevo método de cuantización en Transformers, asegúrese de que el método cumpla con los siguientes requisitos. Solo se admiten los métodos de cuantización que pueden ejecutarse con módulos de PyTorch.

  • El método de cuantización está disponible a través de un paquete de Python que se puede instalar con pip (también está bien si solo puedes instalar el paquete desde el código fuente). Idealmente, el paquete de pip incluye kernels precompilados.

  • El método puede ejecutarse en hardware de uso común (CPU, GPU, etc.).

  • El método está envuelto en un nn.Module (~bitsandbytes.nn.Linear8bitLt, ~bitsandbytes.nn.Linear4bit), y la capa lineal cuantizada debe tener la siguiente definición.

    class Linear4bit(nn.Module):
        def __init__(self, ...):
            ...
    
        def forward(self, x):
            return my_4bit_kernel(x, self.weight, self.bias)

    De esta manera, los modelos de Transformers se cuantizan fácilmente reemplazando las instancias de nn.Linear por una clase objetivo.

  • El método de cuantización debe ser serializable. Puede guardar los pesos cuantizados localmente o subirlos al Hub.

  • Asegúrese de que el paquete que contiene los kernels/primitivas de cuantización sea estable (sin cambios frecuentes que rompan la compatibilidad).

Algunos métodos de cuantización pueden requerir “pre-cuantizar” el modelo mediante calibración de datos (AWQ). En este caso, preferimos admitir solo la inferencia en Transformers y dejar que la biblioteca de terceros mantenida por la comunidad de ML se encargue de la cuantización del modelo.

Crear la nueva clase HFQuantizer

  1. El mejor punto de partida sería echar un vistazo a otro método de cuantización, como Finegrained Fp8. Tendrá que actualizar o crear un total de tres archivos: el archivo de configuración, el archivo de integración y el archivo del cuantizador.

  2. Cree una nueva clase de configuración de cuantización dentro de src/transformers/utils/quantization_config.py. Añada la nueva configuración de cuantización a _import_structure dentro del archivo src/transformers/__init__.py de Transformers.

  3. Cree un nuevo archivo dentro de src/transformers/quantizers/ llamado quantizer_your_method.py y haga que herede de [`~quantizers.HfQuantizer`]. Asegúrese de agregar el nuevo cuantizador y la configuración de cuantización en el mapeo automático de cuantización en src/transformers/quantizers/auto.py.

  4. Defina los siguientes atributos de clase y métodos de propiedad para su método de cuantización:

    • requires_calibration: Indica si el método de cuantización requiere un proceso de calibración de datos. Si se establece en True, solo se puede admitir la inferencia (con pesos cuantizados) y no la inferencia y la cuantización.
    • is_serializable: un método de propiedad para determinar si el método es serializable o no.
    • is_trainable: un método de propiedad para determinar si se pueden ajustar modelos sobre el método de cuantización (con o sin enfoques PEFT).
  5. Escribe los métodos validate_environment y update_dtype. Estos métodos se llaman antes de crear el modelo cuantizado para asegurarse de que los usuarios utilicen la configuración correcta. Consulta otros cuantizadores para ver un ejemplo de cómo se implementan.

  6. Escriba el método _process_model_before_weight_loading. En Transformers, los modelos cuantizados se inicializan primero en el dispositivo "meta" antes de cargar los pesos. Esto significa que el método _process_model_before_weight_loading se encarga de manipular el esqueleto del modelo para reemplazar algunos módulos (nn.Linear) con los módulos objetivo (módulos de cuantización).

Puede definir la lógica de reemplazo de módulos o cualquier otro método de utilidad creando un nuevo archivo en transformers/src/integrations/ y exponiendo los métodos relevantes en el archivo __init__.py de esa carpeta.

  1. Agregue el método get_quantize_ops a la clase cuantizadora si la cuantización admite la cuantización sobre la marcha. En transformers, materializamos cada tensor y aplicamos una secuencia de operaciones diferentes sobre él. En nuestro caso, la operación de cuantización ocurre al final. Debe crear un XXXQuantize, una subclase de ConversionOps, y agregar un método convert. En el método convert, debe cuantizar los pesos y devolver un diccionario de parámetros cuantizados.

  2. Agregue el método get_weight_conversions a la clase cuantizador si la cuantización admite la carga de pesos precuantizados. En transformers, podemos recopilar varios tensores y aplicar operaciones sobre ellos. Esto resulta especialmente útil cuando tenemos tensores en el checkpoint que deben reagruparse para recrear los tensores cuantizados.

  3. Escriba el método _process_model_after_weight_loading si es necesario. Este método permite implementar funciones adicionales que requieran manipular el modelo después de cargar los pesos.

  4. ¡Documenta todo! Asegúrate de que tu método de cuantización esté documentado añadiendo un nuevo archivo bajo docs/source/en/quantization.

  5. Deberías añadir pruebas incluyendo el paquete en nuestro Dockerfile de pruebas nocturnas dentro de docker/transformers-quantization-latest-gpu y luego añadiendo un nuevo archivo de pruebas en tests/quantization/xxx. Puedes consultar los métodos de cuantización existentes para ver cómo se implementa.

Resumen de archivos

ArchivoPropósito
utils/quantization_config.pyDefina YourMethodConfig heredando de QuantizationConfigMixin
quantizers/quantizer_your_method.pyImplementa YourMethodHfQuantizer heredando de HfQuantizer
integrations/your_method.pyImplementa subclases de ConversionOps y funciones auxiliares
quantizers/auto.pyRegistra el cuantizador y la configuración en AUTO_QUANTIZER_MAPPING y AUTO_QUANTIZATION_CONFIG_MAPPING
docs/source/en/quantization/your_method.mdDocumentar el uso para los usuarios
tests/quantization/your_method/Agregar pruebas de integración

Comprender get_quantize_ops frente a get_weight_conversions

Estos dos métodos gestionan diferentes escenarios para la carga de pesos. Comprender cuándo usar cada uno es esencial.

get_quantize_ops — Cuantización en tiempo real

Utilice esto al cargar un punto de control no cuantizado (p. ej., pesos float16/bfloat16) y cuantizar durante la carga.

Checkpoint: model.safetensors (float16 weights for example)
     ↓
get_quantize_ops → YourQuantize.convert()
     ↓
Result: Quantized weights in memory

El método convert recibe un tensor a la vez, lo cuantiza y puede devolver un diccionario de parámetros cuantizados, por ejemplo:

class YourQuantize(ConversionOps):
    def convert(self, input_dict, model, full_layer_name, missing_keys, **kwargs):
        # input_dict = {"layer.weight": <float16 tensor>}
        value = list(input_dict.values())[0]
        module, tensor_name = get_module_from_name(model, full_layer_name)

        # Quantize and assign
        quantized, scale, zero_point = your_quantize_fn(value)
        return {full_layer_name: quantized, full_layer_name + ".scale": scale, full_layer_name + ".zero_point": zero_point}

get_weight_conversions — Cargar puntos de control pre-cuantizados

Utilice esto al cargar un punto de control pre-cuantizado en el que los pesos cuantizados se guardan como varios componentes separados (como datos, escala y punto cero) y que deben combinarse en un solo tensor durante la carga. No todos los métodos de cuantización requieren esta etapa de reconstrucción: por ejemplo, algunos métodos como FP8 simplemente cargan los pesos y las escalas tal como están, sin combinarlos. Otros, como torchao, sí requieren reensamblar el tensor cuantizado a partir de sus múltiples componentes guardados.

Checkpoint: model.safetensors (quantized components)
  - layer._weight_qdata
  - layer._weight_scale
  - layer._weight_zero_point
     ↓
get_weight_conversions → WeightConverter + YourDeserialize.convert()
     ↓
Result: Reconstructed quantized tensor → layer.weight

El WeightConverter recopila los tensores relacionados en función de source_patterns y luego los pasa a su método convert:

def get_weight_conversions(self):
    if self.pre_quantized:
        return [
            WeightConverter(
                source_patterns=["_weight_qdata", "_weight_scale", "_weight_zero_point"],
                target_patterns="weight",
                operations=[YourDeserialize(self)],
            ),
        ]
    return []


class YourDeserialize(ConversionOps):
    def convert(self, input_dict, model, full_layer_name, **kwargs):
        # input_dict contains all collected tensors
        # Reconstruct the quantized tensor from components
        reconstructed_tensor = reconstruct_from_components(input_dict)
        return {full_layer_name: reconstructed_tensor}
Update on GitHub