Resumen de cuantificación del modelo

En vista de los grandes parámetros del modelo de algoritmo de aprendizaje profundo, después de la implementación, ocupará memoria, aumentará la cantidad de cálculo y consumirá mucha energía. No es propicio para un mantenimiento posterior, porque cada tarea se realiza por separado. El aprendizaje multitarea puede aliviar eficazmente los problemas anteriores. Pero el aprendizaje multitarea puede traer algunos problemas, confusión entre tareas similares. Si es difícil de entrenar, el modelo se puede cuantificar desde otra perspectiva: reduce principalmente los requisitos de memoria y computación al reducir la cantidad de parámetros del modelo original o la cantidad de bits, lo que reduce aún más el consumo de energía. En la actualidad, el rendimiento más estable es la tecnología de cuantificación del modelo de INT8. En comparación con el cálculo FP32 del modelo original, la cuantificación de INT8 puede reducir el tamaño del modelo en 4 veces y reducir el requisito de ancho de banda de memoria en 4 veces. El soporte de hardware para INT8 el cálculo suele ser de 2 a 4 veces más rápido. Vale la pena señalar que la cuantificación es principalmente una técnica para acelerar la inferencia directa, y la mayoría de los operadores de cuantificación solo admiten el paso hacia adelante.

1. Ámbito de aplicación

tipo de datos:

Cuantificación de peso de 8 bits: data_type = qint8, el rango de datos es [-128, 127]
Cuantificación de activación de 8 bits: data_type = quint8, el rango de datos es [0, 255]
El sesgo generalmente no realiza operaciones de cuantificación, y todavía mantiene float32 Hay otro tipo de datos que necesita ser explicado de antemano. El peso generalmente se fija después de que converge el entrenamiento del modelo de punto flotante, por lo que puede cuantificarse directamente de acuerdo con los datos originales. Cada vez es diferente, por lo que para esto Problema, habrá un proceso de calibración en el proceso de cuantificación, es decir, un pequeño conjunto de datos de calibración se prepara de antemano, y el rango de datos de cada activación se registrará al probar este conjunto de datos de calibración.Luego determine un rango fijo basado en el valor registrado.

Backends soportados:

具有 AVX2 支持或更高版本的 x86 CPU:fbgemm
ARM CPU:qnnpack

2. Clasificación de los métodos de cuantificación

Cuantificación dinámica posterior al entrenamiento: este es el método de cuantificación más simple. El entrenamiento posterior se refiere a la operación de cuantificación después de que converge el entrenamiento del modelo de punto flotante, donde el peso se cuantifica por adelantado y la activación se cuantifica dinámicamente durante el proceso de razonamiento directo, es decir Cada vez, la escala y el punto cero deben calcularse para cada capa de acuerdo con el rango de datos de punto flotante real y luego cuantificarse;

Post Training Static Quantization: El primero no es muy común. En términos generales, Post Training Quantization se refiere a este método estático, y este método es el más utilizado. El peso también se cuantifica de antemano como el anterior, y luego la Activación también ser cuantificado en base a la escala fija y punto_cero registrados en el proceso de calibración anterior, y no hay recálculo de los parámetros de cuantificación _(_escala y punto_cero) en todo el proceso;

Entrenamiento consciente de la cuantificación: para algunos modelos con una pérdida grave de precisión durante el entrenamiento de punto flotante + cuantificación, se requiere un entrenamiento consciente de la cuantificación, es decir, el proceso de cuantificación se simula durante el proceso de entrenamiento. Aunque los datos se expresan como float32, el valor real valor Sin embargo, el intervalo está limitado por el parámetro de cuantificación. En cuanto a por qué la operación de cuantificación no se simula al comienzo del entrenamiento, se debe a que la precisión de 8 bits no es suficiente para hacer que el modelo no converja fácilmente, e incluso el uso directo de 16 bits para el entrenamiento de cuantificación desde cero es extremadamente fácil de fallar. para converger.

3. Tome la cuantificación del modelo onnx como ejemplo

La cuantificación en ONNXRuntime se refiere a la cuantificación lineal de 8 bits del modelo ONNX.Durante el proceso de cuantificación, el valor real de punto flotante se asigna al espacio de cuantificación de 8 bits, y su forma es: VAL_fp32 = Scale * (VAL_quantized - Zero_point), la escala es un número real positivo, se utiliza para asignar números de coma flotante al espacio de cuantificación, el método de cálculo es el siguiente: para cuantificación asimétrica: escala = (rango_máximo_datos - rango_mínimo_datos) / (rango_máximo_cuantificación - rango_mínimo_cuantificación), para cuantificación simétrica : scale = abs(data_range_max, data_range_min) * 2 / (quantization_range_max - quantization_range_min), Zero_point representa cero en el espacio de cuantificación: Es importante que los valores cero de coma flotante sean exactamente representables en el espacio de cuantificación. Esto se debe a que muchas CNN usan relleno cero. Si no se representa el 0 de forma única después de la cuantificación, se producirán errores de precisión.

