1. Configuração do Ambiente
!pip install gymnasium
import numpy as np
import gymnasium as gym
import random
# Inicializa o ambiente (mapa 4x4, versão não escorregadia para facilitar o aprendizado inicial)
env = gym.make("FrozenLake-v1", is_slippery=False, render_mode="ansi")
2. Hiperparâmetros e Q-Table
A Q-table é uma matriz onde as linhas são os estados (16 posições no gelo) e as colunas são as ações (Esquerda, Baixo, Direita, Cima).
# Inicializa a Q-table com zeros
state_size = env.observation_space.n
action_size = env.action_space.n
q_table = np.zeros((state_size, action_size))
# Hiperparâmetros
total_episodes = 2000 # Total de tentativas de treino
learning_rate = 0.1 # Alpha: o quanto o agente aceita a nova informação
gamma = 0.95 # Gamma: importância das recompensas futuras (desconto)
epsilon = 1.0 # Epsilon: probabilidade inicial de explorar (agir ao acaso)
max_epsilon = 1.0
min_epsilon = 0.01
decay_rate = 0.005 # Taxa de decréscimo da exploração
3. Treinamento com Q-Learning
Aqui aplicamos a fórmula: $Q(s, a) = Q(s, a) + \alpha [R + \gamma \max Q(s', a') - Q(s, a)]$.
print("Treinando o agente...")
for episode in range(total_episodes):
state, info = env.reset()
step = 0
terminated = False
truncated = False
for step in range(100):
# Política Epsilon-Greedy (Explorar vs Explorar)
if random.uniform(0, 1) < epsilon:
action = env.action_space.sample() # Explora: ação aleatória
else:
action = np.argmax(q_table[state, :]) # Explota: melhor ação da Q-table
# Executa a ação
new_state, reward, terminated, truncated, info = env.step(action)
# Atualiza a Q-table
# Recompensa + (gamma * valor do melhor estado futuro) - valor atual
q_table[state, action] = q_table[state, action] + learning_rate * (
reward + gamma * np.max(q_table[new_state, :]) - q_table[state, action]
)
state = new_state
if terminated or truncated:
break
# Reduz o Epsilon para o agente explorar menos com o passar do tempo
epsilon = min_epsilon + (max_epsilon - min_epsilon) * np.exp(-decay_rate * episode)
print("Treinamento concluído!")
4. Avaliação do Desempenho
Agora testamos o que o agente aprendeu sem nenhuma aleatoriedade.
total_test_episodes = 100
successes = 0
for episode in range(total_test_episodes):
state, info = env.reset()
terminated = False
truncated = False
for step in range(100):
action = np.argmax(q_table[state, :]) # Escolhe sempre a melhor opção
new_state, reward, terminated, truncated, info = env.step(action)
state = new_state
if terminated:
if reward == 1: # Chegou ao presente (G de Gift)
successes += 1
break
print(f"Sucesso em {successes}/{total_test_episodes} episódios de teste.")
Visualizar a curva de aprendizado
import matplotlib.pyplot as plt
# Listas para armazenar dados de progresso
rewards_per_episode = []
# Resetando a Q-table para um novo experimento
q_table = np.zeros((state_size, action_size))
epsilon = max_epsilon
print("Treinando e coletando dados para o gráfico...")
for episode in range(total_episodes):
state, info = env.reset()
terminated = False
truncated = False
total_reward = 0
for step in range(100):
if random.uniform(0, 1) < epsilon:
action = env.action_space.sample()
else:
action = np.argmax(q_table[state, :])
new_state, reward, terminated, truncated, info = env.step(action)
q_table[state, action] = q_table[state, action] + learning_rate * (
reward + gamma * np.max(q_table[new_state, :]) - q_table[state, action]
)
state = new_state
total_reward += reward # No FrozenLake é 0 ou 1
if terminated or truncated:
break
epsilon = min_epsilon + (max_epsilon - min_epsilon) * np.exp(-decay_rate * episode)
rewards_per_episode.append(total_reward)
# --- Geração do Gráfico ---
# Para o gráfico não ficar muito poluído, calculamos a média móvel a cada 100 episódios
average_rewards = []
for i in range(0, total_episodes, 100):
average_rewards.append(np.mean(rewards_per_episode[i:i+100]))
plt.figure(figsize=(10, 5))
plt.plot(range(0, total_episodes, 100), average_rewards, marker='o', color='purple')
plt.title("Progresso do Agente no FrozenLake (Média de Sucesso)")
plt.xlabel("Episódios")
plt.ylabel("Taxa de Sucesso Média (0.0 a 1.0)")
plt.grid(True)
plt.show()