# 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發生τ的機率

* **Reward function**:
* 僅能計算Reward期望值

* **如何最佳化?** ==採用Gradient ascent==
若在st執行at,使R(τ)為正,則增加Pθ(st|at)機率,反之則減少

* **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,所以目標式必須加上一個負號

## 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

2. 加上discount factor,因為離time t越遠的action與在time t所得到的reward關係越小

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>)