Ensinei uma IA a Jogar Sonic — e Aprendi Muito Sobre Aprendizado por Reforço no Processo
Como cientista de dados, boa parte do meu trabalho envolve ensinar máquinas a reconhecer padrões em dados históricos. Mas existe uma…
Ensinei uma IA a Jogar Sonic — e Aprendi Muito Sobre Aprendizado por Reforço no Processo

Como cientista de dados, boa parte do meu trabalho envolve ensinar máquinas a reconhecer padrões em dados históricos. Mas existe uma categoria de problemas onde não temos dados históricos — onde o agente precisa descobrir sozinho o que fazer, tentando, errando e aprendendo com as consequências. Isso é Aprendizado por Reforço, e é exatamente o que usei para treinar um agente para jogar Sonic The Hedgehog do zero.
Neste artigo vou te explicar os conceitos fundamentais do Aprendizado por Reforço (RL) e mostrar como implementei um agente do zero usando Python, Stable-Baselines3 e o algoritmo PPO. Sem enrolação — vamos direto ao que importa.
O que é Aprendizado por Reforço?
Imagine que você quer ensinar um cachorro a sentar. Você não mostra exemplos rotulados de “cachorros sentados” — você dá um comando, o cachorro reage, e você recompensa quando ele acerta. Com o tempo, ele aprende que sentar gera recompensa.
O Aprendizado por Reforço funciona exatamente assim, só que com matemática.
Os três componentes centrais são:
- Agente: quem toma as decisões (no nosso caso, o Sonic controlado pela IA)
- Ambiente: o mundo com o qual o agente interage (o jogo)
- Recompensa: o sinal que diz se a ação foi boa ou ruim
O ciclo funciona assim: o agente observa o estado atual do ambiente, escolhe uma ação, recebe uma recompensa (positiva ou negativa) e observa o novo estado. Esse loop se repete milhões de vezes até o agente aprender uma política — uma estratégia de comportamento — que maximize a recompensa acumulada.
O objetivo do agente não é maximizar a recompensa imediata, mas a recompensa total ao longo do tempo. Às vezes vale sacrificar um ponto agora para ganhar muito mais depois.
Por que jogos são o ambiente perfeito para RL?
Jogos são ideais para pesquisa em RL por três motivos:
- Feedback imediato: o jogo já fornece pontuação, que serve como recompensa natural
- Simulação rápida: podemos rodar milhares de partidas em horas, coisa impossível no mundo real
- Ambiente controlado: as regras são fixas, sem variáveis externas imprevisíveis
Foi assim que a DeepMind treinou o AlphaGo, que a OpenAI treinou agentes no Dota 2, e que eu treinei o Sonic — a lógica é a mesma, a escala é diferente.
O Algoritmo: PPO (Proximal Policy Optimization)
Existem dezenas de algoritmos de RL. Para esse projeto usei o PPO, desenvolvido pela OpenAI em 2017 e até hoje um dos mais usados na prática. O motivo é simples: ele é estável, eficiente e funciona bem em uma grande variedade de problemas.
A ideia central do PPO é aprender uma política — uma função que mapeia observações em ações — de forma conservadora. A cada atualização, ele limita o quanto a política pode mudar, evitando que o agente “desaprenda” tudo por causa de uma atualização muito agressiva.
Matematicamente, o PPO otimiza um objetivo com clipping:
L = min(r * A, clip(r, 1-ε, 1+ε) * A)
Onde r é a razão entre a nova e a antiga política, A é a vantagem estimada da ação, e ε (epsilon) controla o quanto a política pode mudar — geralmente 0.2.
Na prática, isso significa que o agente aprende de forma gradual e consistente, sem colapsar no meio do treino.
Preparando o Ambiente
Para o projeto usei:
- Python 3.10 via pyenv
- stable-retro: fork mantido do gym-retro para emulação de jogos clássicos
- Stable-Baselines3: implementação de alta qualidade de algoritmos de RL
- Gymnasium: interface padrão para ambientes de RL
- PyTorch com suporte a CUDA para treinar na GPU
A ROM do Sonic precisa ser importada manualmente para o emulador. Após isso, o ambiente já expõe observações (frames do jogo) e aceita ações (botões do controle).
Processando as Observações
O agente recebe como entrada os pixels da tela — uma imagem de 224x320 pixels com 3 canais de cor. Isso é muita informação para processar diretamente. Por isso apliquei uma sequência de transformações:
- Frame Skip: repete cada ação por 4 frames. O jogo muda lentamente entre frames consecutivos, então não precisamos decidir a cada frame — isso acelera muito o treino
- Escala de Cinza: remove os canais de cor, que não são necessários para tomar decisões. Reduz de 3 canais para 1
- Redimensionamento: reduz de 224x320 para 84x84 pixels, padrão na literatura de RL com jogos
- Frame Stacking: empilha os últimos 4 frames em um único input. Isso dá ao agente noção de movimento — ele consegue inferir a direção e velocidade do Sonic
O resultado final é um tensor de shape (4, 84, 84) — 4 frames em escala de cinza de 84x84 pixels. Esse é o input que a rede neural recebe.
A Recompensa Customizada
A recompensa padrão do jogo (pontuação) não é ideal para treino de RL. O Sonic pode ficar parado e não perder pontos, o que não incentiva progresso.
Por isso implementei uma função de recompensa customizada com três componentes:
- +1.0 cada vez que o Sonic avança para uma nova posição máxima no eixo X — incentiva exploração e progresso
- -0.01 quando fica parado ou recua — penaliza comportamento passivo
- +0.001 a cada step que sobrevive — pequeno bônus por continuar vivo
Essa engenharia de recompensa é um dos aspectos mais críticos e subestimados do RL. Uma recompensa mal definida pode fazer o agente aprender comportamentos completamente indesejados — como ficar pulando no mesmo lugar para acumular bônus de sobrevivência sem avançar.
A Rede Neural: CnnPolicy
Como a entrada é uma imagem, usei uma política baseada em CNN (Convolutional Neural Network) — a CnnPolicy do Stable-Baselines3, inspirada na arquitetura da DeepMind usada no paper original do DQN.
A arquitetura consiste em três camadas convolucionais para extrair features visuais do jogo, seguidas de camadas densas que mapeiam essas features em valores de ação. A rede aprende a reconhecer padrões visuais relevantes — inimigos, plataformas, obstáculos — sem que precisemos programar isso explicitamente.
Os Hiperparâmetros
Os principais hiperparâmetros que usei:
learning_rate = 0.00025 # taxa de aprendizado
n_steps = 2048 # frames coletados antes de cada atualização
batch_size = 64 # tamanho do mini-batch
n_epochs = 10 # passagens pelo buffer a cada atualização
gamma = 0.99 # fator de desconto para recompensas futuras
clip_range = 0.2 # epsilon do PPO
ent_coef = 0.01 # coeficiente de entropia (incentiva exploração)
O gamma merece atenção especial. Ele controla o quanto o agente valoriza recompensas futuras versus imediatas. Com gamma = 0.99, uma recompensa 100 steps no futuro vale 0.99^100 ≈ 0.37 do valor de uma recompensa imediata. Valores próximos de 1 fazem o agente pensar mais a longo prazo.
O Treino
O treino rodou por 1.000.000 de timesteps em uma RTX 4060. O processo é iterativo:
- O agente coleta 2048 frames interagindo com o ambiente
- Calcula as vantagens de cada ação (foi melhor ou pior do que o esperado?)
- Atualiza a política com 10 épocas de gradiente descendente sobre o buffer coletado
- Repete
Nos primeiros episódios o Sonic fica praticamente parado — as ações são quase aleatórias. Após algumas dezenas de milhares de steps ele começa a ir consistentemente para a direita. Com centenas de milhares de steps começa a desviar de obstáculos e inimigos.
Ver o agente evoluir de completamente aleatório para competente é uma das experiências mais fascinantes que já tive como cientista de dados. É difícil não antropomorfizar — parece que ele está pensando.
O que aprendi com esse projeto
Alguns insights práticos que não aparecem nos tutoriais:
- Engenharia de recompensa é tudo: o algoritmo é só uma ferramenta. A recompensa define o comportamento. Errou na recompensa, o agente aprende a errar de formas criativas.
- Ordem dos wrappers importa: aplicar o frame stacking antes ou depois do wrapper de recompensa pode quebrar silenciosamente o shape das observações.
- Debugging em RL é difícil: o agente pode parecer que está aprendendo (loss caindo) mas estar convergindo para um comportamento subótimo. Monitorar o reward médio por episódio é essencial.
- GPU faz diferença: treinar em CPU levaria horas para o que a GPU faz em minutos — mas o driver precisa estar atualizado e o PyTorch instalado com suporte a CUDA correto.
Próximos Passos
Esse projeto é um ponto de partida. Algumas direções naturais para evoluir:
- Treinar em múltiplas fases simultaneamente para generalização
- Experimentar com outros algoritmos como SAC ou A3C
- Usar curiosity-driven exploration para fases mais complexas
- Implementar curriculum learning — começar em fases fáceis e aumentar a dificuldade progressivamente
Conclusão
Aprendizado por Reforço é uma das áreas mais fascinantes da IA — e também uma das mais desafiadoras. Diferente do aprendizado supervisionado, onde você tem respostas certas para aprender, no RL você só tem consequências. O agente precisa descobrir o que “certo” significa através da experiência.
Implementar um projeto como esse do zero, lidando com todos os problemas práticos — compatibilidade de bibliotecas, formato de observações, engenharia de recompensa, bugs de emulação — ensina muito mais do que qualquer curso teórico.
Se você é cientista de dados e ainda não experimentou RL, recomendo fortemente. O campo está evoluindo rápido, e a intuição que você desenvolve implementando esses projetos é cada vez mais valiosa.
O código completo do projeto está disponível no GitHub. Qualquer dúvida, deixa nos comentários.
메타데이터
- post_id
- 59611bddf681
- slug
- ensinei-uma-ia-a-jogar-sonic-e-aprendi-muito-sobre-aprendizado-por-reforço-no-processo-59611bddf681
- url
- https://medium.com/@linkolnsr/ensinei-uma-ia-a-jogar-sonic-e-aprendi-muito-sobre-aprendizado-por-refor%C3%A7o-no-processo-59611bddf681
- canonical_url
- https://medium.com/@linkolnsr/ensinei-uma-ia-a-jogar-sonic-e-aprendi-muito-sobre-aprendizado-por-refor%C3%A7o-no-processo-59611bddf681
- author_url
- https://medium.com/@linkolnsr
- status
- ok
- fetched_at
- 2026-07-10 15:20:15