Аннотация
Используя нейронную сеть, мы хотим, чтобы транспортное средство управляло собой само, избегая препятствий. Мы добиваемся этого путем выбора соответствующих входов/выходов и тщательного обучения нейронной сети. Мы скармливаем сети расстояния до ближайших препятствий вокруг автомобиля, имитируя зрение водителя-человека. На выходе получаем ускорение и поворот руля транспортного средства. Нам также необходимо обучить сеть на множестве стратегий ввода-вывода. Результат впечатляющий даже с использованием всего лишь нескольких нейронов! Автомобиль ездит, обходя препятствия, но возможно сделать некоторые модификации, чтобы это программное средство справлялось с более специфическими задачами.
Введение
Идея в том, чтобы иметь транспортное средство, которое управляет собой само и избегает препятствий в виртуальном мире. Каждое мгновение оно само решает, как изменить свою скорость и направление в зависимости от окружающей среды. Для того чтобы сделать это более реальным, ИИ должен видеть только то, что видел бы человек, если бы находился за рулем, так что ИИ будет принимать решения только на основе препятствий, которые находятся спереди транспортного средства. Имея реалистичный ввод, ИИ мог бы быть использован в реальном автомобиле и работать так же хорошо.
Когда я слышу фразу: "Управление транспортным средством с помощью ИИ", я сразу же задумываюсь о компьютерных играх. Многие из гоночных игр могут использовать эту технику для контроля транспортных средств, но есть целый ряд других приложений, которые ищут средство управления транспортом в виртуальном или же реальном мире.
Так как же мы это будем делать? Существует множество способов реализации ИИ, но ведь если нам нужен "мозг" для управления транспортным средством, то нейронные сети подойдут как нельзя лучше. Нейронные сети работают так же, как и наш мозг. Они, наверное, и будут правильным выбором. Мы должны определить, что будет входом, а что выходом нашей нейронной сети.
Нейронные сети
Нейронные сети появились при изучении строения мозга. Наш мозг состоит из 1011 клеток-нейронов, которые посылают электрические сигналы друг к другу. Каждый нейрон состоит из одного или двух аксонов, которые «выдают результат», и большого числа дендритов, которые принимают входные электрические сигналы. Нейрону нужна определенная сила входного сигнала, который складывается со всех дендритов, чтобы быть активированным. После активации нейрон отправляет электрический сигнал вниз по его аксону к другим нейронам. Связи (аксонов и дендритов) укрепляются, если они часто используются.
Этот принцип применяется в нейронных сетях меньших масштабов. Современные компьютеры не обладают мощностями вычислений, которые создают двадцать миллиардов нейронов, но даже с несколькими нейронами, нейронная сеть может давать разумный ответ.
Нейроны организовываются в слои, как показано на рисунке 1. Входной слой будет иметь входы, и в зависимости от прочности соединения с каждым нейроном в следующем слое, входной сигнал подается на следующий уровень. Прочность соединения называется весом. Значение каждого нейрона в каждом слое будет зависеть от веса связи и значения нейронов предыдущего слоя.
Рисунок 1
Водителя можно сравнить с "функцией". Есть множество входов: то, что видит водитель. Эти данные обрабатываются мозгом, как функцией, и реакция водителя является выходом из функции.
Функция f(х) =у преобразует значение х (одно измерение) в у (одно измерение).
Мы используем нейронную сеть обратного распространения для "мозга" водителя, поскольку такие нейронные сети способны аппроксимировать любую функцию с областями определения и значения, которые могут иметь несколько измерений: F(x1,x2,..., хn) = y1,y2,…,yn.
Это именно то, что нам и нужно, поскольку мы должны работать с несколькими входами и выходами.
Когда нейронная сеть состоит всего из нескольких нейронов, мы можем вычислить веса, необходимые для получения приемлемого результата. Но по мере увеличения числа нейронов, увеличивается и сложность вычислений. Сеть обратного распространения можно обучить, что установит необходимые веса. Мы просто должны предоставить искомые результаты с соответствующими им входами.
После обучения, нейронная сеть будет реагировать выдавать результат близкий к желаемому при подаче известного результата, и "угадывать" правильный ответ при любом входе, не соответствующем обучающему.
Фактические вычисления выходят за рамки данной статьи. Есть много хороших книг, объясняющих работу сетей с обратным распространением ошибки.
Нейронная сеть, используемая в данном случае, имеет 4 слоя (рис. 2). Я пробовал различные комбинации от трех до шести слоев. Все отлично работало с тремя слоями, но, когда я обучал сеть на наборе из двадцати двух входов-выходов, приближение функции оказывалось недостаточно точным. Пять и шесть слоев прекрасно выполняли свою задачу, но на обучение пришлось потратить значительное время (от 20 до 30 минут на PII), и когда я запускал программу, требовалось много процессорного времени на вычисления.
В данной сети три нейрона во входном слое и два – в результирующем. Позже я объясню почему. Между ними два слоя по восемь нейронов в каждом. Опять же, я тестировал слоя с большим и меньшим числом нейронов и остановился на восьми, поскольку это число дает приемлемый результат.
При выборе числа нейронов имейте в виду, что каждый слой и каждый нейрон, добавленные в систему, будут увеличивать время, необходимое для расчета весов.
Рисунок 2
Добавление нейронов:
Мы имеем входной слой I с i нейронами, и результирующий слой O с o нейронами. Мы хотим добавить один нейрон в средний слой М. Число соединений между нейронами, которые мы добавляем равно (i+o).
Добавление слоев:
Мы имеем входной слой I с i нейронами, и результирующий слой O с o нейронами. Мы хотим добавить M слоев с m нейронов в каждом. Число соединений между нейронами, которые мы добавляем равно (m*(i+o)).
Теперь, когда мы рассмотрели, как "мозг" работает, мы должны понять, как определить входы и выходы нейронной сети. Нейронная сеть сама по себе не делает ничего, если мы даем ей информацию из виртуального мира и не подаем ответ сети контроллеру транспортного средства.
Вход
Какая информация важна для управления транспортным средством? Во-первых, мы должны знать положение препятствия по отношению к нам. Это положение справа, слева от нас или перед нами? Если есть здания по обе стороны от дороги, но ничего нет впереди, мы ускоряемся. Но если автомобиль остановился перед нами, мы тормозим. Во-вторых, мы должны знать расстояние от нашей позиции до объекта. Если объект находится далеко, мы будем продолжать движение, пока он не приблизится, и в этом случае, мы замедляемся или останавливаемся.
Это именно та информация, которую мы будем использовать для нашей нейронной сети. Для простоты введем три относительных направления: слева, спереди и справа. А также расстояния от препятствия до транспортного средства.
Рисунок 3
Определим поле зрения нашего ИИ-водителя и составим список объектов, которые он видит. Для простоты мы используем круг в нашем примере, но могли бы использовать реальный усеченный шестью пересекающимися плоскостями конус. Теперь для каждого объекта в этом кругу, проверяем, находится он в левом поле зрения, правом, или по центру.
На вход в нейронную сеть подается массив: float Vision[3]. Расстояния до ближайшего препятствия слева, в центре, и справа от транспортного средства будут храниться в Vision[0], Vision [1] и Vision[2] соответственно. На рисунке 3 показано, как этот массив выглядит. Препятствие слева на расстоянии 80% от максимального расстояния, справа — на 40%, и нет никаких препятствий по центру.
Для того чтобы вычислить это, нам нужна позиция (х, у) каждого объекта, положение (х, у) автомобиля и угол транспортного средства. Нам также необходимы r (радиус окружности) и dright, dleft – векторы между автомобилем и линиями Lright и Lleft. Эти линии параллельны направлению движения автомобиля. Оба вектора перпендикулярны линиям.
Хотя это 3D мир, вся математика двумерная, так как автомобиль не может двигаться в третьем измерении, поскольку он не летает. Все уравнения включают только x и y, но не z.
Во-первых, мы вычислим уравнения линий Lright и Lleft, которые помогут нам определить, находится препятствие справа, слева или по центру от транспортного средства.
Рисунок 4 является иллюстрацией всех вычислений.
Рисунок 4
где
Тогда мы вычисляем координаты точки на линии
где Vx и Vу положение транспортного средства.
Теперь мы, наконец, можем вычислить cr
Аналогично находим уравнение линии Lleft с помощью вектора dleft.
Далее, мы должны вычислить центр окружности. Все, что внутри круга, будет видно ИИ. Центр окружности С(х, у) на расстоянии r от положения автомобиля V(х, у).
где Vх, Vу положение транспортного средства и Сх, Су — центр круга.
Затем мы проверим, находится ли каждый объект в мире в пределах круга (если объекты организованы в квадрадерево или октодерево, этот процесс гораздо быстрее, чем связанный список).
Если , то объект находится в кругу, где Oх, Oy координаты препятствия.
Для каждого объекта в пределах круга, мы должны проверить, находится он справа, слева или по центру от транспортного средства.
Если , то объект находится в правой части круга
иначе если , то в левой части
иначе по центру.
Вычислим расстояние от объекта до автомобиля
Теперь мы сохраняем расстояние в соответствующей части массива (Vision[0], Vision[1] или Vision[2]) при условии, что ранее сохраненное расстояние больше, чем только что вычисленное. Изначально, массив Vision должен быть инициализирован значениями 2r.
После проверки каждого объекта, у нас есть массив Vision с расстояниями до ближайших объектов справа, по центру и слева от автомобиля. Если не было найдено ни одного объекта в данном поле зрения, элемент массива будет иметь значение по умолчанию , что означает: "нет ни одного объекта в пределах видимости".
Поскольку нейронная сеть использует сигмовидную функцию, входные данные должны лежать в пределах от 0,0 до 1,0. 0,0 будет означать, что объект касается транспортного средства и 1,0 означает, что нет объектов в пределах видимости. Поскольку мы установили максимальное расстояние, на котором может видеть ИИ-водитель, мы легко можем привести все расстояния к диапазону от 0,0 до 1,0.
Выход
На выходе мы должны получить указания по изменению скорости автомобиля и направления. Это могут быть ускорение, торможение и угол поворота рулевого колеса. Так что нам нужно два выхода; один будет значением ускорения/торможения (торможение это просто отрицательное ускорение), а другой будет указывать изменение направления.
Результат лежит между 0,0 и 1,0 по той же причине, что и входные данные. Для ускорения 0,0 означает "полный тормоз"; 1,0 — "полный газ" и 0,5 — отсутствие торможения или ускорения. Для рулевого управления, 0,0 означает «полностью влево», 1,0 – «полностью вправо» и 0,5 – не изменять направление. Так что мы должны перевести результаты в значения, которые мы можем использовать.
Следует отметить, что "отрицательное ускорение" означает торможение, если транспортное средство движется вперед, но это также означает двигаться в обратном направлении, если автомобиль находится в состоянии покое. Кроме того, "положительное ускорение" означает торможение, если транспортное средство движется в обратном направлении.
Обучение
Как я упоминал ранее, мы сначала должны обучить нейронную сеть. Нам необходимо создать набор входов и соответствующих им выходов.
Выбор правильных входов-выходов для обучения нейронной сети, вероятно, самая сложная часть работы. Мне пришлось обучать сеть с множеством данных, смотреть, как автомобиль действовал в окружающей среде, а затем изменять записи по мере необходимости. В зависимости от того, как мы обучаем сеть, транспортное средство может «колебаться» в некоторых ситуациях и оказываться обездвиженным.
Составим таблицу (табл. 1) различного положения препятствий относительно транспортного средства и желаемой реакции ИИ.
Таблица 1
Входные нейроны Относительное расстояние до препятствия |
Выходные нейроны | |||
Слева | По центру | Справа | Ускорение | Направление |
Нет препятствий | Нет препятствий | Нет препятствий | Полный газ | Прямо |
Половина пути | Нет препятствий | Нет препятствий | Небольшое ускорение | Немного правее |
Нет препятствий | Нет препятствий | Половина пути | Небольшое ускорение | Немного левее |
Нет препятствий | Половина пути | Нет препятствий | Торможение | Немного левее |
Половина пути | Нет препятствий | Половина пути | Ускорение | Прямо |
Касание объекта | Касание объекта | Касание объекта | Обратный ход | Влево |
Половина пути | Половина пути | Половина пути | Без изменений | Немного левее |
Касание объекта | Нет препятствий | Нет препятствий | Торможение | Полное вправо |
Нет препятствий | Нет препятствий | Касание объекта | Торможение | Полное влево |
Нет препятствий | Касание объекта | Нет препятствий | Обратный ход | Влево |
Касание объекта | Нет препятствий | Касание объекта | Полный газ | Прямо |
Касание объекта | Касание объекта | Нет препятствий | Обратный ход | Полное вправо |
Нет препятствий | Касание объекта | Касание объекта | Обратный ход | Полное влево |
Объект близко | Объект близко | Объект очень близко | Без изменений | Влево |
Объект очень близко | Объект близко | Объект близко | Без изменений | Вправо |
Касание объекта | Объект очень близко | Объект очень близко | Торможение | Полное вправо |
Объект очень близко | Объект очень близко | Касание объекта | Торможение | Полное влево |
Касание объекта | Объект Закрыть | Объект Дальнего | Без изменений | Вправо |
Объект далеко | Объект близко | Касание объекта | Без изменений | Влево |
Объект очень близко | Объект близко | Объект ближе, чем на полпути | Без изменений | Полное вправо |
Объект ближе, чем на полпути | Объект близко | Объект очень близко | Торможение | Полное влево |
И вот теперь можно перевести это в цифры в таблице 2.
Таблица 2
Входные нейроны | Выходные нейроны | |||
Слева | По центру | Справа | Ускорение | Направление |
1,0 | 1,0 | 1,0 | 1,0 | 0,5 |
0,5 | 1,0 | 1,0 | 0,6 | 0,7 |
1,0 | 1,0 | 0,5 | 0,6 | 0,3 |
1,0 | 0,5 | 1,0 | 0,3 | 0,4 |
0,5 | 1,0 | 0,5 | 0,7 | 0,5 |
0,0 | 0,0 | 0,0 | 0,2 | 0,2 |
0,5 | 0,5 | 0,5 | 0,5 | 0,4 |
0,0 | 1,0 | 1,0 | 0,4 | 0,9 |
1,0 | 1,0 | 0,0 | 0,4 | 0,1 |
1,0 | 0,0 | 1,0 | 0,2 | 0,2 |
0,0 | 1,0 | 0,0 | 1,0 | 0,5 |
0,0 | 0,0 | 1,0 | 0,3 | 0,8 |
1,0 | 0,0 | 0,0 | 0,3 | 0,2 |
0,3 | 0,4 | 0,1 | 0,5 | 0,3 |
0,1 | 0,4 | 0,3 | 0,5 | 0,7 |
0,0 | 0,1 | 0,2 | 0,3 | 0,9 |
0,2 | 0,1 | 0,0 | 0,3 | 0,1 |
0,0 | 0,3 | 0,6 | 0,5 | 0,8 |
0,6 | 0,3 | 0,0 | 0,5 | 0,2 |
0,2 | 0,3 | 0,4 | 0,5 | 0,9 |
0,4 | 0,3 | 0,2 | 0,4 | 0,1 |
Вход:
0,0: Объект почти касается транспортного средства.
1,0: Объект на максимальном расстоянии от автомобиля или нет объекта в поле зрения
Выход:
Ускорение
0,0: Максимальное отрицательное ускорение (торможение или наоборот)
1,0: Максимальное положительное ускорение
Направление
0,0: Полный поворот влево
0,5: Прямо
1,0: Полный поворот вправо
Заключение / Пути улучшения
Использование нейронной сети обратного распространении пригодно для наших целей, но есть некоторые проблемы, выявленные в ходе тестирования. Некоторые изменения могли бы сделать программу надежнее и адаптировать ее к другим ситуациям. Сейчас я опишу вам некоторые проблемы, над решением которых вы могли бы подумать.
Рисунок 5
Транспортное средство «застревает» на время, поскольку оно колеблется в решении вопроса – ехать вправо или влево. Этого следовало ожидать: люди иногда имеют ту же проблему. Исправить это не так легко, пытаясь настроить веса нейронной сети. Но мы можем добавить строку кода, которая гласит:
"Если (транспортное средство не движется в течение 5 секунд), то (взять на себя контроль и повернуть его на 90 градусов вправо)".
Этим мы может гарантировать, что автомобиль никогда не стоять на месте, не зная, что делать.
Транспортное средство не увидит небольшой разрыв между двумя домами, как показано на рисунке 5. Поскольку мы не имеем высокого уровня точности в зрении (слева, в центре, справа), два здания, находящиеся близко друг к другу, будут для искусственного интеллекта похожи на стену. Чтобы иметь более ясный взор нашего ИИ, нам нужно иметь 5 или 7 уровней точности на входе в нейронную сеть. Вместо "справа, по центру, слева", мы могли бы иметь "далеко справа, рядом справа, по центру, рядом слева, далеко слева". При хорошем обучении нейронной сети, искусственный интеллект будет видеть разрыв и понимать, что он может пройти сквозь него.
Это работает в 2D мире, но что если транспортное средство способно пролетать через пещеру? С некоторыми изменениями этой техники, мы можем заставить ИИ летать, а не ездить. По аналогии с последней проблемой, мы увеличиваем точность взора. Но вместо того чтобы добавить "прав" и "лев", мы можем сделать так, как показано в таблице 3.
Таблица 3
Слева вверху | Вверху | Справа вверху |
Слева | Центр | Справа |
Слева внизу | Внизу | Справа внизу |
Теперь, когда наша нейронная сеть может увидеть мир в 3D, нам просто необходимо изменить наше управление и реакцию транспортного средства на мир.
Транспортное средство лишь «бродит» без какой-либо конкретной цели. Оно не делает ничего, кроме обхода препятствий. В зависимости от того, куда мы хотим попасть, мы можем "подстраивать" мозг по мере необходимости. Мы можем иметь множество различных нейронных сетей и использовать нужную в конкретной ситуации. Например, мы могли бы следовать за автомобилем в поле зрения. Нам просто нужно подключить еще одну нейронную сеть, обученную следовать за другим транспортным средством, получающую в качестве входных данных местоположение второго транспорта.
Как мы только что увидели, этот метод может быть улучшен и применен в самых различных областях. Даже если он не используется для какой-либо полезной цели, нам все равно будет интересно наблюдать, как система искусственного интеллекта ведет себя в окружающей среде. Если наблюдать достаточно долго, мы поймем, что в сложных условиях, транспортное средство не всегда будет идти по одному и тому же пути из-за небольшой разницы в решении в связи с характером нейронной сети. Автомобиль будет иногда ездить слева от здания, а иногда справа от того же здания.
Литература
- Joey Rogers, Object-Oriented Neural Network in C++, Academic Press, San Diego, CA, 1997
- M.T. Hagan, H.B. Demuth and M.H. Beale, Neural Network Design, PWS Publishing, Boston, MA, 1995