# Policy Gradient ###### tags: `Tag(Reinforcement learning)` ## **Policy π** * a network with parameter θ * input 為對於環境state的觀察,output為機器採取的行為,且在output layer中每個neuron代表一個行為 * network會給予每個action一個機率 ## env、actor與reward間的關係 * **Trajectory τ**: 由一個episode中所有的(s,a) pair組成 * **Pθ(τ)**: 在policy θ下,某個episode發生τ的機率 ![](https://i.imgur.com/kfbEDGo.png) * **Reward function**: * 僅能計算Reward期望值 ![](https://i.imgur.com/uQJA4ci.png) * **如何最佳化?** ==採用Gradient ascent== 若在st執行at,使R(τ)為正,則增加Pθ(st|at)機率,反之則減少 ![](https://i.imgur.com/eFOpwWP.png) * **training過程** :warning: **每次sample的data只能使用一次** 1. 先以actor跟環境互動蒐集多個trajectory τ(τ1,τ2...) 2. 利用Gradient ascent更新參數(依照上面算式) 3. model(policy)更新後,再以新的policy蒐集data 4. 回到第一步,繼續蒐集,並更新policy * **實作想法** 將訓練過程視為分類問題,(s,a)pair data中a視為ground truth,而模型根據s輸出的action機率,必須與ground truth越近越好,因此,可利用minimize cross entropy來最佳化,但由於是要最大化reward,所以目標式必須加上一個負號 ![](https://i.imgur.com/O34SD0Y.png) ## Tips 1. **Add a Baseline** * **理由**: 由於我們只能sample有限數量的data,若所有R(τ)皆是正的,則未sample到的(s,a)pair機率就會下降,例如:只sample到左右但未sample到上下,則上下的action機率就會降低,然上下只是因為未sample到,不是因為他們不適當 * **方法**: R(τ)減掉一個base值,而base值可從計算R(τ)的平均值而得 2. **Assign Suitable Credit** * **理由**: 所有的action皆weighted同樣的weight是不合理的,action有先後順序,對episode最後的reward帶來的影響不同 * **方法**: 1. 將R(τ)(整場遊戲的reward)改為在time t所執行的action at,從時間點t一直到遊戲結束所加總得到的reward ![](https://i.imgur.com/cOdt6ZV.png) 2. 加上discount factor,因為離time t越遠的action與在time t所得到的reward關係越小 ![](https://i.imgur.com/QRHrpli.png) 3. **Advantage function** * R(τ)-b可視為Advantage function Aθ(st,at) * 評估在某個state s執行某個action a相較於其他action有多好 =>**由critic預估** ## Code ```python= class PolicyNet(torch.nn.Module): def __init__(self, state_dim, hidden_dim, action_dim): super(PolicyNet, self).__init__() self.fc1 = torch.nn.Linear(state_dim, hidden_dim) self.fc2 = torch.nn.Linear(hidden_dim, action_dim) def forward(self, x): x = F.relu(self.fc1(x)) return F.softmax(self.fc2(x), dim=1) class REINFORCE: def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma, device): self.policy_net = PolicyNet(state_dim, hidden_dim, action_dim).to(device) self.optimizer = torch.optim.Adam(self.policy_net.parameters(), lr=learning_rate) # 使用Adam优化器 self.gamma = gamma # 折扣因子 self.device = device def take_action(self, state): # 根据动作概率分布随机采样 state = torch.tensor([state], dtype=torch.float).to(self.device) probs = self.policy_net(state) action_dist = torch.distributions.Categorical(probs) action = action_dist.sample() return action.item() def update(self, transition_dict): reward_list = transition_dict['rewards'] state_list = transition_dict['states'] action_list = transition_dict['actions'] G = 0 self.optimizer.zero_grad() for i in reversed(range(len(reward_list))): # 从最后一步算起 reward = reward_list[i] state = torch.tensor([state_list[i]], dtype=torch.float).to(self.device) action = torch.tensor([action_list[i]]).view(-1, 1).to(self.device) log_prob = torch.log(self.policy_net(state).gather(1, action)) G = self.gamma * G + reward loss = -log_prob * G # 每一步的损失函数 loss.backward() # 反向传播计算梯度 self.optimizer.step() # 梯度下降 ``` L<sup>PG</sup>(θ) = E<sub>t</sub> [ r<sub>t</sub> * A<sub>t</sub> * (π<sub>θ</sub>(a<sub>t</sub>|s<sub>t</sub>) / π<sub>θ<sub>old</sub></sub>(a<sub>t</sub>|s<sub>t</sub>)) ] L<sup>CLIP</sup>(θ) = E<sub>t</sub> [ min( r<sub>t</sub>(θ) * A<sub>t</sub>, clip(r<sub>t</sub>(θ), 1-ε, 1+ε) * A<sub>t</sub> ) ] r_t(θ) = π<sub>θ</sub>(a<sub>t</sub>|s<sub>t</sub>) / π<sub>θ<sub>old</sub></sub>(a<sub>t</sub>|s<sub>t</sub>)