Pular para o conteúdo principal

A Combinação de MCTS e Redes Neurais

Nos artigos anteriores, apresentamos separadamente as redes neurais (Policy Network e Value Network) e os conceitos de aprendizado por reforço. Agora, vamos explorar a inovação central do AlphaGo — como combinar perfeitamente a Busca em Árvore de Monte Carlo (MCTS) com redes neurais.

Esta combinação é a chave do sucesso do AlphaGo: as redes neurais fornecem "intuição", o MCTS fornece "raciocínio", e ambos se complementam.


Revisão do MCTS Tradicional

O que é MCTS?

Monte Carlo Tree Search (MCTS) é um algoritmo de busca baseado em amostragem aleatória, particularmente adequado para IA de jogos.

A ideia central do MCTS é: em vez de enumerar todas as jogadas possíveis, é melhor simular aleatoriamente um grande número de partidas e usar estatísticas para estimar a qualidade de cada jogada.

Quatro Fases

O MCTS tradicional contém quatro fases que se repetem continuamente:

Vamos entender cada fase em detalhes:

1. Selection (Seleção)

A partir do nó raiz, descer pela árvore, selecionando o nó filho "mais promissor" até chegar a um nó folha.

O critério de seleção é a fórmula UCB1 (Upper Confidence Bound):

UCB1(s,a)=Xˉs,a+clnNsNs,a\text{UCB1}(s, a) = \bar{X}_{s,a} + c \sqrt{\frac{\ln N_s}{N_{s,a}}}

Onde:

  • Xˉs,a\bar{X}_{s,a}: Retorno médio a partir do nó (s,a)(s, a) (termo de exploração)
  • lnNsNs,a\sqrt{\frac{\ln N_s}{N_{s,a}}}: Bônus de exploração (termo de exploração)
  • NsN_s: Número de visitas do nó pai
  • Ns,aN_{s,a}: Número de visitas do nó filho
  • cc: Constante que equilibra exploração e aproveitamento

A sabedoria desta fórmula está em:

  • Nós com menos visitas recebem um bônus de exploração maior
  • À medida que o número de visitas aumenta, a seleção tende cada vez mais para nós com valor real mais alto

2. Expansion (Expansão)

Ao chegar a um nó folha, escolher uma ação não explorada e criar um novo nó filho.

Antes da expansão:              Depois da expansão:
○ (raiz) ○ (raiz)
/│\ /│\
○ ○ ○ ○ ○ ○
/│ → /│
○ ○ ○ ○
↑ \
nó folha ● (novo nó)

3. Simulation (Simulação/Rollout)

A partir do novo nó, usar alguma estratégia (geralmente aleatória ou heurística simples) para completar rapidamente a partida e obter o resultado.

Esta é a origem do nome "Monte Carlo" — usar simulação aleatória para estimar resultados.

A estratégia de rollout do MCTS tradicional pode ser:

  • Puramente aleatória: Escolher uniformemente entre as jogadas legais
  • Heurística leve: Usar regras simples para filtrar jogadas claramente ruins

4. Backpropagation (Retropropagação)

Propagar o resultado da simulação (vitória/derrota) de volta pelo caminho, atualizando as estatísticas de cada nó:

Conteúdo da atualização:
- Contagem de visitas: N(s, a) ← N(s, a) + 1
- Valor acumulado: W(s, a) ← W(s, a) + z
- Valor médio: Q(s, a) = W(s, a) / N(s, a)

Onde zz é o resultado da simulação (+1 ou -1).

Limitações do MCTS Tradicional

O MCTS tradicional tem desempenho limitado no Go, os principais problemas são:

  1. Baixa qualidade do Rollout: A simulação aleatória frequentemente produz partidas irracionais
  2. Necessidade de muitas simulações: Cada jogada pode exigir dezenas de milhares de simulações
  3. Avaliação imprecisa: Dependendo apenas de estatísticas de vitória/derrota, a eficiência da utilização de informação é baixa
  4. Incapacidade de aproveitar padrões: Recomeça a busca toda vez, não acumula experiência

Esses problemas foram elegantemente resolvidos no AlphaGo pelas redes neurais.


Como as Redes Neurais Melhoram o MCTS

Arquitetura Geral

O AlphaGo integra duas redes neurais no MCTS:

O Papel da Policy Network

A Policy Network atua na fase de Expansão.

No MCTS tradicional, todas as ações não exploradas na expansão são consideradas igualmente importantes. Mas a Policy Network fornece probabilidades a priori (prior probability):

P(s,a)=πθ(as)P(s, a) = \pi_\theta(a|s)

Isso faz o MCTS explorar prioritariamente as jogadas que "parecem melhores", aumentando significativamente a eficiência da busca.

Por exemplo, em uma posição:

  • "Tengen" pode ter apenas 0.01% de probabilidade
  • "Joseki de canto" pode ter 15% de probabilidade
  • "Ponto grande" pode ter 10% de probabilidade

O MCTS explorará prioritariamente as jogadas de alta probabilidade, em vez de desperdiçar tempo em escolhas claramente ruins.

O Papel da Value Network

A Value Network atua na fase de Avaliação.

O MCTS tradicional precisa completar toda a partida para obter uma avaliação. Mas a Value Network pode avaliar diretamente a taxa de vitória de qualquer posição:

v(s)=Vϕ(s)v(s) = V_\phi(s)

É como pedir a um mestre para avaliar a posição, em vez de deixar dois iniciantes jogarem toda a partida para ver o resultado.

A versão original do AlphaGo usa uma mistura de Value Network e Rollout:

V(sL)=(1λ)vθ(sL)+λzLV(s_L) = (1 - \lambda) \cdot v_\theta(s_L) + \lambda \cdot z_L

Onde:

  • vθ(sL)v_\theta(s_L): Avaliação da Value Network
  • zLz_L: Resultado do Rollout
  • λ\lambda: Coeficiente de mistura (AlphaGo usa λ=0.5\lambda = 0.5)

Visualização da Árvore de Busca

Vamos visualizar uma árvore de busca MCTS:

載入中...

Nesta visualização, você pode ver:

  • O tamanho do nó reflete o número de visitas
  • O caminho azul é o melhor caminho selecionado pelo MCTS
  • Cada nó mostra o número de visitas N e o valor médio Q

Detalhes do Processo de Busca

Fluxo Completo

Vamos acompanhar uma simulação completa do MCTS:

Algoritmo: Simulação Única MCTS do AlphaGo

Entrada: nó raiz s_root, Policy Network π, Value Network V

1. Selection (Seleção)
s = s_root
caminho = []

while s não é nó folha:
# Usar fórmula PUCT para selecionar ação
a* = argmax_a [Q(s,a) + U(s,a)]

onde U(s,a) = c_puct · P(s,a) · √N(s) / (1 + N(s,a))

caminho.append((s, a*))
s = estado após executar ação a*

2. Expansion (Expansão)
Se s não é estado terminal:
# Usar Policy Network para calcular probabilidades a priori
P(s, ·) = π(·|s)

# Criar nós filhos para todas as ações legais
for a in ações_legais:
criar nó filho (s, a)
definir P(s,a), N(s,a)=0, W(s,a)=0

3. Evaluation (Avaliação)
# Misturar Value Network e Rollout
v = V(s) # Avaliação da Value Network
z = rollout(s) # Resultado do Rollout
valor = (1-λ)·v + λ·z # Mistura

# AlphaGo Zero simplifica para usar apenas Value Network
# valor = V(s)

4. Backpropagation (Retropropagação)
for (s', a') in reverso(caminho):
N(s', a') += 1
W(s', a') += valor
Q(s', a') = W(s', a') / N(s', a')
valor = -valor # Alternar perspectiva

Detalhes da Fase de Seleção

A fase de seleção usa a fórmula PUCT (que será discutida em detalhes no próximo artigo):

a=argmaxa[Q(s,a)+cpuctP(s,a)N(s)1+N(s,a)]a^* = \arg\max_a \left[ Q(s,a) + c_{\text{puct}} \cdot P(s,a) \cdot \frac{\sqrt{N(s)}}{1 + N(s,a)} \right]

Esta fórmula equilibra:

  • Q(s,a): Valor médio conhecido (aproveitamento)
  • U(s,a): Bônus de exploração, combinando probabilidade a priori e contagem de visitas (exploração)

Detalhes da Fase de Expansão

Ao chegar a um nó folha, usar a Policy Network para inicializar novos nós:

def expand(state, policy_network):
# Obter probabilidades para todas as ações legais
action_probs = policy_network(state)

# Filtrar ações ilegais e renormalizar
legal_actions = get_legal_actions(state)
legal_probs = action_probs[legal_actions]
legal_probs = legal_probs / legal_probs.sum()

# Criar nós filhos
for action, prob in zip(legal_actions, legal_probs):
child = create_node(
state=apply_action(state, action),
prior=prob,
visit_count=0,
value_sum=0
)
add_child(current_node, action, child)

Detalhes da Fase de Avaliação

A versão original do AlphaGo usa duas avaliações misturadas:

Avaliação da Value Network:

  • Entrada direta da posição, saída da taxa de vitória
  • Cálculo rápido (uma inferência de rede neural)
  • Fornece avaliação de perspectiva global

Avaliação do Rollout:

  • Usar política rápida (Fast Rollout Policy) para completar a partida
  • Cálculo mais lento mas fornece resultado completo da partida
  • Pode descobrir algumas táticas que a rede neural pode ignorar
def evaluate(state, value_network, rollout_policy, lambda_mix=0.5):
# Avaliação da Value Network
v = value_network(state)

# Avaliação do Rollout
current = state
while not is_terminal(current):
action = rollout_policy(current)
current = apply_action(current, action)
z = get_result(current)

# Mistura
return (1 - lambda_mix) * v + lambda_mix * z

O AlphaGo Zero removeu o Rollout, usando apenas a Value Network. Isso simplificou o sistema e aumentou a eficiência.

Detalhes da Retropropagação

Propagar os resultados da avaliação de volta pelo caminho, atualizando estatísticas:

def backpropagate(path, value):
for state, action in reversed(path):
# Atualizar contagem de visitas
state.visit_count[action] += 1
# Atualizar soma de valores
state.value_sum[action] += value
# Atualizar valor médio
state.Q[action] = state.value_sum[action] / state.visit_count[action]
# Alternar perspectiva (o benefício do oponente é minha perda)
value = -value

Note o passo value = -value: Go é um jogo de soma zero, a vitória de um lado é a derrota do outro.


Alocação de Recursos Computacionais

Número de Simulações

O AlphaGo executa um grande número de simulações MCTS em cada jogada:

VersãoSimulações por jogadaTempo de reflexão
AlphaGo Fan~100.000Minutos
AlphaGo Lee~100.000Minutos
AlphaGo Zero (treinamento)1.600Segundos
AlphaGo Zero (competição)~1.600Segundos

O AlphaGo Zero alcança uma força maior com menos simulações, resultado da melhoria da qualidade da rede neural.

Estratégia de Alocação de Tempo

Diferentes posições podem exigir diferentes tempos de reflexão:

def allocate_time(game_state, remaining_time):
# Alocação básica
num_moves_remaining = estimate_remaining_moves(game_state)
base_time = remaining_time / num_moves_remaining

# Fatores de ajuste
complexity = estimate_complexity(game_state)
importance = estimate_importance(game_state)

# Dar mais tempo para posições complexas ou importantes
allocated_time = base_time * complexity * importance

# Garantir que não exceda o tempo
return min(allocated_time, remaining_time * 0.3)

Em competições reais, o AlphaGo investe mais tempo de reflexão em posições críticas (como momentos próximos à fronteira entre vitória e derrota).

Busca Paralela

O MCTS é naturalmente adequado para paralelização:

Técnica de Perda Virtual (Virtual Loss):

Quando um thread está explorando o caminho P:
1. Temporariamente assumir que este caminho já perdeu (virtual loss)
2. Outros threads tenderão a explorar outros caminhos
3. Quando o resultado retornar, atualizar estatísticas reais e remover virtual loss

Isso garante que múltiplos threads não explorem repetidamente o mesmo caminho.

def parallel_mcts_simulation(root, num_threads=8):
virtual_losses = {}

def simulate(thread_id):
# Fase de seleção (com virtual loss)
path = []
node = root
while not node.is_leaf():
action = select_with_virtual_loss(node, virtual_losses)
add_virtual_loss(node, action, virtual_losses)
path.append((node, action))
node = node.children[action]

# Expansão e avaliação
value = expand_and_evaluate(node)

# Retropropagação e remoção de virtual losses
backpropagate(path, value)
remove_virtual_losses(path, virtual_losses)

# Executar múltiplas simulações em paralelo
threads = [Thread(target=simulate, args=(i,)) for i in range(num_threads)]
for t in threads:
t.start()
for t in threads:
t.join()

Processamento em Lote na GPU

A inferência de rede neural é mais eficiente na GPU com processamento em lote. O AlphaGo usa avaliação em lote:

