Устройтесь поудобнее и позвольте группе нейронных сетей исследовать за вас ландшафт оптимизации, используя дарвиновский отбор.

В этой статье я попытаюсь объяснить 3 вещи:

  1. Основная идея эволюционного алгоритма и того, как вы можете развить популяцию нейронных сетей, используя дарвиновский отбор.
  2. Как можно решить среду CartPole-v0 из спортзала OpenAI, используя эволюционный фреймворк
  3. Как вы можете легко переработать одну и ту же структуру для последовательного развития агентов, которые могут хорошо работать в остальных средах управления

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

Решение проблем оптимизации с помощью алгоритмов, вдохновленных эволюцией

Эволюционные алгоритмы (ЭА) - это методы оптимизации, вдохновленные принципами биологической эволюции. Подходят ли они для искусственного интеллекта или плохо подходят, это вопрос споров, но примите во внимание этот удивительный факт: одна только эволюция была ответственна за то, чтобы взять одноклеточный организм и заселить всю Землю выдающимся количеством форм жизни и, не в последнюю очередь, достичь кульминации с развитие человеческого интеллекта. Конечно, мы должны отдать должное любым правилам, которым следует эволюция для достижения такого подвига.

Чарльз Дарвин предположил, что главным правилом эволюции является естественный отбор. Короче говоря, естественный отбор - это «выживание наиболее приспособленных». Возьмем, к примеру, жирафов: несколько миллионов лет назад у их предков - назовем их жирафами - не было такой длинной шеи. У них был широкий спектр более коротких длин шеи (инициализация). Однако люди с более длинной шеей могли дотянуться до листьев высоких деревьев и, следовательно, имели конкурентное преимущество в отношении доступности пищи, таким образом, выживая больше, чем их сверстники с короткой шеей (отбор). Следствием отбора было то, что жирафы с более длинной шеей могли воспроизводить больше, передавая свои гены, кодирующие длинные шеи (спаривание), и в результате следующее поколение имело более высокую вероятность появления жирафов с более длинные шеи, чем предыдущие. Иногда от двух родителей с короткой шеей рождается жираф с длинной шеей и наоборот (мутация) ¹.

Советники используют все эти концепции (инициализация, мутация, связывание, выбор) для успешного решения задач оптимизации. Они могут показаться вам немного упрощенными, однако это может быть впечатление, потому что я говорил об оптимизации 1 функции (длина шеи) по сравнению с 1 условием (наличие еды) . Хорошая новость в том, что эти правила могут масштабироваться и хорошо работать в многомерных пространствах. Как насчет оптимизации агента с тысячами функций и десятками условий? Внезапно советники начинают казаться более привлекательными, не так ли?

Развитие популяции нейронных сетей для решения задач обучения с подкреплением

Нейронные сети (NN) превосходно распознают сложные шаблоны в данных, и поэтому они являются разумным выбором для решения задач обучения с подкреплением (RL). Например, если мы хотим сбалансировать шест, перемещая тележку влево или вправо, как в задаче cartPole из тренажерного зала OpenAI, мы можем передать в сеть некоторые функции, описывающие состояние игры (положение тележки, скорость машины и т. д.), а затем пусть он решит, следует ли двигать тележку влево или вправо.

Поиск подходящего представления нейронных сетей

Прежде чем применять концепции эволюции для развития популяции нейронных сетей, нам нужна схема представления. Если задуматься, исправляя топологию, нам понадобится только n- -мерный вектор, несущий веса, чтобы представить соответствующую совокупность возможных NN. Например, для сети 2x3x1 нам нужен только 13-мерный вектор.

Фитнес-функция

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

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

О разведении и мутации нейронных сетей

Я хочу завершить этот раздел описанием двух ключевых процессов, создающих новое разнообразие: мутации и спаривания. Мы можем применить первое к NN, случайным образом выбирая веса, а затем немного увеличивая или уменьшая их значение. Для связывания мы можем применить линейную комбинацию весов двух родительских NN и присвоить результат соответствующему весу новой NN. Это всегда возможно, потому что размер вектора, представляющего наши сети, остается неизменным на протяжении всего процесса. Если функция приспособленности является руководством в процессе эволюции, то мутация и спаривание - это механизмы, с помощью которых NN (или агенты в терминологии RL) становятся фактически продвигаясь по ландшафту оптимизации.

Решение среды CartPole-v0 из тренажерного зала OpenAI

Как я объяснил выше, цель этой задачи - максимально увеличить время, в течение которого агент может уравновесить шест, толкая тележку влево или вправо. У нас есть 4 функции, которые описывают состояние игры (положение тележки, скорость машины, угол полюса, скорость полюса на вершине), и мы хотим развить наши NN для вывода 0 (толкать машину влево) или 1 (толкать машину в сторону). справа), чтобы штанга оставалась в равновесии.

Теперь я перейду к критическим частям кода, который решает проблему со средой CartPole-v0. Вы можете найти полный код здесь. Код написан на Python, и я использую Keras для создания прототипа нейронной сети и DEAP для выполнения процесса эволюции.

Сначала мы подключаем нашу NN к Keras:

model = Sequential()
model.add(Dense(10, input_dim=4, activation=’relu’))
model.add(Dense(5, activation=’relu’))
model.add(Dense(1, activation=’sigmoid’))

Это произвольная топология, поэтому экспериментируйте с ней. Важными битами являются входной уровень, который должен быть равен 4, чтобы прочитать все состояние игры, и выходной нейрон, использующий активацию сигмовидной формы для вывода только 0 (слева) или 1 (справа). Обратите внимание, что мы не компилируем модель, потому что советник будет оптимизировать веса для нас, поэтому мы фактически собираемся обойти обратное распространение.

Затем нам нужно определить нашу фитнес-функцию:

def cartPole(agent):
    R=0
    env = gym.make(‘CartPole-v0’)
    obs = env.reset()
    model.set_weights(rollParams(agent,[4,10,5,1]))
    for t in range(200): 
        action = model.predict_classes(np.array([obs]))[0][0]
        obs, reward, done, info = env.step(action)
        R+=reward
    if done:
        return (R),
        break

Эта функция связывает поведение конкретной NN с ее производительностью, поэтому очень важно понимать, что здесь происходит. Функция cartPole принимает вектор (agent), содержащий веса, определяющие конкретную NN, и устанавливает их для нашей ранее определенной модели с помощью функции rollParams. Затем у нас есть цикл, в котором происходит волшебство ... тренажерный зал openAI упрощает обучение агента в новой среде, создавая интерфейс между действиями вашего агента и конкретной игрой. Сначала мы получаем решение нашей NN (0 или 1), передавая ему текущее состояние игры (obs).

action = model.predict_classes (np.array ([obs])) [0] [0]

Затем мы отправляем действие в окружающую среду и получаем обратную связь (вознаграждение) о том, что наше действие вызвало у полюса. Мы также отслеживаем общую награду нашего NN на протяжении всего игрового эпизода с R.

наблюдение, награда, готово, информация = env.step (действие)

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

Давайте продолжим и уточним наши настройки эволюции:

toolbox.register(“attr_float”,random.uniform,-1,1) 
toolbox.register(“individual”, tools.initRepeat, creator.Individual,toolbox.attr_float, 111)
toolbox.register(“evaluate”, cartPole)
toolbox.register(“mate”, tools.cxBlend,alpha=0.5)
toolbox.register(“mutate”, tools.mutGaussian, mu=0.0, sigma=0.2, indpb=0.05)
toolbox.register(“select”, tools.selTournament, tournsize=3)

Toolbox - это класс DEAP, который мы используем для установки важных деталей процесса эволюции. Например, мы определяем создание NN (или индивидуума в терминологии EAs) как 111-мерный вектор со случайными значениями от -1 до 1. Почему? Потому что это общее количество подключений полностью подключенной нейронной сети 4x10x5x1. Мы также указываем, что собираемся оценить физическую пригодность указанного человека с помощью функции cartPole. Затем мы выбираем способы создания разнообразия. Мы соединяем два NN с помощью функции cxBlend, которая представляет собой просто линейную комбинацию их весов (в данном случае среднего), и мы изменяем их с помощью mutGaussian , который случайным образом добавляет к значениям весов, выбранным из гауссовского распределения со средним 0 и стандартным отклонением 0,2. Людей, переходящих в следующее поколение, отбирают путем проведения небольших турниров 3-го размера. Чем выше физическая подготовка, тем выше шансы на победу в турнире.

Наконец, давайте определим нашу основную функцию:

def main():
    pop = toolbox.population(n=100)
    hof = tools.HallOfFame(1)
 
    pop, log = algorithms.eaSimple(pop, toolbox, cxpb=0.8, mutpb=0.2, ngen=10, stats=stats, halloffame=hof, verbose=True)
    print(‘\nBest: ‘)
    print(hof)

Здесь мы переходим к алгоритму DEAP eaSimple - функции, отвечающей за запуск процесса эволюции - размер каждого поколения (n = 100) , параметры эволюции, которые мы только что определили выше (t объект oolbox), вероятность спаривания двух особей (cpxb), вероятность мутации особи (mutpb ), количество поколений, в которых мы планируем развивать наши сети (ngen), и, наконец, что не менее важно, мы просим алгоритм отслеживать лучших людей на протяжении всего процесса ( Halloffame).

Результатом выполнения всей программы будет 111-мерный вектор, содержащий веса наиболее эффективной NN при балансировке полюса.

Новая среда, новый подход? Не так быстро…

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

Если вы подозреваете, что мы также должны изменить функцию пригодности, ваша интуиция абсолютно верна, однако OpenAI уже определил подходящую функцию вознаграждения для каждой среды, которую мы можем автоматически получить из env.step (action), помните? поэтому код фитнес-функции остается прежним!

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

Дискретный горный автомобиль

model = Sequential()
model.add(Dense(10, input_dim=2, activation=’relu’))
model.add(Dense(5, activation=’relu’))
model.add(Dense(3, activation=’sigmoid’))

Непрерывный горный автомобиль

model = Sequential()
model.add(Dense(10, input_dim=2, activation=’relu’))
model.add(Dense(5, activation=’relu’))
model.add(Dense(1, activation=’linear’))

Акробот

model = Sequential()
model.add(Dense(10, input_dim=6, activation=’relu’))
model.add(Dense(5, activation=’relu’))
model.add(Dense(3, activation=’sigmoid’))

Маятник

model = Sequential()
model.add(Dense(10, input_dim=3, activation=’relu’))
model.add(Dense(5, activation=’relu’))
model.add(Dense(1, activation=’linear’))

Заключение

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

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

Надеюсь, я вдохновил вас попробовать аналогичные стратегии для решения других задач в тренажерном зале. Буду очень рад увидеть ваши результаты!

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