Pular para o conteúdo principal

Fase de Aprendizado Supervisionado

Antes que o AlphaGo pudesse jogar contra si mesmo, ele precisou primeiro "assistir" a uma grande quantidade de partidas humanas. Este processo é chamado de Aprendizado Supervisionado.

Ao analisar 30 milhões de posições de partidas humanas, a Policy Network do AlphaGo alcançou 57% de precisão de previsão — capaz de adivinhar o próximo movimento do especialista humano em mais da metade dos casos.

Isso pode não parecer impressionante, mas considerando que cada posição tem em média 250 movimentos legais, é uma conquista notável.


Por que começar com partidas humanas?

O ponto de partida do aprendizado

Imagine que você precisa ensinar alguém que não sabe absolutamente nada sobre Go. Como você faria?

Opção A: Exploração aleatória

Deixá-lo jogar aleatoriamente, descobrindo gradualmente o que são boas jogadas
→ Eficiência extremamente baixa, pode nunca aprender

Opção B: Observar mestres jogando

Deixá-lo assistir um grande número de partidas de jogadores profissionais, imitando suas jogadas
→ Após ter uma base, então explorar por conta própria

AlphaGo escolheu a opção B. Aprendizado supervisionado é a versão matemática de "observar mestres jogando".

O valor das partidas humanas

Humanos passaram milhares de anos desenvolvendo a teoria do Go. Todo esse conhecimento está codificado nas partidas:

  • Joseki de abertura: jogadas de abertura verificadas ao longo do tempo
  • Táticas de meio de jogo: sabedoria de ataque e defesa
  • Técnicas de endgame: essência do cálculo de pontos
  • Visão global: intuição de julgamento geral

O aprendizado supervisionado permite que o AlphaGo "herde" essa sabedoria humana, sem precisar começar do zero.


Fonte dos dados de treinamento

KGS Go Server

Os dados de treinamento do AlphaGo vieram principalmente do KGS Go Server (também conhecido como Kiseido Go Server), uma plataforma de Go online bem conhecida.

Características do KGS

CaracterísticaDescrição
UsuáriosPrincipalmente amadores, também profissionais
Faixa de nívelDe iniciante a 9 dan profissional
Registros de partidasPartidas completas salvas em SGF
Período ativo2000 até hoje

Por que escolher KGS?

  1. Grande volume de dados: milhões de partidas
  2. Formato unificado: formato SGF fácil de analisar
  3. Etiquetas de nível: cada usuário tem classificação
  4. Diversidade: jogadores de diferentes estilos

30 milhões de posições

Das partidas do KGS, a DeepMind extraiu aproximadamente 30 milhões de posições:

Dados brutos:
- Cerca de 160.000 partidas
- Cada partida tem cerca de 200 movimentos
- Total ~32 milhões de posições

Filtragem de dados:
- Filtrar partidas de baixo nível
- Filtrar posições de desistência no meio do jogo
- Aproximadamente 30 milhões de posições de alta qualidade no final

Formato dos dados

Cada amostra de treinamento contém:

{
"board_state": [[0, 1, 2, ...], ...], # Tabuleiro 19×19
"features": [...], # 48 planos de características
"next_move": 123, # Posição jogada pelo humano (0-360)
"game_result": 1, # 1=preto vence, -1=branco vence
"player_rank": "5d", # Nível do jogador que fez o movimento
}

Pré-processamento de dados

Análise de SGF

SGF (Smart Game Format) é o formato padrão para partidas de Go:

(;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2]
RU[Japanese]SZ[19]KM[6.50]
PW[White]PB[Black]
;B[pd];W[dd];B[pq];W[dp];B[qk];W[nc]...
)

Precisa extrair:

  • Tamanho do tabuleiro (SZ[19])
  • Cada movimento (B[pd], W[dd]...)
  • Resultado da partida (RE[B+2.5])
def parse_sgf(sgf_string):
"""Analisar partida SGF"""
moves = []
# Extrair todos os movimentos
pattern = r';([BW])\[([a-s]{2})\]'
for match in re.finditer(pattern, sgf_string):
color = match.group(1) # 'B' or 'W'
coord = match.group(2) # 'pd', 'dd', etc.

# Converter coordenadas
x = ord(coord[0]) - ord('a')
y = ord(coord[1]) - ord('a')

moves.append((color, x, y))

