Когда мы пишем веса — мы растим граф

Продолжение WSP-цикла: матрица весов есть граф, онтология→топология→веса, компилятор вместо руки и четыре рефери, которых нельзя уговорить

📖 Читать ⬇ Скачать PDF версия материала для печати и чтения офлайн

Когда мы пишем веса — мы растим граф

Эссеистический цикл «От жёсткой логики — к графу»:
1. От жёсткой логики — к живой вероятности
2. WSP на практике: непобедимая сетка из весов — игра из 473 весов
3. От живой вероятности — к весам, которые пишут как код
4. Когда мы пишем веса — мы растим граф (вы здесь)
5. Характер в полкилобайта: куда встраивать веса в производство — про контроллеры

Продолжение эссе «От живой вероятности — к весам, которые пишут как код». Там веса стали текстом, который можно читать. Здесь мы отвечаем на вопрос, который тогда только наметился: что, собственно, происходит, когда мы «считаем веса»? Оказывается — мы перестаём писать правила и начинаем растить граф.

Пролог. Палимпсест

Первую непобедимую сетку мы не спроектировали. Мы её вырастили по болям.

Сначала была простая идея: клетки, линии, угрозы — пишем в матрицу, и пусть себе течёт. Сеть играла, пока однажды противник не поставил вилку — ход, после которого угрожают сразу две линии и закрыть обе невозможно. Мы залатали одну вилку. Через десять партий вспыхнула другая, устроенная иначе. Каждая правка была точечной: два числа в файле — и поведение менялось, как от патча. Это было волшебно и это был палимпсест — так называют пергамент, с которого веками смывали текст ради нового, пока нижние, стёртые слои не начинали проступать сквозь верхний. Вот и у нас под последней правкой проступали все предыдущие, и никто уже не мог сказать, почему именно эти числа дают именно эту игру.

Мы тогда вывели первый закон: посчитать пару может только слой над одиночными детекторами. Вилка — это пара чистых линий через одну клетку; ни один одиночный детектор её не увидит, нужен новый слой, новые узлы, новые рёбра. Мы построили слой — и вилки кончились. Закон пережил игру и стал частью методики.

Но закон был добыт кровью, а не чертежом. И оставался соблазн, который мы наконец себе разрешили: переписать всё заново — так, чтобы чертёж был первичен.

Раздел 1. Что мы делали, когда «считали веса»

Стоит один раз посмотреть на матрицу весов свежими глазами — и маска падает. Матрица весов это взвешенная матрица смежности. Наш формат логов — связь i→j с весом w, потому что аксиома такая-то — это буквально список рёбер графа. А разреженность, эти гордые десять процентов заполненности, означает странное: нули и есть топология. Узор отсутствующих рёбер решает, что вообще может влиять на что; числа при оставшихся рёбрах — только громкость уже возможных связей.

Значит, меняя логику на веса, мы поменяли её не на «числа». Мы поменяли её на граф и течение по графу.

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

Отсюда — онто-цепочка, которую теперь мы проходим всегда, в любой задаче методом весов:

ОНТОЛОГИЯ → ТОПОЛОГИЯ → ВЕСА.

Сначала — что существует: клетки, линии, углы, противоположные углы. Потом — какие связи между сущностями: через клетку проходят такие-то линии; паре нужен общий узел. Это и есть топология, те самые десять процентов. И только потом, в последнюю очередь, — знак и величина каждого ребра. Числа — последняя мишура. Без первых двух слоёв писать нечего: веса являются онтологию в числовую среду.

Раздел 2. Компилятор вместо руки

Вторая перепрошивка была злее: мы запретили себе писать числа.

Каждый узел графа объявляется как предикат: «активен, если из этих родителей со знаками истинных не меньше k». Точка. Ни одного числа в исходнике. А дальше работает компилятор: он перебирает все паттерны входов узла, находит худший истинный и лучший ложный, ставит порог ровно посередине и требует зазор — чтобы в активации тангенс гарантированно давал ±0,995, а не «ну примерно единица». Если зазор получить невозможно, потому что функция пороговой суммой не выражается, — компилятор падает. Не сеть тихо врёт на редкой доске, а сборка немедленно говорит: эта формулировка невозможна в этой топологии, ищи другой слой.

Мы споткнулись об это трижды, и каждое спотыкание было подарком:

Запрет на константные наклоны вскрыл, что «центр лучше угла» — тоже правило, а правило обязано висеть на условии («пока доска открыта»), иначе решатель bias законно съедает его как шум. Так в графе родился узел «доска открыта» — и первый ход сетки, центр, перестал быть хитростью и стал следствием аксиомы: ландшафт устроен так, что пустая доска скатывается в центр. По построению.

Невыразимость «ловушки И центр пуст» одной суммой заставила guard переехать в нижний слой, где входы ещё сырые. А невыразимость «вилки на пустой клетке» — оставить занятым клеткам их безвредный артефакт: их логит всё равно никто не читает. Честность топологии оказалась дороже косметики.

И наконец приоритеты решений мы сделали лексикографикой степеней двойки: блок неминуемого выигрыша весит 8192, ответная вилка — 4096, защита от ловушки — 3072, и так до мелочи. Худшая сумма всех младших классов меньше старшего — значит числа физически не могут переорать иерархию. В первой версии приоритеты были мягкими и работали «по удаче подбор»; теперь арифметика обязана.

Раздел 3. Рефери, который не уговаривается

Новую сетку — сто десять узлов, двести восемьдесят девять рёбер — мы приняли не глазами. Её принимали четыре инстанции, и каждая уже кусала нас раньше:

Булевы двойники. Из того же манифеста, из которого скомпилировались числа, автоматически рождается булева спецификация каждого узла — «истинный ответ без всякой сети». Перебор: все двадцать две тысячи двести пятьдесят легальных досок, обе стороны, каждый из ста десяти узлов против своего двойника. Ноль расхождений.

Карта приоритетов. Для каждой доски и каждой пустой клетки — булев класс («здесь выигрыш», «здесь блок», «здесь вилка»), и проверка: argmax логитов обязан совпасть с лексикографическим победителем карты. Ноль расхождений.

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

Паритет со старичком. Та же доска, две реализации, два ответа. Двести с лишним расхождений из четырёх с половиной тысяч — и это не стыдно, а интересно: новая сетка открывает игру центром, старая углом; обе непобедимы, это два разных характера, две разные геометрии падения. Расхождение ≠ ошибка — и рефери обязан это различать, иначе он лжёт.

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

Раздел 4. Что изменилось в существе

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

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

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

Мы перестаём писать правила и растим граф, в котором правила — единственные возможные течения.

Эпилог. Две стены

⬇ Скачать игру (офлайн HTML, 24 КБ)

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

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

Лестница, оказывается, была. Просто её надо было сначала построить из сущностей.


Продолжение и источник

• Начало цикла — «От жёсткой логики — к живой вероятности».

• Прошлое продолжение — «От живой вероятности — к весам, которые пишут как код».

• Практика — «WSP на практике: непобедимая сетка из весов, написанных как код»; игра на новом графе подключается сюда же.


Идеи и эксперименты — автора (Telegram: @Tottop10, pro100.D); литературная и программная огранка — агент. CC BY 4.0.


Продолжение — «Характер в полкилобайта: куда встраивать веса в производство»: куда встраивать веса в живой контроллер — и куда не надо.

← Характер в полкилобайта: куда встраивать веса в производство От живой вероятности — к весам, которые пишут как код →