Aprendizado por Reforço com Python: Construindo Agentes Inteligentes

Introdução a RL, Q-Learning e Deep RL usando Gym e PyTorch

Aprendizado por Reforço (Reinforcement Learning – RL) é uma área da inteligência artificial onde agentes aprendem a tomar decisões em um ambiente através de recompensas e punições. Diferente do aprendizado supervisionado, o agente não recebe respostas corretas diretamente, mas precisa explorar o ambiente e aprender uma política ótima para maximizar a recompensa cumulativa.

Python oferece um ecossistema robusto para RL, combinando OpenAI Gym para simulação de ambientes e PyTorch para modelagem de redes neurais profundas, tornando possível criar desde agentes simples de Q-Learning até Deep Reinforcement Learning.

Neste artigo, vamos explorar conceitos fundamentais, algoritmos clássicos e modernos, e construir exemplos práticos usando Python.


1. Conceitos Fundamentais do Aprendizado por Reforço

1.1 Agente e Ambiente

  • Agente: responsável por executar ações no ambiente.

  • Ambiente: representa o mundo em que o agente opera e fornece recompensas.

Exemplo: Um robô (agente) navegando em um labirinto (ambiente).

1.2 Estados, Ações e Recompensas

  • Estado (s): representa a situação atual do agente no ambiente.

  • Ação (a): escolha disponível para o agente em cada estado.

  • Recompensa (r): feedback do ambiente após uma ação.

1.3 Política e Função de Valor

  • Política (π): estratégia do agente, mapeando estados para ações.

  • Função de valor (V(s)): estima a recompensa esperada a partir de um estado.

  • Função Q (Q(s,a)): estima a recompensa esperada de uma ação em um estado.


2. OpenAI Gym: Criando e Interagindo com Ambientes

OpenAI Gym é uma biblioteca padrão para simulação de ambientes RL.

Instalando Gym

pip install gym

Exemplo: Ambiente CartPole

import gym

# Criar ambiente
env = gym.make("CartPole-v1")

# Resetar ambiente
state = env.reset()

for _ in range(10):
    env.render()          # Mostra a simulação
    action = env.action_space.sample()  # Ação aleatória
    state, reward, done, info = env.step(action)
    if done:
        state = env.reset()

env.close()
  • state: vetor representando a posição do carrinho e do pêndulo;

  • action: 0 ou 1 (mover para esquerda/direita);

  • reward: +1 por manter o pêndulo equilibrado;

  • done: indica fim do episódio.


3. Q-Learning: Algoritmo Clássico de RL

O Q-Learning é um algoritmo de aprendizado por reforço baseado em tabelas (tabular). Ele aprende a função Q iterativamente usando a equação de Bellman:

[
Q(s, a) \leftarrow Q(s, a) + \alpha \Big[r + \gamma \max_{a'} Q(s', a') - Q(s, a)\Big]
]

  • α (alpha): taxa de aprendizado;

  • γ (gamma): fator de desconto para recompensas futuras.

Implementação em Python

import numpy as np
import gym

env = gym.make("FrozenLake-v1", is_slippery=False)
n_states = env.observation_space.n
n_actions = env.action_space.n

Q = np.zeros((n_states, n_actions))
alpha = 0.1
gamma = 0.99
epsilon = 0.1
episodes = 2000

for ep in range(episodes):
    state = env.reset()
    done = False
    while not done:
        if np.random.rand() < epsilon:
            action = env.action_space.sample()
        else:
            action = np.argmax(Q[state])

        next_state, reward, done, _ = env.step(action)
        Q[state, action] += alpha * (reward + gamma * np.max(Q[next_state]) - Q[state, action])
        state = next_state

print("Q-table treinada:\n", Q)
  • Após treinamento, o agente consegue alcançar o objetivo de forma consistente;

  • Q-Learning é eficiente para ambientes pequenos e discretos.


4. Deep Q-Learning (DQN) com PyTorch

Para ambientes grandes ou contínuos, tabelas Q tornam-se inviáveis. É aí que Deep Q-Learning (DQN) entra, usando redes neurais para aproximar Q(s,a).

4.1 Instalando PyTorch

pip install torch torchvision

4.2 Estrutura do DQN

  1. Replay Buffer: armazena transições (state, action, reward, next_state) para treino aleatório.

  2. Rede Neural: aproxima a função Q.

  3. Target Network: rede para estabilidade do treinamento.

4.3 Exemplo simples: CartPole DQN

import torch
import torch.nn as nn
import torch.optim as optim
import random
import numpy as np
import gym
from collections import deque

class DQN(nn.Module):
    def __init__(self, state_dim, action_dim):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_dim, 128)
        self.fc2 = nn.Linear(128, 128)
        self.fc3 = nn.Linear(128, action_dim)
    
    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

env = gym.make("CartPole-v1")
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n

model = DQN(state_dim, action_dim)
optimizer = optim.Adam(model.parameters(), lr=0.001)
criterion = nn.MSELoss()
replay_buffer = deque(maxlen=10000)
batch_size = 64
gamma = 0.99
epsilon = 0.1
episodes = 500

for ep in range(episodes):
    state = env.reset()
    state = torch.FloatTensor(state)
    done = False
    while not done:
        if random.random() < epsilon:
            action = env.action_space.sample()
        else:
            with torch.no_grad():
                action = torch.argmax(model(state)).item()
        next_state, reward, done, _ = env.step(action)
        next_state_tensor = torch.FloatTensor(next_state)
        replay_buffer.append((state, action, reward, next_state_tensor, done))
        state = next_state_tensor

        if len(replay_buffer) >= batch_size:
            batch = random.sample(replay_buffer, batch_size)
            states, actions, rewards, next_states, dones = zip(*batch)
            states = torch.stack(states)
            next_states = torch.stack(next_states)
            rewards = torch.tensor(rewards, dtype=torch.float32)
            actions = torch.tensor(actions)
            dones = torch.tensor(dones, dtype=torch.float32)

            q_values = model(states).gather(1, actions.unsqueeze(1)).squeeze(1)
            with torch.no_grad():
                max_next_q_values = model(next_states).max(1)[0]
                target_q = rewards + gamma * max_next_q_values * (1 - dones)
            
            loss = criterion(q_values, target_q)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
  • Este é um exemplo simplificado, mas mostra como Q-Learning clássico evolui para Deep RL;

  • Para produção, adiciona-se target network, epsilon decay e normalização de estados.


5. Boas práticas em Aprendizado por Reforço

  1. Normalização de estados: ajuda redes a convergir mais rápido.

  2. Epsilon decay: diminui exploração ao longo do tempo.

  3. Replay buffer grande e aleatório: melhora estabilidade do aprendizado.

  4. Target network: reduz instabilidade em DQN.

  5. Monitoramento: track de recompensas e perdas por episódio.

  6. Testes periódicos: verificar se agente realmente está aprendendo.


6. Casos de uso de RL

  • Robótica: controle de braços, drones e veículos autônomos.

  • Jogos: agentes em jogos de tabuleiro, videogames e puzzles.

  • Finanças: estratégias de trading adaptativas.

  • Otimização industrial: gestão de estoques, logística e produção.

  • Sistemas adaptativos: personalização de conteúdo e IA interativa.

Comentários

Postagens mais visitadas deste blog

Laços de Repetição em Python: Conceitos e Exemplos Práticos

Manipulação de Arquivos no C#: Como Ler, Escrever e Trabalhar com Arquivos de Forma Simples

Como Instalar o Xamarin com C#: Passo a Passo Completo