Método de cuantificación: ONNXRuntime admite dos métodos de cuantificación de modelos:

Cuantificación dinámica:

Para la cuantificación dinámica, el factor de escala (Scale) y el punto cero (Zero Point) se calculan en el momento de la inferencia y son específicos para cada activación.
Por lo tanto, son más precisos, pero introducen una sobrecarga computacional adicional

Cuantificación estática:

Para la cuantificación estática, se calculan fuera de línea utilizando el conjunto de datos de calibración
Todas las activaciones tienen el mismo factor de escala (Escala) y punto cero (Punto cero)

Selección de método:

En general, se recomienda utilizar cuantificación dinámica para modelos basados ​​en RNN y transformadores y cuantificación estática para modelos CNN.

Tipo de cuantificación: ONNXRuntime admite dos tipos de datos de cuantificación

Int8 (QuantType.QInt8): entero de 8 bits con signo
UInt8 (QuantType.QUInt8): entero de 8 bits sin signo

Selección del tipo de datos:

Combinando activación y peso, el formato de los datos puede ser (activación: uint8, peso: uint8), (activación: uint8, peso: int8), etc.
Aquí U8U8 se usa como abreviatura de (activación: uint8, peso: uint8), U8S8 se usa como abreviatura de (activación: uint8, peso: int8) y S8U8, S8S8 es la abreviatura de los otros dos formatos. OnnxRuntime Quantization en la CPU puede ejecutar U8U8, U8S8 y S8S8.
S8S8 con formato QDQ es el predeterminado para rendimiento y precisión y debe ser la primera opción. Solo prueba U8U8 si la precisión cae mucho.
Tenga en cuenta que S8S8 con formato QOperator será lento en CPU x86-64 y, en general, debe evitarse. OnnxRuntime Quantization en GPU solo admite el formato S8S8. En máquinas x86-64 con extensiones AVX2 y AVX512, OnnxRuntime usa la instrucción VPMADDUBSW de U8S8 para mejorar el rendimiento, pero esta instrucción sufre problemas de saturación. En términos generales, esto no es un gran problema para el resultado final. Si la precisión de algunos modelos cae significativamente, puede deberse a la saturación. En este caso puedes probar con el formato reduce_range o U8U8, no hay problema de saturación. No existe tal problema en otras arquitecturas de CPU (x64 con VNNI y ARM).

Formato de cuantificación: ONNXRuntime admite dos formatos de modelo de cuantificación:

Orientado al tensor, también conocido como Quantize y DeQuantize (QuantFormat.QDQ)

Este formato utiliza DQ (Q (tensor)) para simular el proceso de cuantificación y descuantificación, y los operadores QuantizeLinear y DeQuantizeLinear también transportan parámetros de cuantificación.

Orientado a operadores (QuantFormat.QOperator)

Todos los operadores de cuantificación tienen sus propias definiciones ONNX, como QLinearConv, MatMulInteger, etc.

Script de cuantificación 4.onnx, cuantificación de modelo dividido como ejemplo, disponible para pruebas personales

# fp_32 to int8
import onnx
from onnxruntime.quantization import quantize_dynamic, QuantType
#需要量化的onnx模型
model_fp32 = 'input.onnx'
#量化后的模型
model_quant = 'output1.onnx'
quantized_model = quantize_dynamic(model_fp32, model_quant, weight_type=QuantType.QUInt8)


# QAT quantization  QAT量化
import onnx
from onnxruntime.quantization import quantize_qat, QuantType
#需要量化的onnx模型
model_fp32 = 'input.onnx'
#量化后的模型
model_quant = 'output2.onnx'
quantized_model = quantize_qat(model_fp32, model_quant)
#fp_32 to fp_16
import onnx
from onnxconverter_common import float16
model=onnx.load('your.onnx')
model_fp16=float16.convert_float_to_float16(model)
onnx.save(model_fp16,'your_result.onnx')

Si el modelo funciona mal después de la cuantificación, se puede considerar la cuantificación parcial, es decir, precisión mixta.

import onnx
from onnxconverter_common import float16
model=onnx.load('your.onnx')
model_fp16=auto_convert_mixed_precision(model,test_data,rtol=0.01,atol=0.001,keep_io_types=Ture)
onnx.save(model_fp16,'your_result.onnx')

Supongo que te gusta

Origin blog.csdn.net/hasque2019/article/details/129952687
Recomendado
Clasificación