A quantização pode ser definida como o processo de mapeamento de valores de um grande conjunto de números reais para valores de um pequeno conjunto discreto. Normalmente, isso envolve mapear entradas contínuas para valores fixos na saída. Uma maneira comum de conseguir isso é arredondando ou truncando. Em caso de arredondamento, calculamos o número inteiro mais próximo. Por exemplo, um valor de 1,8 torna-se 2. Mas um valor de 1,2 torna-se 1. Em caso de truncamento, removemos cegamente os valores após o decimal para converter a entrada em um número inteiro.
Seja como for, a principal motivação por trás da quantização de redes neurais profundas é melhorar a velocidade de inferência, pois é desnecessário dizer que a inferência e o treinamento de redes neurais são bastante caros do ponto de vista computacional. Com o advento dos modelos de linguagem grande , o número de parâmetros nesses modelos está apenas aumentando, o que significa que o consumo de memória está cada vez maior.
Com a velocidade a que estas redes neurais estão a evoluir, há uma procura crescente para executar estas redes neurais nos nossos computadores portáteis ou telemóveis e até mesmo em dispositivos minúsculos como relógios. Nada disso é possível sem quantização.
Antes de mergulhar na quantização, não esqueçamos que Redes Neurais treinadas são meros números flutuantes armazenados na memória do computador.
Algumas das representações ou formatos bem conhecidos para armazenar números em computadores são float32 ou FP32, float16 ou FP16, int8, bfloat16 onde B significa Google Brain ou, mais recentemente, tensor float 32 ou TF32, um formato especializado para lidar com matrizes ou tensores. operações. Cada um desses formatos consome uma parte diferente da memória. Por exemplo, float32 aloca 1 bit para sinal, 8 bits para expoente e 23 bits para mantissa.
Da mesma forma, float16 ou FP16 aloca 1 bit para sinal, mas apenas 5 bits para expoente e 10 bits para mantissa. Por outro lado, o BF16 aloca 8 bits para o expoente e apenas 7 bits para a mantissa.
Chega de representações. O que quero dizer é que a conversão de um formato de memória superior para um formato de memória inferior é chamada de quantização. Falando em termos de aprendizado profundo, Float32 é chamado de precisão única ou total, e Float16 e BFloat16 são chamados de meia precisão . A maneira padrão pela qual os modelos de aprendizado profundo são treinados e armazenados é com total precisão. A conversão mais comumente usada é de precisão total para o formato int8.
A quantização pode ser uniforme ou não uniforme . No caso uniforme, o mapeamento da entrada para a saída é uma função linear que resulta em saídas uniformemente espaçadas para entradas uniformemente espaçadas. No caso não uniforme, o mapeamento da entrada para a saída é uma função não linear, portanto as saídas não serão espaçadas uniformemente para uma entrada uniforme.
Mergulhando no tipo uniforme, a função de mapeamento linear pode ser uma operação de escalonamento e arredondamento. Assim, a quantização uniforme envolve um fator de escala, S na equação.
Ao converter de digamos float16 para int8, observe que sempre podemos restringir valores entre -127 e mais 127 e garantir que o zero da entrada mapeie perfeitamente para o zero da saída levando a um mapeamento simétrico e esta quantização é, portanto, chamada de simétrica quantização .
Por outro lado, se os valores em ambos os lados de zero não forem iguais, por exemplo, entre -128 e +127. Além disso, se estivermos mapeando o zero da entrada para algum outro valor diferente de zero na saída, isso será chamado de quantização assimétrica . Como agora temos o valor zero deslocado na saída, precisamos contar isso em nossa equação incluindo o fator zero, Z , na equação.
Para saber como podemos escolher o fator de escala e o ponto zero, tomemos um exemplo de entrada distribuída como na figura acima no eixo dos números reais. O fator de escala divide essencialmente todo esse intervalo de entrada desde o valor mínimo r_min até o valor máximo r_max em partições uniformes. No entanto, podemos optar por cortar esta entrada em algum momento, digamos alfa para valores negativos e beta para valores positivos. Qualquer valor além de alfa e beta não é significativo porque mapeia para a mesma saída de alfa. Neste exemplo, são -127 e +127. O processo de escolha desses valores de recorte alfa e beta e, portanto, do intervalo de recorte é chamado de calibração .
Para evitar cortes excessivos, a opção mais fácil seria definir alfa como igual a r_min e beta como igual a r_max. E podemos calcular felizmente o fator de escala S , usando esses valores r_min e r_max . No entanto, isso pode tornar a saída assimétrica. Por exemplo, r_max na entrada poderia ser 1,5, mas r_min poderia ser apenas -1,2. Portanto, para restringir a quantização simétrica, precisamos que alfa e beta sejam os valores máximos dos dois e, claro, defina o ponto zero como 0.
A quantização simétrica é exatamente o que é usado ao quantizar pesos de redes neurais, pois os pesos treinados já são pré-calculados durante a inferência e não serão alterados durante a inferência. A computação também é mais simples em comparação com o caso assimétrico, pois o ponto zero é definido como 0.
Agora vamos revisar um exemplo em que as entradas são distorcidas em uma direção, digamos, para o lado positivo. Isso se assemelha ao resultado de algumas das funções de ativação mais bem-sucedidas, como ReLU ou GeLU. Além disso, as saídas das ativações mudam com a entrada. Por exemplo, a saída das funções de ativação é bem diferente quando mostramos duas imagens de um gato. Portanto, a questão agora é: “Quando calibramos a faixa para quantização?” É durante o treino? Ou durante a inferência e quando obtemos os dados para previsão?
Esta questão leva a vários modos de quantização, particularmente na Quantização Pós-Treinamento (PTQ). No PTQ, iniciamos com um modelo pré-treinado sem realizar treinamento adicional. Os dados cruciais exigidos do modelo envolvem dados de calibração, que são usados para calcular a faixa de recorte e, posteriormente, o fator de escala (S) e o ponto zero (Z). Normalmente, esses dados de calibração são derivados dos pesos do modelo. Após o processo de calibração, podemos proceder à quantização do modelo, resultando no modelo quantizado.
No Quantization Aware Training ou QAT, abreviadamente, quantizamos o modelo treinado usando procedimento padrão, mas depois fazemos ajustes adicionais ou retreinamento, usando novos dados de treinamento para obter o modelo quantizado. O QAT geralmente é feito para ajustar o parâmetro do modelo a fim de recuperar a precisão perdida ou qualquer outra métrica que nos preocupe durante a quantização. Portanto, o QAT tende a fornecer modelos melhores do que a quantização pós-treinamento.
Para fazer o ajuste fino, o modelo deve ser diferenciável. Mas a operação de quantização não é diferenciável. Para superar isso, usamos quantizadores falsos, como estimadores diretos. Durante o ajuste fino, esses estimadores estimam o erro de quantização, e os erros são combinados junto com o erro de treinamento para ajustar o modelo para um melhor desempenho. Durante o ajuste fino, as passagens para frente e para trás são realizadas no modelo quantizado em ponto flutuante. No entanto, os parâmetros são quantizados após cada atualização do gradiente.
Confira o vídeo abaixo explicando a quantização do modelo em aprendizado profundo
Isso cobre praticamente o básico da quantização. Começamos com a necessidade de quantização e os diferentes tipos de quantização, como simétrica e assimétrica. Também aprendemos rapidamente como podemos escolher os parâmetros de quantização – ou seja, o fator de escala e o ponto zero. E terminamos com diferentes modos de quantização. Mas como tudo isso é implementado em PyTorch ou TensorFlow? Isso fica para outro dia. Espero que este vídeo tenha fornecido algumas dicas sobre quantização em aprendizado profundo.
Espero ver você no meu próximo. Até lá, tome cuidado!