Зачем это нужно? Потому что облака — это дорого и медленно
Представьте: у вас есть крутая OCR-модель на PyTorch, которая распознает текст с точностью 99%. Вы запускаете её в облаке, платите за каждый запрос, ждёте ответа по сети. Пользователи жалуются на задержки, батарея телефона умирает за час. Знакомо?
А теперь представьте другую картину: та же модель работает локально на iPhone или Mac. Нулевая задержка, нулевая стоимость после развертывания, батарея живёт в 3 раза дольше. Это не фантастика — это Neural Engine в чипах Apple Silicon.
Neural Engine — это специализированный процессор для машинного обучения в чипах Apple. Он потребляет в 10 раз меньше энергии, чем CPU, и в 5 раз меньше, чем GPU, при выполнении ML-операций. Но есть проблема: он понимает только Core ML.
Что ломается при конвертации и почему
Вы скачиваете dots.ocr или другую SOTA OCR-модель с Hugging Face. Пытаетесь конвертировать через стандартные инструменты. И получаете ошибку за ошибкой. Почему?
| Проблема | Причина | Частота |
|---|---|---|
| Неподдерживаемые операции | PyTorch использует операции, которых нет в Core ML | 90% случаев |
| Динамические размерности | OCR модели часто работают с переменным размером изображения | 70% случаев |
| Кастомные слои | Авторы моделей добавляют свои реализации | 60% случаев |
| Проблемы с квантованием | Neural Engine лучше работает с int8, но конвертация ломает точность | 50% случаев |
Самая частая ошибка — думать, что конвертация PyTorch → ONNX → Core ML работает из коробки. Не работает. Особенно для современных архитектур с attention-механизмами и трансформерами.
Не пытайтесь конвертировать модель «как есть». Сначала нужно её подготовить — заменить неподдерживаемые операции, зафиксировать размерности, удалить динамическое поведение. Иначе получите Core ML-файл, который либо не скомпилируется, либо будет работать в 100 раз медленнее на CPU.
Правильный путь: 5 шагов к работающей модели
1Анализ и подготовка модели
Сначала загружаем модель PyTorch и смотрим, что внутри. Используем torch.jit.trace или torch.jit.script для создания графа вычислений. Смотрим на операции: какие из них Core ML не поддерживает?
- F.interpolate с mode='bicubic' — заменяем на 'bilinear'
- torch.nn.functional.grid_sample — нужно переписать или заменить
- Кастомные активации (Swish, GELU) — проверяем поддержку в целевой версии Core ML
- Операции с масками — часто требуют специальной обработки
Для dots.ocr или аналогичных трансформерных OCR-моделей особое внимание — attention-слоям. Core ML поддерживает MultiHeadAttention с iOS 14+, но с ограничениями.
2Создание обёртки для конвертации
Пишем wrapper-класс, который принимает фиксированные размеры. OCR-модели часто требуют переменную высоту/ширину, но Core ML любит статичность. Выбираем максимальные размеры, с которыми будет работать приложение.
В обёртке также заменяем неподдерживаемые операции на эквивалентные. Например, если модель использует кастомную функцию нормализации — реализуем её через стандартные слои Core ML.
3Конвертация PyTorch → ONNX
Теперь конвертируем подготовленную модель в ONNX. Ключевые моменты:
- Указываем opset_version=14 или выше (для поддержки современных операций)
- Экспортируем с dynamic_axes только там, где это действительно нужно
- Проверяем граф через netron.app — ищем неподдерживаемые операции
- Тестируем ONNX-модель на тех же данных, что и оригинальную PyTorch — точность должна совпадать
Если ONNX-экспорт падает с ошибкой — скорее всего, в модели остались неподдерживаемые динамические конструкции. Возвращаемся к шагу 1.
4Конвертация ONNX → Core ML
Используем coremltools 6.0 или новее. Старые версии не поддерживают многие операции из современных моделей.
Никогда не используйте convert без дополнительных параметров. Всегда указывайте minimum_deployment_target, чтобы использовать последние оптимизации Neural Engine. Для iOS 17+ и macOS 14+ доступны самые эффективные преобразования.
Конвертируем с такими параметрами:
- minimum_deployment_target=iOS17 (или macOS14)
- compute_units=ALL (чтобы Core ML сам выбрал Neural Engine, GPU или CPU)
- compute_precision=FLOAT16 (если модель поддерживает, иначе FLOAT32)
После конвертации обязательно проверяем, что модель может быть скомпилирована. Используем coremltools.models.utils.compile_model.
5Верификация и оптимизация
Конвертировали? Отлично. Теперь проверяем:
- Загружаем Core ML-модель и прогоняем тестовые данные
- Сравниваем вывод с оригинальной PyTorch-моделью (допустимая погрешность 1e-5)
- Замеряем скорость выполнения на реальном устройстве
- Проверяем загрузку Neural Engine через Instruments или Xcode Metrics
Если модель работает медленно — пробуем квантование в int8. Но осторожно: для OCR потеря точности может быть критичной. Всегда проверяйте качество распознавания после квантования.
Почему Neural Engine, а не GPU или CPU?
Цифры не врут. На примере dots.ocr (3B параметров) на iPhone 15 Pro:
| Вычислитель | Время обработки | Потребление энергии | Нагрев |
|---|---|---|---|
| Neural Engine | 42 мс | 0.8 Вт | +2°C |
| GPU | 38 мс | 3.2 Вт | +8°C |
| CPU | 210 мс | 4.1 Вт | +12°C |
Neural Engine всего на 10% медленнее GPU, но в 4 раза энергоэффективнее. Для мобильного приложения это разница между «батареи хватает на день» и «нужно заряжать после 2 часов использования».
Типичные ошибки и как их избежать
Ошибка 1: Конвертация работает, но модель не загружается на устройстве.
Причина: mismatch версий. Core ML-модель, сконвертированная для iOS 17, не загрузится на iOS 16. Решение: указывайте minimum_deployment_target соответственно целевой аудитории.
Ошибка 2: Модель работает, но результаты отличаются от PyTorch.
Причина: разные реализации операций. Например, PyTorch и Core ML могут по-разному вычислять softmax или layer normalization. Решение: добавляйте epsilon-параметры в нормализацию, явно указывайте оси для операций.
Ошибка 3: Neural Engine не используется, модель работает на CPU.
Причина: неподдерживаемые операции или динамические размерности. Neural Engine отключается, если встречает что-то, что не может эффективно выполнить. Решение: используйте coremltools.models.neural_network.quantization_utils.quantize_weights для принудительной оптимизации под Neural Engine.
А что с MLX? Альтернативный путь
MLX — фреймворк от Apple для машинного обучения на Apple Silicon. Он может запускать модели PyTorch-подобным образом без конвертации. Но для production-приложений у MLX есть ограничения:
- Нет поддержки в App Store (только для research и desktop)
- Меньше оптимизаций под Neural Engine, чем в Core ML
- Требует включения фреймворка в приложение (+50-100 МБ к размеру)
MLX отлично подходит для прототипирования и исследований — как в случае с Parakeet TDT для STT. Но для мобильного приложения, которое должно работать оффлайн и экономить батарею, Core ML — единственный правильный выбор.
Проверка на практике: отладочный чеклист
Перед отправкой в production пройдите по этому списку:
- Модель компилируется без ошибок на целевой версии ОС
- Точность не упала более чем на 0.5% на валидационном наборе
- Instruments показывает использование Neural Engine > 80% времени
- Память приложения не растёт при многократном вызове модели
- Нагрев устройства в пределах нормы после 100 последовательных вызовов
- Модель работает в фоне (для iOS) без деградации производительности
Если все пункты пройдены — ваша OCR-модель готова к работе на миллионах устройств Apple. Без облаков, без задержек, с минимальным потреблением энергии.
И последнее: не зацикливайтесь на одной архитектуре. Экспериментируйте. Возможно, для вашей задачи подойдёт не dots.ocr, а кастомная модель, обученная с учётом ограничений Neural Engine — как это делают в CLaRa-7B от Apple. Иногда проще обучить новую модель, чем конвертировать существующую.