Нейросеть вместо решателя ветра: первый пилот и разбор

Ночью вышла 1.0.2 (мышь — всегда крыло, ветер из меню — над стартом). Днём выбрано новое направление для модели воздуха — заменить решатель поля ветра в игре нейросетью, обученной на честном офлайн-решателе. Пилот «работает ли идея» собран, посчитан и разобран за день: сеть заметно лучше простых оценок, но до ориентиров не дотянула. Главная причина — сеть учили не на том рельефе, на котором она будет работать. Решено повторить пилот на 300 реальных горных рельефах.

Выпуск 1.0.2

Deltaplan 1.0.2 (9360397): мышь управляет только крылом, W/S/A/D — пилот на земле и голова в полёте; ветер из меню — это ветер на 10 м над стартом (раньше над стартом выходило в 1,2–2 раза сильнее); болтанка у земли — по теории приземного слоя.

«Апогей» — 14,4 м²

Со слов пилота «Апогей» — копия Airwave Magic 155, площадь 14,4 м², а не 16 (44d451b). Поляра пересчитана на √(16/14,4) ≈ 1,054 (меньше площадь — выше скорости при той же нагрузке), модель крыла пересобрана (97f1fb7), страницы крыльев на сайте перегенерированы из конфигов (cc56167).

Сеть вместо решателя: решения

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

  • в игре решателя нет — только сеть (ONNX, нативное расширение Godot), проход на процессоре;
  • «физика» поля — офлайн-решатель на Python + GPU без ограничений по времени и сетке; сеть — его сжатие;
  • охват — любая точка рельефа Земли; новая физика (поглощение света подложкой, влажность) добавляется через офлайн-решатель и новые входы сети;
  • самым первым — пилот: на готовом решателе проверить, что идея вообще рабочая, до всех инструментов;
  • всё, что можно сделать детерминированно, делает скрипт; любой шаг прерывается и продолжается той же командой.

Попутно: таблица параметров поверхности по классам покрова (альбедо, отношение Боуэна, шероховатость) по литературе и пробник «ONNX Runtime внутри Godot» — работает, но конфликтует по символам с аддоном отладочной отрисовки. Решение пользователя — аддон удалить целиком, стрелки ветра F5 перерисовать своим мешем.

Пилот: что посчитали

  • Набор: 1890 случаев эталонного решателя «как в игре» (область 38,4 км, клетка 400 м) — 4 встроенных места, 5 синтетических форм и 40 процедурных рельефов (хребты, холмы, седловины, долины со случайными размерами); около 7,5 ч видеокарты. 238 случаев упёрлись в предел итераций (на ошибку сети это не повлияло).
  • Сеть: U-Net с условием по числам погоды (FiLM), 3,2 млн параметров. Вход — карта рельефа, поток тепла, координаты; ветер повёрнут «с запада». Выход — ветер, подъём и температура на 13 высотах над рельефом, как поправка к профилю притока.
  • Проход сети на процессоре — 19 мс (4 потока), это почти даром по сравнению с бюджетом.
  • Проверка — Онгудай целиком вне обучения, 5 отложенных процедурных рельефов, знакомые места при новых условиях и кривая по числу рельефов в обучении (5/10/20/40).

Пилот: итоги

наборветер на 60 м, медианаветер < 0,3 м/сподъём < 0,1 м/с
знакомый рельеф, новые условия0,47 м/с (профиль притока — 1,21)29 %92 %
отложенные процедурные0,63 м/с (профиль — 1,75)21 %83 %
Онгудай0,87 м/с (профиль — 1,12)12 %61 %

Ориентиры шлюза (90 % точек в пределах 0,3 м/с по ветру и 0,1 м/с по подъёму) не выполнены. Разбор:

  • Сеть учится, но данных мало. Ошибка на проверке выходит на полку к ~30-й эпохе, а на обучении продолжает падать — 1188 случаев на 44 рельефах.
  • Кривая по числу рельефов по ветру падает (Онгудай 1,16 → 0,82 м/с при 5 → 40 рельефах) — больше рельефов, точнее ветер. Это главный довод за идею.
  • Онгудай — не шум, а систематическая ошибка: при сильном ветре сеть завышает скорость почти по всей области (в одном случае в среднем на 1,7 м/с, +14 %). Причина нашлась в рельефе: на клетке 400 м типичный уклон Онгудая 0,22, у Алтая 0,12, у Аскарово и Аушкуля ~0,035, у процедурных — 0,016. Сеть экстраполировала за пределы того, что видела.

Решения по итогам

  • Критерий ветра — относительный (предложение Claude, принято): ошибка в пределах max(0,3 м/с; 10 %). Пользователь: «на +9 никто не летает (а там ±1 м/с роли не играет)». Кроме того, сам эталон — решатель на сетке 400 м — расходится с природой больше чем на 10 %. Подъём оставлен строгим (0,1 м/с): здесь это не термик 1–4 м/с, а среднее поле, и десятые доли решают, держит ли склон и включится ли источник термиков.
  • Нормировку не менять. Пользователь предложил привести всё к [0, 1]. Отклонено: нормировка на случай стирает масштаб (холм 100 м и хребет 2 км станут одинаковыми), а общие константы — то же линейное преобразование, что сейчас, экстраполяцию не лечат. Вместо этого во вход добавляются безразмерные по природе карты: уклон вдоль и поперёк ветра по точному направлению (у земли подъём ≈ ветер × уклон), положение в рельефе (хребет, склон, долина) и открытость на ветер.
  • Гладкие или реальные рельефы. Пользователь спросил: зачем реальный рельеф, и не проверить ли аппроксимацию на гладких? Разобрались, что в игре вход — всегда реальный рельеф Земли через наш загрузчик, а гладкие процедурные уже показали, что на знакомом классе рельефа сеть работает. Выбор пользователя — сразу 300 реальных рельефов по горам всей суши, от пологих до очень крутых. Вырезки готовятся тем же путём, что вход игры (те же тайлы, сглаживание и осреднение), целые горные системы отложены для проверки. Уже посчитанные процедурные остаются в наборе.
  • Конвейер — стадиями пачкой (пользователь): сначала скачать всё, потом всё обработать одним прогоном, потом счёт решателя и обучение, с распараллеливанием по процессору и видеокарте. Никаких «одно место → посмотрел → следующее».

Координатор модуля готовит пилот 2: оценка — одна ночь видеокарты на ~3500 случаев только по области.