Представьте: вам нужно научить камеру планшета почти мгновенно определять, что происходит в кадре. И это не просто «автомобиль» или «человек»: нужно различать и связывать разные категории объектов: документы, текст, людей, QR и штрихкоды. Казалось бы, достаточно взять предобученную модель и заточить для запуска на конкретном железе, в нашем случае это планшет KVADRA_T. Но задача оказалась сложнее из-за доменов классов. Для них не нашлось моделей, которые соответствовали заданным в проекте метрикам и времени исполнения. 

Меня зовут Анастасия Шпилёва, и я работаю в команде разработки программных ИИ-компонентов компании YADRO. В статье расскажу, почему я остановилась на multi-label классификации изображений. А также — как я собирала, размечала и валидировала датасет, от которого во многом зависит эффективность модели. 

Зачем такие сложности

В кадре и на изображении могут быть несколько объектов, которые должна распознать модель. Например, для умной камеры, которая умеет подстраивать режим под типы объектов в кадре или давать пользователю подсказки.

Допустим, вам нужно сделать селфи с паспортом для верификации. Если модель сможет распознать в кадре объекты «человек» и «документ», то подскажет, что не надо перекрывать пальцами текст, и выставит экспозицию так, чтобы лицо не было темным, а буквы засвеченными. Приложение умной галереи с помощью нейросети найдет на фото друзей с пятничной вечеринки или снимки квитанций за воду и электричество. 

Как научить планшет лучше видеть в темноте: MEFLUT, FastBurstDenoising и другие алгоритмы.

Бизнес-пользователям наша модель также упростит работу с изображениями. Например, с ее помощью можно реализовать автоматический поиск фото с чувствительными данными для банков. Менеджеры за день делают сотни снимков клиентов с паспортами: нужно их найти и удалить, чтобы не хранить персональные данные. Нейросеть поможет распознать объекты классов «документ» и «человек».

Подобные случаи на мобильных устройствах сложно решить с помощью мультиклассовой (multiclass) классификации. Разберемся, почему.

Dev-версия умной галереи на планшете KVADRA_T
Dev-версия умной галереи на планшете KVADRA_T

При мультиклассовом (multi-class) подходе модель выбирает один доминирующий класс на фото, выдавая вектор confidence (оценка уверенности). Если на фото одновременно и человек, и документ, то модель выберет только один класс.

Селфи с паспортом классифицировать с ее помощью не получится. Основная проблема будет в том, что четыре нужных класса относятся к разным визуальным доменам:

  • «Люди» — высокоуровневые объекты со сложной геометрией и вариативностью поз.

  • «Документы» — чаще всего прямоугольные области с определенной структурой.

  • «QR-коды» и «штрихкоды» — текстурные признаки, то есть повторяющиеся черные микроузоры элементов кодов. 

  • «Текст» — разнообразные по формату последовательности символов, организованных в строки и предложения.

Обычно в multi-class классификации классы стараются подбирать так, чтобы они были взаимоисключающими: либо QR, либо документ. В моей задаче метки неизбежно пересекаются. Например, документ с QR-кодом, который держит человек.

Можно запустить несколько моделей: первую для обнаружения людей на фото, вторую — текста, третью — QR и так далее. Но тогда придется жертвовать скоростью работы нейросети на планшете.

Что такое multi-label классификация

Вместо multi-class я использовала multi-label (многометочную) классификацию. Она позволяет определить каждый класс объектов независимо. На синтетическом примере ниже модель бинарной классификации сумела определить только то, что на фото с уверенностью 90% — собака. Мультиклассовая — выбрала собаку с уверенностью 50% из четырех вариантов. Multi-label же верно определила сразу два класса объектов, собаку и растение, присвоив фото соответствующие метки.

Multi-label модель лучше остальных подходит для классификации сцен, где на одном фото есть несколько объектов.

Приглашаем в ИИ-дивизион компании YADRO, где можно работать не только над ИИ-компонентами для планшета KVADRA_T, но и решать массу других задач, связанных с искусственным интеллектом и компьютерным зрением.

При проектировании архитектуры для multi-label классификации нельзя просто взять произвольный бэкбон и присоединить к нему одну голову. На практике такой подход не всегда работает. Поскольку классы (например, «люди» и «текст») относятся к разным доменам и имеют разное распределение признаков, в одной общей голове классификации веса неизбежно смещаются либо к слишком общим, либо к случайным признакам. Эмпирические тесты подтвердили: такая модель плохо справляется с разделением специфических сущностей, поэтому от идеи единого классификатора пришлось отказаться. F1-score одноголовой модели модели была около 82%, в то время как метрика F1-score многоголовой модели достигла 94%. 

F1-score — это метрика качества модели классификации, которая объединяет точность (precision) и полноту (recall) в одно число через их гармоническое среднее: 2* (precision * recall) / (precision + recall). Она показывает баланс между тем, насколько модель правильно находит положительные объекты и насколько мало делает ложных срабатываний. F1-score особенно важно использовать при несбалансированных классах, потому что обычная accuracy (доля правильных ответов) может быть высокой даже у плохой модели, а F1-score отражает реальное качество распознавания целевого класса.

Решение с разделением на несколько специализированных голов (multi-head) позволяет точнее моделировать зависимости, но оно напрямую влияет на время инференса. Здесь важно найти баланс: усложнение архитектуры повышает метрики, но может сделать модель слишком тяжелой для мобильного устройства. Требовалось решение, которое сохранит высокую скорость работы, не жертвуя при этом качеством классификации.

Наконец, нужно учитывать ограничения при конвертации в мобильные форматы, такие как TFLite и RKNN. Это простая задача только для классических моделей. На практике многие современные решения (например, сложные трансформерные архитектуры или слои вроде Adaptive Average Pool) плохо поддерживаются мобильным железом. Эти аппаратные ограничения продиктовали выбор архитектуры, которая легла в основу итоговой модели.

Здесь может возникнуть вопрос, почему не решается задача детекции, которая дает больше информации об объекте, в частности — его локализацию. Но основная проблема — сложный процесс сборки датасета: общедоступных данных нет, а SOTA-модели для получения bounding boxes выдают неточные результаты. Для этого требовалось бы больше времени, а сроки были сжатые. 

Разберем самый сложный и важный этап: сборку датасета. 

Сбор и автоматическая разметка данных

Корректный датасет во многом обеспечивает итоговое качество модели, и чем тщательнее удастся подобрать и очистить данные, тем лучше будет результат классификации кадров. 

Мне пришлось собирать данные по большей части с нуля. Дело в том, что у задачи очень специфичный набор классов из разных доменов, и готовых публичных multi-label датасетов под такую комбинацию просто нет — только фрагменты, которые имеют только один класс в качестве разметки.

Исходные изображения я собирала на Roboflow, Kaggle и в различных open source- репозиториях. При таком подходе очень важно следить за лицензиями: если вы планируете использовать модель в продакшене, нужно убедиться, что исходные данные это позволяют.

Пайплайн автоматической разметки строился на выборе подходящих SOTA-моделей для каждого класса. Процесс выглядел следующим образом: чтобы разметить, например, наличие текста на изображениях, я брала собранные датасеты с текстом и тестировала на них несколько SOTA-моделей, в основном предобученные модели из YOLO-семейства. Однако их F1 score был не выше 0,75, и я обратилась к VLM-моделям. 

В качестве разметчика я попробовала следующие VLM-модели: 

  • Google Gemini 2.5 Pro, 

  • GPT-4.1-mini (Vision), 

  • Claude-4.5-Sonnet (Vision), 

  • Qwen2.5-VL-72B-Instruct, 

  • InternVL3.5.

Особенно выделю модель Qwen2.5-VL-72B-Instruct: она отлично понимает контекст изображения и выдает точные описания или теги, при этом не превышая допустимый проектом порог потребления ресурсов GPU. Поскольку все датасеты имели хотя бы один класс, то я могла посчитать метрики между реальными классами и ответами Qwen. В итоге для всех классов F1-score получилась около 98%.

В процессе сборки я столкнулась с проблемой: в датасетах оказалось много дубликатов. При агрегации данных из Kaggle или Roboflow легко попасть в ловушку: разные датасеты часто включают в себя одни и те же изображения из open source-источников (например, из COCO). Имена файлов могут быть разными, а сами изображения — одинаковыми.

