Статья Искусственный мозг. Создаем собственную нейросеть

neuronet.png

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

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

Среда для разработки

Как говорилось ранее, тебе потребуются базовые знания языка программирования C#, а также наличие под рукой движка Unity. Я использую Unity версии 2021 года, но для работы может сгодиться и более новая среда. Также тебе нужно установить Python 3.10 и выше. К нему мы подключим модули глубокого обучения и библиотеки для математических вычислений. Весь список необходимых модулей я предоставил ниже:
  1. mlagents
  2. numpy (не выше 1.19.4)
  3. torch (не ниже 1.7.1)
  4. 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 проект. Я выбрал этот движок, чтобы визуально показать тебе как работает и обучается нейросеть в режиме реального времени. После создания добавим несколько объектов в наш проект. Это будут стены, кубик, пространство и небольшой мячик. Основная идея в том, чтобы научить нейросеть добираться до определенного объекта. В проекте все выглядит таким образом:

Scene.jpg


Также не забываем накинуть на 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();
        }
    }
}
 
Последнее редактирование модератором:
Мы в соцсетях: