1.4 – Por Reforco
1.4.3 – Metodos Baseados em Politica
1.4.3.1 – Metodos de Gradiente de Politica
LEGENDA
Principal
Ramo
Metodo
Problemas
Modelo
Arquitetura
O que é um método baseado em política?
Em aprendizado por reforço, métodos baseados em política otimizam diretamente a estratégia do agente. Essa estratégia é chamada de política \(\pi(a|s)\). Ela mapeia cada estado para uma distribuição de probabilidade sobre ações. Diferentemente dos métodos baseados em valor, nenhum Q-valor é estimado. Por conseguinte, a política é aprendida sem um passo intermediário de valor. Isso é especialmente útil para ações contínuas ou ambientes estocásticos. Primeiramente, a política pode ser representada por uma rede neural. Então, ajustamos seus parâmetros \(\theta\) para maximizar a recompensa esperada. Essa abordagem é chamada de Policy Gradient (PG). Além disso, ela lida naturalmente com problemas de exploração. A política estocástica sempre tem alguma chance de explorar ações diferentes. Consequentemente, métodos baseados em política são mais robustos em muitos cenários.Características e arquitetura dos modelos
Nesses modelos, a arquitetura principal é uma rede neural que recebe o estado. Ela produz as probabilidades de cada ação (softmax) ou parâmetros de uma distribuição gaussiana. Por exemplo, para ações discretas, a saída é um vetor de logits. A função softmax converte esses logits em probabilidades. Já para ações contínuas, a rede gera média e variância. Uma amostra da distribuição normal é usada para escolher a ação. Os hiperparâmetros comuns incluem a taxa de aprendizado (\(\alpha\)), fator de desconto (\(\gamma\)), e a entropia regularizadora. A entropia encoraja exploração ao penalizar políticas muito determinísticas. Frequentemente, usa-se o algoritmo REINFORCE ou o Actor-Critic. O REINFORCE é um método de gradiente de política simples. Porém, ele sofre com alta variância nas estimativas. Uma solução é adicionar um baseline (valor do estado). Isso é feito naturalmente no Actor-Critic. No Actor-Critic, duas redes são usadas: o ator (política) e o crítico (valor). O crítico estima \(V(s)\) para reduzir a variância. Então, o gradiente da política é calculado com a vantagem \(A(s,a)\). Essa arquitetura é frequentemente empregada em problemas complexos. Portanto, métodos baseados em política são muito flexíveis.Fórmulas matemáticas fundamentais
O objetivo é maximizar a recompensa esperada \(J(\theta)\). A política é parametrizada por \(\theta\). O gradiente da política é dado pelo teorema do gradiente da política: \[ \nabla_\theta J(\theta) = \mathbb{E}_{\pi_\theta} \left[ \nabla_\theta \log \pi_\theta(a|s) \cdot Q^{\pi_\theta}(s,a) \right] \] Para reduzir variância, substituímos \(Q\) pela vantagem \(A(s,a)\). A vantagem é definida como \(A(s,a) = Q(s,a) – V(s)\). No REINFORCE com baseline, a atualização é: \[ \theta \leftarrow \theta + \alpha \cdot \nabla_\theta \log \pi_\theta(a|s) \cdot (G_t – V(s)) \] Aqui, \(G_t\) é o retorno acumulado a partir do tempo \(t\). O crítico é treinado para minimizar o erro quadrático médio com \(G_t\). A perda do crítico é \(\mathcal{L}_V = (G_t – V(s))^2\). Já a perda do ator é \(-\log \pi_\theta(a|s) \cdot (G_t – V(s))\). Adicionalmente, adiciona-se um termo de entropia: \(\mathcal{H} = -\sum \pi_\theta(a|s) \log \pi_\theta(a|s)\). Isso incentiva a exploração e evita convergência prematura.Exemplo clássico: Lunar Lander com Policy Gradient
Enunciado: Um foguete deve pousar suavemente na plataforma de pouso. O ambiente LunarLander-v2 (gymnasium) tem 8 estados contínuos e 4 ações discretas. A recompensa é positiva por tocar a plataforma e negativa por combustível ou quedas. O episódio termina com pouso bem-sucedido (recompensa +100) ou acidente (-100). Você deve implementar um agente baseado em política (Actor-Critic) que aprenda a pousar. Treine por 1000 episódios e mostre a evolução da recompensa média. Gere também um gráfico da perda do crítico e da entropia da política.|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# Instalação inicial (se necessário) !pip install gymnasium torch matplotlib numpy -q import gymnasium as gym import numpy as np import torch import torch.nn as nn import torch.optim as optim from torch.distributions import Categorical import matplotlib.pyplot as plt # 1. Ambiente (CartPole - mais simples, sem dependências extras) env = gym.make('CartPole-v1') state_dim = env.observation_space.shape[0] # 4 estados action_dim = env.action_space.n # 2 ações print(f"Ambiente: CartPole-v1") print(f"Dimensão do estado: {state_dim}") print(f"Dimensão da ação: {action_dim}") # 2. Rede Actor-Critic class ActorCritic(nn.Module): def __init__(self, state_dim, action_dim, hidden=64): super().__init__() self.common = nn.Sequential( nn.Linear(state_dim, hidden), nn.ReLU() ) self.actor = nn.Sequential( nn.Linear(hidden, hidden//2), nn.ReLU(), nn.Linear(hidden//2, action_dim) ) self.critic = nn.Sequential( nn.Linear(hidden, hidden//2), nn.ReLU(), nn.Linear(hidden//2, 1) ) def forward(self, x): common_out = self.common(x) logits = self.actor(common_out) value = self.critic(common_out) return logits, value # 3. Agente Policy Gradient (Actor-Critic) class PolicyGradientAgent: def __init__(self, state_dim, action_dim, lr=0.001, gamma=0.99, entropy_coef=0.01): self.network = ActorCritic(state_dim, action_dim) self.optimizer = optim.Adam(self.network.parameters(), lr=lr) self.gamma = gamma self.entropy_coef = entropy_coef self.log_probs = [] self.values = [] self.rewards = [] self.dones = [] self.states = [] def act(self, state): state_t = torch.FloatTensor(state).unsqueeze(0) logits, value = self.network(state_t) dist = Categorical(logits=logits) action = dist.sample() log_prob = dist.log_prob(action) self.log_probs.append(log_prob) self.values.append(value.squeeze()) return action.item() def remember(self, reward, done): self.rewards.append(reward) self.dones.append(done) def store_state(self, state): self.states.append(state.copy() if hasattr(state, 'copy') else state) def learn(self): # Calcular retornos (G_t) returns = [] R = 0 for reward, done in zip(reversed(self.rewards), reversed(self.dones)): if done: R = 0 R = reward + self.gamma * R returns.insert(0, R) returns = torch.tensor(returns).detach() # Normalizar retornos para estabilidade if len(returns) > 1 and returns.std() > 1e-8: returns = (returns - returns.mean()) / (returns.std() + 1e-8) log_probs = torch.stack(self.log_probs) values = torch.stack(self.values) # Vantagem = Retorno - Valor estimado advantages = returns - values.detach() # Perda do ator: -log_prob * vantagem actor_loss = -(log_probs * advantages).mean() # Perda do crítico: MSE entre valor e retorno critic_loss = nn.MSELoss()(values, returns) # Entropia para exploração if len(self.states) > 0: state_t = torch.FloatTensor(np.array(self.states)).detach() with torch.no_grad(): logits, _ = self.network(state_t) probs = torch.softmax(logits, dim=-1) entropy = -(probs * torch.log(probs + 1e-8)).sum(dim=1).mean() else: entropy = torch.tensor(0.0) loss = actor_loss + critic_loss - self.entropy_coef * entropy self.optimizer.zero_grad() loss.backward() self.optimizer.step() # Limpar memória do episódio self.log_probs = [] self.values = [] self.rewards = [] self.dones = [] self.states = [] return actor_loss.item(), critic_loss.item(), entropy.item() # 4. Treinamento agent = PolicyGradientAgent(state_dim, action_dim) episodes = 300 reward_history = [] actor_losses = [] critic_losses = [] entropies = [] print("\n🚀 Treinando o agente no CartPole...") print("O objetivo é equilibrar o poste por 500 passos.\n") for ep in range(episodes): state, _ = env.reset() total_reward = 0 agent.states = [] step_count = 0 while True: agent.store_state(state) action = agent.act(state) next_state, reward, terminated, truncated, _ = env.step(action) done = terminated or truncated agent.remember(reward, done) total_reward += reward state = next_state step_count += 1 if done: break a_loss, c_loss, ent = agent.learn() reward_history.append(total_reward) actor_losses.append(a_loss) critic_losses.append(c_loss) entropies.append(ent) # Mostrar progresso a cada 50 episódios if (ep + 1) % 50 == 0: avg_reward = np.mean(reward_history[-50:]) if len(reward_history) >= 50 else np.mean(reward_history) print(f"Episódio {ep+1:3d}/{episodes} | Média recompensa (últimos 50): {avg_reward:6.2f} | Entropia: {ent:.3f}") env.close() # 5. Gráficos plt.figure(figsize=(15, 5)) plt.subplot(1, 3, 1) plt.plot(reward_history, alpha=0.7, linewidth=0.8) plt.title('Recompensa por Episódio', fontsize=12) plt.xlabel('Episódio') plt.ylabel('Passos equilibrados') plt.grid(True, alpha=0.3) plt.subplot(1, 3, 2) plt.plot(actor_losses, label='Ator (Policy)', alpha=0.7, linewidth=0.8) plt.plot(critic_losses, label='Crítico (Value)', alpha=0.7, linewidth=0.8) plt.title('Perdas do Ator e Crítico', fontsize=12) plt.xlabel('Episódio') plt.ylabel('Loss') plt.legend() plt.grid(True, alpha=0.3) plt.subplot(1, 3, 3) plt.plot(entropies, color='green', alpha=0.7, linewidth=0.8) plt.title('Entropia da Política', fontsize=12) plt.xlabel('Episódio') plt.ylabel('Entropia (exploração)') plt.grid(True, alpha=0.3) plt.suptitle('Aprendizado por Reforço - Método Baseado em Política (Actor-Critic)', fontsize=14) plt.tight_layout() plt.show() print("\n✅ Treinamento concluído!") print(f"Melhor pontuação: {max(reward_history):.0f} passos") print(f"Pontuação média final: {np.mean(reward_history[-50:]):.2f} passos") |