Vanilla Policy Gradient关于loss mean与sum的探讨
最近在逐一复现RL算法过程中,策略梯度算法的收敛性一直有问题。经过一番探究和对比实验,学习了网上和书本上的很多实验代码之后,发现了代码实现中的一个小问题,即Policy Gradient在计算最终loss时求平均和求和对于网络训练的影响,本文将对此进行展开讨论。
先说结论:
在进行策略梯度下降优化策略时,可以对每个动作的loss逐一(for操作)进行反向传播后进行梯度下降,也可以对每个动作的loss求和(sum操作)之后进行反向传播后梯度下降,但尽量避免对所有动作的loss求平均(mean操作)之后进行反向传播后梯度下降,这会导致收敛速度较慢,甚至无法收敛。
具体的论证和实验过程见下文。
Vanilla Policy Gradient简介
Vanilla Policy Gradient(VPG, i.e. REINFORCE),中文译为策略梯度算法。是策略优化的经典算法。首先进行一些符号说明:
: 环境在 时刻的状态 : 智能体在 时刻选择的动作 : 智能体的策略,在这里由参数为 的神经网络表示 : 智能体在状态 时选择动作 的概率 : 环境在 时刻给出的奖励 : 折扣因子 : 累计折扣奖励,
VPG的大致分为两步:收集经验和优化策略。两部分的具体实现如下:
- 智能体与环境进行交互,直到一个episode终止,获得一系列轨迹
- 对
进行更新:
为何要对loss使用sum和mean
策略梯度算法是一个梯度上升算法,为了便于使用当前的梯度下降框架,采用
在VPG算法实现过程中,神经网络的训练部分使用了pytorch
框架来进行实现。在该框架中,进行反向传播的loss
必须是一个标量。而策略梯度算法对每一时刻的
对每个时刻的
三元组逐一进行反向传播,最后进行梯度下降,实现代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13batch = self._buffer.sample(0)[0]
G = 0
self._optimizer.zero_grad()
for i in reversed(range(len(reward))):
r = reward[i]
state = torch.FloatTensor([batch["obs"][i]]).to(self.device)
action = torch.LongTensor([batch["act"][i]]).view(-1,1).to(self.device)
log_prob = torch.log(self._network(state)).gather(1,action)
G = self._gamma * G + r
loss = -log_prob * G
loss.backward()
self._optimizer.step()对所有计算出的
进行求和再反向传播,实现代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19batch = self._buffer.sample(0)[0]
state = torch.FloatTensor(batch['obs']).to(self.device)
action = torch.LongTensor(batch['act']).to(self.device)
reward = torch.FloatTensor(batch['rew']).to(self.device)
# clean gradient
self.optimizer.zero_grad()
# computing discount reward
discount_reward=torch.zeros_like(reward)
discount_reward[-1]=reward[-1]
for t in reversed(range(len(reward)-1)):
discount_reward[t]=reward[t]+self._gamma*discount_reward[t+1]
# forward computing
action_probs=self._network(state)
# computing loss
loss = -torch.log(action_probs.gather(1, action.view(-1,1)))
loss = torch.sum(loss*discount_reward.unsqueeze(-1))
# back propagation
self.optimizer.step()对所有计算出的
进行平均再反向传播,实现代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19batch = self._buffer.sample(0)[0]
state = torch.FloatTensor(batch['obs']).to(self.device)
action = torch.LongTensor(batch['act']).to(self.device)
reward = torch.FloatTensor(batch['rew']).to(self.device)
# clean gradient
self.optimizer.zero_grad()
# computing discount reward
discount_reward=torch.zeros_like(reward)
discount_reward[-1]=reward[-1]
for t in reversed(range(len(reward)-1)):
discount_reward[t]=reward[t]+self._gamma*discount_reward[t+1]
# forward computing
action_probs=self._network(state)
# computing loss
loss = -torch.log(action_probs.gather(1, action.view(-1,1)))
loss = torch.mean(loss*discount_reward.unsqueeze(-1))
# back propagation
self.optimizer.step()
为何对loss使用sum和mean会导致结果不同
假设一个episode的长度为
若对
若对
乍一看,对
实验对比
实验采用gym
中的CartPole-v1
环境作为训练环境,训练网络及学习超参数如下:
- 隐层大小:[128, 128]
- 优化器:Adam
- 学习率:3e-4
按照上述参数对
总结:由上述实验结果可知,若对
预览: