Como modelar probabilidades no futebol e evitar drift e overfitting

Como modelar probabilidades no futebol e evitar drift e overfitting

Quando eu vejo um supercomputador “cravar” favorito para uma final, eu não penso só no placar. Eu penso no pipeline: como esses modelos pegam estatísticas, transformam em probabilidade e como a gente replica essa lógica em sistemas reais — sem cair em armadilhas de validação, drift e overfitting. Segundo o OlharDigital.com.br, o supercomputador da Opta dá 56,05% para a Espanha contra 43,95% para a Argentina na final da Copa do Mundo de 2026. Legal como previsão; mais importante ainda como exemplo de engenharia de decisão.

O que a projeção da Opta realmente está medindo (além do “favorito”)

O número 56,05% vs. 43,95% não é só “achismo”. Em geral, modelos desse tipo tentam estimar P(vitória no tempo regulamentar) para cada time, usando sinais como: histórico recente, força relativa por posição, desempenho por adversário, eficiência ofensiva/defensiva e fatores situacionais.

O detalhe técnico que quase ninguém comenta: probabilidade não é igual a “confiança humana”. Probabilidade vem de um modelo treinado com dados passados e calibrado. Se a calibragem estiver ruim, o modelo até acerta o vencedor em vários cenários, mas erra quando você usa o “porcentual” para tomar decisões (como apostas, alocação de recursos, ou qualquer automação baseada em risco).

Por que “tempo regulamentar” importa para o modelo

O OlharDigital.com.br cita chances de vencer “nos 90 minutos”. Isso muda o tipo de pergunta que o modelo resolve. Modelos probabilísticos costumam inferir resultados via eventos (gols esperados/expected goals, finalizações, conversão, etc.). Ao condicionar ao tempo regulamentar, o modelo precisa estimar a chance de vitória antes de prorrogação.

Na prática, isso costuma exigir:

  • modelagem de gols ao longo do tempo (ou agregados por janela);
  • tratamento explícito de empate;
  • saídas calibradas para “win vs draw vs lose” em 90 minutos.

Comparando “supercomputador” com alternativas reais de modelagem

Eu já vi empresas usarem desde Poisson models simples até ensembles complexos. Em termos de engenharia, as alternativas mais comuns que você encontra (ou implementa) são:

1) Modelos estatísticos (ex.: Poisson/Skellam, xG agregados)

Prós: rápidos, interpretáveis, fáceis de recalibrar. Contras: frágeis para mudanças de contexto (táticas novas, coping de estilo, jogadores que mudam muito a dinâmica).

2) Machine Learning supervisionado

Prós: pega não-linearidades e sinais heterogêneos. Contras: sem validação temporal correta, você cria modelos “bonitos” que quebram em produção.

3) Deep Learning / modelos temporais

Prós: capturam relações complexas. Contras: exigem dados consistentes, pipeline forte e cuidado extra com drift.

O ponto: o número que aparece na notícia é a “última milha” de um sistema. O que separa um modelo útil de um modelo que só “diverge bonito” é engenharia: treino correto no tempo, calibração e monitoramento.

Contexto técnico que a notícia não traz (e que devs precisam saber)

Quando você transforma esporte em probabilidade, você está criando um serviço de decisão. Isso tem implicações diretas no seu dia a dia como dev/engenheiro.

Validação temporal (o erro nº 1)

O maior erro que eu vejo devs cometendo em modelos preditivos é usar cross-validation aleatória. Em dados esportivos, isso vaza informação porque temporadas e forma dos times mudam. O correto é validação por tempo (walk-forward), ou ao menos split temporal.

Calibração de probabilidades (o erro nº 2)

Muita gente só mede acurácia. Mas aqui o que importa é a probabilidade. Se o modelo retorna 56% mas, na realidade, vence só 48% dos casos, você está “enganando” quem consome a saída.

Como corrigir? Platt scaling, isotonic regression, ou ajustar via métodos de calibração em cima de logits/score do modelo.

Feature leakage (o erro nº 3)

Em esportes, dá para vazar: por exemplo, usar estatísticas que dependem do resultado agregado do jogo que você quer prever. O modelo aprende atalhos e perde generalização.

Na Prática: como transformar sinais em probabilidade sem cair em armadilhas

Vou te passar um fluxo prático que eu usaria para construir algo equivalente (mesmo que não use a mesma base da Opta). A ideia é: estimar probabilidade de vitória no tempo regulamentar com calibração.

Passo a passo

  1. Defina o alvo (y): por exemplo, {home win, draw, away win} em 90 minutos.
  2. Monte features: força recente, desempenho contra adversários similares, métricas ofensivas/defensivas, incerteza por lineup (quando aplicável).
  3. Separe por tempo: treino em janelas anteriores, teste em janelas posteriores.
  4. Treine um classificador: regressão logística multinomial, Gradient Boosting, ou ensemble.
  5. Calibre: ajuste para probabilidades confiáveis (não só para classificar).
  6. Monitore drift: estilo tático muda, escala de dados muda, jogadores mudam. Atualize o modelo com frequência ou use online learning.

Exemplo de código funcional: calibração simples com scikit-learn

Esse exemplo não “adivinha” Copa do Mundo, mas mostra o padrão que devs usam para tornar a probabilidade confiável. Se você troca a calibração, seu “56,05%” pode se tornar algo bem mais útil.

import numpy as np
from sklearn.model_selection import TimeSeriesSplit
from sklearn.linear_model import LogisticRegression
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import log_loss, brier_score_loss

# X: features (numérica), y: 0=home win, 1=draw, 2=away win
# Aqui é só estrutura; no seu projeto você carrega do dataset real.
X = np.random.randn(5000, 20)
y = np.random.randint(0, 3, size=5000)

