Журнал исследования

Проблемы и отрицательные результаты

Здесь то, что не получилось или получилось не так. Отрицательный результат с понятой причиной ценнее удачного прогона без объяснения, а половина находок в этом проекте пришла именно из разбора неудач.

ЧАСТИЧНОСИГНАЛБаза Contra: первая комната открыта, дальше нет

Наблюдение: второй уровень Contra — псевдо-3D база, камера не скроллит, значение планировщика (уровень × 4000 + камера) стоит на месте. Первая запись второго уровня к тому же выявила баг харнесса: авто-continue срабатывал по «камера = 0» и ставил живую игру на паузу; исправлено условием на жизни.

Что дальше: тот же класс задачи, что и сцены без скролла (Ninja Gaiden, Kung Fu): нужен счётчик комнат/дверей или сигнал из таблицы объектов. Сохранено состояние старта уровня для скана.

СНЯТОМЕТРИКА«Попадания по стене» были суммой не тех байтов

Что было: урон стены считался как 66 минус сумма четырёх адресов; 66 было суммой в одном сейвстейте, а три из четырёх адресов оказались байтами типа объектов, не HP. В реальном прибытии сумма равнялась 38 до единого выстрела, и кампания отчитывалась о «28 попаданиях» на каждом сиде.

Что стало: найдены таблицы объектов (тип по слоту, HP по слоту); стена — четыре объекта на 72 HP. Член урона переписан по типам, критерий убийства — только счётчик уровня. После этого стена пала: 2/8 из состояния прибытия в лаборатории, затем 12/32 с включения. Снятое утверждение оставлено в журнале рядом с таблицами.

ЗАКРЫТОМЕТОДОткат по срабатыванию не лечит обрыв

Замер: три варианта отката (спаситель целиком; композиции в точке спасения; окно опасности) на обрыве Contra дают 0/8, 0/8 и 5/8 против 8/8 у двухшагового поиска при той же цене. Спасители — стойки, обречённость возвращается, глубина эскалирует до потолка.

Вывод: оставшиеся провалы на пути — провалы набора кандидатов; дешёвое лекарство — пять пар таймингов прыжка, дистиллированных из выбора поиска (обрыв 7/8 за половину цены). Код отката оставлен под флагом, ветка закрыта.

ВАЖНОМЕТОДИКАПредрегистрированный критерий оказался неразрешимым на доступной выборке

Замер: адаптивное распределение розыгрышей — два всем, ещё два там, где ранжирование ненадёжно. Офлайн правило безупречно: триггер по нестабильности победителя и пер-точечному шуму, замороженный на пяти чужих уровнях, забирает 94% экономии перехода 2→4 за 69% цены против независимого референса. Онлайн — ноль дважды: −503 на своём шуме, −202 [−1438, +1068] на общих случайных числах, при +29% вычислений.

Причина: критерий требовал точку выше линии между oracle-2 и oracle-4, а сами концы неразличимы: o4−o2 = +254 [−919, +1441] на 32 парных сидах при полностью общем шуме. Медианы отличаются вдвое (3124 против 7106 — распределение бимодально вокруг прохождения уровня), средние — нет. Линия была натянута между двумя статистически неразличимыми точками.

Итог: предрегистрация без предварительной прорисовки шумового пола — методическая ошибка, записанная как таковая. Онлайн на этом масштабе сертифицирует только эффекты масштаба самого планировщика (+3800 к политике); тонкая экономика розыгрышей меряется офлайн на сохранённых матрицах. Остались инструменты: CRN-харнесс (армы байт-идентичны до первого настоящего расхождения) и офлайн-стенд калибровки.

ЗАКРЫТОПРОГРАММАДистилляция планировщика закрыта: пять ролей, пять провалов, три механизма

Замер: ученик оценён во всех ролях, которые для него определялись: оценщик ценности (~0 сверх константы онлайн), выборщик из шести (−432 px на замороженных сидах, p=0.0005), бинарный gate (AUC 0.57 при 0.583 у одного бита «в воздухе»), DAgger (не обошёл мажоритарную константу), аллокатор вычислений (хуже равномерного утоньшения при равном компьюте). Эпизодическая память по месту проиграла политике −209 шестой.

Причина — три механизма, показанные порознь: реактивное представление не даёт межуровневого ранжирования (семь входов батареи захватывают одни и те же ~20%); накопление ошибки уводит каждую модель в её самое дешёвое поведение — «стоять», «отдать руль», осторожные шаблоны; и hard-pruning асимметричен — ошибочно выброшенного кандидата не вернуть дополнительными симуляциями.

Итог: программа закрыта с рабочим инженерным выводом: равномерное сокращение симуляций сохраняет 86% выигрыша при 54% вычислений, и дальнейшая работа — последовательное распределение бюджета самого планировщика, а не новый ученик.

РЕШЕНОЦЕННОСТЬПлан оценивался одним случайным будущим