Sem lote:
Simulação 1 → Avaliação 1 → Simulação 2 → Avaliação 2 → ...
Baixa utilização da GPU

Com lote:
Coletar 32 posições para avaliar
→ Enviar todas de uma vez para a GPU avaliar
→ Retornar 32 resultados
Alta utilização da GPU

Isso requer agendamento mais complexo, mas aumenta significativamente o throughput.


Temperatura e Seleção Final

Temperatura Durante o Treinamento

Durante o treinamento de auto-jogo, o AlphaGo usa temperatura para controlar a exploração:

π(a)=N(s,a)1/τaN(s,a)1/τ\pi(a) = \frac{N(s,a)^{1/\tau}}{\sum_{a'} N(s,a')^{1/\tau}}

Onde τ\tau é o parâmetro de temperatura.

  • τ=1\tau = 1: Probabilidade proporcional à contagem de visitas (manter diversidade)
  • τ0\tau \to 0: Selecionar a ação com mais visitas (seleção determinística)

Estratégia do AlphaGo Zero:

  • Primeiras 30 jogadas: τ=1\tau = 1, manter diversidade de abertura
  • Depois: τ0\tau \to 0, selecionar a melhor jogada

Seleção Durante a Competição

Em competições reais, a seleção é geralmente determinística:

def select_move(root, temperature=0):
if temperature == 0:
# Selecionar ação com mais visitas
return argmax(root.visit_counts)
else:
# Amostrar pela distribuição de probabilidade ajustada pela temperatura
probs = root.visit_counts ** (1 / temperature)
probs = probs / probs.sum()
return np.random.choice(actions, p=probs)

Considerando Taxa de Vitória

Às vezes também se considera o valor médio em vez de apenas a contagem de visitas:

def select_move_with_value(root, temperature=0):
# Misturar contagem de visitas e valor
scores = root.visit_counts * (1 + root.Q_values)
scores = scores / scores.sum()

if temperature == 0:
return argmax(scores)
else:
probs = scores ** (1 / temperature)
probs = probs / probs.sum()
return np.random.choice(actions, p=probs)

Comparação com Rede Neural Pura

Por que Precisamos de Busca?

Uma pergunta natural é: Se a rede neural já pode prever boas jogadas, por que ainda precisamos de busca?

A resposta é: A busca pode corrigir erros da rede neural e descobrir jogadas melhores.

MétodoVantagensDesvantagens
Rede neural puraRápida, intuitivaPode ter pontos cegos
MCTS puroPode analisar profundamenteLento, precisa de avaliação
Rede neural + MCTSCombina vantagens de ambosAlto custo computacional

Evidências Experimentais

Experimentos da DeepMind mostram:

Policy Network pura: ~3000 Elo
Policy + pouco MCTS: ~3500 Elo
Policy + Value + MCTS: ~4500 Elo

A busca proporciona uma melhoria significativa na força do jogo.

O Valor da Busca

A busca é particularmente valiosa nas seguintes situações:

  1. Cálculo tático: Calcular ataques e capturas complexos
  2. Correção de viés: Corrigir erros sistemáticos da rede neural
  3. Lidar com posições raras: A rede neural pode não ter visto durante o treinamento
  4. Verificar intuição: Confirmar que uma jogada que "parece boa" realmente é boa

Diferenças Entre Versões do AlphaGo

AlphaGo Fan/Lee

Arquitetura:
- SL Policy Network (aprendizado supervisionado)
- RL Policy Network (aprendizado por reforço)
- Value Network
- Fast Rollout Policy

Durante a busca:
- Usa probabilidades a priori da SL Policy Network
- Mistura avaliação da Value Network e Rollout

AlphaGo Master

Arquitetura:
- Rede neural maior
- Mais dados de treinamento
- Características melhoradas

Durante a busca:
- Similar ao AlphaGo Lee
- Rede mais forte = menos necessidade de busca

AlphaGo Zero

Arquitetura:
- Única ResNet de cabeça dupla
- Treinamento do zero
- Sem Rollout

Durante a busca:
- Cabeça de política fornece probabilidades a priori
- Cabeça de valor avalia diretamente
- Mais simples e mais forte

Resumo da Evolução

AlphaGo Fan (2015)

│ + rede maior, mais treinamento

AlphaGo Lee (2016)

│ + mais auto-jogo

AlphaGo Master (2017)

│ + remoção de dados humanos, rede unificada, remoção de Rollout

