Transformers documentation

Contribuer

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

Contribuer

Transformers prend en charge de nombreuses méthodes de quantification, telles que QLoRA, GPTQ, LLM.int8 et AWQ. Cependant, il existe encore de nombreuses autres approches de quantification qui n’ont pas encore été intégrées. Pour faciliter l’ajout et l’utilisation de ces méthodes de quantification avec Transformers, utilisez la classe HfQuantizer. HfQuantizer est conçue comme une classe d’assistance interne pour ajouter une méthode de quantification, plutôt que comme un élément appliqué à chaque module PyTorch.

Ce guide vous montre comment intégrer une nouvelle méthode de quantification avec HfQuantizer.

Prérequis

Avant d’intégrer une nouvelle méthode de quantification dans Transformers, assurez-vous que la méthode répond aux exigences suivantes. Seules les méthodes de quantification qui peuvent être exécutées avec des modules PyTorch sont prises en charge.

  • La méthode de quantification est disponible via un paquet Python installable avec pip (il est également acceptable que le paquet ne soit installable qu’à partir du code source). Idéalement, des noyaux précompilés sont inclus dans le paquet pip.

  • La méthode peut s’exécuter sur du matériel couramment utilisé (CPU, GPU, etc.).

  • La méthode est encapsulée dans un nn.Module (~bitsandbytes.nn.Linear8bitLt, ~bitsandbytes.nn.Linear4bit), et la couche linéaire quantifiée doit avoir la définition suivante.

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

    De cette manière, les modèles Transformers sont facilement quantisés en remplaçant les instances de nn.Linear par une classe cible.

  • La méthode de quantification doit être sérialisable. Vous pouvez enregistrer les poids quantifiés localement ou les pousser sur le Hub.

  • Vérifiez que le package contenant les noyaux/primitives de quantisation est stable (sans modifications cassantes fréquentes).

Certaines méthodes de quantification peuvent nécessiter une « pré-quantification » du modèle par calibration sur des données (AWQ). Dans ce cas, nous préférons ne prendre en charge que l’inférence dans Transformers et laisser la bibliothèque tierce maintenue par la communauté ML gérer elle-même la quantification du modèle.

