Curso

Aprendizado por reforço (RL) é a parte do ecossistema de machine learning em que o agente aprende interagindo com o ambiente para encontrar a melhor estratégia para atingir seus objetivos. Ele é bem diferente dos algoritmos de aprendizado supervisionado, em que precisamos ingerir e processar dados. No aprendizado por reforço, não há necessidade de dados rotulados: o agente aprende com o ambiente e com o sistema de recompensas para tomar decisões cada vez melhores.
Por exemplo, no jogo do Mario, se o personagem toma uma ação aleatória (como ir para a esquerda), com base nessa ação ele pode receber uma recompensa. Depois de agir, o agente (Mario) vai para um novo estado, e o processo se repete até o personagem terminar a fase ou morrer.
Esse episódio se repete várias vezes até o Mario aprender a navegar pelo ambiente maximizando as recompensas.

Imagem do autor
Podemos dividir o aprendizado por reforço em cinco etapas simples:
- O agente está no estado zero em um ambiente.
- Ele toma uma ação com base em uma estratégia específica.
- Recebe uma recompensa ou punição com base nessa ação.
- Aprende com os movimentos anteriores e otimiza a estratégia.
- O processo se repete até encontrar uma estratégia ótima.
Saiba mais lendo nosso tutorial, uma introdução ao aprendizado por reforço. Você vai explorar em detalhes como o RL funciona com exemplos de código.
Neste tutorial, você vai aprender sobre Q-learning e entender por que precisamos de Deep Q-learning. Além disso, vamos criar e treinar algoritmos de Q-learning do zero usando Numpy e Gymnasium.
Observação: se você é novo em machine learning, recomendamos nossa trilha de carreira Machine Learning Scientist with Python para entender melhor aprendizado por reforço e Q-learning.
Torne-se um cientista de ML
O que é Q-learning?
Q-learning é um algoritmo sem modelo, baseado em valor e off-policy que encontra a melhor sequência de ações a partir do estado atual do agente. A letra “Q” vem de quality (qualidade). Qualidade representa o quão valiosa é a ação para maximizar recompensas futuras.
Algoritmos com modelo usam funções de transição e recompensa para estimar a política ótima e criar o modelo. Já os algoritmos sem modelo aprendem as consequências de suas ações pela experiência, sem funções de transição e recompensa explícitas.
O método baseado em valor treina a função de valor para aprender quais estados são mais valiosos e, então, agir. Por outro lado, métodos baseados em política treinam diretamente a política para aprender qual ação tomar em um estado específico.
No off-policy, o algoritmo avalia e atualiza uma política diferente daquela usada para agir. Já o algoritmo on-policy avalia e melhora a mesma política usada para agir.
Principais termos no Q-learning
Antes de ver como o Q-learning funciona, vale conhecer alguns termos úteis para entender seus fundamentos.
- States (s): a posição atual do agente no ambiente.
- Action (a): um passo tomado pelo agente em um determinado estado.
- Rewards: para cada ação, o agente recebe uma recompensa ou penalidade.
- Episodes: o fim de uma fase, quando o agente não pode mais agir. Acontece quando o agente atinge o objetivo ou falha.
- Q(St+1, a): valor Q ótimo esperado ao executar a ação em um determinado estado.
- Q(St, At): estimativa atual de Q(St+1, a).
- Q-table: tabela mantida pelo agente com os conjuntos de estados e ações.
- Temporal Differences (TD): usado para estimar o valor esperado de Q(St+1, a) a partir do estado e ação atuais e do estado e ação anteriores.
Como o Q-learning funciona?
Vamos entender em detalhes como o Q-learning funciona usando o exemplo de um lago congelado. Nesse ambiente, o agente precisa atravessar o lago, do início até o objetivo, sem cair nos buracos. A melhor estratégia é alcançar o objetivo seguindo o caminho mais curto.

Gif do autor
Q-table
O agente usa uma Q-table para tomar a melhor ação possível com base na recompensa esperada para cada estado do ambiente. Em termos simples, a Q-table é uma estrutura de dados com conjuntos de ações e estados, e usamos o algoritmo de Q-learning para atualizar os valores dessa tabela.
Função Q
A função Q usa a equação de Bellman e recebe estado (s) e ação (a) como entrada. A equação simplifica o cálculo do valor de estados e do valor estado-ação. 
Imagem de freecodecamp.org
Algoritmo de Q-learning

