Альтернатива ML-Agents: интегрируем нейросети в Unity-проект с помощью PyTorch C API. Как интегрировать нейросеть в игру

В Horizon Zero Dawn есть сразу шесть навигационных сеток: четыре для существ разного размера, одна для тех, кто плавает, одна, чтобы игрок мог корректно оседлать существо

Альтернатива ML-Agents: интегрируем нейросети в Unity-проект с помощью PyTorch C++ API

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

Другими словами, мне нужно превратить исследовательский проект на PyTorch в готовое решение, способное вместе с движком Unity работать в боевых условиях.

Можно несколькими способами интегрировать нейронную сеть в Unity. Я предлагаю использовать C++ API для PyTorch (под названием libtorch) для создания нативной разделяемой библиотеки, которую затем можно будет подключить к Unity как плагин. Существуют и другие подходы (например, использовать ML-Agents), которые в определённых случаях могут быть проще и эффективнее. Но преимущество моего подхода заключается в том, что он обеспечивает большую гибкость и даёт больше возможностей.

Допустим, у вас есть какая-то экзотическая модель и вы просто хотите использовать существующий PyTorch-код (который был написан без намерения общаться с Unity); или ваша команда разрабатывает собственную модель и не хочет отвлекаться на мысли о Unity. В обоих случаях код модели может быть сколь угодно сложным и использовать все возможности PyTorch. А если вдруг дело дойдёт до интеграции, в игру вступит C++ API и завернёт всё в библиотеку без малейшего изменения изначального PyTorch-кода модели.