Créer la nouvelle classe HFQuantizer

  1. Le meilleur point de départ serait de consulter une autre méthode de quantification, telle que Finegrained Fp8. Vous devrez mettre à jour ou créer trois fichiers au total : le fichier de configuration, le fichier d’intégration et le fichier de quantiseur.

  2. Créez une nouvelle classe de configuration de quantification dans src/transformers/utils/quantization_config.py. Ajoutez la nouvelle configuration de quantification à _import_structure dans le fichier src/transformers/__init__.py de Transformers.

  3. Créez un nouveau fichier dans src/transformers/quantizers/ nommé quantizer_your_method.py, et faites-le hériter de [`~quantizers.HfQuantizer]. Pensez à ajouter le nouveau quantizer et la configuration de quantification dans le mappage automatique de quantification dans src/transformers/quantizers/auto.py.

  4. Définissez les attributs de classe et les méthodes de propriété suivants pour votre méthode de quantification :

    • requires_calibration : Indique si la méthode de quantification nécessite un processus de calibration des données. Si défini sur True, vous ne pouvez prendre en charge que l'inférence (avec des poids quantifiés) et non l'inférence et la quantification.
    • is_serializable : une méthode de propriété permettant de déterminer si la méthode est sérialisable ou non.
    • is_trainable : une méthode de propriété qui détermine si vous pouvez effectuer un fine-tuning de modèles au-dessus de la méthode de quantisation (avec ou sans approches PEFT).
  5. Écrivez les méthodes validate_environment et update_dtype. Ces méthodes sont appelées avant la création du modèle quantifié afin de garantir que les utilisateurs utilisent la bonne configuration. Reportez-vous à d'autres quantiseurs pour un exemple de leur implémentation.

  6. Écrivez la méthode _process_model_before_weight_loading. Dans Transformers, les modèles quantifiés sont d'abord initialisés sur l'appareil "meta" avant le chargement des poids. Cela signifie que la méthode _process_model_before_weight_loading s'occupe de manipuler le squelette du modèle pour remplacer certains modules (nn.Linear) par les modules cibles (modules de quantification).

Vous pouvez définir la logique de remplacement de module ou toute autre méthode utilitaire en créant un nouveau fichier dans transformers/src/integrations/ et en exposant les méthodes pertinentes dans le fichier __init__.py de ce dossier.

  1. Ajoutez la méthode get_quantize_ops à la classe de quantiseur si la quantisation prend en charge la quantisation à la volée. Dans transformers, nous matérialisons chaque tenseur et y appliquons une séquence d’opérations différentes. Dans notre cas, l’opération de quantisation a lieu à la fin. Vous devez créer un XXXQuantize, une sous-classe de ConversionOps, et ajouter une méthode convert. Dans la méthode convert, vous devez quantiser les poids et retourner un dictionnaire de paramètres quantifiés.

  2. Ajoutez la méthode get_weight_conversions à la classe de quantiseur si la quantisation prend en charge le chargement de poids pré-quantifiés. Dans transformers, il est possible de regrouper plusieurs tenseurs et d’appliquer des opérations dessus. C’est particulièrement utile lorsque les tenseurs présents dans le checkpoint doivent être regroupés pour recréer les tenseurs quantifiés.

  3. Écrivez la méthode _process_model_after_weight_loading si nécessaire. Cette méthode permet de mettre en œuvre des fonctionnalités supplémentaires nécessitant de manipuler le modèle après le chargement des poids.

  4. Documentez tout ! Assurez-vous que votre méthode de quantification est documentée en ajoutant un nouveau fichier sous docs/source/en/quantization.

  5. Vous devez ajouter des tests en ajoutant le paquet dans notre Dockerfile de nuit à l’intérieur de docker/transformers-quantization-latest-gpu, puis en ajoutant un nouveau fichier de test dans tests/quantization/xxx. N’hésitez pas à consulter les méthodes de quantification existantes pour voir comment elles sont implémentées.

Aperçu des fichiers

FichierObjectif
utils/quantization_config.pyDéfinissez YourMethodConfig en héritant de QuantizationConfigMixin
quantizers/quantizer_your_method.pyImplémenter YourMethodHfQuantizer en héritant de HfQuantizer
integrations/your_method.pyImplémenter des sous-classes de ConversionOps et des fonctions utilitaires
quantizers/auto.pyEnregistrer le quantizer et la config dans AUTO_QUANTIZER_MAPPING et AUTO_QUANTIZATION_CONFIG_MAPPING
docs/source/en/quantization/your_method.mdUtilisation de la documentation pour les utilisateurs
tests/quantization/your_method/Ajouter des tests d'intégration

Comprendre get_quantize_ops et get_weight_conversions

Ces deux méthodes gèrent des scénarios différents pour le chargement des poids. Il est essentiel de comprendre quand utiliser chacune d'elles.

get_quantize_ops — Quantification à la volée

Utilisez ceci lors du chargement d'un point de contrôle non quantifié (par exemple, des poids en float16/bfloat16) et de la quantification pendant le chargement.

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

La méthode convert reçoit un tenseur à la fois, le quantifie et peut renvoyer un dictionnaire de paramètres quantifiés, par exemple :

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 — Charger des points de contrôle pré-quantifiés

Utilisez cette option lors du chargement d'un point de contrôle pré-quantifié dont les poids quantifiés sont enregistrés sous forme de plusieurs composants distincts (tels que les données, l'échelle et le point zéro) et qui doivent être fusionnés en un seul tenseur au moment du chargement. Toutes les méthodes de quantification n'exigent pas cette étape de reconstruction : par exemple, certaines méthodes comme FP8 chargent simplement les poids et les échelles tels quels, sans les combiner. D'autres, comme torchao, nécessitent en revanche de reconstituer le tenseur quantifié à partir de ses multiples composants enregistrés.

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

Le WeightConverter collecte les tenseurs liés en fonction de source_patterns, puis les transmet à votre méthode 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