Замер: продолжение после плана играет политика, сэмплирующая действия, поэтому одна прокрутка даёт одно случившееся будущее. Два замера одного и того же плана расходились на 22 px при разбросе между разными планами 21 px, и лучший из шести кандидатов совпадал сам с собой в половине точек.

Итог: усреднение по четырём продолжениям подняло медиану планировщика 3121 → 7117, прохождения 1-1 12/32 → 20/32, смерти 42 → 20 — без единого изменения в наборе кандидатов. Самая крупная методическая ошибка проекта из найденных: неверно был определён измерительный прибор, а не модель.

Тупик по дороге: детерминированное продолжение делает цель идеально воспроизводимой и роняет результат до +2458 — жадная политика застревает у трубы, и ценность начинает описывать мир, которого не бывает. Дисперсия была обменяна на смещение.

РЕШЕНОРАЗМЕТКА45% обучающих меток говорили «точно не прыгать» про решение без последствий

Симптом: любой классификатор, обученный выбирать между «пусть играет политика» и «прыгнуть», скатывался в отказ от вмешательства.

Причина: в 40% решений прыжок и отказ дают побайтово одинаковое будущее — 88% таких точек это состояния в воздухе, где кнопка ничего не делает. Метка считалась как вероятность того, что прыжок лучше, и на точной ничьей она равна нулю. В наборе, на котором учился классификатор, таких точек 45%.

Итог: почти половина обучения была систематическим толчком в отказ. Ошибка в цели, а не свойство задачи; ничью надо метить как ничью.

ВАЖНОПРОВЕРКАРазбиение по прогонам не проверяло пространственное обобщение

Симптом: k-ближайших соседей без всякого обучения обходил обученную сеть, 0.707 против 0.562.

Причина: все прогоны идут по одному уровню. У 74.6% отложенных точек есть обучающая точка ровно в той же мировой координате, у 99.3% — в пределах четырёх пикселей. Заучивание географии уровня переживает такое разбиение, и копирование метки ближайшего соседа работает.

Следствие: офлайновые сравнения представлений, сделанные на таком разбиении, оптимистичны — включая те, по которым отбраковывались варианты. Игровые результаты это не затрагивает. Нужны блоки по координате с буфером вокруг тестового участка и, для настоящего переноса, отложенные уровни целиком.

ВАЖНООБУЧЕНИЕВыучить выбор планировщика не удалось

Замер: предрегистрированный эксперимент — архитектура, цель, функция потерь и критерий зафиксированы до сбора данных, проверка один раз на 32 сидах, которых не касалась ни одна рука. Ученик проиграл политике −432 px (p=0.0005) и обошёл только константу.

Что он выучил вместо: «стоять» в 34% решений против 5% у учителя. Лишнее стояние стоит 2 px внутри горизонта, на котором считается метка, и это самая дешёвая из возможных ошибок; за прогон она же стоит 432. Причём 83% лишних стояний приходятся на самые медленные состояния — то есть ровно на те, из которых нужно действие, чтобы выйти.

Цена ошибки, измеренная отдельно: точный оценщик с равномерной ошибкой в 20% сохраняет треть преимущества, в 50% — +181 из +4590. Ориентир для половины выигрыша — не более 15.7% ошибок при 42% у лучшего классификатора.

РЕШЕНОМЕТОДИКАКонтрольная рука оказалась оракулом под чужим именем

Симптом: «всегда один и тот же шаблон» показал +3178 против политики и сравнялся с полноценным планировщиком с точностью до трёх пикселей. Из этого были сделаны три вывода и отправлены заказчику.

Причина: оценка замыкалась на константу, но цикл оракульной оценки продолжал работать и дописывал настоящие числа в тот же список. Максимум брал оракула везде, где хоть один план давал больше пикселя. «Константа» выбирала свой шаблон в 42% решений, а в остальных играла как планировщик.

Итог: три вывода отозваны, настоящая константа даёт −413 против политики. Коммиты с ошибочными заголовками оставлены в истории вместе с ретракцией: ошибочный шаг тоже результат, и он объясняет, зачем появился контроль.

ВАЖНОМЕТРИКИТочность предсказания действий не измеряет качество игры

Замер: две модели Double Dragon, обученные одинаково, но на разных данных — 0.864 и 0.618 точности. Разрыв в 25 пунктов. В игре, десять парных прогонов: 149.4 против 146.2 очков. Разницы нет.

Причина: метрика отвечает на вопрос «насколько предсказуемо поведение, которое клонировали», а не «насколько хорошо агент играет». Старые инстинкты стояли в углу и повторяли один манёвр — такое клонируется отлично. Новые реагируют на обстановку, и угадать их по четырём кадрам труднее.

Следствие: колонка точности в таблице моделей ранжирует клонируемость, а не силу игры. Рядом с бейзлайном она по-прежнему говорит полезное — выучено ли хоть что-то, — но большее число не значит лучшего игрока.

