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

O Transformers suporta muitos métodos de quantização, como QLoRA, GPTQ, LLM.int8 e AWQ. No entanto, ainda existem muitas outras abordagens de quantização que não foram integradas. Para facilitar a adição e o uso desses métodos de quantização com o Transformers, utilize a classe HfQuantizer. O HfQuantizer foi projetado para ser uma classe auxiliar interna para adicionar um método de quantização, em vez de algo aplicado a cada módulo do PyTorch.

Este guia mostrará como integrar um novo método de quantização com HfQuantizer.

Requisitos

Antes de integrar um novo método de quantização ao Transformers, verifique se o método atende aos seguintes requisitos. Somente métodos de quantização que podem ser executados com módulos do PyTorch são suportados.

  • O método de quantização está disponível por meio de um pacote Python que pode ser instalado via pip (também é aceitável se você puder instalar o pacote apenas a partir do código-fonte). Idealmente, kernels pré-compilados estão incluídos no pacote pip.

  • O método pode ser executado em hardware comumente utilizado (CPU, GPU, etc.).

  • O método é encapsulado em um nn.Module (~bitsandbytes.nn.Linear8bitLt, ~bitsandbytes.nn.Linear4bit), e a camada linear quantizada deve ter a seguinte definição.

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

    Dessa forma, os modelos do Transformers são facilmente quantizados substituindo as instâncias de nn.Linear por uma classe de destino.

  • O método de quantização deve ser serializável. Você pode salvar os pesos quantizados localmente ou enviá-los para o Hub.

  • Verifique se o pacote que contém os kernels/primitivas de quantização está estável (sem mudanças frequentes que quebrem a compatibilidade).

Alguns métodos de quantização podem exigir a “pré-quantização” do modelo por meio de calibração de dados (AWQ). Nesse caso, preferimos suportar apenas inferência no Transformers e deixar que a biblioteca de terceiros mantida pela comunidade de ML trate a quantização do modelo em si.

Criar a nova classe HFQuantizer

  1. O melhor ponto de partida seria dar uma olhada em outro método de quantização, como Finegrained Fp8. Você precisará atualizar ou criar, no total, três arquivos: o arquivo de configuração, o arquivo de integração e o arquivo do quantizador.

  2. Crie uma nova classe de configuração de quantização dentro de src/transformers/utils/quantization_config.py. Adicione a nova configuração de quantização ao _import_structure no arquivo src/transformers/__init__.py do Transformers.

  3. Crie um novo arquivo dentro de src/transformers/quantizers/ chamado quantizer_your_method.py e faça-o herdar de [`~quantizers.HfQuantizer`]. Certifique-se de adicionar o novo quantizer e a configuração de quantização no auto-mapping de quantização em src/transformers/quantizers/auto.py.

  4. Defina os seguintes atributos de classe e métodos de propriedade para o seu método de quantização:

    • requires_calibration: Indica se o método de quantização requer um processo de calibração de dados. Se definido como True, você só pode suportar inferência (com pesos quantizados) e não inferência e quantização.
    • is_serializable: Um método de propriedade para determinar se o método é serializável ou não.
    • is_trainable: Um método de propriedade para determinar se é possível fazer fine-tuning de modelos por cima do método de quantização (com ou sem abordagens PEFT).
  5. Escreva os métodos validate_environment e update_dtype. Esses métodos são chamados antes da criação do modelo quantizado para garantir que os usuários utilizem a configuração correta. Consulte outros quantizadores para ver um exemplo de como eles são implementados.

  6. Escreva o método _process_model_before_weight_loading. No Transformers, os modelos quantizados são inicializados primeiro no dispositivo "meta" antes de carregar os pesos. Isso significa que o método _process_model_before_weight_loading se encarrega de manipular o esqueleto do modelo para substituir alguns módulos (nn.Linear) pelos módulos de destino (módulos de quantização).

Você pode definir a lógica de substituição de módulos ou qualquer outro método utilitário criando um novo arquivo em transformers/src/integrations/ e expondo os métodos relevantes no arquivo __init__.py dessa pasta.

  1. Adicione o método get_quantize_ops à classe do quantizador se a quantização suportar quantização em tempo real. No Transformers, materializamos cada tensor e aplicamos uma sequência de operações diferentes nele. No nosso caso, a operação de quantização ocorre no final. Você precisa criar um XXXQuantize, uma subclasse de ConversionOps, e adicionar um método convert. No método convert, você precisa quantizar os pesos e retornar um dicionário de parâmetros quantizados.

  2. Adicione o método get_weight_conversions à classe do quantizador, caso a quantização suporte o carregamento de pesos pré-quantizados. No transformers, é possível coletar múltiplos tensors e aplicar operações neles. Isso é particularmente útil quando há tensors no checkpoint que precisam ser reagrupados para recriar os tensors quantizados.

  3. Escreva o método _process_model_after_weight_loading, se necessário. Esse método permite implementar recursos adicionais que exigem manipular o modelo após o carregamento dos pesos.

  4. Documente tudo! Certifique-se de que seu método de quantização está documentado adicionando um novo arquivo em docs/source/en/quantization.

  5. Você deve adicionar testes incluindo o pacote no nosso Dockerfile noturno dentro de docker/transformers-quantization-latest-gpu e, em seguida, adicionando um novo arquivo de teste em tests/quantization/xxx. Sinta-se à vontade para consultar os métodos de quantização existentes para ver como é implementado.

Visão geral dos arquivos

ArquivoPropósito
utils/quantization_config.pyDefina YourMethodConfig herdando de QuantizationConfigMixin
quantizers/quantizer_your_method.pyImplemente YourMethodHfQuantizer herdando de HfQuantizer
integrations/your_method.pyImplemente subclasses de ConversionOps e funções auxiliares
quantizers/auto.pyRegistre o quantizador e a configuração em AUTO_QUANTIZER_MAPPING e AUTO_QUANTIZATION_CONFIG_MAPPING
docs/source/en/quantization/your_method.mdUso da documentação para usuários
tests/quantization/your_method/Adicionar testes de integração

Entendendo get_quantize_ops vs get_weight_conversions

Esses dois métodos lidam com cenários diferentes para o carregamento de pesos. Compreender quando usar cada um é essencial.

get_quantize_ops — Quantização em tempo real

Use isso ao carregar um checkpoint não quantizado (por exemplo, pesos em float16/bfloat16) e quantizar durante o carregamento.

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

O método convert recebe um tensor por vez, o quantiza e pode retornar um dicionário de parâmetros quantizados, por exemplo:

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 — Carregar checkpoints pré-quantizados

Use isso ao carregar um checkpoint pré-quantizado em que os pesos quantizados são salvos como vários componentes separados (como data, scale e zero point) e que precisam ser combinados em um único tensor durante o carregamento. Nem todos os métodos de quantização exigem essa etapa de reconstrução: por exemplo, alguns métodos, como FP8, simplesmente carregam os pesos e as escalas como estão, sem combiná-los. Outros, como torchao, exigem a reconstituição do tensor quantizado a partir de seus múltiplos componentes salvos.

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

O WeightConverter coleta os tensores relacionados com base em source_patterns e, em seguida, os passa para o seu 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