tscv = TimeSeriesSplit(n_splits=5)

base_model = LogisticRegression(max_iter=2000, multi_class="multinomial")

# Calibração: 'isotonic' costuma ser forte quando você tem dados suficientes
calibrated = CalibratedClassifierCV(
    estimator=base_model,
    method="isotonic",
    cv=3
)

for fold, (train_idx, test_idx) in enumerate(tscv.split(X)):
    X_train, X_test = X[train_idx], X[test_idx]
    y_train, y_test = y[train_idx], y[test_idx]

    calibrated.fit(X_train, y_train)
    proba = calibrated.predict_proba(X_test)

    ll = log_loss(y_test, proba, labels=[0,1,2])
    # Brier para multinomial pode ser implementado de várias formas;
    # aqui a métrica é ilustrativa em termos de saída probabilística.
    brier = brier_score_loss(
        y_test == 0, proba[:,0]  # exemplo: prob vitória do time da casa
    )

    print(f"Fold {fold}: log_loss={ll:.4f}, brier(win_home)={brier:.4f}")

Por que isso importa? Porque o consumo de probabilidade é sensível. Se você só maximiza log loss sem calibrar, o score pode estar “desalinhado”. Calibração tenta fazer com que eventos com 0,56 de probabilidade realmente ocorram perto disso na frequência real.

O que os devs podem aprender com essa previsão (e como aplicar em produto)

Mesmo que você não faça previsão esportiva, o padrão é o mesmo quando seu produto decide com risco: recomendação, scoring de crédito, detecção de falhas, previsão de demanda.

1) Probabilidade é um contrato

Quando eu vejo “56,05%”, eu trato como contrato entre sistema e usuário. Se o sistema diz probabilidades, ele precisa ser previsível estatisticamente. Sem calibração e validação temporal, você quebra o contrato.

2) Condicionantes mudam tudo

“Vencer no tempo regulamentar” é condicionante forte. Em outros domínios, isso vira “resolver em até X horas” ou “sem escalonar para tier 2”. Modelar sem respeitar essa condição faz seu sistema errar exatamente onde você mede sucesso.

3) Drift é inevitável

Times mudam de técnico, escalação e estilo. No software, muda versão, muda tráfego, muda dataset. Se você não monitora drift, o seu modelo vira ruído com API “bonita”.

Erros Comuns (o que evitar quando você for construir algo parecido)

Confundir “acertar o vencedor” com “probabilidade útil”

Você pode ter boa taxa de acerto e probabilidades ruins. Para automação, isso é perigoso. O time de produto vai agir com base numa confiança que não existe.

Treinar com split aleatório

Em dados temporais, split aleatório cria vazamento. Você mede desempenho “de laboratório”, não desempenho real.

Ignorar calibração

Sem calibração, métricas como AUC podem subir, mas a saída percentual pode estar enviesada. E quando alguém usa seu percentual para decisão, você perde credibilidade.

Features sem causalidade (atalhos)

Se uma feature é um proxy do resultado, o modelo aprende o atalho. Em produção, o atalho falha e você vê degradação.

Não versionar pipeline de dados

Em previsão, “mudar um detalhe” no pré-processamento muda o modelo. Sem versionamento e rastreabilidade, você não sabe por que o “56,05%” virou “49,12%”.

Detalhes do evento que ajudam a pensar em cenários (e no consumo do modelo)

Segundo o OlharDigital.com.br, a final será no MetLife Stadium, com capacidade de até 82.500 torcedores, e o estádio recebeu nome temporário ligado a New York–New Jersey. Por que isso importa para o dev?

Porque previsões que incorporam ambiente (ex.: torcida, altitude, clima) precisam de dados. E quando não há dados confiáveis, você não “inventar feature”. Ou o modelo assume correlação espúria, ou você deixa a feature sem efeito — e a expectativa do usuário vira frustração.

FAQ

O supercomputador da Opta “entende” de futebol ou só calcula probabilidades?

Ele calcula probabilidades a partir de dados e modelos. O “entendimento” vem do conjunto de sinais e do treinamento. O que entrega utilidade é validação temporal e calibração — não a narrativa.

Por que a manchete fala em 56,05% e 43,95% em vez de só dizer “Espanha é favorita”?

Porque probabilidades permitem comparar incerteza. A manchete escolhe um número para dar granularidade. Mas esse número só é confiável se o modelo for calibrado e avaliado corretamente.

Como eu avalio se meu modelo de probabilidade é bom, além de acurácia?

Use log_loss, métricas de calibração (Reliability Diagram), Brier score e validação temporal. Se o modelo “classifica bem” mas tem calibração ruim, ele é ruim para decisão baseada em risco.

“Vencer no tempo regulamentar” é diferente de “vencer na partida toda”. Como isso afeta o modelo?

Porque muda o alvo e a distribuição. Empates viram classe diferente (ou são tratados como “não venceu no tempo regulamentar”). Você precisa modelar o desfecho condicionado corretamente.

O que eu faço quando o modelo começa a divergir após algumas partidas?

Verifique drift, reavalie calibração, compare distribuição de features e revise o pipeline. Se possível, re-treine com janela mais recente ou aplique métodos de atualização incremental.

Gostou? Me segue no GitHub e deixa um comentário se tiver dúvida ou quiser aprofundar algum ponto.

Y

Yuri Sousa

Front-End Developer / Designer

Desenvolvedor apaixonado por criar experiências digitais acessíveis e visualmente perfeitas. Escrevo sobre desenvolvimento web, design e tecnologia.