return moves

Extração de características

Para cada posição, extrair 48 planos de características (veja Design de características de entrada):

def extract_features(board, history, current_player):
"""Extrair 48 planos de características"""
features = np.zeros((48, 19, 19))

# Posição das pedras
features[0] = (board == 1) # Pedras pretas
features[1] = (board == 2) # Pedras brancas
features[2] = (board == 0) # Pontos vazios

# Histórico
for i, hist in enumerate(history[:8]):
features[3+i] = (hist == 1)
features[11+i] = (hist == 2)

# Liberdades, atari, escada, etc...
# (implementação detalhada omitida)

return features

Aumento de dados

O tabuleiro de Go tem 8 simetrias (4 rotações × 2 reflexões). Cada amostra original pode se tornar 8:

Cada um espelhado horizontalmente, obtendo 8 amostras de treinamento equivalentes

Isso aumenta os dados de treinamento efetivos em 8 vezes, garantindo que os padrões aprendidos pelo modelo não dependam de uma direção específica.

def augment(state, action):
"""Aumento de 8 simetrias"""
augmented = []

for rotation in [0, 1, 2, 3]: # 0, 90, 180, 270 graus
rotated_state = np.rot90(state, rotation, axes=(1, 2))
rotated_action = rotate_action(action, rotation)
augmented.append((rotated_state, rotated_action))

# Espelho horizontal
flipped_state = np.flip(rotated_state, axis=2)
flipped_action = flip_action(rotated_action)
augmented.append((flipped_state, flipped_action))

return augmented

Função de perda

Perda de entropia cruzada

O aprendizado supervisionado usa Perda de Entropia Cruzada (Cross-Entropy Loss) para treinar a Policy Network:

L(θ) = -Σ log p_θ(a | s)

Onde:

  • s: estado do tabuleiro
  • a: posição real jogada pelo humano (rótulo)
  • p_θ(a | s): probabilidade prevista pelo modelo para essa posição

Compreensão intuitiva

A perda de entropia cruzada mede "a diferença entre a previsão do modelo e o rótulo":

CenárioPrevisão do modeloPerdaDescrição
Previsão perfeitaprobabilidade de a = 1.00Melhor
Confiante e corretoprobabilidade de a = 0.90.1Muito bom
Incerto mas corretoprobabilidade de a = 0.50.7Razoável
Previsão erradaprobabilidade de a = 0.12.3Muito ruim
Completamente erradoprobabilidade de a = 0.014.6Pior

A função de perda impulsiona o modelo a aumentar a probabilidade da posição correta.

Comparação com MSE

Por que não usar Erro Quadrático Médio (MSE)?

# MSE:
loss_mse = (prediction - target)^2

# Cross-Entropy:
loss_ce = -log(prediction[target])
CaracterísticaMSECross-Entropy
Tipo de objetivoRegressão (valor contínuo)Classificação (distribuição de probabilidade)
Comportamento do gradienteErro maior, gradiente maiorErro confiante, gradiente maior
Cenário adequadoValue NetworkPolicy Network

A Policy Network produz uma distribuição de probabilidade de 361 classes, entropia cruzada é a escolha natural.


Processo de treinamento

Configuração de hardware

A DeepMind usou recursos computacionais extensivos:

RecursoQuantidade
GPU50
Tempo de treinamentoCerca de 3 semanas
Tamanho do lote16
Total de passos de treinamento~340M

Otimizador

Usando Descida de Gradiente Estocástico (SGD) + momentum:

optimizer = torch.optim.SGD(
model.parameters(),
lr=0.003, # Taxa de aprendizado inicial
momentum=0.9, # Coeficiente de momentum
weight_decay=1e-4 # Regularização L2
)

Por que SGD em vez de Adam?

Em 2016, SGD + momentum ainda era a escolha principal para tarefas de imagem. Na verdade, pesquisas posteriores (incluindo KataGo) descobriram que otimizadores do tipo Adam podem ser melhores.

Agendamento da taxa de aprendizado

A taxa de aprendizado decai gradualmente durante o treinamento:

scheduler = torch.optim.lr_scheduler.StepLR(
optimizer,
step_size=80_000_000, # A cada 80M passos
gamma=0.1 # Taxa de aprendizado multiplicada por 0.1
)

Loop de treinamento