Выборку пришлось автоматически почистить от дубликатов, потому что повторяющиеся или очень похожие изображения заставляют модель чрезмерно фокусироваться на конкретных примерах, ухудшая обобщающую способность. Чтобы решить данную проблему, я использовала pHash. Если хеши подозрительно близки, запускала SSIM для подтверждения, что это одно и то же фото. Можно было решить проблему по-другому, взяв эмбеддинги изображений, которые получены через любой image embedder (ResNet, ViT и т.д.), и сравнив их косинусное расстояние. Результат должен быть лучше, но такой подход требует больше вычислительных ресурсов, а мне хватило моего решения. 

Наконец, расскажу о балансировке классов. Я стремилась избежать сильных перекосов, чтобы модель не начала отдавать предпочтение позитивному или негативному классу. Однако при автоматической разметке в задаче milti-label классификации это превращается в настоящий квест. Дело в том, что классы взаимосвязаны. Допустим, мне надо добавить позитивные примеры в класс «документ». Эти примеры содержат текст. Поэтому позитивных примеров текста станет больше, что влечет за собой дисбаланс в классе «текст». 

Поскольку я использовала Qwen в качестве разметчика изображений, то решила проблему следующим образом:

  1. Взяла сырой пул неразмеченных данных.

  2. Дала Qwen промпт-фильтр: «Есть ли на этом фото текст, но нет документа?» или «Присутствует ли класс X без класса Y?».

  3. Отобрала только те изображения, которые попадают в «просевшие» п�� статистике корзины.

Итоговое распределение классов в датасете мощностью 193K изображений
Итоговое распределение классов в датасете мощностью 193K изображений

Учитывая специфику данных и редкость некоторых сочетаний меток, я считаю этот результат достаточно хорошим. Однако вы можете видеть значительно больший дисбаланс в классе «текст». Из-за домена самого класса модель срабатывает на любые текстуры, напоминающие буквы (решетки, узоры, линии), поэтому было принято решение уменьшить количество экземпляров в данном классе.

Архитектура модели: как я пришла к multi-head подходу 

Изначально была протестирована архитектура с бэкбоном MobileNet V3 и одной головой классификации с сигмоидой на конце при функции потерь BCEWithLogitsLoss. Однако из-за разного домена классов и разной сложности извлекались более общие признаки (на это также указывали одинаковые карты активации), что не могло не влиять на результат. F1-score такой модели была около 82%.

Вторая итерация модели была не менее лаконичной: легкий бэкбон MobileNet V3 и четыре независимые головы классификации. Каждая голова состояла из линейных слоев с сигмоидами на конце. Количество слоев в каждой голове я вынесла в гиперпараметры, чтобы иметь возможность настраивать сложность классификатора под конкретный домен. Метрика F1-score достигла 94%. 

Вся итоговая модель стала результатом множества итеративных экспериментов. Я добавляла части архитектуры постепенно, каждый раз отслеживая изменения метрик карт активаций. Разница в F1-score между базовой моделью и итоговой моделью составила примерно 12%. Так в результате я добилась средней метрики F1-score в 94%. При этом в рамках проекта моя цель была получить не менее 92%.

Обучение и эксперименты

Посмотрим, какие гиперпараметры влияли на результат.

Первое, что очень сильно влияет, — разрешение картинки. Изначально я взяла разрешение 1024, но команда поправила: для инференса поступает слишком много информации и она дольше обрабатывается. Это правда: при обучении на разрешении 1024 инференс занимал около 96 мс без учета полного пайплайна с ресайзом, нормализацией и предобработкой. Поэтому я спустилась на разрешения 640 и 224 и в итоге остановилась на 640, так как это сразу уменьшило время инференса при схожих с 1024 метриках. 

Второе — количество слоев. Я задавала как гиперпараметр количество слоев в головах классификации и количество размораживаемых слоев в бэкбоне. Используя Optuna, я запускала эксперименты, например, «разморозь мне 30 слоев бэкбона», затем «40 слоев бэкбона», и смотрела, какое количество размораживаемых слоев будет оптимальным, чтобы на основе лучшего результата обучить итоговую модель.

Третье — это классика: перебор стандартных гиперпараметров через Optuna (batch size, learning rate, параметры оптимизатора и так далее). 

Наконец, выбор бэкбона, о чем я кратко рассказала выше. Я провела эксперименты и в итоге выбрала MobileNetV3 Large. Также я попробовала EfficientNet-Lite0, FastViT и другие модели из семейства MobileNet, но они либо уступали по качеству, либо не укладывались в нужное время.

