Especificação de quantização de 8 bits do TensorFlow Lite

O documento a seguir descreve a especificação do esquema de quantização de 8 bits do TensorFlow Lite. O objetivo é ajudar os desenvolvedores de hardware a fornecer suporte de hardware para inferência com modelos quantizados do TensorFlow Lite.

Resumo das especificações

Estamos fornecendo uma especificação e só podemos fornecer algumas garantias de comportamento se a especificação for seguida. Também entendemos que diferentes hardwares podem ter preferências e restrições que podem causar pequenos desvios ao implementar as especificações que resultam em implementações que não são exatas em termos de bits. Embora isso possa ser aceitável na maioria dos casos (e forneceremos um conjunto de testes que, até onde sabemos, incluem tolerâncias por operação que coletamos de vários modelos), a natureza do aprendizado de máquina (e do aprendizado profundo nos casos mais comuns) caso) torna impossível fornecer quaisquer garantias concretas.

A quantização de 8 bits aproxima os valores de ponto flutuante usando a seguinte fórmula.

\[real\_value = (int8\_value - zero\_point) \times scale\]

Os pesos por eixo (também conhecidos como por canal em Conv ops) ou por tensor são representados por valores de complemento de dois int8 no intervalo [-127, 127] com ponto zero igual a 0. Ativações/entradas por tensor são representadas por int8 valores de complemento de dois no intervalo [-128, 127] , com um ponto zero no intervalo [-128, 127] .

Existem outras exceções para operações específicas documentadas abaixo.

Inteiro assinado versus inteiro não assinado

A quantização do TensorFlow Lite priorizará principalmente ferramentas e kernels para quantização int8 para 8 bits. Isso ocorre para a conveniência da quantização simétrica ser representada pelo ponto zero igual a 0. Além disso, muitos back-ends têm otimizações adicionais para acumulação int8xint8 .

Por eixo vs por tensor

A quantização por tensor significa que haverá uma escala e/ou ponto zero por tensor inteiro. A quantização por eixo significa que haverá uma escala e/ou zero_point por fatia na quantized_dimension . A dimensão quantizada especifica a dimensão da forma do Tensor à qual correspondem as escalas e os pontos zero. Por exemplo, um tensor t , com dims=[4, 3, 2, 1] com parâmetros de quantização: scale=[1.0, 2.0, 3.0] , zero_point=[1, 2, 3] , quantization_dimension=1 será quantizado em a segunda dimensão de t :

t[:, 0, :, :] will have scale[0]=1.0, zero_point[0]=1
t[:, 1, :, :] will have scale[1]=2.0, zero_point[1]=2
t[:, 2, :, :] will have scale[2]=3.0, zero_point[2]=3

Freqüentemente, o quantized_dimension é o output_channel dos pesos das convoluções, mas em teoria pode ser a dimensão que corresponde a cada produto escalar na implementação do kernel, permitindo mais granularidade de quantização sem implicações de desempenho. Isso traz grandes melhorias na precisão.

TFLite tem suporte por eixo para um número crescente de operações. No momento deste documento, existe suporte para Conv2d e DepthwiseConv2d.

Simétrico vs assimétrico

As ativações são assimétricas: elas podem ter seu ponto zero em qualquer lugar dentro do intervalo int8 assinado [-128, 127] . Muitas ativações são de natureza assimétrica e um ponto zero é uma maneira relativamente barata de obter efetivamente um bit binário extra de precisão. Como as ativações são multiplicadas apenas por pesos constantes, o valor constante do ponto zero pode ser bastante otimizado.

Os pesos são simétricos: forçados a ter ponto zero igual a 0. Os valores dos pesos são multiplicados pelos valores de entrada dinâmica e ativação. Isso significa que há um custo de tempo de execução inevitável de multiplicar o ponto zero do peso pelo valor de ativação. Ao impor que o ponto zero é 0, podemos evitar esse custo.

Explicação da matemática: é semelhante à seção 2.3 em arXiv:1712.05877 , exceto pela diferença de que permitimos que os valores da escala sejam por eixo. Isso generaliza prontamente, como segue:

\(A\) é um \(m \times n\) matriz de ativações quantizadas.
\(B\) é um \(n \times p\) matriz de pesos quantizados.
Considere multiplicar o \(j\)ª linha de \(A\), \(a_j\) pelo \(k\)a coluna de\(B\), \(b_k\), ambos de comprimento \(n\). Os valores inteiros quantizados e os valores de pontos zero são \(q_a\), \(z_a\) e \(q_b\), \(z_b\) respectivamente.

\[a_j \cdot b_k = \sum_{i=0}^{n} a_{j}^{(i)} b_{k}^{(i)} = \sum_{i=0}^{n} (q_{a}^{(i)} - z_a) (q_{b}^{(i)} - z_b) = \sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)} - \sum_{i=0}^{n} q_{a}^{(i)} z_b - \sum_{i=0}^{n} q_{b}^{(i)} z_a + \sum_{i=0}^{n} z_a z_b\]