AlphaGo Zero (2017)

│ + generalização para outros jogos

AlphaZero (2018)

Considerações de Implementação

Gerenciamento de Memória

A árvore MCTS pode ficar muito grande:

Suponha:
- Média de 200 ações legais por jogada
- Profundidade de busca 10
- Expansão completa: 200^10 ≈ 10^23 nós (impossível)

Abordagem real:
- Expandir apenas nós visitados
- Limpar periodicamente nós raramente visitados
- Reutilizar a árvore de busca da jogada anterior

Reutilização da Árvore

Quando o oponente joga, parte da árvore de busca pode ser reutilizada:

def reuse_tree(root, opponent_move):
if opponent_move in root.children:
new_root = root.children[opponent_move]
# Limpar outros ramos desnecessários
for action in root.children:
if action != opponent_move:
delete_subtree(root.children[action])
return new_root
else:
# Oponente jogou uma jogada inesperada, precisa recomeçar
return create_new_root()

Cache da Rede Neural

A mesma posição pode ser avaliada múltiplas vezes, usar cache evita cálculos repetidos:

class NeuralNetworkCache:
def __init__(self, max_size=100000):
self.cache = LRUCache(max_size)

def evaluate(self, state, network):
state_hash = hash(state)
if state_hash in self.cache:
return self.cache[state_hash]
else:
result = network(state)
self.cache[state_hash] = result
return result

Utilização de Simetria

O tabuleiro de Go tem 8 simetrias que podem ser usadas para melhorar a busca:

def evaluate_with_symmetry(state, network):
# Gerar todas as transformações simétricas
symmetries = generate_symmetries(state) # 8 versões

# Avaliar todas as versões
values = [network(s) for s in symmetries]

# Média (mais estável)
return np.mean(values)

Profundidade e Largura da Busca

Ajuste Dinâmico

O MCTS equilibra automaticamente profundidade e largura:

  • Largura: Controlada pelas probabilidades a priori da Policy Network
  • Profundidade: Determinada pela precisão da Value Network

Quando a rede neural é boa:

  • Jogadas de alta confiança são exploradas mais profundamente
  • Jogadas de baixa confiança são rapidamente descartadas
  • A busca naturalmente foca nos ramos importantes

Comparação com Busca Tradicional

MétodoControle de ProfundidadeControle de Largura
MinimaxProfundidade fixaPoda Alpha-Beta
MCTS tradicionalDeterminado por simulaçãoUCB1
AlphaGo MCTSGuiado por Policy + ValuePUCT + Policy

A busca do AlphaGo é mais "inteligente" — ela sabe quais lugares valem a pena aprofundar e quais podem ser rapidamente ignorados.


Correspondência de Animações

Conceitos centrais discutidos neste artigo e números de animação:

NúmeroConceitoCorrespondência Física/Matemática
🎬 C5Quatro fases do MCTSBusca em árvore

Resumo

A combinação de MCTS com redes neurais é a inovação central do AlphaGo. Aprendemos:

  1. MCTS tradicional: Selection, Expansion, Simulation, Backpropagation
  2. Melhorias das redes neurais: Policy Network guia a expansão, Value Network substitui o Rollout
  3. Processo de busca: Seleção PUCT, avaliação em lote, retropropagação
  4. Alocação de recursos: Número de simulações, gerenciamento de tempo, busca paralela
  5. Seleção por temperatura: Diferentes estratégias para treinamento e competição
  6. Detalhes de implementação: Gerenciamento de memória, reutilização da árvore, cache

No próximo artigo, exploraremos os detalhes matemáticos da fórmula PUCT.


Leitura Adicional


Referências

  1. Silver, D., et al. (2016). "Mastering the game of Go with deep neural networks and tree search." Nature, 529, 484-489.
  2. Silver, D., et al. (2017). "Mastering the game of Go without human knowledge." Nature, 550, 354-359.
  3. Coulom, R. (2006). "Efficient Selectivity and Backup Operators in Monte-Carlo Tree Search." Computers and Games.
  4. Kocsis, L., & Szepesvári, C. (2006). "Bandit based Monte-Carlo Planning." ECML.
  5. Browne, C., et al. (2012). "A Survey of Monte Carlo Tree Search Methods." IEEE TCIAIG.
  6. Rosin, C. D. (2011). "Multi-armed bandits with episode context." Annals of Mathematics and Artificial Intelligence.