РЕШЕНОДАННЫЕДатасет Double Dragon оказался записью титульного экрана

Симптом: модель обучалась, показывала точность 0.42–0.48 и «просто плохо играла».

Что было на самом деле: все 41 эпизод — статичная заставка. Игра ни разу не запускалась: её титульник не проходится с холодного старта ничем, интеграция рассчитана на штатный снапшот.

Почему не заметили сразу: метрика не отличает «слабую игру» от «модели, выучившей неподвижную картинку». Нашлось только когда посмотрели глазами.

Итог: добавлен флаг стартового снапшота, данные пересобраны, точность 0.864. Прежний результат переноса на эту игру отозван из журнала.

РЕШЕНОУПРАВЛЕНИЕАгент непрерывно ставил игру на паузу

Симптом: в двухминутной записи треть кадров неподвижна.

Причина: детектор титульного экрана запоминал картинку в начале эпизода. При старте из снапшота «титульником» запомнилась улица первого уровня — и каждый похожий кадр принимался за возврат в меню, агент жал START, то есть паузу. Плюс слепые START-импульсы первых секунд били по уже идущей игре.

Итог: неподвижных кадров 33.9% → 7.3%, самое долгое замирание с 1.1 с до 0.1 с. Очки за прогон выросли вдвое.

РЕШЕНОВНИМАНИЕМодель смотрела мимо объектов — хуже случайного взгляда

Замер: лишь 12.5% массы тепловой карты попадало в рамки объектов при 13.8% у равномерного взгляда. То есть сеть систематически смотрела на декорации.

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

Итог: добавлен штраф за взгляд мимо рамок трекера. Стало 21.5%, на мультиигровой базе 38.0% при 10.4% случайных. Цена — 2–3 п.п. точности.

РЕШЕНОВОСПРИЯТИЕВ ближнем бою трекер сливал бойцов в один объект

Симптом: в битемапе агент бил в сторону, противоположную врагу.

Причина: когда спрайты перекрываются, разностный трекер видит один блоб. Он доставался ближайшему треку, второй боец «призрачился» на последней позиции, а центр победителя уезжал в промежуток между телами — знак направления становился шумом.

Решение: двум трекам разрешено претендовать на один блоб, и тогда он режется между их предсказанными позициями. Кадров, в которых агент бьёт отслеживаемого врага, стало 254 → 462.

Но не всё: на очках это не сказалось — +10 при разбросе 24 на 12 прогонах, пять побед и пять поражений. Записано как есть.

ОТКРЫТОПЕРЕНОСZero-shot перенос политики не работает

Замер: на невиданной игре мультиигровая база без дообучения проходит 9.7 единиц дистанции против 126 у случайного агента. То есть уверенно жмёт то, что было верно в её играх, и этим мешает себе.

Контраст: few-shot с тремя эпизодами работает (+7–8 п.п.). Значит переносятся признаки, а не поведение.

ЗАКРЫТОВОСПРИЯТИЕНейрослоты: разложение на объекты не вышло

Slot attention учит реконструкцию (ошибка 0.027 → 0.012), но делит кадр позиционной «рябью» вместо объектов.

Причина: фон NES сам по себе — повторяющийся тайловый узор, и пятнистое разложение описывает его не хуже объектного. Других градиентов у метода нет.

Решение: оставлен классический трекер движения. Планы на вторую версию — в журнале экспериментов.

ОТКРЫТОДАННЫЕЧужие TAS-записи расходятся с нашим ядром

Масштаб источника: 933 записи NES, 763 игры, 240 часов покадрово точного ввода — и в них уже есть нажатые кнопки, распознавать нечего.

Препятствие: записи сделаны в других эмуляторах, и часть «уезжает» на середине. Сравнение трёх ядер показало, что единого победителя нет: на восьми записях лучше fceumm, на двух — quicknes, вдвое.

План: подбирать ядро под запись и брать проверенный префикс до места поломки.

РЕШЕНОИНФРАСТРУКТУРАЧужое ядро молча подменило основное

Симптом: после добавления второго эмулятора рядом с основным все игры стали отдавать другое разрешение и частоту звука. Ошибок в логах — ноль.

Чем поймали: регрессионными тестами с золотыми хэшами кадра и звука, добавленными в тот же день.

Итог: переключение ядра теперь происходит только в памяти процесса, общие папки не трогаются. Молчаливая деградация моделей была бы худшим из возможных исходов.

РЕШЕНОЗВУКДжингл смерти «отсутствовал» — виноват был не эмулятор

Сначала решили, что ядро не воспроизводит звук смерти. На деле счётчик жизней в памяти уменьшается на ~120 кадров позже самого звука, поэтому событие искали не в том месте.

Итог: привязка звуков к событиям обратным окном; порог кластеризации ужат, иначе джингл слипался с музыкой.

Полный журнал с таблицами и параметрами — в документации проекта.