Введение
Недавно появились устройства, способные думать на таком же уровне, что и человек. Сегодня же ты можешь спокойно написать дипломную работу не листая тонны контента и имея под рукой всего-лишь один сайт. Наверное, ты уже понял, о чем идет речь. Нейросети буквально заполняют наш мир. Но я думаю тебе всегда было интересно, как на самом деле работает такой алгоритм, и какие методы обучения лежат в основе искусственного интеллекта. В этой статье я приоткрою завесу и расскажу тебе, как создать свою нейросеть без каких-либо особых усилий.
План работы
Давай я коротко опишу план нашей работы. Первым делом следует разобраться с тем, как вообще работает нейросеть и что она из себя представляет. После этого мы воспользуемся утилитами для ее написания и попробуем реализовать все это дело. Для визуализации всей работы я решил воспользоваться движком Unity, поэтому тебе следует его установить. По мимо этого мы создадим тестовую комнату и зададим цель для нашей нейронной сети. Думаю, нескольких строчек для описания всей работы будет достаточно, поэтому перейдем в раздел установки необходимых компонентов.
Среда для разработки
Как говорилось ранее, тебе потребуются базовые знания языка программирования C#, а также наличие под рукой движка Unity. Я использую Unity версии 2021 года, но для работы может сгодиться и более новая среда. Также тебе нужно установить Python 3.10 и выше. К нему мы подключим модули глубокого обучения и библиотеки для математических вычислений. Весь список необходимых модулей я предоставил ниже:- mlagents
- numpy (не выше 1.19.4)
- torch (не ниже 1.7.1)
- CUDA Toolkit
Добавлю немного слов к требованиям по железу. Я тестировал все на процессоре Intel Core i7 4700MQ в сочетании с древней видеокартой NVIDIA GeForce GTX 765M. В основном подойдет любой другой процессор. Видеокарту придется использовать от зеленых и я сейчас не про внеземную цивилизацию, а компанию NVIDIA. Вся проблема заключается именно в утилите CUDA, которая позволяет использовать видеочипы в полную мощь и от них будет зависеть скорость обучения твоей нейросети. Также более современные версии модуля Numpy не подойдут в работе и будут вызывать критические ошибки, а все дело в том, что разработчики изменили метод, который позволяет программе присваивать определенные типы переменных. Поэтому если ты увидишь в логах слова float и ему подобные, будь уверен в том, что вся проблема кроется в версии numpy. В остальном проблем возникнуть не должно. Также для установки основного компонента PyTorch нужно использовать эту команду:
Код:
pip3 install torch -f https://download.pytorch.org/whl/torch_stable.html
Установка займет достаточно много времени, так как файл с пакетами и необходимыми функциями весит 2.4 ГБ. Во время процедуры не выключай систему и не позволяй ей уходить в режим сна.
Как работают нейросети
Перед любой работой нужно иметь базовые знания, чем мы сейчас и займемся. Я постараюсь максимально понятно и подробно рассказать про работу нейронной сети, затрагивая как можно меньше математических кривых и других непонятных терминов. Первым делом давай определимся с тем, что такое нейронная сеть:Нейросеть - это понятие напрямую связано с биологией и понимается как последовательность нейронов, которые соединяются между собой синапсами. Благодаря такой структуре, машина обретает способность анализировать и даже запоминать различную информацию. Нейронные сети также способны не только анализировать входящую информацию, но и воспроизводить ее из своей памяти.
Если сравнивать с человеком, то нейронная сеть на основе определенных событий делает вывод и записывает его во временную память. Всю ее работу можно буквально представить в виде весов, на которых лежат благоприятные и неблагоприятные условия. К примеру если нейросеть упадет в пропасть, то шанс смерти от этого события возрастет. Повторив процедуру пару раз она сделает вывод о том, что благоприятных исходов такого события нету и его лучше не повторять в дальнейшем. Теперь давай разберемся с составляющими частями слова нейросеть. Ведь оно напрямую связано с таким термином, как нейрон.
Нейрон - вычислительная единица, которая способна получать и обрабатывать информацию, а также передавать ее. В основном нейроны бывают трех типов: входные, скрытые и выходные. Если в нейросети имеется огромное количество нейронов, то используется термин слоя, для более точной классификации.
На языке машин нейроны способы работать только с тремя числами в диапазоне от -1 до 1. Если работа происходит с более большими числами, то они записываются в виде дроби, а точнее единица делится на это число. Для передачи информации между нейронами используется такое понятие как синапс. Он содержит всего лишь один параметр - вес. От него зависит значимость информации и скорость ее дальнейшей обработки. Чем больше вес, тем быстрее информация перейдет от одного нейрона в другой. На этапе инициализации нейронной сети весь вес распределяется в абсолютно случайном порядке.
Чтобы на выходе получать нужный диапазон принято использовать функции активации, которые нормализируют входную информацию и на выходе получают требуемые значения. Всего существует три основных типа функций: линейная, сигмоид и гиперболический тангенс. Их я затрагивать не стану, более подробную информацию ты сможешь найти на просторах интернета.
Итак, после небольшого экскурса в мир нейросетей стоит приступить к ее реализации, чем мы сейчас и займемся.
Создаем нейронную сеть в Unity
На первом этапе открываем сам Unity и создаем 3D проект. Я выбрал этот движок, чтобы визуально показать тебе как работает и обучается нейросеть в режиме реального времени. После создания добавим несколько объектов в наш проект. Это будут стены, кубик, пространство и небольшой мячик. Основная идея в том, чтобы научить нейросеть добираться до определенного объекта. В проекте все выглядит таким образом:Также не забываем накинуть на Goal и Cube физику, а точнее Rigidbody. В случае с главным объектом все физические свойства стоит применять к родительскому объекту (Agent). Таким образом мы создадим необходимые условия для того, чтобы фигуры могли взаимодействовать. После этого стоит добавить к стене и нашему шарику скрипты. В них мы ничего записывать не будем, а всего лишь инициализируем для дальнейшей работы. Чтобы это сделать, тебе нужно перейти в инспектор и добавить компонент с названием C# Script. Отлично, теперь перейдем к созданию сенсоров, которые отвечают за перемещение в пространстве и накинем пульт управления для нашего кубика. Все это делается таким же образом, что я описал чуть выше. Теперь перейдем к написанию скриптов. Перед этим стоит перейти на вкладку Window -> Packet Manager и после на просторах Unity скачать пакет ML-Agents.
Первым делом убираем все лишние и оставляем публичный класс с идентификатором Agent:
C#:
public class Cube_AI : Agent
{
}
Сюда мы поместим все необходимые компоненты для управления. Перед этим у тебя должны быть импортированы следующие библиотеки:
C#:
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
После этого нашей нейросети потребуется объект, до которого она должна дойти. Для этого используем такую команду:
C#:
public override void OnEpisodeBegin()
{
transform.position = Vector3.zero;
}
Переходим к главному этапу, а именно сенсорам, которые отвечают за координацию в пространстве. Я не буду добавлять зрение и ограничусь объектами вокруг нашей нейросети. Проще говоря, сама нейронка будет слепая, но ее главным ориентиром будет служить пространство вокруг, как раз для этого и потребуются наши сенсоры. Задаем основной объект и цель, до которой следует добраться:
C#:
public override void CollectObservations(VectorSensor sensor)
{
sensor.AddObservation(transform.position);
sensor.AddObservation(targetTransform.position);
}
Отлично, ориентация в пространстве у нас есть, но стоит подумать о передвижении, для этого зададим координаты по которым можно перемещаться и скорость передвижения. Делается это в несколько строчек кода:
C#:
public override void OnActionReceived(ActionBuffers actions)
{
float moveX = actions.ContinuousActions[0];
float moveZ = actions.ContinuousActions[1];
float moveSpeed = 1f;
transform.position += new Vector3(moveX, 0, moveZ) * Time.deltaTime * moveSpeed;
}
Теперь наш объект умеет двигаться, но пока что у него нет интеллекта и цели, ради которой он будет это делать. Поэтому займемся этим, попутно добавляя векторы перемещения для более точной координации:
C#:
public override void Heuristic(in ActionBuffers actionsOut)
{
ActionSegment<float> continuousActions = actionsOut.ContinuousActions;
continuousActions[0] = Input.GetAxisRaw("Horizontal");
continuousActions[1] = Input.GetAxisRaw("Vertical");
}
private void OnTriggerEnter(Collider other)
{
if (other.TryGetComponent<Goal>(out Goal goal))
{
SetReward(+1f);
EndEpisode();
}
if (other.TryGetComponent<Wall>(out Wall wall))
{
SetReward(-1f);
EndEpisode();
}
}
Чтобы не заморачиваться давай я коротко объясню, что здесь происходит. В первом цикле мы задаем векторы передвижения вместе с их индексом. После этого создаем условные циклы, которые будут поощрять нашу нейросеть за выполнение цели или наоборот "ругать" за попытку побега. Таким образом мы делаем баланс для наших весов, которые позволят нейронке в дальнейшем понимать, что можно делать, а что лучше не стоит. Также добавляем функцию завершения эпизода и настройку управления можно спокойно закрывать.
Теперь к родительской точке Agent подключаем наш скрипт и добавляем также Decision Requester, его ты можешь найти по пути Add Component -> ML Agents. По мимо этого у тебя должен появиться параметр Target Transform. В него мы помещаем наш шар, который будет главной целью нейросети. Все настройки оставляем по умолчанию и приступаем к подключению мозга кубика.
Для этого тебе потребуется перейти в папку с проектом и в пути к нему прописать команду cmd, чтобы открыть папку через консоль. Начало обучения происходит непосредственно через само окно терминала. Мы всего лишь даем нейросети цель и объект, которым она может управлять. Нейронка является самым простым прототипом и имеет функцию глубокого обучения (к сожалению, оно еще и долгое). Чтобы произошла магия и кубик начал подавать признаки жизни тебе следует ввести в консоли следующую команду:
Код:
mlagents-learn
Спустя несколько секунд должен появиться логотип Unity и фраза о том, что открылся порт для прослушивания и подключения. Если у тебя появилась ошибка, то следует переустановить все пакеты или проверить инспектор и правильность настроенных параметров. Теперь остается нажать кнопку Пуск в самом окне проекта и ждать результата. В случае успеха ты увидишь метаданные объекта и Behavior Name. Остается только ждать и наблюдать за обучением. При последующих запусках рекомендуется добавить флаг --force, чтобы обнулить всю работу и перезапустить нейросеть. Перед этим убедись, что Python находится в самом проекте. Если его нет, то стоит прописать такие команды:
Код:
py -m venv venv
venv\Scripts\activate
Результат работы может быть непредсказуемый и совершенно неожиданный, начиная от обычного выполнения задачи методом проб и ошибок и заканчивая простым бездействием. Также есть несколько вариантов базовых нейросетей, с которыми можно ознакомиться в самом пакете. Для их эксплуатации тебе придется добавить зрение объекту и несколько других сенсоров для ориентации. В остальном песочница и искусственный интеллект в твоем распоряжении. Небольшой аквариум с примитивным, но саморазвивающимся мозгом. Теперь стоит подвести итоги.
Подводим итоги
Написание нейронных сетей это очень трудоемкий и затратный процесс, но наблюдать за их обучением очень интересно и завораживающе. В этой статье я максимально коротко и понятно рассказал тебе как визуализировать работу нейронной сети и разместить ее прямо у себя на устройстве. Ты можешь натренировать ее под свои нужды или использовать в дальнейшем для развлечения, но с точки зрения безопасности такие "искусственные мозги" могут нанести огромный ущерб в неумелых руках или просто тех, кто хочет развлечься.
C#:
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
public class Cube_AI : Agent
{
[SerializeField] private Transform targetTransform;
public override void OnEpisodeBegin()
{
transform.position = Vector3.zero;
}
public override void CollectObservations(VectorSensor sensor)
{
sensor.AddObservation(transform.position);
sensor.AddObservation(targetTransform.position);
}
public override void OnActionReceived(ActionBuffers actions)
{
float moveX = actions.ContinuousActions[0];
float moveZ = actions.ContinuousActions[1];
float moveSpeed = 1f;
transform.position += new Vector3(moveX, 0, moveZ) * Time.deltaTime * moveSpeed;
}
public override void Heuristic(in ActionBuffers actionsOut)
{
ActionSegment<float> continuousActions = actionsOut.ContinuousActions;
continuousActions[0] = Input.GetAxisRaw("Horizontal");
continuousActions[1] = Input.GetAxisRaw("Vertical");
}
private void OnTriggerEnter(Collider other)
{
if (other.TryGetComponent<Goal>(out Goal goal))
{
SetReward(+1f);
EndEpisode();
}
if (other.TryGetComponent<Wall>(out Wall wall))
{
SetReward(-1f);
EndEpisode();
}
}
}
Последнее редактирование модератором: