-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PPO #28
base: main
Are you sure you want to change the base?
PPO #28
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
" with torch.no_grad():\n", | ||
" _, v = self.actorcritic.forward(states)\n", | ||
" _, v2 = self.actorcritic.forward(next_states)\n", | ||
" \n", | ||
" advantages, returns = self.compute_gae(rewards, dones, v, v2)\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Existe um jeito mais elegante e mais eficiente de pegar v e v2 sem ter que dar esse forward? Achei que ficou meio desnecessário mas foi o que eu pensei na hora.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Como os estados estão em ordem cronológica, o v2[i] é sempre o v[i+1]. Se você quiser otimizar ao máximo dá pra colocar o último v2 no v e passar tudo de uma vez no forward
, aí mudar o gae pra usar só o v[i] e o v[i+1]. Mas não sei se tem muita coisa pra melhorar ;P, esse que eu comentei já pode ficar um pouco estranho também.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Não precisaria necessariamente mudar o gae. Da pra fazer isso:
with torch.nograd():
_, vs = self.actorcritic.forward(numpy.append(states, next_states[[-1]], axis=0))
v1 = vs[:-1]
v2 = vs[1:]
I.e. a mesma coisa que o @Berbardo disse, mas separando em v1 e v2 antes de mandar pro gae. Se tiver um comentário apropriado lá no meio, acho que não tem muito problema. Mas é certamente menos didático.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nelsonayamashita @fernandokm mexemos nisso aqui?
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Nota-se que: | ||
- Quando a vantagem é positiva, se **r** aumentar, então **L** aumenta. No entanto, esse benefício é limitado pelo clip: se **r > 1+ε**, não há mais benefício para **r** aumentar. | ||
- Quando a vantagem é negativa, se **r** diminuir, então **L** aumenta. No entanto, esse benefício é limitado pelo clip: se **r < 1-ε**, não há mais benefício para **r** diminuir. | ||
|
||
A seguinte imagem pode te ajudar a visualizar o clip. Note que todos os valores fora do clip estipulado estão constantes: | ||
|
||
![imagem ilustrando o clip](imgs/clip.png) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Está sem o algoritmo de PPO em LaTeX (não sei se precisa)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acho legal por consistência, mas não vejo muito problema em não ter
"Como já foi mencionado, a restrição ($KL < \\delta$) imposta em TRPO torna o algoritmo relativamente complicado. PPO é uma tentativa de simplificar esse algoritmo. Ao invés de utilizar trust regions, PPO mexe diretamente com a função objetivo:\n", | ||
"\n", | ||
"$$\n", | ||
" L(\\theta_{\\mathrm{old}},\\theta) = E_{s,a\\sim\\pi_{\\theta_{\\mathrm{old}}}} \\Bigl[\\min\\left(r A^{\\pi_{\\theta_{\\mathrm{old}}}}(s,a),\\, \\operatorname{clip}(r,1-\\varepsilon,1+\\varepsilon) A^{\\pi_{\\theta_{\\mathrm{old}}}}(s,a)\\right)\\Bigr],\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
não entendi esse (r, 1-eps, 1+eps)
"\n", | ||
"A seguinte imagem pode te ajudar a visualizar o clip. Note que todos os valores fora do clip estipulado estão constantes:\n", | ||
"\n", | ||
"![imagem ilustrando o clip](imgs/clip.png)\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acho que compensa explicar melhor o resultado prático disso
" def __init__(self, observation_shape, action_shape):\n", | ||
" super(ActorCritic, self).__init__()\n", | ||
" self.policy1 = nn.Linear(observation_shape, 64)\n", | ||
" self.policy2 = nn.Linear(64, 64)\n", | ||
" self.policy3 = nn.Linear(64, action_shape)\n", | ||
" \n", | ||
" self.value1 = nn.Linear(observation_shape, 64)\n", | ||
" self.value2 = nn.Linear(64, 64)\n", | ||
" self.value3 = nn.Linear(64, 1)\n", | ||
"\n", | ||
" def forward(self, state):\n", | ||
" dists = torch.relu(self.policy1(state))\n", | ||
" dists = torch.relu(self.policy2(dists))\n", | ||
" dists = F.softmax(self.policy3(dists), dim=-1)\n", | ||
" probs = Categorical(dists)\n", | ||
" \n", | ||
" v = torch.relu(self.value1(state))\n", | ||
" v = torch.relu(self.value2(v))\n", | ||
" v = self.value3(v)\n", | ||
"\n", | ||
" return probs, v" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adicionar comentários, mesmo que pequenos, para ficar mais explicativo. O do Buffer possui tantos e as outras classes não possuem nenhum, tadinha delas :(((
"class PPO:\n", | ||
" def __init__(self, observation_space, action_space, lr=7e-4, gamma=0.99, lam=0.95, vf_coef=0.5, entropy_coef=0.005,clip_param =0.2, epochs =10, memory_len=16):\n", | ||
" self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", | ||
"\n", | ||
" self.gamma = gamma\n", | ||
" self.lam = lam\n", | ||
" self.vf_coef = vf_coef\n", | ||
" self.entropy_coef = entropy_coef\n", | ||
" self.clip_param = clip_param\n", | ||
" self.epochs = epochs\n", | ||
"\n", | ||
" self.memory_len = memory_len\n", | ||
" self.memory = MemoryBuffer(memory_len, observation_space.shape[0])\n", | ||
"\n", | ||
" self.actorcritic = ActorCritic(observation_space.shape[0], action_space.n).to(self.device)\n", | ||
" self.actorcritic_optimizer = optim.Adam(self.actorcritic.parameters(), lr=lr)\n", | ||
"\n", | ||
" def act(self, state):\n", | ||
" state = torch.FloatTensor(state).to(self.device).unsqueeze(0)\n", | ||
" probs, v = self.actorcritic.forward(state)\n", | ||
" action = probs.sample()\n", | ||
" log_prob = probs.log_prob(action)\n", | ||
" return action.cpu().detach().item(), log_prob.detach().cpu().numpy()\n", | ||
"\n", | ||
" def remember(self, state, action, reward, next_state, done, logp):\n", | ||
" self.memory.update(state, action, reward, next_state, done, logp)\n", | ||
"\n", | ||
" def compute_gae(self, rewards, dones, v, v2):\n", | ||
" T = len(rewards)\n", | ||
"\n", | ||
" returns = torch.zeros_like(rewards)\n", | ||
" gaes = torch.zeros_like(rewards)\n", | ||
" \n", | ||
" future_gae = torch.tensor(0.0, dtype=rewards.dtype)\n", | ||
" next_return = torch.tensor(v2[-1], dtype=rewards.dtype)\n", | ||
"\n", | ||
" not_dones = 1 - dones\n", | ||
" deltas = rewards + not_dones * self.gamma * v2 - v\n", | ||
"\n", | ||
" for t in reversed(range(T)):\n", | ||
" returns[t] = next_return = rewards[t] + self.gamma * not_dones[t] * next_return\n", | ||
" gaes[t] = future_gae = deltas[t] + self.gamma * self.lam * not_dones[t] * future_gae\n", | ||
"\n", | ||
" gaes = (gaes - gaes.mean()) / (gaes.std() + 1e-8) # Normalização\n", | ||
"\n", | ||
" return gaes, returns\n", | ||
"\n", | ||
" def train(self):\n", | ||
" if self.memory.length < self.memory_len:\n", | ||
" return\n", | ||
"\n", | ||
" (states, actions, rewards, next_states, dones, old_logp) = self.memory.get_batch()\n", | ||
"\n", | ||
" states = torch.FloatTensor(states).to(self.device)\n", | ||
" actions = torch.FloatTensor(actions).to(self.device)\n", | ||
" rewards = torch.FloatTensor(rewards).unsqueeze(-1).to(self.device)\n", | ||
" next_states = torch.FloatTensor(next_states).to(self.device)\n", | ||
" dones = torch.FloatTensor(dones).unsqueeze(-1).to(self.device)\n", | ||
" old_logp = torch.FloatTensor(old_logp).to(self.device)\n", | ||
" \n", | ||
" with torch.no_grad():\n", | ||
" _, v = self.actorcritic.forward(states)\n", | ||
" _, v2 = self.actorcritic.forward(next_states)\n", | ||
" \n", | ||
" advantages, returns = self.compute_gae(rewards, dones, v, v2)\n", | ||
" \n", | ||
" for epoch in range(self.epochs):\n", | ||
" \n", | ||
" probs, v = self.actorcritic.forward(states)\n", | ||
"\n", | ||
" new_logp = probs.log_prob(actions)\n", | ||
"\n", | ||
" #Equações principais do algoritmo\n", | ||
" ratio = (new_logp.unsqueeze(-1) - old_logp.unsqueeze(-1)).exp() \n", | ||
" surr1 = ratio * advantages.detach()\n", | ||
" surr2 = torch.clamp(ratio, 1.0 - self.clip_param, 1.0 + self.clip_param) * advantages.detach()\n", | ||
"\n", | ||
" entropy = probs.entropy().mean()\n", | ||
"\n", | ||
" policy_loss = - torch.min(surr1,surr2).mean()\n", | ||
" value_loss = self.vf_coef * F.mse_loss(v, returns.detach())\n", | ||
" entropy_loss = -self.entropy_coef * entropy\n", | ||
"\n", | ||
" self.actorcritic_optimizer.zero_grad()\n", | ||
" (policy_loss + entropy_loss + value_loss).backward()\n", | ||
" self.actorcritic_optimizer.step()\n", | ||
"\n", | ||
" return policy_loss + entropy_loss + value_loss" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Colocar comentários também, principalmente nas linhas mais importantes (relacionadas com a teoria).
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"O agente demonstra consegue solocuinar o ambiente. Porém acaba desaprendendo, para solucionar isso é possível fazer uma otimização nos hiper-parâmetros. É possível também implementar algum tipo de parada antecipada." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"O agente demonstra consegue solocuinar o ambiente. Porém acaba desaprendendo, para solucionar isso é possível fazer uma otimização nos hiper-parâmetros. É possível também implementar algum tipo de parada antecipada." | |
"O agente aparentemente consegue solucionar o ambiente, no entanto, acaba desaprendendo. Para solucionar isso, é possível fazer uma otimização nos hiper-parâmetros ou implementar algum tipo de parada antecipada." |
Co-authored-by: Fernando Matsumoto <[email protected]>
Coloquei o notebook com a implementação de PPO da aula, mas acredito que ela não esteja funcionando 100% (vide gráfico no fim do nb).