Месяцы против дней: анатомия неверного подхода в компьютерном зрении

Как игровой робот учился видеть доску: подход, который поддавался, детектив с Captum, вечно деревянная доска — и почему самое дорогое знание в прикладном ML не гуглится.
Мы с приятелем делаем игрового робота-компаньона для настольных игр. Сейчас он умеет понимать позицию на игровой доске (гомоку, оно же «пять в ряд»), обдумывать ход (самописный аналог AlphaZero) и понимать базовые фразы для сопровождения игры — моделью, которая вместе с эмбеддингами весит меньше 80 мегабайт.

Исходные данные задачи: доска, фигурки, кадр с вебки
Забегая вперёд, расскажу сразу главный тезис статьи:
Самое дорогое знание в прикладном ML — не «как устроены методы»: это расскажет любой курс и любая нейронка. Самое дорогое — где у каждого метода границы применимости и в каких местах он ломается. Дальнейший текст — просто иллюстрация этого тезиса: длинная история неверного подхода, взятого из в целом хорошего курса на Coursera, и короткая успешная после смены подхода.
Авторы:
Юрий Зибрин — автор статьи и соавтор проекта. AI-инженер (RL, CV, LLM Agents, Embodied AI), ведущий системный аналитик, сооснователь solved-by.ml
Антон Петров — соавтор проекта. AI-инженер (CV, LLM Agents), разработчик, тимлид, сооснователь solved-by.ml
Fable 5.0 — редактор текста и советчик, раскопщик миллиона блокнотов. Если вам покажется, что слишком много запятых и длинных тире — все претензии к ней)
Задача и план: Робот играет в гомоку на физической доске так, как многие из нас играли в детстве в клетчатой тетрадке — ставя крестики и нолики в центры клеток (не как в классическом гомоку). И правила взяли попроще: без ограничений на ходы и выигрышные комбинации для первого игрока.
Чтобы сделать ход, робот должен по кадру с камеры понять игровую позицию — и передать её движку, который принимает решение о ходе.
План, как это реализовать, мы придумали почти сразу, и он до сих пор живёт в рабочей версии:
найти углы доски;
найти фигурки;
зная размерность доски, сопоставить одно с другим и получить матрицу с позицией.

Так как готового рецепта найти не удалось, подход пришлось выбирать самому. Выбор для шага с углами подсказал курс Эндрю Ына: там свёрточную сеть-классификатор лёгким движением превращали в детектор координат — «просто замените классификационную голову на регрессионную, и всё заработает. Лосс? Да берите любой» (цитирую по памяти, но небрежную интонацию передаю точно). Забегая вперёд: и «всё заработает», и особенно «любой лосс» оказалось преувеличенно оптимистично для нашего класса задач.
Подход, который поддавался
Итак, регрессия координат.
В Blender мы сделали датасет 224×224 с таргетами — координатами углов доски (точнее, углов игрового поля, без паддинга). Первая версия — около 12 тысяч рендеров, финальная — 66 тысяч сцен.
За год регрессия углов прошла путь: полный рандом на реальных фото → три угла встают на место, четвёртый улетает → почти попадание → ошибка в полклетки или в две… Каждая ступень выглядела прогрессом. В этом и ловушка: неверный подход не отказал ни разу — он поддавался. А курсы воспитали во мне главное правило прилежного студента: заработало — дожимай.

Внутри этого пути нас ждало много ошибок — простых и неочевидных — и много боковых веток: выбор разных сетей, ручные фильтры и многое другое.
статичный фон — все рендеры сняты в одной и той же виртуальной комнате: стол, доска, штора;
сплит синтетики на train и test — тест из того же генератора не проверял ничего;
вечно деревянная доска — фон рандомизировали, а материал самой доски забыли;
процедурные фоны — искусственный «шум» вместо фона так и не заработал, и мы до сих пор не знаем почему;
ручные фильтры — попытка скормить сети контуры вместо цвета;
баг с порядком углов — разметка выдавала углы в случайном порядке;
перебор лоссов — от классического MSE до самодельного линейного с «мёртвой зоной»;
перевёрнутая ось Y Блендера — след на весь проект.
Но даже после фикса всех ошибок максимум, что удалось выжать из регрессионного подхода, выглядит так:

Кому интересно — под спойлерами подробности попыток выжать из этого подхода максимум. Попытки эти, кстати, длились около года, и каждый раз результат становился лучше и лучше, но мы всё-таки упёрлись в потолок, изображённый на картинке выше.
Спойлер
Сплит синтетики: разбиение на трейн и тест и почему это бесполезнобсзполезно на синетике
Captum: как понять на какие точки на самом деле смотрит сеть и как понять в чем ошибка в данных
Фоны: линии с точками против чужих залов - почему важен разнообразный фон
Вечно деревянная доска: важность аугментации цвета и текстуры ключевого объекта
Ручные фильтры: Собель, Лаплас, Canny - попытка упростить картинку убившая обучение
Лоссы и порядок углов, или как запутать сеть
Выводы
Оглядываясь, я вижу в этой эпопее два слоя ошибок. Стратегический — сам выбор регрессии координат для точек, которые локально неотличимы друг от друга: эта дорога была тупиком независимо от качества исполнения. И тактический — все те ветки, что выше: обычные ошибки периода обучения, мы ведь учились параллельно с проектом.
Коварство в том, как слои взаимодействуют: каждый исправленный тактический косяк давал реальное улучшение и создавал ощущение «ещё чуть-чуть — и дожмём». Неверный подход не отказывает сразу — он поддаётся. Именно поэтому он съедает не неделю, а месяцы: прогресс в мелочах маскирует потолок в главном.
Сам же потолок объясняется просто. Регрессия координат плохо переносит задачи, где целевая точка локально не уникальна. Угол доски — это пересечение линий; таких же линий на доске ещё две дюжины, а похожих друг на друга пересечений — полторы сотни. Чтобы выдать координату, сеть должна глобально рассудить, какое из одинаковых пересечений крайнее, и свернуть этот вывод в два числа через регрессионную голову; при неоднозначности между кандидатами регрессия к тому же склонна усреднять — «мазать» между похожими точками. С глазом на кропе лица из примера Ына всё наоборот: точка одна, локально узнаваема, всегда примерно в одном месте кадра — потому тот пример и работал.
Дни, которые всё решили
Дальше вы уже догадались. Мы попробовали сегментационную сетку с предсказанием маски доски — точнее, игрового поля. Это был U-Net с претрейненным на ImageNet энкодером (у нас это EfficientNet-B7 — самый жирный из линейки, другие давали результат хуже). Уже через час обучения на этом датасете стало ясно, что метод выбран верно: маска доски получалась уверенно. Дальше решение только улучшалось — но это были дни доводки, а не месяцы борьбы.
Дальше чистая геометрия. По маске строится выпуклая оболочка, и с неё жадно срезаются вершины: на каждом шаге две соседние заменяются пересечением их рёбер — выбирается срез с минимальной потерей площади, — и так, пока не останется ровно четыре угла. Алгоритм раскопали в интернете: очень похожая реализация, вплоть до той же sympy-версии, описана в заметке «Efficiently Drawing Bounding Boxes Around Segmented Masks». Он был переписан на numpy — и всё полетело: 4 миллисекунды, и готов наш четырёхугольник с игровым полем.

Причина успеха тоже прозаична: попиксельная разметка не оставляет сети места для шорткатов, а точность углов обеспечивает уже не нейросеть, а геометрия.
Пайплайн целиком: фиксация успеха
Фигурки ищутся похожей связкой: сегментация плюс тепловая карта центров. Карта даёт кандидатов на фигурки, ложные пики подавляются, а уверенные подтверждаются сегментацией; эта связка дала около 99% успеха — пайплайн работал почти всегда. Чтобы «почти всегда» превратилось в «всегда, под почти любым углом и при почти любом освещении», пришлось сделать ещё ряд мелких костылей, которыми не буду перегружать статью.