def train_epoch(model, dataloader, optimizer):
model.train()
total_loss = 0
correct = 0
total = 0

for batch in dataloader:
states, actions = batch

# Forward pass
policy = model(states) # (batch, 361)

# Calcular perda
loss = F.cross_entropy(policy, actions)

# Backward pass
optimizer.zero_grad()
loss.backward()
optimizer.step()

# Estatísticas
total_loss += loss.item()
predictions = policy.argmax(dim=1)
correct += (predictions == actions).sum().item()
total += actions.size(0)

accuracy = correct / total
avg_loss = total_loss / len(dataloader)

return avg_loss, accuracy

Curva de treinamento

Processo de treinamento típico:

Perda e precisão melhoram rapidamente, depois estabilizam.


Análise de resultados

57% de precisão

Após treinamento completo, a Policy Network alcançou 57.0% de precisão top-1.

O que é precisão top-1?

Previsão: modelo produz 361 probabilidades
Top-1: posição com maior probabilidade
Precisão: proporção em que esta posição é igual à posição realmente jogada pelo humano

57% significa: o modelo tem mais da metade de chance de adivinhar o próximo movimento do especialista humano.

Comparação com outros programas

ProgramaPrecisão Top-1Descrição
Seleção aleatória0.4%Linha de base
Características tradicionais + modelo linear~24%Nível de 2008
CNN rasa~44%Nível de 2014
AlphaGo Policy Network57%Avanço de 2016
AlphaGo Zero~60%2017

A CNN profunda da DeepMind melhorou 13 pontos percentuais em relação ao melhor método anterior.

Avaliação de força de jogo

Força de jogo usando apenas a Policy Network (sem busca):

載入中...
ConfiguraçãoClassificação EloNível aproximado
Melhor tradicional (Pachi)~2500Amador 4-5 dan
SL Policy Network~2800Amador 6-7 dan

Aprendizado supervisionado puro já alcançou nível amador alto, isso foi um grande avanço em 2016.

Precisão vs força de jogo

Interessantemente, precisão e força de jogo não têm relação linear:

Precisão:  44% → 57% (melhoria de 13%)
Elo: ~2500 → ~2800 (melhoria de ~300)

Proporção de melhoria de precisão: 13% / 44% ≈ 30%
Proporção de melhoria de Elo: 300 / 2500 ≈ 12%

Pequenas melhorias na precisão podem trazer melhorias significativas na força de jogo, porque:

  • Escolhas corretas em posições críticas são mais importantes
  • Evitar erros óbvios é mais importante do que jogar muitas boas jogadas

Limitações do aprendizado supervisionado

Problema 1: Efeito teto

O aprendizado supervisionado só pode alcançar "nível humano", não pode ultrapassar:

Objetivo da SL Policy: imitar humanos

Se humanos têm hábitos errados

SL Policy também aprenderá esses erros

Por exemplo, se os jogadores nos dados de treinamento raramente jogam jogadas não-tradicionais como "Movimento 37", a SL Policy também não aprenderá.

Problema 2: Não consegue distinguir boas e más jogadas

O aprendizado supervisionado só olha "o que o humano jogou", não se a jogada foi boa:

Posição A: humano jogou K10 (na verdade é jogada ruim)
Posição B: humano jogou Q4 (boa jogada)

SL Policy trata igualmente, deve aprender ambas

Os dados de treinamento incluem partidas de amadores, que contêm muitos erros. SL Policy aprenderá esses erros.

Problema 3: Exploração insuficiente

SL Policy só aprende jogadas já conhecidas pelos humanos:

Conjunto de jogadas humanas: {A, B, C, D, E}

SL Policy só escolherá entre essas jogadas

Pode existir jogada melhor F, mas nunca foi descoberta

Esta é a limitação fundamental do aprendizado supervisionado: só pode aprender o que existe nos dados de treinamento.

Solução: Aprendizado por reforço

Para superar humanos, AlphaGo realizou aprendizado por reforço após o aprendizado supervisionado:

SL Policy (nível humano)
↓ Autopartida
RL Policy (supera humanos)

Veja Introdução ao Aprendizado por Reforço e Autopartida para detalhes.


Pontos de implementação

Código de treinamento completo

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset

class GoDataset(Dataset):
def __init__(self, data_path):
# Carregar dados pré-processados
self.states = np.load(f"{data_path}/states.npy")
self.actions = np.load(f"{data_path}/actions.npy")

