Оптимізація ML-моделі (квантизація) для мобільного пристрою
Ми стикалися з задачею: модель детекції об'єктів займає 100 МБ і працює 200 мс на пристрої користувача. Після квантизації — 25 МБ і 50 мс. Але іноді точність падає непередбачувано. Ділимося досвідом: як обирати метод, аналізувати шари та верифікувати результат. Нижче — перевірені підходи для iOS та Android з кодом і конкретними метриками.
Квантизація — це переведення ваг та активацій моделі з float32 у формат з меншою розрядністю: float16, int8, int4. Квантизація використовує техніки масштабування (scale factor) та зміщення (zero point) для відображення діапазону float32 у цілочисельний діапазон int8 ([-128, 127] або [0, 255]). Формула: real_value = scale * (quantized_value - zero_point). Вибір scale та zero_point мінімізує похибки обрізання та округлення, які виникають через обмежену розрядність. Модель ResNet-50 важить 98 МБ у FP32. Після int8 квантизації — 25 МБ. Швидкість інференсу на мобільному CPU зростає в 2–4× за рахунок зменшення об'єму даних та використання цілочисельних інструкцій ARM NEON/SVE. Але проста квантизація часто знижує точність сильніше, ніж хотілося б. Правильна квантизація — це підбір методу, аналіз чутливих шарів та верифікація деградації.
Наші показники: понад 5 років досвіду в мобільній оптимізації, 15+ успішних проєктів з квантизації для клієнтів з e-commerce, fintech та IoT. Гарантуємо збереження точності в межах обговорених допусків. Замовте оптимізацію моделі — отримайте консультацію по вашому проєкту. Вартість PTQ для однієї моделі від $500, QAT — від $2000, залежно від складності.
Як обрати метод квантизації?
Post-Training Quantization (PTQ) — квантизуємо вже навчену модель без донавчання. Два варіанти:
- Dynamic quantization — ваги в int8, активації обчислюються в float32 у рантаймі. Не потребує калібрувальних даних. Для RNN/Transformer (BERT, LLM) дає хороший приріст. Для CNN менш ефективний.
- Static quantization — і ваги, і активації в int8. Потребує calibration dataset (100–500 репрезентативних прикладів) для визначення діапазону активацій через KL-дивергенцію або гістограмний метод (MinMax, Entropy). Швидший за dynamic, але потрібна калібровка.
Quantization-Aware Training (QAT) — модель донавчається з «симульованою» квантизацією (використання straight-through estimator). Ваги адаптуються до зниженої точності. Найкраща якість, але потребує доступу до навчального датасету та GPU-часу.
| Метод | Дані | Точність | Швидкість | Складність |
|---|---|---|---|---|
| Dynamic PTQ | Не потрібні | Середня | Висока | Низька |
| Static PTQ | 100–500 прикладів | Висока | Дуже висока | Середня |
| QAT | Повний датасет | Дуже висока | Висока | Висока |
Код Static PTQ в PyTorch
import torch
from torch.quantization import quantize_static, get_default_qconfig
model.eval()
model.qconfig = get_default_qconfig('qnnpack') # для ARM
torch.quantization.prepare(model, inplace=True)
with torch.no_grad():
for batch in calibration_loader:
model(batch)
torch.quantization.convert(model, inplace=True)
TFLite квантизація: full integer
Код TFLite full int8 конвертації
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
def representative_dataset():
for sample in calibration_data[:500]:
yield [sample.astype(np.float32)]
converter.representative_dataset = representative_dataset
tflite_model = converter.convert()
Full int8 модель працює на NNAPI та Hexagon DSP — там, де FP16 не підтримується. NNAPI delegation дозволяє виконувати квантизовані моделі на спеціалізованих апаратних прискорювачах (DSP, NPU) з мінімальним споживанням енергії. На Snapdragon 778G через Hexagon — 5–8× швидше CPU при правильній INT8 квантизації. Порівняно з FP32, INT8 модель в 4 рази менше за розміром. За швидкістю INT8 квантизація краще за FP16 у 2 рази на Android.
Core ML квантизація на iOS
import coremltools as ct
from coremltools.optimize.coreml import (
OptimizationConfig,
OpLinearQuantizerConfig,
linearly_quantize_weights
)
mlmodel = ct.models.MLModel("model_fp32.mlpackage")
config = OptimizationConfig(
global_config=OpLinearQuantizerConfig(
mode="linear_symmetric",
dtype=np.int8,
granularity="per_channel"
)
)
compressed_model = linearly_quantize_weights(mlmodel, config)
compressed_model.save("model_int8.mlpackage")
per_channel квантизація — окремий scale factor для кожного вихідного каналу згорткового шару. Значно точніше per_tensor (один scale на весь шар). Для CNN зазвичай виправдано, хоча трохи повільніше.
| Характеристика | Core ML | TensorFlow Lite |
|---|---|---|
| Формат ваг | FP16/INT8 (weight-only) | INT8 (full integer) |
| Калібровка | Не потрібна для weight-only | Потрібна для static |
| Підтримка NNAPI | Немає (iOS) | Так (Android) |
| Інструмент | coremltools | TFLiteConverter |
| Performance | ~2× на iPhone | ~3-4× на Android з DSP |
Як компенсувати втрату точності?
Не всі шари однаково переносять квантизацію. Перший і останній шари мережі, а також шари attention у трансформерах — часто найчутливіші. Інструмент: per-layer sensitivity analysis.
baseline_accuracy = evaluate(float_model, test_loader)
for layer_name in get_all_quantizable_layers(model):
single_layer_model = quantize_single_layer(model, layer_name)
layer_accuracy = evaluate(single_layer_model, test_loader)
sensitivity = baseline_accuracy - layer_accuracy
print(f"{layer_name}: sensitivity={sensitivity:.4f}")
Шари з високою чутливістю залишаємо у FP32 — це mixed precision quantization. Решту переводимо в INT8. 5–10% «важких» шарів залишаються у FP32, модель втрачає тільки 20–30% об'єму замість 75%, але точність зберігається.
Критерії коректної квантизації
Після квантизації обов'язково:
-
Точність на тестовому датасеті — порівнюємо top-1/top-5 accuracy з оригіналом. Допустима деградація: FP16 — <0.5%, INT8 — <2%. Якщо більше — переходимо до QAT або mixed precision.
-
Числова похибка — на однакових входах порівнюємо виходи float та quantized моделі. MSE < 0.01 зазвичай прийнятно.
-
Швидкість на реальних пристроях — не на симуляторі. Xcode Instruments → Core ML Profiler для iOS,
adb shell am instrument+ TFLite Benchmark Tool для Android. -
Краш-тест — різні входи, edge cases (чорне зображення, дуже яскраве, нестандартний aspect ratio). INT8 моделі іноді overflow на екстремальних входах.
Практичний кейс з нашої практики (наш клієнт)
У нашій практиці ми оптимізували модель детекції об'єктів YOLOv8n для нашого клієнта з e-commerce. У FP32 — 6.3 МБ, 45 мс на iPhone 13. Після Core ML INT8 квантизації — 1.8 МБ, 12 мс. mAP впав з 37.3 до 36.1 — в межах допустимого для задачі. На Snapdragon 8 Gen 1 через TFLite INT8 + NNAPI — 8 мс. Економія на хмарних обчисленнях становить до 75%, що при типових витратах $2000/міс дає $1500 економії щомісяця.
Що входить у роботу
- Аудит вихідної моделі та вибір методу (PTQ/QAT, INT8/FP16).
- Підготовка калібрувального датасету та налаштування calibration.
- Аналіз чутливих шарів та налаштування mixed precision.
- Повна квантизація з верифікацією точності.
- Вимірювання швидкості на цільових пристроях (iOS/Android).
- Звіт по деградації та рекомендації.
- Інтеграція квантизованої моделі у ваш пайплайн.
Згідно з PyTorch documentation (https://pytorch.org/docs/stable/quantization.html), PTQ може зменшити розмір моделі до 4 разів. Для детального вивчення також рекомендуємо TensorFlow Lite post-training quantization guide (https://www.tensorflow.org/lite/performance/post_training_quantization).
Орієнтири за строками та вартістю
PTQ для однієї моделі з верифікацією — 1–2 тижні, вартість від $500. QAT з повним циклом донавчання та тестуванням — 3–6 тижнів залежно від розміру датасету, вартість від $2000. Квантизація знижує витрати на зберігання моделі до 75%.
Зв'яжіться з нами, щоб оцінити ваш проєкт. Ми допоможемо підібрати оптимальний метод і гарантуємо результат.







