Вопрос или проблема
У меня есть многопользовательская среда, основанная на боидов, использующая gymnasium. Я использовал PPO с политикой Mlp и сетью из 8 слоев по 512 нейронов в каждом слое. Хотя я достиг объединения, это было централизованное решение.
Результаты:
Объединение (Централизованный PPO)
Сеть:
policy_kwargs = dict(
activation_fn=th.nn.Tanh, # Использование функции активации ReLU
net_arch=[dict(pi=[512, 512, 512, 512, 512, 512, 512, 512],
vf=[512, 512, 512, 512, 512, 512, 512, 512])] # Отдельные сети для политики (pi) и функции ценности (vf)
)
Код обучения:
model = PPO("MlpPolicy", env, kwargs=policy_kwargs tensorboard_log="./ppo_Agents_tensorboard/", verbose=1, device=device)
model.set_random_seed(SimulationVariables["ModelSeed"])
Моя цель, однако, состоит в том, чтобы создать децентрализованную версию Это по сути MAPPO (PPO с децентрализованными акторами, но с общим критиком, как утверждается, это хорошо для группового поведения, в отличие от IPPO — полностью децентрализованного). Вот почему я создал индивидуальную политику сети следующим образом с SB3:
class CustomMultiAgentPolicy(ActorCriticPolicy): # Убедитесь, что наследуетесь от правильного родительского класса
def __init__(self, observation_space, action_space, lr_schedule, **kwargs):
super(CustomMultiAgentPolicy, self).__init__(observation_space, action_space, lr_schedule, **kwargs)
self.obs_size = observation_space.shape[0] # только это в соответствии с вашей структурой наблюдения
self.hidden_size = 128 # Вы можете изменить это в зависимости от ваших потребностей
self.action_space = action_space
self.actor = CustomActor(observation_space, action_space)
self.critic = SharedCritic(observation_space)
def forward(self, obs, **kwargs):
action_mean = self.actor(obs) # Это среднее нормального распределения
# Создайте нормальное распределение со средним значением и log_std (преобразованным в std)
# action_std = th.exp(self.log_std) # Преобразуйте log_std в std
action_std = th.clamp(th.exp(self.log_std), min=1e-3, max=1.0) # Стабилизируйте std
action_distribution = th.distributions.Normal(action_mean, action_std)
# Выбор действий и получение логарифмических вероятностей
actions = action_distribution.sample()
log_probs = action_distribution.log_prob(actions).sum(dim=-1) # Сумма по измерениям действия
values = self.critic(obs)
# Убедитесь, что действия соответствуют ожидаемой форме для среды (в вашем случае, [1, 6])
return actions, values, log_probs
Независимый актор и общий критик:
class CustomActor(th.nn.Module):
def __init__(self, observation_space, action_space):
super(CustomActor, self).__init__()
self.device = th.device("cuda" if th.cuda.is_available() else "cpu")
# Создайте 8 слоев с 512 нейронами каждый
self.layers = th.nn.ModuleList()
input_size = observation_space.shape[0]
for _ in range(8):
layer = th.nn.Linear(input_size, 512).to(self.device) # Переместите слой на устройство
self.layers.append(layer)
input_size = 512 # Обновите размер ввода для следующего слоя
# Обновите голову действия в зависимости от типа пространства действий
if isinstance(action_space, spaces.Box): # Непрерывное пространство действий
self.action_head = th.nn.Linear(512, action_space.shape[0]).to(self.device)
elif isinstance(action_space, spaces.Discrete): # Дискретное пространство действий
self.action_head = th.nn.Linear(512, action_space.n).to(self.device)
else:
raise NotImplementedError("Тип пространства действий не поддерживается")
def forward(self, x):
# Преобразуйте ввод в тензор torch, если это массив numpy, и переместите его на правильное устройство
if isinstance(x, np.ndarray):
x = th.tensor(x, dtype=th.float32).to(self.device) # Переместите на устройство здесь
# Пропустите ввод через слои сети
for layer in self.layers:
# print(f"layer weight device: {layer.weight.device}") # Печать внутри цикла
x = F.relu(layer(x)) # Все слои должны уже находиться на self.device
# Получите логиты действий из головы действия
action_logits = self.action_head(x) # action_head также должен находиться на том же устройстве
return action_logits
class SharedCritic(th.nn.Module):
def __init__(self, observation_space):
super(SharedCritic, self).__init__()
# Создайте 8 слоев с 512 нейронами каждый
self.layers = th.nn.ModuleList()
input_size = observation_space.shape[0]
for _ in range(8):
self.layers.append(th.nn.Linear(input_size, 512)) # Переместите на устройство в методе forward, если это необходимо
input_size = 512 # Обновите размер ввода для следующего слоя
self.value_head = th.nn.Linear(512, 1)
def forward(self, x):
# Преобразуйте ввод в тензор torch, если это массив numpy
if isinstance(x, np.ndarray):
x = th.tensor(x, dtype=th.float32)
for layer in self.layers:
x = F.relu(layer(x))
value = self.value_head(x)
return value
Мои результаты с этим, несмотря на то что я абсолютно ничего не изменил, кроме сети, составляют 180 к тому, что должно быть (пробовал с аналогичным количеством временных шагов и меньшим тоже).
Результаты (MAPPO) Они движутся в совершенно разные стороны, несмотря на то, что моя функция вознаграждения осталась неизменной.
Функция вознаграждения:
CohesionReward = 0
AlignmentReward = 0
total_reward = 0
outofflock = False
midpoint = (SimulationVariables["SafetyRadius"] + SimulationVariables["NeighborhoodRadius"]) / 2
if len(neighbor_positions) > 0:
for neighbor_position in neighbor_positions:
distance = np.linalg.norm(agent.position - neighbor_position)
if distance <= SimulationVariables["SafetyRadius"]:
CohesionReward -= 10
elif SimulationVariables["SafetyRadius"] < distance <= midpoint:
CohesionReward += 20 - (20 / (midpoint - SimulationVariables["SafetyRadius"])) * (distance - SimulationVariables["SafetyRadius"])
elif midpoint < distance <= SimulationVariables["NeighborhoodRadius"]:
CohesionReward += 10 - (10 / (SimulationVariables["NeighborhoodRadius"] - midpoint)) * (distance - midpoint)
average_velocity = np.mean(neighbor_velocities, axis = 0)
dot_product = np.dot(average_velocity, agent.velocity)
norm_product = np.linalg.norm(average_velocity) * np.linalg.norm(agent.velocity)
if norm_product == 0:
cos_angle = 1.0
else:
cos_angle = dot_product / norm_product
cos_angle = np.clip(cos_angle, -1.0, 1.0)
orientation_diff = np.arccos(cos_angle)
alignment = (orientation_diff / np.pi)
AlignmentReward = -20 * alignment + 10
else:
CohesionReward -= 20
outofflock = True
total_reward = CohesionReward + AlignmentReward
return total_reward, outofflock
Ответ или решение
Проблема с производительностью MAPPO по сравнению с PPO в окружении многоместного боидного роя
Введение
Работа с многопользовательскими средами в области обучения с подкреплением (RL) требует тонкой настройки как алгоритмов обучения, так и архитектуры нейронных сетей. В вашем случае вы столкнулись с проблемой снижения производительности при переходе от централизованного метода PPO к многопользовательскому MAPPO, что является довольно распространенной проблемой. Давайте подробно разберем возможные причины этого поведения и предложим пути их решения.
Архитектура нейронной сети
Вы используете глубокую нейронную сеть с 8 скрытыми слоями по 512 нейронов в каждом, что является значительной архитектурой. Однако при переходе к MAPPO необходимо обратить внимание на распределение нагрузки и потребности в параметрах:
-
Параметры сети: Обратите внимание, что использование большого количества параметров может привести к переобучению, особенно в децентрализованной среде. Для MAPPO вам может потребоваться настройка размера сети, чтобы избежать переобучения.
-
Адаптация сети: Убедитесь, что ваша
CustomMultiAgentPolicy
и ее компоненты (CustomActor
иSharedCritic
) имеют соответствующую архитектуру для децентрализованного обучения. Проверка корректности передачи тензоров между устройствами также важна, чтобы избежать неожиданного поведения.
Обеспечение совместного критика
Основная идея MAPPO заключается в использовании совместного критика, который может помочь в обучении, но он должен правильно обрабатывать наблюдения от всех агентов. Вам следует:
-
Оптимизация нормализации наблюдений: Убедитесь, что ваш критик принимает в расчет статистику всех агентов. Если критик использует информацию только от одного агента, обучение может быть неэффективным.
-
Совместное состояние: Проверьте, что ваши наблюдения передаются корректно, и структура состояния (например, наличие информации о других агентах) выглядит так же, как и в PPO.
Проверьте стратегию наград
Вы используете фиксированную функцию награды, но в контексте децентрализованного обучения её стоит дополнительно протестировать:
-
Награда за взаимодействие: Возможно, стоит добавить дополнительные функции награды, которые поощряют агентов для взаимодействия друг с другом более явно, что поможет им развивать групповые поведения.
-
Награда за "выход из стаи": Проверка на разные ситуации может быть полезной для устранения проблем, связанных с поведением, когда агент оказывается вне группы. Возможно, стоит более жестко наказывать за это, или модель должна иметь четкие сигналы, когда «выход из роя» заслуживает поощрения.
Обучение и гиперпараметры
Переход с PPO на MAPPO часто требует пересмотра гиперпараметров:
-
Learning Rate: Настройка параметров обучения в децентрализованной среде может потребовать более тонкой настройки learning rate.
-
Масштабируемость: Проверьте batch size и количество временных шагов (timesteps), которые вы используете для обучения. Для MAPPO может потребоваться больше или меньше данных для достижения приемлемой производительности.
-
Частота обновления: Подумайте о том, как часто обновляется общий критик по сравнению с индивидуальными акторами. Настройка этого параметра может существенно повлиять на обучение.
Заключение
Проблемы с производительностью при использовании MAPPO вместо PPO могут возникать по различным причинам, начиная от архитектуры моделей до стратегии наград и гиперпараметров. Убедитесь, что вы анализируете каждую из этих областей и корректируете соответственно. Проверка всех этапов разработки и тестирования дает возможность выявить скрытые недостатки.
Предлагаем вам тщательно пересмотреть свою реализацию с учетом данной информации и провести дополнительные эксперименты для нахождения оптимальных параметров и архитектуры. Успехов в ваших исследованиях!