def __len__(self):
return len(self.states)

def __getitem__(self, idx):
state = torch.FloatTensor(self.states[idx])
action = torch.LongTensor([self.actions[idx]])[0]
return state, action

def train_policy_network():
# Modelo
model = PolicyNetwork(input_channels=48, num_filters=192, num_layers=12)
model = model.cuda()

# Dados
dataset = GoDataset("data/kgs")
dataloader = DataLoader(
dataset, batch_size=16, shuffle=True, num_workers=4
)

# Otimizador
optimizer = optim.SGD(
model.parameters(),
lr=0.003,
momentum=0.9,
weight_decay=1e-4
)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=80_000_000, gamma=0.1)

# Loop de treinamento
best_accuracy = 0

for epoch in range(100):
model.train()
total_loss = 0
correct = 0
total = 0

for states, actions in dataloader:
states = states.cuda()
actions = actions.cuda()

# Forward pass
policy = model(states)
loss = nn.functional.cross_entropy(policy, actions)

# Backward pass
optimizer.zero_grad()
loss.backward()
optimizer.step()
scheduler.step()

# Estatísticas
total_loss += loss.item()
predictions = policy.argmax(dim=1)
correct += (predictions == actions).sum().item()
total += actions.size(0)

accuracy = correct / total
print(f"Epoch {epoch}: Loss={total_loss/len(dataloader):.4f}, Acc={accuracy:.4f}")

# Salvar melhor modelo
if accuracy > best_accuracy:
best_accuracy = accuracy
torch.save(model.state_dict(), "best_policy.pth")

print(f"Best accuracy: {best_accuracy:.4f}")

Código de avaliação

def evaluate_policy(model, test_dataloader):
model.eval()

correct_top1 = 0
correct_top5 = 0
total = 0

with torch.no_grad():
for states, actions in test_dataloader:
states = states.cuda()
actions = actions.cuda()

policy = model(states)

# Precisão Top-1
top1_pred = policy.argmax(dim=1)
correct_top1 += (top1_pred == actions).sum().item()

# Precisão Top-5
top5_pred = policy.topk(5, dim=1)[1]
for i, action in enumerate(actions):
if action in top5_pred[i]:
correct_top5 += 1

total += actions.size(0)

print(f"Top-1 Accuracy: {correct_top1/total:.4f}")
print(f"Top-5 Accuracy: {correct_top5/total:.4f}")

Problemas comuns e soluções

ProblemaSintomaSolução
OverfittingAlta precisão de treinamento, baixa precisão de testeAumentar aumento de dados, Dropout
Treinamento instávelPerda oscila muitoDiminuir taxa de aprendizado, aumentar tamanho do lote
Convergência lentaPrecisão estagnadaAjustar taxa de aprendizado, verificar dados
Memória insuficienteErro OOMDiminuir tamanho do lote, usar precisão mista

Correspondência de animações

Os conceitos centrais abordados neste artigo e números de animação:

NúmeroConceitoCorrespondência física/matemática
D3Aprendizado supervisionadoEstimação de máxima verossimilhança
D5Perda de entropia cruzadaDivergência KL
D6Descida de gradienteOtimização
A6Pré-processamento de dadosNormalização

Leitura adicional


Pontos-chave

  1. Partidas do KGS são a fonte de dados de treinamento: cerca de 30 milhões de posições de alta qualidade
  2. Perda de entropia cruzada impulsiona o aprendizado: faz o modelo aumentar a probabilidade da posição correta
  3. 57% de precisão é um grande avanço: superando o melhor método anterior em 13 pontos percentuais
  4. Aumento de 8 simetrias: aumenta efetivamente os dados de treinamento
  5. Aprendizado supervisionado tem teto: não pode superar o nível dos dados de treinamento

O aprendizado supervisionado é o "ponto de partida" do AlphaGo — ele herdou milhares de anos de sabedoria humana do Go, estabelecendo a base para o subsequente aprendizado por reforço.


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. Maddison, C. J., et al. (2014). "Move Evaluation in Go Using Deep Convolutional Neural Networks." arXiv:1412.6564.
  3. Clark, C., & Storkey, A. (2015). "Training Deep Convolutional Neural Networks to Play Go." ICML.
  4. KGS Game Archives: https://www.gokgs.com/archives.jsp