Экспериментов было много, поэтому их нужно было где-то удобно отслеживать. Расскажу, что для этого пригодилось мне.

Полезные инструменты и анализ обучения

Мне нужен был отдельный веб-интерфейс, чтобы легко считывать, как проходят эксперименты, на каких датасетах, какие показывают метрики и так далее. 

Для этого я использовала ClearML, Optuna, а в самом начале — TensorBoard. В целом ClearML и TensorBoard похожи по своему назначению, но ClearML оказался для меня более удобным и информативным. Туда можно вывести все сложные графики: распределение градиентов в слоях, распределение весов и все остальные классические графики метрик качества.

ClearML

Позволяет удобно сравнивать и визуализировать результаты обучения, оркестрировать запуски задач, а также помогает проводить версионирование данных и моделей. А заодно автоматически логировать результаты.

Синтетический пример визуализации обучения с ClearML
Синтетический пример визуализации обучения с ClearML

Это иллюстрация первой итерации экспериментов с моей моделью. Очень удобно смотреть на распределение градиентов, чтобы контролировать сам процесс обучения. Ведь даже если взять классику вроде EfficientNet, не всегда понятно, почему модель выдает странные логиты. В ClearML или TensorBoard можно отследить эти градиенты, а также посмотреть карты активаций, чтобы наглядно увидеть, на каких именно изображениях и почему модель допускает ошибки

Линия распределения градиентов помогает понять, взрываются они или нет. И чтобы не собирать эту статистику куда-то отдельно и не выводить в файл, можно в режиме онлайн сразу посмотреть, как они изменяются и всё ли там хорошо. Соответственно, мы получаем удобное сравнение и визуализацию результатов.

В ClearML можно настроить оркестрацию и распределенный запуск задач, если позволяет мощность. А еще здесь очень удобное версионирование данных. Поскольку я запускала обучение на разных датасетах с разными SOTA-моделями, для меня такая возможность была очень полезной.

Optuna

Классический инструмент, которым, думаю, пользуются многие. Поэтому очень подробно останавливаться на нем не буду, ограничусь кратким перечислением основных возможностей: 

  • эффективный подбор гиперпараметров (TPE, CMA ES, Pruning, Bayesian optimization);

  • механизм Pruning: автоматическое раннее завершение бесперспективных экспериментов;

  • простая интеграция в код;

  • визуализация результатов оптимизации;

  • параллельные запуски без сложной настройки.

Я перебирала с помощью Optuna гиперпараметры, которые задавала в функции Objective. Единственное, что нужно интегрировать в код, — это сама функция Objective, а дальше нужно просто собирать метрики.

Конвертируем в мобильный формат

Конвертация состоит из двух этапов: сначала модель конвертируется в ONNX и только потом в TFLite, RKNN. Для формата ONNX я использовала torch.onnx.export со следующей конфигурацией: opset=18, режим EVAL, dynamo=True. 

При экспорте задаются имена входа/выхода, включается оптимизация констант, а затем выполняется валидация: модель проверяется через onnx.checker, после чего инференс сравнивается между PyTorch и ONNX Runtime (CUDA или CPU). 

Выходы сверяются по форме и значениям с некоторым допуском, что гарантирует численную эквивалентность и корректность конвертации. Далее ONNX-модель конвертируется в платформенные форматы. С помощью библиотеки RKNN SDK модель адаптируется под аппаратные ускорители Rockchip, а через утилиту onnx2tf преобразуется в TFLite для мобильных и веб-приложений. 

Вместо заключения

Время инференса на RKNN, то есть на NPU, при входном разрешении 640x640 составило 20 мс, а на CPU — около 30 мс. Качество после конвертации в среднем по всем классам (F1-score) осталось на уровне 94%, но для каждого класса оно было разным в зависимости от сложности домена.

20 мс — время без учета инициализации нейросети, но уже с препроцессингом и ресайзом. То есть исходное изображение в 12 мегапикселей сжимается до 640x640, а затем запускается уже заранее проинициализированная модель на NPU. Суммарно все вместе занимает порядка 30 миллисекунд. У меня была задача добиться инференса на планшете менее 50 мс, поэтому «запасные» 20 мс я планирую использовать для усложнения модели. 

На планшетах KVADRA_T функционал умной галереи с multi-label классификацией мы планируем добавить в следующем релизе kvadraOS.

Статьи, которые могут вас заинтересовать: