Multi-agent Actor-Critic for Mixed Cooperative-Competitive Environments
pytorch-maddpg在下面的场景中实现了MADDPG,所以就结合程序来说明MADDPG的原理和运行过程。
场景及设置介绍
如上图,红点是毒药,蓝点和黑线都是捕猎者Pursuers,绿点为猎物Evaders,为蓝点的食物,青色的为障碍物Obstacles。图中两个蓝点需要学习合作策略,最大化共同的reward。
-
Reward 设置如下:
food_reward = 10. poison_reward = -1. encounter_reward = 0.01 //蓝点相遇时的reward, n_coop = 2 //表示需要训的合作的agent个数。
Reward为2x2的numpy.ndarray,个数为蓝点捕猎者的个数,在本场景中为2。
全局和局部的计算方法分别为:
if self.reward_mech == 'global':
rewards += (
(len(ev_caught) * self.food_reward) + (len(po_caught) * self.poison_reward) +
(len(ev_encounters) * self.encounter_reward))
else:
rewards[which_pursuer_caught_ev] += self.food_reward
rewards[which_pursuer_caught_po] += self.poison_reward
rewards[which_pursuer_encounterd_ev] += self.encounter_reward
全局:遇到#的次数 * # reward的总和。
局部:分别更新第一个或第二个蓝点的reward,遇到食物增加food_reward等。
Action表示为2x2的FloatTensor,用于改变agent的速度和位置。
每一步的状态Obs由2x213的FloatTensor表示,为从环境中提取的各个agent的位置和速度等信息。环境的观测值由长度为2的list,用obs_表示,list中每个元素为numpy.ndarray,ndarray长度为213。
-
Agent的网络结构定义为:
class Actor(nn.Module): def __init__(self, dim_observation, dim_action): super(Actor, self).__init__() self.FC1 = nn.Linear(dim_observation, 500) self.FC2 = nn.Linear(500, 128) self.FC3 = nn.Linear(128, dim_action) # action output between -2 and 2 def forward(self, obs): result = F.relu(self.FC1(obs)) result = F.relu(self.FC2(result)) result = F.tanh(self.FC3(result)) return result
输入观察,输出动作。这也就是图中蓝点的网络,调用父类的初始化操作,使用pytorch默认的随机初始化方法。初始化之后就可以用到游戏当中。
-
Critic的网络结构为:
class Critic(nn.Module): def __init__(self, n_agent, dim_observation, dim_action): super(Critic, self).__init__() self.n_agent = n_agent self.dim_observation = dim_observation self.dim_action = dim_action obs_dim = dim_observation * n_agent act_dim = self.dim_action * n_agent self.FC1 = nn.Linear(obs_dim, 1024) self.FC2 = nn.Linear(1024+act_dim, 512) self.FC3 = nn.Linear(512, 300) self.FC4 = nn.Linear(300, 1) # obs: batch_size * obs_dim def forward(self, obs, acts): result = F.relu(self.FC1(obs)) combined = th.cat([result, acts], 1) result = F.relu(self.FC2(combined)) return self.FC4(F.relu(self.FC3(result)))
输入观察和动作,输出每个agent的Q值。也是使用随机初始化方法。这个网络在上图中不表示任何实物,因为它只在训练过程中出现,每个actor都有一个critic,收集所有agent的观察和动作,输出单个actor的Q值,用于更新actor。
训练
- 开始游戏
指定episode数目n_episode、每个episode最大steps max_steps、开始训练之前玩的局数episodes_before_train,初始化观测obs,total_reward,batch_size。
对一局游戏的每一个steps,根据Agent网络输出动作,根据动作得到reward。将动作前后的状态,动作本身,reward 即(x,x’,a,r) 都保存到reply buffer D用于接下来的训练(,)。当完成episodes_before_train局数,收集到足够的reply buffer后,就可以开始训练了。设置episodes_before_train是为了满足训练中minibatch的要求。
并且为了更好的探索动作,每个step,网络输出动作后增加一个随机过程。
- 训练过程
对于每一个agent,从reply buffer D中随机抽取minibatch的S个(x,x’,a,r)的样本。
如图所示,输入到critic网络中,输出的Q值反馈给actor。
- 目标函数
公式中的Q由上面提到的critic网络输出,r为单个agent即蓝点的reward。用于计算loss。
- 更新critic的loss为:
程序中的实现为:
# 使用从D中取到某一个step的x和a,输出critic计算的Q值。
# 由于每个step都有一个Q和r值,程序中一个episode有max_steps = 1000,
# 所以Q和r都是1000 x 1的FloatTensor。 agent代表当前训练中的agent。
current_Q = self.critics[agent](whole_state, whole_action)
# batch是从D中取到的四元组
non_final_mask = ByteTensor(list(map(lambda s: s is not None,batch.next_states)))
# 输入x’,a’使用critic计算出Q’,critics_target是前一step的critic
self.critics_target = deepcopy(self.critics)
target_Q[non_final_mask] = self.critics_target[agent](
non_final_next_states.view(-1, self.n_agents * self.n_states),
non_final_next_actions.view(-1,self.n_agents * self.n_actions))
# self.GAMMA = 0.95, scale_reward: to scale reward in Q functions.
# reward_batch中存的是两个蓝点1000步的reward,是1000 x 2的FloatTensor
target_Q = (target_Q * self.GAMMA) + (reward_batch[:, agent] * scale_reward)
# torch.nn.MSELoss() 为平方损失函数,detch返回新的Variable,使其跟以前的Q
# 分离
loss_Q = nn.MSELoss()(current_Q, target_Q.detach())
-
Actor接收到Q值后,结合本身的网络计算策略梯度用于优化参数:
程序中处理如下:
actor_loss = -self.critics[agent](whole_state, whole_action) actor_loss = actor_loss.mean() actor_loss.backward() self.actor_optimizer[agent].step()
Critic和actor更新参数的部分写在MADDPG类的update_policy()中,主函数中每执行一个step,就会调用一次,每一步都会更新参数并保存critic前一个step的参数,用于计算loss。
- 对每个agent都完成上述操作后,防止θ更新太快,做一些微调:
然后重新开始一局,让agent用现在已更新的网络加上随机化过程来选择动作,对一局中的每个step,存储(x,x’,a,r),然后对每个agent做上述(1)-(4)的操作。反复训练,直到达到合适的效果,让两个蓝点学会竞争合作,就可以使用训练好的模型来玩游戏了。