Что накопилось с тех пор
Дожать можно почти любой подход — вопрос, какой ценой. Главный навык — понимать цену заранее, и с тех пор именно он стал для меня основным предметом охоты. Накопилось за последние годы:
работающий самописный аналог AlphaZero (MCTS + ResNet, self-play) — про его находки, от диагностики движка до трюков с политикой, будет отдельная статья: подпишитесь, чтобы не пропустить;
уроки DQN: он не осилил даже крестики-нолики 3×3, пока один из противников не стал рукописным — два шумных ученика не могут научить друг друга; подробности в той же статье про движок;
немало раскопанного про RL в целом;
заказные проекты, которые мы делаем с приятелем: LLM-пайплайны для сложных документов, ИИ-наблюдение за критичными событиями, семантический поиск (часть — на solved-by.ml);
сейчас копаем задачи перемещения объектов — впереди манипулятор.

Зачем этим заниматься и почему сейчас
Сейчас ровно то время, когда такие штуки надо делать.
В дата-сайенс есть уже огромное количество подходов, которыми можно реализовать тучу вещей. Много замечательных архитектур сетей. Умные нейронки, которые многое подскажут, и куча материалов в сети. И железо, на котором можно учить довольно нетривиальные вещи, вполне доступно.
Есть огромный спрос на AI — и на производстве, и в науке, и для быта.
Но почему-то 99,99% того, что можно было бы реализовать, даже не придумано.
Вот два простых забавных примера.
В «Перекрёстке» недавно появились ИИ-весы: положили яблоки — на экране только яблоки. Сети такого класса доступны уже больше десяти лет, и совершенно непонятно, почему этого не было раньше.
В ютубе был ролик с занавеской, которая сдвигается за прохожим, чтобы он не заглядывал в квартиру: сегментация или детектор плюс мотор — проект на неделю.
Таких незанятых идей — море, и делать их можем мы все.
Ситуация напоминает раннюю эпоху персональных компьютеров — времена Homebrew Computer Club, когда будущие основатели индустрии паяли машины в гаражах и сами писали к ним софт.
Препятствия тут только время, интерес и некоторое понимание того, в какую сторону думать, решая такие задачи, и, главное, какие подходы выбирать в каждом случае.
Чтобы сделать простого домашнего робота — не то что занавеску или весы, — в принципе достаточно для начала осознать подходы к решению следующих задач:
понять сцену — наличие объектов, их контуры и взаимное расположение (CV);
установить состоявшиеся факты по визуальной информации (VLM/VM);
принимать решения (RL или LLM);
совершать движения — перемещаться в пространстве или работать манипулятором (от простейшего Behavior Cloning до Decision Transformer и честного RL в варианте актор-критик, или, например, VLA);
отдельно стоят вопросы равновесия, но они уже решены;
и отдельно — среды, в которых всё это обкатывают: от чистой концепции без физики до полноценной симуляции.
Существенную часть из этого мы со Слёрмом уже готовы рассказать нашим читателям здесь — или участникам воркшопов и курса, если к таким активностям будет интерес. О чём написать дальше
Напишите в комментариях, о чём было бы интереснее почитать в следующей статье:
мучения с самописным движком AlphaZero и как он начал обыгрывать серьёзных игроков;
простой приём, как научить лёгкий трансформер (читай — LLM) весом 50 мегабайт понимать ограниченный набор пожеланий пользователя, сказанных в совершенно любой форме;
про раннюю стадию активности с переносом фигурки манипулятором — выбор концепции и причины выбора.
А ещё было бы интересно послушать, какие области исследуете вы — по работе или в качестве хобби. А если готовы рассказать подробнее — тем более пишите: может, удастся уговорить вас поучаствовать в теме с воркшопами или запилить тут пару статей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.