Аннотация

Используя нейронные сети для создания ИИ мы хотим добиться самостоятельного движения транспортного средства с избежанием препятствий. Реализуем мы это, используя соответствующие вводы/выводы и тщательно обучив нейронную сеть. Мы будем передавать сеть дистанции до ближайших препятствий вокруг для имитации того, что видит вокруг себя водитель-человек. Выход будет представлять из себя ускорение и поворот (рулевое управление) автомобиля. Нам также необходимо обучить сеть набору стратегических вводов-выводов. Результат работы пары нейронов впечатляет! Автомобиль едет, избегая препятствия, но можно сделать дополнительные улучшения и модификации для работы с конкретной целью.

Введение

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

Игры — это первая мысль, возникающая при упоминании «ИИ, управляющего автомобилем». Многие игры могут использовать эту технику для контроля транспортных средств, но есть и ряд других приложений, которым может понадобиться возможность управления автомобилем как в виртуальном, так и в реальном мире.

Так как же нам сделать всё это? Есть много техник создания ИИ, но так как нам нужен «мозг» для контроля автомобиля, мы применим нейронные сети, принцип которых основан на идее работы нашего мозга. Нам нужно будет определить, какими входными и исходными данными будет оперировать наша нейронная сеть.

Нейронные сети

Идея Искуственного Интеллекта на основе Нейронных Сетей исходит из принципа функционирования нашего мозга. Наш мозг состоит из 10 в одиннадцатой степени клеток-нейронов, которые посылают друг другу электрические сигналы. Каждый нейрон состоит из одного или двух аксонов, которые работают в качестве «выходных» узлов, и множества дендритов, которые работают как входной путь электрических сигналов. Нейронам необходима определённая сила входных сигналов для реагирования на них всех дендритов. После срабатывания нейрон «выстреливает», посылая электрический сигнал вниз к аксону и другим нейронам. Подключения (аксоны и дендриты) усиляются, если используются часто.

Этот принцип в меньших масштабах применяется и в нейронных сетях. Современные компьютеры не имеют той же вычислительной мощности, что и 20 миллиардов нейронов, но даже с несколькими нейронами мы можем получить интеллектуальный ответ от нейронной сети.

Нейроны организованы в слои, как показано на рис. 1. Входной слой будет принимать сигналы и, в зависимости от силы соединения с каждым нейроном в следующем слое, сигнал будет передаваться на следующий уровень. Эта сила связи называется весом. Значение каждого нейрона в каждом слое будет зависеть от веса связи и полученных знаений предыдущего слоя.


Базовая нейронная сеть
Рис. 1: Базовая нейронная сеть
Водителя можно сравнить с «функцией». Существуют различные вводы: то, что водитель видит. Эти данные обрабатываются мозгом как функцией, и реакция водителя — это результат функции.

Функция f(x) = y преобразует значение x (одно измерение) в y (другое измерение).

Мы используем нейронные сети обратного распространения для создания «мозга» водител, так как такие сети способны воспроизводить функции, имеющие область и границы, имеющие каждая несколько измерений: f(x1, x2,…,xn) = y1,y2,…,ym

То есть это именно то, что нам нужно, так как нам нужны несколько вводов и несколько выводов.

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

Обучившись, нейронная сеть будет реагировать близко к желаемым результатам, когда передан известный ввод, и будет «угадывать» ответ на любой ввод, не соответствующий вводам-выводам, заданным при обучении.

Фактические уравнения выходят за рамки данной статьи. Есть много хороших книг, обьясняющиъ, как работают нейронные сети обратного распространения.

Нейронная сеть, используемая в нашем случае, имеет 4 слоя (рис. 2). Я пробовал много различных комбинаций, от 3 до 6 слоёв. Она работала нормально с тремя слоями, но когда я обучал её набором из 22 вводов-выводов, результат функции не был достаточно точен. 5 и 6 слоёв работали прекрасно, но на их обучение уходило определённое время (до 20-30 минут на PII) и когда я запускал программу, требовалось значительное вычислительное время CPU.

Мой входной слой содержит три нейрона, выходной содержит два. Позже я объясню, почему. Между ними находятся два слоя из восьми нейронов каждый. Опять же, я пробовал создать слои с большим и меньшим числом нейронов, и 8 давали оптимальные результаты.

При выборе числа нейронов учтите, что каждый слой и каждый нейрон, который вы добавляете, увеличивает время рассчета веса.



Рис. 2: Нейронная сеть 3-8-8-2.
 

Добавление нейронов:
У нас есть входной слой I с i нейронами и выходной слой O с o нейронами. Мы ходим добавить один нейрон в средний слой M. Число соединений между нейронами, которые мы добавляем, будет (i+o).

 

Добавление слоёв:

У нас есть входной слой I с i нейронами и выходной слой O с o нейронами. Мы хотим добавить между ними слой M с m нейронами. Количество соединений с нейронами, которые мы добавляем, будет (mx(i+o)).

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

Ввод

Какая информация важна при управлении транспортным средством? Во-первых, нам необходимо знать положение препятсвия по отношению к нам. Оно справа от нас, слева или прямо? Если есть здания по обеим сторонам дороги, но спереди пусто, мы ускорим движение. Но если перед нами остановился автомобиль, мы затормозим. Во-вторых, нам необходимо знать расстояние от нас до обьекта. Если объект далеко, мы продолжим движение, пока он не окажется достаточно близко, и только тогда мы замедлимся либо затормозим.

Именно эту информацию мы и будем использовать в нашей нейронной сети. Для простоты у нас есть три относительных направления: слева, спереди, справа. И ещё нам нужно расстояние от препятствия до автомобиля.



Рис. 3
Мы определим, какое поле зрения будет иметь наш ИИ-водитель, и сделаем список обьектов, которые он может увидеть. Для простоты в нашем примере мы используем круг, но мы могли бы использовать и реальную проекцию зрения в шести пересекающихся плоскостях. Теперь для каждого объекта, попадающего в этот круг, мы проверяем, находится ли он в левом поле зрения, в правом, или в центре.

Вход в нейронную сеть будет массивом: float Vision[3]. Расстояние до ближайшего препятствия слева, в центре и справа от автомобиля будут храниться в Vision[0], Vision[1] и Vision[2] соответственно. На рис. 3 показано, как массив работает. Препятствие слева на уровне 80% от максимального расстояния, препятствие справа на 40% от макс. расстояния, и никаких препятствий в центре.

Чтобы иметь возможность сделать это, мы должны установить расположение (x,y) каждого объекта, позицию (x,y) автомобиля и угол автомобиля. Нам также необходим ‘r’ (радиус круга) и d прав. и d лев., векторы от корабля к линиям L прав., L лев. Эти линии параллельны направлению корабля. Оба вектора перпендикулярны линиям.

Даже если наш мир трёхмерен, вся математика — двумерна, т.к. транспортное средство не переходит в третье измерение, оно не летит. Все уравнения будут рассматривать только X и Y, но не Z-координаты.

Во-первых, мы вычислим уравнения линий L лев. и L прав., которые помогут нам определить, существует ли препятствие для левой или правой части автомобиля. См. рис.4 для иллюстрации всех переменных.


Рис. 4
Затем мы проверяем каждый объект в мире, чтобы узнать, находится ли он в пределах круга (если объекты организованы в quadtree или octree, процесс будет гораздо более быстрым, чем со связанным списком).

Теперь сохраняем расстояние в соответствующей части массива (Vision[0], Vision[1] или Vision[2]) только если уже хранящееся там расстояние больше, чем только что полученное. Vision должно быть инициализировано заранее в 2r (2 радиуса круга).

После проверки каждого объекта у нас есть массив Vision с расстоянием до ближайшего объекта справа, слева и по центру автомобиля. Если ничего не было найдено, у нас есть значение по умолчанию (2*r), что означает «ничего нет в поле зрения».

Поскольку нейронная сеть использует сигмовидную функцию, ввод должен быть между 0.0 и 1.0. 0.0 должно означать, что объект уже прикасается к транспортному средству, а 1.0 — что в поле зрения объектов нет. Так как мы установили максимальный радиус зрения, все расстояния могут быть легко конвертированы в float между 0.0 и 1.0.

Вывод

Вывод должен устанавливать скорость и направление движения автомобиля. В жизни это были бы акселлератор, тормоз и рулевое колесо. Так что нам нужны два вывода: один будет ускорением или тормозом (тормоз — это отрицательное ускорение), а второй — изменение в направлении движения.

Вывод из нейронной сети — также значение между 0.0 и 1.0 по тем же причинам, что ввод. В случае ускорения 0.0 означает «полное торможение», 1.0 — «полный газ», и 0.5 — отсутствие ускорения и торможения. Для руля 0.0 — это «максимальный поворот налево», 1.0 — «направо», и 0.5 — движение вперёд. Поэтому нам нужно перевести вывод в значения, которые могут быть использованы.

Следует отметить, что «отрицательное ускорение» — это торможение, если автомобиль движется вперёд, но это значит так же «полный назад», если автомобиль стоит на месте. Соответственно, «положительное ускорение» — это торможение при движении назад.

Обучение

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

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

Создадим таблицу (Табл.1) с различным расположением препятствий по отношению к автомобилю и с реакциями, которые мы хотели бы получить от ИИ.

Таблица 1

А теперь переводим всё это в числа

Таблица 2

Ввод:
0.0: Объект почти касается автомобиля
1.0: Объект на максимальном расстоянии видимости либо нет объектов на расстоянии видимости

Вывод:
Ускорение:
0.0: Максимальное отрицательное ускорение (тормоз или обратный ход)
1.0: Максимальное положительное ускорение

Поворот
0.0: Полный налево
0.5: Прямо
1.0: Полный направо

Заключение / Совершенствование

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



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

«Если (транспортное средство не двигается более 5 секунд), то (взять на себя контроль и повернуть его на 90 градусов вправо)».

С помощью такого простого трюка мы гарантируем, что автомобиль никогда не встанет на месте более, чем на пять секунд, не зная, что делать.

Транспортное средство, как показано на рисунке, не увидит небольшой зазор между зданиями. Так как мы не задали высокого уровня точности зрения, эти два здания для автомобиля — сплошная стена. Для более точного поведения ИИ нам необходимо создать 5 или 7 уровней входа для нейронной сети. Вместо «право, центр, лево» мы могли бы передавать «максимум справа, слегка справа, по центру, слегка слева, максимум слева». При тщательном обучении нейронной сети ИИ будет видеть разрыв и понимать, что он может пройти через него.

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

Таблица 3:

Теперь у нейронной сети появилось 3D-зрение, и нам нужно лишь модифицировать управление автомобилем и физику его движения.

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

Как мы только что видели, этот метод может улучшаться бесконечно и использоваться в самых различных областях. Даже если он применяется не для какой-то полезной деятельности, просто интересно посмотреть, как себя ведёт искусственный интеллект. Если мы будем смотреть достаточно долго, то поймем, что в сложном окружении автомобиль не всегда будет выбирать один и тот же путь из-за небольшой разницы в решениях нейронной сети из-за её природы.