Aprendizado por reforço: política (policy)
A política é o cérebro do agente. Ela define qual ação tomar em cada estado. Podemos pensar nela como uma estratégia ou um mapa. Sem uma política, o agente age aleatoriamente. Portanto, aprender uma boa política é o objetivo principal. A política pode ser determinística ou estocástica.
Tipos de política e sua representação
Uma política determinística mapeia cada estado a uma única ação. Escrevemos isso como \( a = \pi(s) \). Por exemplo, em um labirinto, sempre vá para a direita. Uma política estocástica, por outro lado, dá probabilidades para cada ação. Representamos isso como \( \pi(a|s) = P(A_t = a | S_t = s) \). Essa abordagem é útil para explorar. Consequentemente, políticas estocásticas evitam ficar presas em mínimos locais.
A política pode ser armazenada como uma tabela. Em estados discretos, isso é simples. Porém, em espaços contínuos, usamos redes neurais. A rede recebe o estado e retorna ações. Essa arquitetura chama-se Policy Network. Os hiperparâmetros incluem o tamanho das camadas escondidas. Por exemplo, 64 ou 128 neurônios. A taxa de aprendizado α também é crucial. Além disso, usamos um parâmetro de entropia para incentivar exploração.
Como as políticas são aprendidas e avaliadas
O teorema do gradiente da política é fundamental. Ele permite atualizar a política diretamente. A cada passo, ajustamos π para aumentar o retorno esperado. A fórmula do gradiente é \( \nabla_\theta J(\theta) = \mathbb{E}_\pi \left[ \nabla_\theta \log \pi_\theta(a|s) \cdot G_t \right] \). Aqui θ são os parâmetros da política. Esse método é chamado de REINFORCE. Uma variante comum usa um baseline para reduzir variância. O baseline geralmente é a função valor V(s).
Outra abordagem é a comparação entre políticas. Dizemos que π é melhor que π’ se seu retorno esperado for maior. A equação de Bellman para políticas é \( V^\pi(s) = \sum_a \pi(a|s) \sum_{s’,r} p(s’,r|s,a) [r + \gamma V^\pi(s’)] \). Essa equação é resolvida por iteração de política. Primeiro, avaliamos a política atual. Depois, a melhoramos guloso. Esse processo se repete até a convergência.
Exemplo clássico: subindo a montanha (Mountain Car)
Um carro fraco tenta subir uma montanha íngreme. Ele não consegue ir direto ao topo. Primeiro, precisa ir para trás ganhar inércia. A cada passo, o agente acelera para esquerda, direita ou neutro. A recompensa é -1 por cada passo até o topo. O estado é a posição e velocidade. O objetivo é aprender uma política que minimize os passos. O código abaixo implementa esse problema com gradiente de 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 |
import numpy as np import matplotlib.pyplot as plt import gym from collections import deque # Cria ambiente MountainCar env = gym.make('MountainCar-v0') print(f"Espaço de estados: {env.observation_space}") print(f"Espaço de ações: {env.action_space}") # Hiperparâmetros num_episodios = 500 taxa_aprendizado = 0.001 gamma = 0.99 entropia_beta = 0.01 # Inicializa pesos da política (estado tem 2 dimensões, 3 ações) W = np.random.randn(2, 3) * 0.01 def politica(estado, W): """Política linear softmax""" logits = np.dot(estado, W) exp_logits = np.exp(logits - np.max(logits)) return exp_logits / np.sum(exp_logits) def escolher_acao(estado, W): probs = politica(estado, W) return np.random.choice(3, p=probs) def gradiente_log_politica(estado, acao, W): probs = politica(estado, W) grad = np.zeros_like(W) for a in range(3): grad[:, a] = estado * (1 if a == acao else 0) - estado * probs[a] return grad recompensas_por_episodio = [] passos_por_episodio = [] for ep in range(num_episodios): estado = env.reset()[0] trajetoria_estados = [] trajetoria_acoes = [] trajetoria_recompensas = [] feito = False passos = 0 while not feito and passos < 1000: acao = escolher_acao(estado, W) prox_estado, recompensa, feito, _, _ = env.step(acao) trajetoria_estados.append(estado) trajetoria_acoes.append(acao) trajetoria_recompensas.append(recompensa) estado = prox_estado passos += 1 passos_por_episodio.append(passos) recompensas_por_episodio.append(sum(trajetoria_recompensas)) # Calcula retornos G_t retornos = [] G = 0 for r in reversed(trajetoria_recompensas): G = r + gamma * G retornos.insert(0, G) retornos = np.array(retornos) # Normaliza retornos para estabilidade if len(retornos) > 1: retornos = (retornos - np.mean(retornos)) / (np.std(retornos) + 1e-8) # Atualiza política com REINFORCE grad_total = np.zeros_like(W) for t in range(len(trajetoria_estados)): grad = gradiente_log_politica(trajetoria_estados[t], trajetoria_acoes[t], W) grad_total += grad * retornos[t] W += taxa_aprendizado * grad_total # Gráfico 1: Evolução dos passos por episódio plt.figure(figsize=(12,4)) plt.subplot(1,2,1) plt.plot(passos_por_episodio, alpha=0.7) media_movel = np.convolve(passos_por_episodio, np.ones(20)/20, mode='valid') plt.plot(range(19, num_episodios), media_movel, 'r', linewidth=2, label='Média móvel 20') plt.xlabel('Episódio') plt.ylabel('Passos até o topo') plt.title('Aprendizado da política (MountainCar)') plt.legend() plt.grid(True) # Gráfico 2: Probabilidades da política final para cada estado estados_teste = np.linspace(-1.2, 0.6, 50) velocidades_teste = np.linspace(-0.07, 0.07, 50) X, Y = np.meshgrid(estados_teste, velocidades_teste) Z_esquerda = np.zeros_like(X) Z_direita = np.zeros_like(X) for i in range(len(estados_teste)): for j in range(len(velocidades_teste)): estado = np.array([estados_teste[i], velocidades_teste[j]]) probs = politica(estado, W) Z_esquerda[j,i] = probs[0] # ação esquerda Z_direita[j,i] = probs[2] # ação direita plt.subplot(1,2,2) plt.contourf(X, Y, Z_direita - Z_esquerda, levels=20, cmap='RdBu') plt.colorbar(label='Preferência por direita - esquerda') plt.xlabel('Posição') plt.ylabel('Velocidade') plt.title('Política final aprendida') plt.tight_layout() plt.show() print(f"Melhor episódio: {min(passos_por_episodio)} passos") print(f"Média dos últimos 100 episódios: {np.mean(passos_por_episodio[-100:]):.1f} passos") |
O código implementa REINFORCE para o Mountain Car. O agente aprende uma política estocástica linear. Inicialmente, ele age aleatoriamente e demora muitos passos. Com o tempo, a política melhora e o carro sobe mais rápido. O segundo gráfico mostra a preferência da política final. Regiões em azul indicam tendência a acelerar para a direita. Este exemplo demonstra como políticas são aprendidas na prática.