Автор | Джейкоб Гурски Компилировать|ВКонтакте Источник | К науке о данных
вводить
Что, если бы я сказал вам, что вам не нужно вычислять градиенты для обучения нейронной сети, вам просто нужно распространять антецеденты? Это магия нейроэволюции! В то же время я собираюсь показать, что все это можно легко сделать с помощью всего лишь Numpy! Изучите статистику, и вы узнаете много нового о методах на основе градиента, но некоторое время назад я читал очень интересную статью парня из Uber AI, где он показал, что при решении игр Atari простые генетические алгоритмы ничем не отличаются от самых сложных, основанных на методы Gradient RL вполне конкурентоспособны. Я связал источники ниже, я настоятельно рекомендую вам прочитать их, если вы заинтересованы в обучении с подкреплением.
что такое нейроэволюция
Во-первых, для тех, кто еще не знает, нейроэволюция описывает применение эволюционных и генетических алгоритмов для обучения структур и весов нейронной сети в качестве альтернативы без градиента!Мы будем использовать здесь очень простую нейронную сетьЭволюционный случай, используя только сеть с фиксированной топологией фокусируется только на оптимизации весов и смещений. Процесс нейроэволюции можно определить как четыре основных шага, которые повторяются до тех пор, пока не будет достигнута конвергенция, начиная со случайно сгенерированного пула сетей.
- Оценить физическую форму населения
- Выберите лучших людей для репликации
- Повторное заполнение лучшей копией для сети
- Ввести мутацию нормального распределения в весах сети
Вау, это выглядит так просто! Разберем некоторые термины:
-
Пригодность: это просто описывает, как сеть выполняет конкретную задачу, и позволяет нам определить, какие сети следует развивать. Обратите внимание, что поскольку эволюционные алгоритмы представляют собой форму невыпуклой оптимизации, их можно использовать с любой функцией потерь, независимо от ее дифференцируемости (или ее отсутствия).
-
Вариант: этот, наверное, самый простой! Чтобы улучшить наши подсети, мы должны ввести случайные вариации весов сети, которые обычно получаются из равномерного или нормального распределения. Существует множество различных форм мутации: мутация сдвига (умножение параметра на случайное число), мутация подкачки (замена параметра случайным числом), мутация знака (изменение знака параметра) и так далее. Мы будем использовать только простые аддитивные мутации, но здесь есть много места для инноваций!
Преимущество нейроэволюции
Мы должны также рассмотреть теоретические преимущества нейроэволюционных моделей. Во-первых, нам нужно использовать только прямой проход сети, поскольку нам нужно только вычислить потери, чтобы определить, какую сеть реплицировать. Это означает, что бэкпроп, как правило, самый дорогой! Во-вторых, эволюционные алгоритмы гарантированно находят глобальный минимум поверхности потерь при достаточном количестве итераций, в то время как методы на основе выпуклого градиента застревают в локальных минимумах. Наконец, более сложные формы нейроэволюции позволяют нам оптимизировать не только веса сети, но и саму структуру!
Так почему бы не использовать нейроэволюцию постоянно?
Это сложная проблема, но она сводится к тому, что точный градиентный спуск более эффективен, когда имеется достаточно информации о градиенте. Это означает, что чем более выпукла поверхность потерь, тем больше вы хотите использовать аналитический метод, такой как SGD, вместо генетического алгоритма. Таким образом, использование генетических алгоритмов в контролируемых условиях очень редко, поскольку обычно имеется достаточно информации о градиенте, чтобы традиционные методы градиентного спуска работали нормально. Однако, если вы работаете в условиях RL или с нерегулярными отсутствующими гранями или низкой выпуклостью (например, с непрерывными GAN), тогда нейроэволюция предлагает жизнеспособный вариант!На самом деле, многие недавние исследования показали, что параметрические нейроэволюционные модели могут работать лучше в этих настройки.
выполнить
загрузить библиотеку
Как сказано во введении, мы постараемся использовать в этом проекте только numpy, определяя только нужные нам вспомогательные функции.
import numpy as np
import gym
О данных
Мы будем использовать классическую среду с колесом тележки из тренажерного зала, чтобы протестировать нашу сеть. Наша цель — увидеть, как долго сеть удерживает шест в вертикальном положении, перемещая его влево и вправо. В качестве задачи RL нейроэволюционный подход должен быть хорошим выбором!Наша сеть будет получать 4 наблюдения в качестве ввода и вывода влево и вправо в качестве действия.
вспомогательная функция
Во-первых, мы определим несколько вспомогательных функций для построения нашей сети. Во-первых, это функция активации relu, которую мы будем использовать в качестве функции активации для скрытого слоя и функцию softmax в качестве вывода сети, чтобы получить оценку вероятности вывода сети!Наконец, нам нужно определить функцию, когда нам нужно вычислить категориальную перекрестную энтропию. Эта функция генерирует однократное кодирование вектора ответа.
def relu(x):
return np.where(x>0,x,0)
def softmax(x):
x = np.exp(x — np.max(x))
x[x==0] = 1e-15
return np.array(x / x.sum())
определить нашу сеть
Во-первых, мы определим класс для каждой сети в совокупности. Нам нужно определить метод инициализации, который случайным образом присваивает веса и смещения и принимает структуру сети в качестве входных данных, метод прогнозирования, чтобы мы могли получить вероятность входных данных, и метод окончательной оценки, который возвращает классификационное пересечение сети с учетом ввод и ответ Энтропия! Опять же, мы используем только функции, которые мы определяем, или функции из numpy. Обратите внимание, что метод инициализации также может принимать на вход другую сеть, именно так мы будем выполнять мутацию между поколениями!
# 让我们定义一个新的神经网络类,可以与gym交互
class NeuralNet():
def __init__(self, n_units=None, copy_network=None, var=0.02, episodes=50, max_episode_length=200):
# 测试我们是否需要复制一个网络
if copy_network is None:
# Saving attributes
self.n_units = n_units
# 初始化空列表以容纳矩阵
weights = []
biases = []
# 填充列表
for i in range(len(n_units)-1):
weights.append(np.random.normal(loc=0,scale=1,size=(n_units[i],n_units[i+1])))
biases.append(np.zeros(n_units[i+1]))
# 创建参数字典
self.params = {'weights':weights,'biases':biases}
else:
self.n_units = copy_network.n_units
self.params = {'weights':np.copy(copy_network.params['weights']),
'biases':np.copy(copy_network.params['biases'])}
# 突变权重
self.params['weights'] = [x+np.random.normal(loc=0,scale=var,size=x.shape) for x in self.params['weights']]
self.params['biases'] = [x+np.random.normal(loc=0,scale=var,size=x.shape) for x in self.params['biases']]
def act(self, X):
# 获取权重和偏置
weights = self.params['weights']
biases = self.params['biases']
# 第一个输入
a = relu((X@weights[0])+biases[0])
# 在其他层传播
for i in range(1,len(weights)):
a = relu((a@weights[i])+biases[i])
#获取概率
probs = softmax(a)
return np.argmax(probs)
# 定义评估方法
def evaluate(self, episodes, max_episode_length, render_env, record):
# 为奖励创建空列表
rewards = []
# 首先,我们需要建立我们的gym环境
env=gym.make('CartPole-v0')
# 如果需要的话,我们可以录像
if record is True:
env = gym.wrappers.Monitor(env, "recording")
env._max_episode_steps=1e20
for i_episode in range(episodes):
observation = env.reset()
for t in range(max_episode_length):
if render_env is True:
env.render()
observation, _, done, _ = env.step(self.act(np.array(observation)))
if done:
rewards.append(t)
break
# 关闭环境
env.close()
# 获取最终奖励
if len(rewards) == 0:
return 0
else:
return np.array(rewards).mean()
Определите наш класс GA
Наконец, нам нужно определить класс для управления нашей популяцией, чтобы выполнить четыре ключевых шага нейроэволюции Нам нужны три метода. Во-первых, метод инициализации, который создает случайный сетевой пул и устанавливает свойства. Затем нам нужен метод подгонки, который на основе входных данных повторяет шаги, перечисленные выше: сначала оцените сеть, затем выберите наиболее подходящую сеть, создайте подсети и, наконец, измените подсеть! method , чтобы мы могли использовать лучший сетевой учебный класс. Приступаем к тестированию!
# 定义处理网络种群的类
class GeneticNetworks():
#定义我们的初始化方法
def __init__(self, architecture=(4,16,2),population_size=50, generations=500,render_env=True, record=False,
mutation_variance=0.02,verbose=False,print_every=1,episodes=10,max_episode_length=200):
# 创建我们的网络列表
self.networks = [NeuralNet(architecture) for _ in range(population_size)]
self.population_size = population_size
self.generations = generations
self.mutation_variance = mutation_variance
self.verbose = verbose
self.print_every = print_every
self.fitness = []
self.episodes = episodes
self.max_episode_length = max_episode_length
self.render_env = render_env
self.record = record
# 定义我们的fiting方法
def fit(self):
# 遍历所有代
for i in range(self.generations):
# 评估
rewards = np.array([x.evaluate(self.episodes, self.max_episode_length, self.render_env, self.record) for x in self.networks])
# 跟踪每一代的最佳得分
self.fitness.append(np.max(rewards))
# 选择最佳网络
best_network = np.argmax(rewards)
# 创建子网络
new_networks = [NeuralNet(copy_network=self.networks[best_network], var=self.mutation_variance, max_episode_length=self.max_episode_length) for _ in range(self.population_size-1)]
#设置新网络
self.networks = [self.networks[best_network]]+new_networks
# 如果必要输出结果
if self.verbose is True and (i%self.print_every==0 or i==0):
print('Generation:',i+1,'| Highest Reward:',rewards.max().round(1),'| Average Reward:',rewards.mean().round(1))
# 返回最佳网络
self.best_network = self.networks[best_network]
алгоритм тестирования
Как упоминалось выше, мы проверим нашу сеть на задаче CartPole, используя только один скрытый слой из 16 узлов и два выходных узла, представляющих левые или правые ходы. Нам также нужно усреднить несколько итераций, чтобы мы случайно не выбрали плохую сеть для следующего поколения! Я выбрал многие из этих параметров после некоторых проб и ошибок, поэтому ваша ситуация может отличаться! Кроме того, мы будем вводить мутации только с дисперсия 0,05, чтобы не нарушать функциональность сети.
# 让我们训练一个网络种群
from time import time
start_time = time()
genetic_pop = GeneticNetworks(architecture=(4,16,2),
population_size=64,
generations=5,
episodes=15,
mutation_variance=0.1,
max_episode_length=10000,
render_env=False,
verbose=True)
genetic_pop.fit()
print('Finished in',round(time()-start_time,3),'seconds')
Generation: 1 | Highest Reward: 309.5 | Average Reward: 29.2
Generation: 2 | Highest Reward: 360.9 | Average Reward: 133.6
Generation: 3 | Highest Reward: 648.2 | Average Reward: 148.0
Generation: 4 | Highest Reward: 616.6 | Average Reward: 149.9
Generation: 5 | Highest Reward: 2060.1 | Average Reward: 368.3
Finished in 35.569 seconds
начальная случайная сеть
Во-первых, давайте посмотрим, как случайно инициализированная сеть выполняет эту задачу. Понятно, что никакой стратегии здесь нет, и шест падает почти сразу. Пожалуйста, игнорируйте курсор на гифке ниже
random_network = NeuralNet(n_units=(4,16,2))
random_network.evaluate(episodes=1, max_episode_length=int(1e10), render_env=True, record=False)
Через 5 поколений...
Всего через 5 поколений мы видим, что наша сеть почти полностью освоила CartPole!И это заняло всего около 30 секунд времени обучения!Обратите внимание, что при дальнейшем обучении сеть учится держать его полностью в вертикальном положении, почти все время Вот и все, но на данный момент нас интересует только скорость, 5 поколений это очень мало Мы должны рассматривать это как отличный пример силы нейроэволюции.
# 让我们看看我们最好的网络
genetic_pop.best_network.evaluate(episodes=3, max_episode_length=int(1e10), render_env=True, record=False)
конец
Ясно, что мы можем многое добавить в будущем, чтобы еще больше проверить эффективность нейроэволюции. Во-первых, интересно изучить влияние различных операций мутации, таких как скрещивание.
Переход на современную платформу глубокого обучения, такую как TensorFlow или PyTorch, также является разумной идеей. Обратите внимание, что генетические алгоритмы очень параллельны, потому что все, что нам нужно сделать, это запустить каждую сеть на устройстве, где распространяется предыдущий термин. Нет необходимости отражать веса или сложные стратегии распределения, поэтому с каждым дополнительным процессором время выполнения резко падает.
Наконец, мы должны исследовать нейроэволюцию в различных задачах обучения с подкреплением, даже в других ситуациях, когда градиенты трудно оценить, например, в генеративно-состязательных сетях или сетях LSTM с длинной последовательностью.
дальнейшее чтение
Если вас интересует нейроэволюция и ее приложения, у Uber есть отличная страница в нескольких статьях, демонстрирующая современные преимущества нейроэволюции в обучении с подкреплением:
.UB и .com/tag/deep-что насчет…
Исходный код этого проекта можно найти в репозитории GitHub:
Оригинальная ссылка:к data science.com/gradient - введите…
Добро пожаловать на сайт блога Panchuang AI:panchuang.net/
sklearn машинное обучение китайские официальные документы:sklearn123.com/
Добро пожаловать на станцию сводки ресурсов блога Panchuang:docs.panchuang.net/