Typy kwantyzacji w StableHLO
Kwantyzacja to technika optymalizacji modeli uczenia maszynowego polegająca na przekształcaniu liczb zmiennoprzecinkowych (takich jak te używane w oryginalnych modelach) w liczby całkowite o mniejszej precyzji. Zmniejsza to zużycie pamięci i przyspiesza obliczenia, dzięki czemu modele są bardziej wydajne w przypadku wdrażania na urządzeniach o ograniczonych zasobach.
Kwantyzacja StableHLO jest zgodna ze specyfikacją kwantyzacji LiteRT i wykorzystuje jednolity schemat kwantyzacji z obsługą kwantyzacji na poziomie tensora i osi. Dziedziczy wyrażenie typu z dialektu Quant MLIR, co zapewnia standardowy sposób reprezentowania typów danych poddanych kwantyzacji.
Kwantyzacja jednolita mapuje wartości zmiennoprzecinkowe na liczby całkowite przy użyciu jednolitego rozmiaru kroku, co daje równomiernie rozmieszczone wartości skwantyzowane. Osiąga się to dzięki relacji afinicznej z użyciem 2 kluczowych parametrów kwantyzacji.
Kwantyzacja jednolita upraszcza reprezentację liczb zmiennoprzecinkowych przez mapowanie ich na równomiernie rozmieszczone liczby całkowite. Mapowanie to jest realizowane za pomocą transformacji afinicznej, która wykorzystuje 2 kluczowe parametry: skalę i punkt zerowy. Skala określa rozmiar kroku między kolejnymi skwantowanymi wartościami. Mniejsza skala oznacza, że skwantowane wartości są bliżej siebie. Punkt zerowy określa wartość całkowitą, która reprezentuje zero w oryginalnej przestrzeni liczb zmiennoprzecinkowych.
Związek między pierwotną wartością zmiennoprzecinkową (real_value) a skwantowaną wartością całkowitą (quantized_value) w kwantyzacji równomiernej jest następujący:
real_value = scale * (quantized_value - zero_point)
Kwantyzacja na tensor
W kwantyzacji na tensor używa się jednej skali i jednego punktu zerowego dla wszystkich wartości w tensorze. Skwantowany typ na tensor jest wyrażany w StableHLO jako:
quant.uniform scale:zero_point> Przykład: !quant.uniform<i8:f32, 0.01:50>
Jest to 8-bitowa liczba całkowita (i8) używana do przechowywania 32-bitowej liczby zmiennoprzecinkowej (f32) przy użyciu skali 0.01 i punktu zerowego 50.
Kwantyzacja na osi
Kwantyzacja na osi zapewnia bardziej precyzyjne podejście w porównaniu z kwantyzacją na tensorze. Kwantyzacja na osi przypisuje osobne skale i punkty zerowe do wycinków wzdłuż określonego wymiaru quantized_dimension tensora, zamiast używać jednej skali i jednego punktu zerowego dla całego tensora. Jest to szczególnie przydatne, gdy wartości znacznie różnią się w zależności od wymiarów, ponieważ pozwala lepiej zachować informacje i dokładność.
Rozważ tensor t o rozmiarach wymiarów [4, 3, 2]. Ten tensor kwantyzujemy wzdłuż drugiego wymiaru (quantized_dimension = 1). Oznacza to, że będziemy mieć 3 wycinki (ponieważ drugi wymiar ma rozmiar 3), z których każdy będzie miał własną skalę i punkt zerowy:
t[:, 0, :]: This slice gets scale[0] and zero_point[0].
t[:, 1, :]: This slice gets scale[1] and zero_point[1].
t[:, 2, :]: This slice gets scale[2] and zero_point[2].
W StableHLO typ skwantowany na osi jest wyrażany w ten sposób:
quant.uniform {scale0:zero_point0, scale1:zero_point1, ...}> gdzie długość scale:zero_point odpowiada liczbie wycinków wzdłuż quantized_dimension tensora zawierającego.
Przykład: tensor<4x3x2x!quant.uniform<i8:f32:1, {0.2:20, 0.1:10, 0.3:30}>>
Przekształcenia kwantyzacji w StableHLO
StableHLO udostępnia kilka etapów kompilacji, które umożliwiają różne przekształcenia i optymalizacje związane z kwantyzacją, co daje Ci elastyczność w obsłudze skwantyzowanych modeli. Są to:
stablehlo-legalize-qdq-to-quantized-op
Ten etap łączy wspólny wzorzec w modelach skwantyzowanych: operację dekwantyzacji, po której następuje operacja zmiennoprzecinkowa, a na końcu operacja kwantyzacji, w jedną operację skwantyzowaną. Szczegóły
stablehlo-legalize-quantized-op-to-qdq
Ten karnet działa odwrotnie niż poprzedni. Rozkłada operację skwantowaną na równoważną sekwencję operacji odwrotnej kwantyzacji, operacji zmiennoprzecinkowej i kwantyzacji.Szczegóły
stablehlo-legalize-quant-to-math
Ten etap przekształca operacje StableHLO na typach poddanych kwantyzacji w odpowiednie operacje na typach całkowitych. W zasadzie implementuje arytmetykę kwantyzacji za pomocą standardowych operacji matematycznych. Ten rozkład jest przydatny w przypadku systemów, które nie obsługują kwantyzacji natywnie, ale mogą używać arytmetyki kwantyzacji do wyrażania semantyki modeli skwantyzowanych. Szczegóły
stablehlo-quant-legalize-to-tosa-rescale
StableHLO umożliwia legalizację operacji kwantyzowanych do ich odpowiednich reprezentacji w dialekcie TOSA. Legalizacja ta ułatwia zgodność i interoperacyjność między StableHLO a TOSA. Ten etap strategicznie przekształca skwantowane operacje StableHLO w połączenie operacji StableHLO i TOSA, przy czym dialekt TOSA jest używany głównie w przypadku operacji rescale. Operacja tosa.rescale odgrywa kluczową rolę w dostosowywaniu skali i punktu zerowego wartości skwantowanych, umożliwiając dokładne przedstawienie skwantowanych danych w ramach TOSA. Szczegóły
tosa-rescale-legalize-to-stablehlo
Ten etap przekształca operacje zmiany skali TOSA na operacje matematyczne StableHLO. Jednym z głównych przypadków użycia tego przekazywania jest umożliwienie interpreterowi StableHLO oceny programów zawierających operacje zmiany skali TOSA. Szczegóły
Ocena programów skwantyzowanych
Interpreter referencyjny StableHLO może wydajnie wykonywać programy zawierające operacje kwantyzowane. Aby to osiągnąć, najpierw przekształca program w równoważną reprezentację, używając tylko operacji na liczbach całkowitych. Proces ten obejmuje serię przejść kompilatora, które przekształcają program przed jego interpretacją.
W zasadzie interpreter wykorzystuje stablehlo-legalize-quant-to-math
przekazywanie, aby przekształcić operacje skwantowane w odpowiednie implementacje arytmetyki liczb całkowitych. Ten etap wprowadza operacje transmisji CHLO do obsługi mnożenia/dzielenia skali i dodawania punktu zerowego. Aby zapewnić zgodność z interpreterem StableHLO, te operacje CHLO są następnie legalizowane do operacji StableHLO. Wprowadza to operacje związane z kształtem, które są następnie kanonizowane i optymalizowane za pomocą serii przebiegów kanonizacji.
Pełna sekwencja przebiegów w tym procesie obniżania wygląda tak:
stablehlo-legalize-quant-to-math
chlo-legalize-to-stablehlo
canonicalize
shape-legalize-to-stablehlo
stablehlo-canonicalize-dynamism
Skwantyzowane przypadki testowe
StableHLO udostępnia obszerny zestaw skwantyzowanych przypadków testowych, które pozwalają weryfikować poprawność i działanie skwantyzowanych operacji. Te przypadki testowe służą jako testy jednostkowe, które obejmują różne operacje StableHLO w scenariuszach kwantyzacji.
Typowy przykład skwantyzowanego przypadku testowego wygląda tak:
func.func @main() -> tensor<11xf32> {
%operand_0 = stablehlo.constant dense<...> : tensor<11xf32>
%operand_1 = stablehlo.constant dense<...> : tensor<11xf32>
%golden = stablehlo.constant dense<...> : tensor<11xf32>
%0 = stablehlo.uniform_quantize %operand_0 : (tensor<11xf32>) -> tensor<11x!quant.uniform<i8:f32, 0.3>>
%1 = stablehlo.uniform_quantize %operand_1 : (tensor<11xf32>) -> tensor<11x!quant.uniform<i8:f32, 0.3>>
%2 = stablehlo.add %1, %0 : tensor<11x!quant.uniform<i8:f32, 0.3>>
%result = stablehlo.uniform_dequantize %2 : (tensor<11x!quant.uniform<i8:f32, 0.3>>) -> tensor<11xf32>
%4 = stablehlo.custom_call @check.eq(%golden, %result) : (tensor<11xf32>, tensor<11xf32>) -> tensor<i1>
return %3 : tensor<11xf32>
}
Obejmuje to:
- Dane wejściowe: reprezentatywne wartości wejściowe operacji.
- Dane wyjściowe: oczekiwane dane wyjściowe operacji zastosowanej do danych wejściowych, zgodne z referencyjnym interpreterem StableHLO i ewaluatorem HLO.
Te przypadki testowe są przydatne w przypadku:
- Weryfikacja kwantyzacji StableHLO: sprawdzanie, czy zachowanie kwantyzacji operacji StableHLO jest zgodne z oczekiwanymi wynikami.
- Weryfikacja krzyżowa: porównanie działania kwantyzacji StableHLO z innymi implementacjami lub platformami.
- Debugowanie i rozwijanie: pomoc w rozwijaniu i debugowaniu nowych funkcji kwantyzacji lub optymalizacji.