A pergunta do TCC: modelos Random Forest especializados por posição (goleiro, defensor, meio-campista, atacante) preveem a nota de um jogador no Sofascore melhor do que um único modelo genérico?
A primeira versão alimentava cada modelo com as estatísticas do jogador na própria partida: finalizações, passes, duelos, desarmes, toques, 63 colunas no total. A validação era limpa. Treino nas rodadas 1 a 30, teste nas 31 a 38, hiperparâmetros ajustados com GroupKFold agrupado por partida, pra que jogadores do mesmo jogo nunca caíssem dos dois lados de um fold. Uma baseline que só prevê a nota média da posição definia o piso.
| Modelo | MAE | R² |
|---|---|---|
| Média da posição | 0,391 | |
| Genérico | 0,203 | 0,72 |
| Especialistas | 0,207 | 0,72 |
Os atacantes chegaram a R² 0,80. Cortar pela metade o erro da baseline fica ótimo num TCC. Só que a conta não fechava: as estatísticas de uma única partida não deveriam explicar tanto assim de nada no futebol.
Não era vazamento. Era circularidade.
O primeiro suspeito foi vazamento entre treino e teste. Não havia nenhum. A divisão e os folds mantinham cada partida inteira, e nenhuma linha de teste encostou no treino.
O problema era o alvo. A nota do Sofascore sai de uma fórmula proprietária alimentada por estatísticas muito parecidas com aquelas 63 features: gols, passes, duelos, desarmes. O Random Forest não estava aprendendo o que é uma boa atuação. Estava aproximando a fórmula que gerou o número. O R² alto media o quanto o modelo imita o algoritmo do Sofascore, e nada mais.
Esse experimento ainda respondeu uma pergunta mais estreita (com as estatísticas da partida em mãos, os especialistas só ganharam nos goleiros, a posição estatisticamente mais distinta). Mas não respondia a que importa: dá pra saber a nota antes do jogo?
O redesenho
O segundo experimento proíbe qualquer estatística da partida que está sendo prevista. Cada feature tem que existir antes do apito inicial:
- mandante ou visitante;
- força do adversário: gols marcados e sofridos pelo adversário em partidas anteriores;
- o histórico do próprio jogador: médias da temporada de 61 estatísticas, mais médias da temporada e dos últimos 5 jogos de nota e minutos, todas calculadas só com partidas estritamente anteriores a esta.
O "estritamente anteriores" é o motivo de a ordem das rodadas em Uma temporada de dados do Sofascore, e onde eles mentem ter deixado de ser detalhe. Jogadores ainda sem histórico (14,9% deles jogam 3 partidas ou menos na temporada inteira) recebem NaN, que o Random Forest do scikit-learn trata nativamente desde a versão 1.4. Junto entra uma contagem de partidas anteriores, pro modelo aprender a confiar mais em 20 jogos de histórico do que em 1.
A validação passou a ser walk-forward: treinar com tudo antes de um lote de 10 partidas, prever esse lote, somá-lo ao treino e seguir em frente, ao longo das últimas 80 partidas da temporada. A busca de hiperparâmetros usa folds em ordem temporal dentro da janela de treino, sempre validando em partidas posteriores às de treino.
O R² caiu pra 0,10. Esse é o resultado esperado, não uma piora. Explicar uma nota que já aconteceu, a partir de estatísticas próximas da própria fórmula, é uma tarefa. Prever a nota antes do jogo é outra, bem mais difícil. A maior parte da nota de uma partida não dá pra prever conta o que saiu.