- BrainTools - https://www.braintools.ru -
Привет! Меня зовут Владимир Морозов, я старший дата-сайентист в отделе автоматической модерации Авито [1].
Раньше мы блокировали объявления, которые нарушают правила публикации, а теперь исправляем — с помощью ML-системы. Так мы сохраняем количество контента, сокращаем стоимость модерации и улучшаем пользовательский опыт [2].
В статье подробно расскажу обо всех этапах внедрения новой ML-механики: от идеи и исследования подходов до оптимизации нейронок и вывода в продакшен.

Дано: зачем Авито модерация изображений [3]
Проблемы классической модерации: долго, дорого, неудобно [4]
Идея №1: не удалять, а блюрить изображения [5]
Планируем архитектуру и обучаем ML-модель [6]
Идея №2: не блюрить, а inpainting [8]
Резюме [10]
Мы часто сталкиваемся с нарушениями правил публикации. Чаще всего люди оставляют ссылки на другие площадки или свои контакты на фото, но могут быть и случаи несоблюдения законодательства РФ. Основная задача модерации — находить эти нарушения.
До нашего проекта по автомодерации в Авито применяли три сценария для контента:
уверены, что всё в порядке — публиковали фото;
не уверены на 100% — направляли на ручную модерацию;
уверены, что есть нарушения — блокировали или просили заменить фото в зависимости от того, насколько критичный случай.
На Авито публикуют много объявлений, поэтому модерация для нас очень важна. Но у описанных выше сценариев есть минусы:
ручная модерация стоит дорого. Её нужно качественно валидировать, а ещё она может длиться часами. Автомодерация же в среднем занимает секунды;
блокировка и отклонения уменьшают количество контента. Не все публикуют объявления с исправлениями, а это проблема: чем меньше публикаций на площадке, тем хуже опыт пользователей.
В идеале нам бы хотелось, чтобы модерация работала так: если в публикации есть нарушения, то мы автоматически их исправляем и публикуем контент.
Изначально мы рассматривали самый простой способ — удалять фото с нарушением. Но в таком случае возникает проблема: если в объявлении всего одна фотография, то мы теряем всю визуальную часть публикации, что очень плохо.
Поэтому мы стали думать, как удалять не всю фотографию, а только нарушения на ней. Например, такого результата можно достичь с помощью блюра. Так мы сохраним визуальную информацию и ничего не заблокируем — все довольны.
Мы решили начать с контактов на фото как с некритичной причины. Часто люди даже не задумываются, что это нарушение правил публикации. На Авито примерно 1% фотографий содержит контакты — это очень много.
В одном объявлении в среднем пять изображений. Если переводить это в нагрузку на модерационные сервисы, то в пиковые моменты она достигает 10 000 RPS. В среднем — 1 500 RPS, а мы должны модерировать где-то за секунду, из этого вытекают ограничения.
Шаг 1. Планируем архитектуру. С одной стороны, наша система должна быть быстрой, но с другой, у нас не так уж много фотографий с контактами. Поэтому мы выбрали решение из двух частей:
Классификатор отсеивает большинство потока — фотографии, где нет контактов. Он должен быть довольно быстрым, потому что у нас жёсткие требования по скорости автомодерации. Мы прикинули, что предпроцессинг и сам инференс модели должны занимать меньше 100 миллисекунд. Кроме того, классификатор не должен ничего пропускать. Метрика, на которую будем смотреть в первую очередь — это recall.
Детектор выделяет боксы контактов, к которым мы применяем блюр. Он уже может быть не такой быстрый, потому что классификатор срезал основной поток. Здесь смотрим на метрику precision.
Шаг 2. Собираем данные для обучения [11] модели. На Авито много объявлений, поэтому проблем с данными не было. Для обучения мы взяли фотографии с нарушениями и без. Фотографии с нарушениями разметили на контакты: QR-коды, логины, телефоны и email. Сразу разметили и боксы, чтобы потом использовать для детектора. Из публикаций без нарушений получили синтетику: сгенерировали телефоны, email и другие контакты.
Всего для классификатора собрали 33 000 фотографий, для детектора меньше: там более сложная разметка. Синтетики взяли 5 000.
Бывают случаи, где люди прям постарались. Такое мы пытаемся отлавливать классификатором и отправлять на ручную модерацию. Детектор, скорее всего, здесь ничего не найдёт.
Шаг 3. Обучаем модель. На наших данных для классификатора свёртки показали себя лучше по качеству и скорости, поэтому мы остановились на EfficientNetV2 версии small. По метрикам у неё лучший precision и AUC-ROC.
У нас был опыт внедрения детекторов на основе YOLO. Поэтому для детекции взяли последнюю версию этой архитектуры и обучили на наших данных.
В итоге архитектура ML-сервиса получилась такой:
В классификатор поступает около 1 500 RPS. Он срезает практически всё, а детектор ещё где-то половину: он не всегда находит контакты — некоторые кейсы уходят на ручную модерацию или в блок.
Переходим к внедрению моделей и реализации. Конечно, всегда можно закинуться миллионами GPU, но у нас их не так много, поэтому хотелось это всё ускорить.
Среднестатистический ML-сервис, который работает с картинками, выглядит как-то так:
Сначала есть какой-то CacheStorage, потом блок, внутри которого происходит обработка изображения, потом снова CacheStorage. Подробнее про каждый блок в нашем случае:
CacheStorage. Хранит предсказания по Image_ID, чтобы не проверять дубли в фотографиях. Они встречаются часто: кто-то загрузил две одинаковых картинки или немного поменял текст в описании, что также триггерит модерацию.
Aqueduct (акведук) — это open-source библиотека Авито для эффективного серверного инференса ML-моделей. Она позволяет с минимальными усилиями добиться лёгкого батчевания. Подробнее про Aqueduct рассказал мой коллега Иван Санин в youtube-видео «Разгоняем ML в проде» [12].
Если говорить просто, Aqueduct — это очередь, которая накапливает таски и обрабатывает их батчами в хендлерах, всего их четыре:
DownloadHandler — простой HTTPX-клиент, который загружает изображения по url.
PreprocessorHandler и PostprocessorHandler. Во время обучения для повышения робастности моделей часто используют аугментацию. Самое базовое решение — взять трансформации из TorchVision. Но у него «под капотом» Pillow, который по скорости значительно проигрывает преобразованиям из OpenCV, особенно на больших разрешениях. Поэтому мы стараемся использовать другие библиотеки: на этапе обучения это Albumentations, а в продакшене — OpenCV.
Обратите внимание [13]: если вы обучали модель с помощью трансформаций из TorchVision, а в продакшене используете OpenCV, то могут возникнуть проблемы. На картинке ниже видно, что одно и то же преобразование с использованием Pillow и OpenCV даёт разный результат. Для каких-то моделей это может быть не страшно, для других — добавляет существенный шум. Поэтому будьте аккуратнее.
ModelHandler. Обычно мы обучаем на PyTorch/Lighting, но в продакшене нам не нужны многие операции из торча, которые могут тормозить инференс. Так что в сервисах мы конвертируем модели для более быстрой работы.
Самое большое ускорение получается с TensorRT, но его невозможно использовать для всех случаев, так же как и ONNX. Например, когда у вашей модели есть кастомные слои или операции, которые сильно зависят от входных данных и обрабатываются классическими функциями Python. Дело в том, что ONNX и TensorRT могут работать только со статичными графами.
Разберём небольшой пример:

Слева — модель, которая при конвертации в ONNX будет выдавать статичный граф, но с фиксированными походами по if. Значит, при другой природе данных мы всё равно пойдём в ту ветку, по которой пошли при конвертации.
Чтобы этого избежать, можно заменить операции Python на маску из торча. В таком случае уже нет ветвления и граф получается полностью статичным. Это вариант справа.
То есть конвертировать в ONNX и TensorRT возможно, но на это нужно потратить время, которое не всегда есть. Поэтому иногда проще сразу остановиться на TorchScript, особенно на старте.
YOLO и EfficientNetV2, которые мы берём для задачи блюра контактов, легко конвертируются в TensorRT, что мы и делаем.
В итоге получили адекватное значение задержки. В классификаторе предпроцессинг и инференс модели занимают около 100 миллисекунд, со скачиванием выходит 240. В детекторе задержка ещё больше, но это не критично. Блок блюра занимает больше всего времени, поскольку занимается обновлением фотографий в публикации.
На этом этапе мы довольны результатом: было много срабатываний и мало жалоб. Значительно срезался поток, который шёл на ручную модерацию.
После успешного запуска на контактах мы решили работать и с другими отклонениями. Например, с матом или триггерными словами, которые вводят пользователей в заблуждение. Их находим с помощью OCR — Optical Character Recognition, распознавание текста на фотографии. Нарушение авторских прав выявляем с помощью детектора логотипов.
Все эти модели прекрасно ложатся в схему блюра, так как они занимаются детекцией и могут выдавать боксы для блюра нарушения.
У коллег в Авито Услугах была проблема с сегментом «Частный мастер». Некоторые компании маскируются под частников, чем вводят пользователей в заблуждение. Поэтому мы решили убирать с изображений подобный текст с помощью блюра. На тесте результат получился так себе:
После рефлексии мы вспомнили, что есть такая сфера компьютерного зрения [14], которая восстанавливает изображения — inpainting. Это когда на вход подаётся фотография, на которой маска закрывает часть пикселей. Задача модели — восстановить первоначальное изображение. Если полностью маскировать объект — это выглядит так, будто модель его удаляет. Попробовали на наших данных — получилось хорошо:
Внедряем inpainting. Мы изучили GitHub, нас заинтересовал репозиторий IOPaint с зоопарком моделей для редактирования фото. Мы составили список потенциальных кандидатов и отфильтровали их по качеству из paperwithcode. В итоге остановились на трёх моделях: LaMa, ZITS и MAT.
Шаг 1. Выбираем модель для инпейнтинга. Любую метрику для задачи inpainting можно взломать, поэтому часто обращают внимание на совокупность разных метрик и глазами отсматривают результат. Мы решили взять метрики: PSNR, SSIM, FID и LPIPS. Все они работают на парах: сравнивают оригинальное и обработанное изображение.
PSNR [15] — peak signal-to-noise ratio. Чтобы получить PSNR, сначала считают MSE попиксельно. Затем нужно взять максимальную активацию пикселя в оригинальном изображении, поделить на MSE и вычислить из этого логарифм. Чем больше MSE, тем меньше PSNR, а значит, мы должны растить эту метрику.
PSNR не берёт во внимание близость между пикселями, которая важна для восприятия [16] глазом человека. Для этого нужна следующая метрика:
SSIM [17] — structure similarity, учитывает взаимосвязь близких пикселей.
SSIM смотрит на дисперсию — в окне проходит по всей фотографии. Получается чуть лучше, но и SSIM можно взломать. Есть много примеров, где значения получаются одинаковыми.
FID [18] — frechet inception distance. Метрика учитывает глобальные признаки изображения, которые извлекают с помощью нейронки. Сейчас для этого в основном применяют модель CLIP. С её помощью получают векторное представление оригинального изображения, после чего считают расстояние между гауссианами.
FID учитывает разницу только на одном слое, а нам бы хотелось видеть изменения относительно первоначального изображения на признаках разного размера. Для этого используем финальную метрику:
LPIPS [19] — learned perceptual image patch similarity. Чтобы получить эту метрику, нужно взять признаки с разных слоёв изображения, посмотреть между ними разницу и усреднить.
Но и с этой метрикой есть много примеров, где фото разного качества, при этом LPIPS одинаковая. Идеальной метрики нет, поэтому смотрим на результаты в совокупности.
Новая модель ZITS++ показала себя лучше всех. Потом идёт ZITS, чуть хуже LaMa, а MAT сильно отстаёт. На картинках с рандомной маской видно, что MAT местами создаёт странные артефакты. LaMa в целом работает нормально, хотя может немного размывать фото.
Однако метрик качества нам недостаточно, модель должна быть быстрой, так что мы сравнили и производительность. Здесь уже LaMa гораздо быстрее других, а при небольшом отставании по метрикам от ZITS она вырывается вперёд. Кроме того, у LaMa более качественный GitHub-репозиторий и российские авторы — мы решили поддержать соотечественников.
Шаг 2. Оптимизируем выбранную модель. Внутри LaMa используются быстрые свёртки Фурье, которые нужны, чтобы увеличить receptive field. Это количество пикселей в первоначальном изображении, на которое смотрит внутренний слой нейронки.
Преобразования Фурье — нетипичная операция для нейронок, которая плохо конвертируется в ONNX и TensorRT. Хотя при такой конвертации граф получается динамическим и без Фурье. Поэтому по схеме из оптимизаций моделей мы остановились на TorchScript, получили небольшой бесплатный прирост. Для изображения 1920×1440 и GPU A100 достигли таких значений:
Мы перевели на inpainting часть причин, по которым раньше отклоняли или блокировали объявления.
Нагрузка немного выросла: в этой системе добавляется OCR, которая в среднем выдаёт 16 RPS. Inpainting тоже работает немного дольше, чем блюр.
В результате ML-система помогает не терять количество контента — убирает нарушения без потери качества фотографий. Вот примеры, где изображение не заблюрено, а восстановлено:
Конечно, бывают случаи, где что-то идёт не по плану: здесь модель не смогла выделить весь текст:
Это связано с тем, что бокс не полностью попал на буквы: модель не заинпейнтила их фрагменты. Проблему можно решить с помощью предпроцессинга с сегментацией.
Вместо стандартного бокса от модели нужно подать его как промпт в сегментатор, который более качественно выделит объект для инпейнтинга. Для этого можно использовать SAM — Segmentation Anything Model. В таком случае мы сначала подаём бокс, затем сегментируем его и отдаём в LaMa уже более правильную маску, которая удаляет объект. Это решение мы внедряем в Авито прямо сейчас.
Классические методы модерации обходится дорого, а простой бан фотографий с нарушениями снижает количество контента. Чтобы улучшить модерацию, мы внедрили ML-модель, которая блюрит нарушения на фото.
Блюр может добавлять дефекты на изображения, поэтому мы перешли на inpainting. Результат инпейнтинга оказался лучше. Теперь мы убираем нарушения без потери качества фотографий и количества контента, а значит, и денег.
Кроме таких автоисправлений, у нас в команде есть другие интересные проекты. Например, блокировка дублей, внедрение LLM, few-shot подходы, модерация сложных доменов, таких как видео, и всё это — в высоконагруженной системе с большим влиянием на всю компанию.
Спасибо за уделённое время! На любые вопросы об опыте в модерации я с радостью отвечу в комментариях или личке:
Telegram: @vladitm [20]
LinkedIn: Vladimir Morozov [21]
Больше о том, какие задачи решают инженеры Авито, — на нашем сайте [1] и в телеграм-канале AvitoTech [22]. А вот здесь [23] — свежие вакансии в нашу команду.
Автор: vladitmor
Источник [24]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/12256
URLs in this post:
[1] Авито: https://clc.to/LV3FBg
[2] опыт: http://www.braintools.ru/article/6952
[3] Дано: зачем Авито модерация изображений: #section1
[4] Проблемы классической модерации: долго, дорого, неудобно: #section2
[5] Идея №1: не удалять, а блюрить изображения: #section3
[6] Планируем архитектуру и обучаем ML-модель: #section4
[7] Ускоряем работу сервиса: #section5
[8] Идея №2: не блюрить, а inpainting: #section6
[9] Что получили в итоге: #section7
[10] Резюме: #section8
[11] обучения: http://www.braintools.ru/article/5125
[12] в youtube-видео «Разгоняем ML в проде»: https://youtu.be/E8Kk7BZLbbg?si=ooV0eyeAheCZr1bx
[13] внимание: http://www.braintools.ru/article/7595
[14] зрения: http://www.braintools.ru/article/6238
[15] PSNR: https://ru.wikipedia.org/wiki/%D0%9F%D0%B8%D0%BA%D0%BE%D0%B2%D0%BE%D0%B5_%D0%BE%D1%82%D0%BD%D0%BE%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%B0_%D0%BA_%D1%88%D1%83%D0%BC%D1%83
[16] восприятия: http://www.braintools.ru/article/7534
[17] SSIM: https://ru.wikipedia.org/wiki/SSIM
[18] FID: https://en.wikipedia.org/wiki/Fr%C3%A9chet_inception_distance
[19] LPIPS: https://github.com/richzhang/PerceptualSimilarity
[20] @vladitm: https://t.me/vladitm
[21] Vladimir Morozov: https://www.linkedin.com/in/vladimir-morozov-ai/
[22] в телеграм-канале AvitoTech: https://clc.to/mnG_FQ
[23] вот здесь: https://clc.to/1QTHTg
[24] Источник: https://habr.com/ru/companies/avito/articles/882572/?utm_campaign=882572&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.