Imagem do autor
Inicializar a Q-table
Primeiro, vamos inicializar a Q-table. Vamos construí-la com colunas baseadas no número de ações e linhas baseadas no número de estados.
No nosso exemplo, o personagem pode se mover para cima, baixo, esquerda e direita. Temos quatro ações possíveis e quatro estados (início, inerte, caminho errado e fim). Você também pode considerar “caminho errado” como cair no buraco. Vamos inicializar a Q-table com valores 0.

Imagem do autor
Escolher uma ação
O segundo passo é simples. No início, o agente escolhe uma ação aleatória (baixo ou direita) e, na segunda rodada, usa a Q-table atualizada para selecionar a ação.
Executar uma ação
Escolher e executar ações se repete várias vezes até o loop de treino parar. A primeira ação e o primeiro estado são selecionados usando a Q-table. No nosso caso, todos os valores da Q-table são zero.
Depois, o agente se move para baixo e atualiza a Q-table usando a equação de Bellman. A cada movimento, atualizamos os valores na Q-table e também a usamos para determinar o melhor curso de ação.
No começo, o agente está em modo de exploração e escolhe ações aleatórias para explorar o ambiente. A estratégia epsilon-greedy é um método simples para equilibrar exploração e exploração de fato (exploitation). O epsilon representa a probabilidade de explorar; quando essa probabilidade diminui, o agente passa a explorar menos e explorar de fato mais.
No início, a taxa de epsilon é alta, ou seja, o agente está explorando. Conforme explora o ambiente, o epsilon diminui e o agente passa a explorar de fato o que aprendeu. Durante a exploração, a cada iteração o agente fica mais confiante na estimativa dos Q-values.

Imagem do autor
No exemplo do lago congelado, o agente não conhece o ambiente, então começa com uma ação aleatória (mover para baixo). Como vemos na imagem acima, a Q-table é atualizada usando a equação de Bellman.
Medição das recompensas
Depois de agir, medimos o resultado e a recompensa.
- A recompensa por alcançar o objetivo é +1
- A recompensa por pegar o caminho errado (cair no buraco) é 0
- A recompensa por ficar inerte ou se mover no lago congelado também é 0.
Atualizar a Q-table
Vamos atualizar a função Q(St, At) usando a equação. Ela usa os Q-values estimados no episódio anterior, a taxa de aprendizado e o erro de diferenças temporais (TD). O erro TD é calculado usando a recompensa imediata, a recompensa futura máxima esperada descontada e a antiga estimativa do Q-value.
O processo se repete várias vezes até que a Q-table seja atualizada e a função de valor Q seja maximizada.

Imagem do autor | Equações visuais de Thomas Simonini
No começo, o agente explora o ambiente para atualizar a Q-table. Quando a Q-table está pronta, o agente passa a explorar de fato e a tomar decisões melhores. 
Imagem do autor
No caso do lago congelado, o agente aprende a seguir o caminho mais curto até o objetivo e a evitar cair nos buracos.
Tutorial de Q-learning em Python
Nesta seção, vamos construir nosso modelo de Q-learning do zero usando o ambiente Gymnasium, Pygame e Numpy. O tutorial em Python é uma versão modificada do Notebook do Thomas Simonini. Ele inclui a inicialização do ambiente e da Q-table, definição da política greedy, configuração de hiperparâmetros, criação e execução do loop de treino e avaliação, além da visualização dos resultados.
Se você tiver problemas ao criar e executar seu loop de treino, confira o código-fonte com a saída.
Preparação
Configurar uma tela virtual
Vamos instalar primeiro todas as dependências para gerar um vídeo de replay (Gif). Precisamos de uma tela virtual (pyvirtualdisplay) para renderizar o ambiente e gravar os frames.
Observação: ao usar %%capture estamos suprimindo a saída da célula do Jupyter.
%%capture
!pip install pyglet==1.5.1
!apt install python-opengl
!apt install ffmpeg
!apt install xvfb
!pip3 install pyvirtualdisplay
# Virtual display
from pyvirtualdisplay import Display
virtual_display = Display(visible=0, size=(1400, 900))
virtual_display.start()
Instalar dependências
Agora vamos instalar as dependências que vão nos ajudar a criar, executar e avaliar o loop de treino.
- gymnasium: usado para inicializar o ambiente FrozenLake-v1.
- pygame: usado para a interface do FrozenLake-v1.
- numPy: usado para criar e manipular a Q-table.
%%capture
!pip install gymnasium
!pip install pygame
!pip install numpy
!pip install imageio imageio_ffmpeg
Importar os pacotes
Agora vamos importar as bibliotecas necessárias.
- Imageio é usado para criar a animação.
- tqdm é usado para barras de progresso.
import numpy as np
import gymnasium as gym
import random
import imageio
from tqdm.notebook import trange
Ambiente Frozen Lake do Gymnasium
Vamos criar um ambiente 4x4 sem escorregar usando a biblioteca Frozen Lake do Gymnasium.
- Existem duas versões de grade, “4x4” e “8x8”.
- Se
is_slippery=True, o agente pode não se mover na direção pretendida devido à superfície escorregadia do lago congelado.
Depois de inicializar o ambiente, vamos fazer uma análise do ambiente.
env = gym.make("FrozenLake-v1",map_name="4x4",is_slippery=False)
print("Observation Space", env.observation_space)
print("Sample observation", env.observation_space.sample()) # display a random observation
Há 16 espaços únicos no ambiente exibidos em posições aleatórias.
Observation Space Discrete(16)
Sample observation 15
Vamos descobrir o número de ações e exibir uma ação aleatória.
Espaço de ações:
- 0: mover para a esquerda
- 1: mover para baixo
- 2: mover para a direita
- 3: mover para cima
Função de recompensa:
- Alcançar o objetivo: +1
- Cair no buraco: 0
- Permanecer no lago congelado: 0
print("Action Space Shape", env.action_space.n)
print("Action Space Sample", env.action_space.sample())
Action Space Shape 4
Action Space Sample 1
Criar e inicializar a Q-table
A Q-table tem as ações como colunas e os estados como linhas. Podemos usar o Gymnasium para encontrar o espaço de ações e o espaço de estados. Então usamos essas informações para criar a Q-table.
state_space = env.observation_space.n
print("There are ", state_space, " possible states")
action_space = env.action_space.n
print("There are ", action_space, " possible actions")
There are 16 possible states
There are 4 possible actions
Para inicializar a Q-table, vamos criar um array Numpy de state_space por action_space. Vamos criar um array 16 x 4.
def initialize_q_table(state_space, action_space):
Qtable = np.zeros((state_space, action_space))
return Qtable
Qtable_frozenlake = initialize_q_table(state_space, action_space)
Política epsilon-greedy
Na seção anterior, vimos a estratégia epsilon-greedy que lida com o trade-off entre exploração e exploração de fato. Com probabilidade de 1 - ɛ, exploramos de fato; com probabilidade ɛ, exploramos o ambiente.
Na função epsilon_greedy_policy, vamos:
- Gerar um número aleatório entre 0 e 1.
- Se o número for maior que epsilon, fazemos exploração de fato. Ou seja, o agente toma a ação de maior valor para o estado dado.
- Caso contrário, fazemos exploração (ação aleatória).
def epsilon_greedy_policy(Qtable, state, epsilon):
random_int = random.uniform(0,1)
if random_int > epsilon:
action = np.argmax(Qtable[state])
else:
action = env.action_space.sample()
return action
Definir a política greedy
Como sabemos, Q-learning é um algoritmo off-policy, o que significa que a política usada para agir e a política usada para atualizar a função são diferentes.
Neste exemplo, a política epsilon-greedy é a política de ação, e a política greedy é a política de atualização.
A política greedy também será a política final quando o agente estiver treinado. Ela seleciona o maior valor de estado e ação na Q-table.
def greedy_policy(Qtable, state):
action = np.argmax(Qtable[state])
return action
Hiperparâmetros do modelo
Esses hiperparâmetros são usados no loop de treino, e ajustá-los bem traz resultados melhores.
O agente precisa explorar bem o espaço de estados para aprender boas aproximações de valor; por isso, precisamos de um decaimento progressivo do epsilon. Se a taxa de decaimento for alta demais, o agente pode ficar preso por não ter explorado o suficiente.
- São 10.000 episódios de treino e 100 de avaliação.
- A taxa de aprendizado é 0,7.
- Estamos usando "FrozenLake-v1" como ambiente, com 99 passos máximos por episódio.
- O gamma (fator de desconto) é 0,95.
- eval_seed: semente de avaliação do ambiente.
- A probabilidade epsilon inicial de exploração é 1,0, e a mínima será 0,05.
- A taxa de decaimento exponencial do epsilon é 0,0005.
# Training parameters
n_training_episodes = 10000
learning_rate = 0.7
# Evaluation parameters
n_eval_episodes = 100
# Environment parameters
env_id = "FrozenLake-v1"
max_steps = 99
gamma = 0.95
eval_seed = []
# Exploration parameters
max_epsilon = 1.0
min_epsilon = 0.05
decay_rate = 0.0005
Treinamento do modelo
No loop de treino, vamos:
- Criar um loop para os episódios de treino.
- Reduzir o epsilon primeiro. A cada episódio, precisamos de menos exploração e mais exploração de fato.
- Reiniciar o ambiente.
- Criar um loop aninhado para o número máximo de passos.
- Escolher a ação usando a política epsilon-greedy.
- Executar a ação (At) e observar a recompensa esperada (Rt+1) e o estado (St+1).
- Tomar a ação (a) e observar o novo estado (s') e a recompensa (r).
- Atualizar a função Q usando a fórmula.
- Se
done = True, finalizar o episódio e sair do loop. - Por fim, mudar o estado atual para o novo estado.
- Após concluir todos os episódios de treino, a função retorna a Q-table atualizada.
def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable):
for episode in trange(n_training_episodes):
epsilon = min_epsilon + (max_epsilon - min_epsilon)*np.exp(-decay_rate*episode)
# Reset the environment
state = env.reset()
step = 0
done = False
# repeat
for step in range(max_steps):
action = epsilon_greedy_policy(Qtable, state, epsilon)
new_state, reward, done, info = env.step(action)
Qtable[state][action] = Qtable[state][action] + learning_rate * (reward + gamma * np.max(Qtable[new_state]) - Qtable[state][action])
# If done, finish the episode
if done:
break
# Our state is the new state
state = new_state
return Qtable
Levou 3 segundos para concluir 10.000 episódios de treino.
Qtable_frozenlake = train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable_frozenlake)

Como podemos ver, a Q-table treinada tem valores, e o agente agora vai usar esses valores para navegar no ambiente e alcançar o objetivo.
Qtable_frozenlake
array([[0.73509189, 0.77378094, 0.77378094, 0.73509189],
[0.73509189, 0. , 0.81450625, 0.77378094],
[0.77378094, 0.857375 , 0.77378094, 0.81450625],
[0.81450625, 0. , 0.77378094, 0.77378094],
[0.77378094, 0.81450625, 0. , 0.73509189],
[0. , 0. , 0. , 0. ],
[0. , 0.9025 , 0. , 0.81450625],
[0. , 0. , 0. , 0. ],
[0.81450625, 0. , 0.857375 , 0.77378094],
[0.81450625, 0.9025 , 0.9025 , 0. ],
[0.857375 , 0.95 , 0. , 0.857375 ],
[0. , 0. , 0. , 0. ],
[0. , 0. , 0. , 0. ],
[0. , 0.9025 , 0.95 , 0.857375 ],
[0.9025 , 0.95 , 1. , 0.9025 ],
[0. , 0. , 0. , 0. ]])
Avaliação
A função evaluate_agent roda por n_eval_episodes episódios e retorna a média e o desvio padrão da recompensa.
- No loop, primeiro verificamos se há semente de avaliação. Se não houver, reiniciamos o ambiente sem semente.
- O loop interno roda até max_steps.
- O agente toma a ação com a maior recompensa futura esperada em um dado estado usando a Q-table.
- Calculamos a recompensa.
- Mudamos o estado.
- Se done (agente cai no buraco ou alcança o objetivo), encerramos o loop.
- Registramos os resultados.
- Ao final, usamos esses resultados para calcular a média e o desvio padrão.
def evaluate_agent(env, max_steps, n_eval_episodes, Q, seed):
episode_rewards = []
for episode in range(n_eval_episodes):
if seed:
state = env.reset(seed=seed[episode])
else:
state = env.reset()
step = 0
done = False
total_rewards_ep = 0
for step in range(max_steps):
# Take the action (index) that have the maximum reward
action = np.argmax(Q[state][:])
new_state, reward, done, info = env.step(action)
total_rewards_ep += reward
if done:
break
state = new_state
episode_rewards.append(total_rewards_ep)
mean_reward = np.mean(episode_rewards)
std_reward = np.std(episode_rewards)
return mean_reward, std_reward
Como você pode ver, obtivemos a pontuação perfeita com desvio padrão zero. Isso significa que nosso agente alcançou o objetivo em todos os 100 episódios.
# Evaluate our Agent
mean_reward, std_reward = evaluate_agent(env, max_steps, n_eval_episodes, Qtable_frozenlake, eval_seed)
print(f"Mean_reward={mean_reward:.2f} +/- {std_reward:.2f}")
Mean_reward=1.00 +/- 0.00
Visualizando o resultado
Até agora, trabalhamos só com números. Para uma demo, vamos criar um Gif animado do agente desde o início até alcançar o objetivo.
- Primeiro, criamos o estado reiniciando o ambiente com um inteiro aleatório entre 0 e 500.
- Renderizamos o ambiente usando rdb_array para criar um array de imagem.
- Depois, adicionamos o
imgao arrayimages. - No loop, damos os passos usando a Q-table e renderizamos a imagem a cada passo.
- No final, usamos esse array e o imageio para criar um Gif de um frame por segundo.
def record_video(env, Qtable, out_directory, fps=1):
images = []
done = False
state = env.reset(seed=random.randint(0,500))
img = env.render(mode='rgb_array')
images.append(img)
while not done:
# Take the action (index) that have the maximum expected future reward given that state
action = np.argmax(Qtable[state][:])
state, reward, done, info = env.step(action) # We directly put next_state = state for recording logic
img = env.render(mode='rgb_array')
images.append(img)
imageio.mimsave(out_directory, [np.array(img) for i, img in enumerate(images)], fps=fps)
Se estiver em um Jupyter Notebook, você pode exibir o Gif usando a função Image do IPython.display.
video_path="/content/replay.gif"
video_fps=1
record_video(env, Qtable_frozenlake, video_path, video_fps)
from IPython.display import Image
Image('./replay.gif')
Agora você pode compartilhar esses resultados com colegas de trabalho e de turma ou postar nas redes sociais.
Obtenha uma das melhores certificações de IA
Perguntas frequentes sobre Q-learning
Qual é a desvantagem do Q-learning?
O processo de aprendizado no Q-learning é custoso para o agente, especialmente no começo. Por quê? Para convergir para a política ótima, cada par estado-ação precisa ser visitado com frequência.
Por que Q-learning se chama assim?
No Q-learning, o “Q” vem de quality (qualidade). Ele representa quão útil é uma ação para obter recompensas futuras, o que é usado para criar um mapeamento de estados e ações a fim de maximizar as recompensas esperadas.
Por que o Q-learning é off-policy?
No Q-learning, a política atualizada é diferente da política de comportamento (ação), e por isso ele é chamado de algoritmo off-policy.
O Q-learning sempre converge?
Sim. Durante o treinamento, o algoritmo converge para a política ótima.
Por que precisamos de deep Q-learning?
Q-learning é um algoritmo simples, pensado para ambientes menores e discretos. Em ambientes maiores, precisaríamos de uma Q-table enorme de estados e ações, exigindo muita memória e computação para treinar. Já o Deep Q-learning substitui a Q-table por uma rede neural para lidar com ambientes grandes que envolvem ações e estados contínuos.