Итак, мой подход сводится к четырём ключевым шагам:

  1. Настройка окружения.
  2. Подготовка нативной библиотеки (C++).
  3. Импорт функций из библиотеки / подключение плагина (Unity / C#).
  4. Сохранение / развёртывание модели.

Подготовка нативной библиотеки

Следующий шаг — конфигурирование CMake. Я взял за основу пример из документации PyTorch и изменил его так, чтобы после сборки мы получали библиотеку, а не исполняемый файл. Положите этот файл в корневой каталог вашего проекта с нативной библиотекой.

CMakeLists.txt

Исходный код библиотеки будет размещён в networks.cpp.

В этом подходе есть ещё одна приятная особенность: нам пока не нужно думать, какую именно нейронную сеть мы хотим использовать с Unity. Причина (немного забегая вперед) заключается в том, что мы в любой момент можем запустить сеть в Python, получить её трассировку и просто сказать libtorch «применить эту трассировку для этих входов». Поэтому, можно сказать, что наша нативная библиотека просто обслуживает некий чёрный ящик, работая с вводом-выводом.

Но если вы хотите усложнить задачу и, например, реализовать обучение сети прямо во время работы среды Unity, то вам придётся написать на C++ архитектуру сети и обучающий алгоритм. Однако это выходит за рамки данной статьи, поэтому для получения дополнительной информации я отсылаю вас к соответствующему разделу документации PyTorch и репозиторию с примерами кода.

В любом случае, в network.cpp нам нужно определить внешнюю функцию для инициализации сети (загрузка с диска) и внешнюю функцию, которая запускает сеть с входными данными и возвращает результаты.

networks.cpp

Чтобы вызывать функции нашей библиотеки непосредственно из Unity, нужно передать информацию об их точках входа. В Linux я использую для этого __attribute__((visibility(«default»))). В Windows для этого существует спецификатор __declspec( dllexport ), но, честно говоря, я не проверял, работает ли он там.

Итак, начнём с функции загрузки трассировки нейросети с диска. Файл имеет относительный путь — он лежит в корневом каталоге проекта Unity, а не в Assets/. Так что будьте внимательны. Вы также можете просто передать имя файла из Unity.

Теперь перейдём к функции, которая кормит сеть входными данными. Напишем на С++ код, который использует указатели (ими управляет Unity) для перегонки данных туда и обратно. В этом примере я полагаю, что моя сеть имеет входы и выходы фиксированной размерности и запрещаю Unity менять это. Здесь, например, я возьму Tensor и Tensor (например, такая сеть нужна для сегментации пикселей RGB-изображений на 5 групп).

В общем случае вам придётся передать информацию о размерности и объёме данных, чтобы избежать переполнения буфера.

Чтобы преобразовать данные в формат, с которым работает libtorch, мы используем функцию torch::from_blob. Она принимает массив чисел с плавающей запятой и описание тензора (с указанием размерности) и возвращает созданный Тензор.

Нейросети могут принимать несколько входных аргументов (например, вызов forward () принимает x, y, z в качестве входных данных). Чтобы справиться с этим, все входные тензоры упаковываются в вектор стандартной библиотеки шаблонов torch::jit::IValue (даже если аргумент только один).

Чтобы получить данные из тензора, проще всего обработать их поэлементно, но если из-за этого упадёт скорость обработки, для оптимизации процесса чтения данных можно использовать Tensor::accessor. Хотя лично мне это не понадобилось.

В результате для моей нейросети получается вот такой простой код:

На этом этапе оценивается приспособленность генома и определяется, выполнилось ли требование done . Смотрим на переменную « x » из data.json и проверяем, превысила ли она длину уровня. Если это так, повышаем приспособленность до нашего порога, и это означает, что задание выполнено.

Генерация 3D из 2D изображений

Модели, которые решают эту задачу, генерируют 3D модель объекта на основе одного или нескольких входных изображений. В разработке игр такие модели могут использоваться для виртуализации каких-то объектов из реальной жизни и создания более персонализированного аватара.

Synthesizing Coupled 3D Face Modalities by Trunk-Branch Generative Adversarial Networks

Используют GAN для генерации 3D модели лица человека. Дополнительно накладывают условия на GAN, чтобы модель генерировала 3D модель лица с заданным выражением.

Структура модели

Pixel-Aligned Implicit Function for High-Resolution Clothed Human Digitization

На вход модели подается одно или несколько изображений. Цель — восстановить 3D геометрию и текстуру одетого человека, сохранив при этом детали изображения. Предложенный алгоритм состоит из полностью сверточного кодировщика изображений и непрерывной функции, которая переводит эмбеддинг изображения в 3D-поверхность. Эта функция основана на многослойных перцептронах.

Dual Attention MobDenseNet for Robust 3D Face Alignment

Реконструируют 3D модель лица человека из одного изображения. Архитектура — сверточная нейросеть, в каждом слое есть блок Spatial group-wise enhance для улучшения распространения черт лица в разных ракурсах.

Генерация уровней и локаций

Модель принимает на вход изображение локации или граф, описывающий ее составляющие, и генерирует локацию в формате изображения или 3D модели. Это может упростить процесс создания игровых сцен при разработке игр.

DeepView: View Synthesis with Learned Gradient Descent

Нейросеть принимает на вход изображения одного вида с разных ракурсов и восстанавливает 3D модель.

Multi-branch Volumetric Semantic Completion from a Single Depth Image

Восстанавливают 3D модель локации из одного изображения глубины (depth image). Используют несколько дискриминаторов, чтобы повысить реалистичность сгенерированной модели.

Image Generation from Scene Graphs

Где: CVPR’18

Генерируют изображение из image graph (графовое представление содержания изображения). Используют один слой графовой сверточной сети и cascaded refinement network (CRN).

Graph R-CNN for Scene Graph Generation

Где: ECCV’18

Решают ту же задачу, что Image Generation from Scene Graphs. Добавляют к графовой сверточной нейросеть механизм внимания и называют это attentional Graph Convolutional Network (aGCN).

Пайплайн обучения Graph R-CNN

Probabilistic Neural Programmed Networks for Scene Generation

Где: NeurIPS’18

Генерируют изображение из текстового описания. Решение — PNPNet, вариационный автокодировщик.

Multi-Scale Local Planar Guidance for Monocular Depth Estimation

Где: State-of-the-art на задаче Monocular Depth Estimation on KITTI Eigen split

Предлагают метод для генерации depth images на основе одного изображения. Суть подхода — последовательно генерировать depth image для 1/8, 1/4 и 1/2 изображения (local planar guidance).

F.E.A.R. запоминается достаточно умными противниками, которые умеют работать сообща. На самом деле они даже не знают о существовании друг друга — просто ИИ грамотно координирует их действия

Выбор игры

Лучше выбрать игру начала 2000-х годов и старше, поскольку для более новых предлагаемые методы, скорее всего, окажутся бесполезными. Это может быть 3D или 2D-игра со спрайтами низкого разрешения (спрайт — графический объект в компьютерной графике). Апскейлинг обычно хорошо работает в играх с около-реалистичной графикой (не рисованной), которая не выглядела реалистично из-за аппаратных ограничений того времени. Текстуры таких игр могли содержать много деталей, но из-за маленького разрешения экранов не было смысла делать их более качественными. Современные мониторы имеют высокое разрешение, поэтому мы можем использовать методы машинного обучения для улучшения текстур.

Если повезёт, файлы текстур будут находиться в отдельной папке в виде обычных изображений. Иногда данные могут иметь формат, специфичный для движка игры. В этом случае они, скорее всего, будут сжаты и собраны в один или несколько файлов. Если выбранная вами игра была достаточно популярна, то фанаты и модеры наверняка уже нашли способы извлечь из неё всё необходимое. Поищите информацию в интернете — там точно найдется подходящее руководство. В Warcraft 3, например, текстуры хранятся в виде .blp-файлов, которые можно сконвертировать в JPG или PNG и обратно.

Выбор модели

Это самый сложный этап: чтобы найти подходящую нейросеть, вам нужно поэкспериментировать с разными решениями и настройками. Если вы преданы своему делу, можете выбрать и самостоятельно обучить модель на основе одного из популярных алгоритмов. Но проще взять готовую предварительно обученную архитектуру. Предлагаем несколько наиболее известных.

Waifu2x

Метод использует глубокие свёрточные нейронные сети для масштабирования изображений в 1.6 или 2 раза. Ссылка выше позволит работать прямо в браузере, но вы можете настроить модель, скачав её с GitHub. Нейросеть обучена на изображениях в стиле аниме, поэтому больше подходит для мультипликационных текстур.

Пример улучшенных с помощью Waifu2x моделей в Morrowind:

ESRGAN

Модель применяет генеративно-состязательные сети для увеличения разрешения изображений в 4 раза. Мы писали о её более ранней версии. Она была создана, чтобы работать на реальных фотографиях, поэтому хорошо подойдёт для детализированных текстур. В этом посте вы найдёте руководство по её использованию в Windows.

Улучшенные c ESRGAN текстуры Morrowind:

GameWorks: Materials & Textures

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

Пример улучшенных с её помощью текстур Doom:

AI Gigapixel

Платный коммерческий продукт. Работает с реальными фотографиями и позволяет выполнять масштабирование до 600%. Если у вас есть деньги и желание поэкспериментировать, то результаты могут оказаться очень хорошими.

Пример улучшения графики в Final Fantasy VII:

Приведённые выше архитектуры можно переобучать, настраивать и даже комбинировать между собой. Например, модификация для Fallout: New Vegas сделана с использованием сразу трёх нейросетей: Waifu2x, ESRGAN и AI Gigapixel.

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