Voltar aos trabalhos
No ar

NBA Basketball Prediction

Uma previsão que não dá para auditar é um palpite com uma porcentagem em cima.

58.84%

melhor taxa de acerto

632

partidas usadas no treino

9

métricas ponderadas do time

12.5-15k

gerações analisadas

Por que existe

Um Projeto Integrador: o trabalho de conclusão do Curso Técnico Integrado em Informática do IFSC câmpus Gaspar, escrito com três colegas e defendido em 2021. O tema veio de um mercado que crescia rápido no Brasil naquele momento e não sofria de falta de gente confiante — o de apostas esportivas. Todo mundo tinha um método. Ninguém publicava um.

Algoritmos genéticos eram a lacuna. Redes neurais e regressão já tinham sido apontadas para o basquete várias vezes; um vetor de pesos evoluído contra dados reais de partidas, não. E a pergunta que de fato nos interessava nunca foi se aquilo ganharia dinheiro. Era o que aquilo diria sobre o jogo: quais números do primeiro quarto de um time carregam o primeiro quarto, e o quanto.

O problema

Prever uma partida inteira da NBA é um problema disputado, com muito trabalho anterior. Prever só o primeiro quarto é mais estreito e mais duro — menos posses de bola, menos tempo para o time melhor se impor, mais ruído por minuto jogado.

A entrada é deliberadamente magra. De cada time, as médias dos seus próprios primeiros quartos anteriores: pontos, saldo, rebotes, porcentagens de acerto, turnovers, vitórias. Coletadas do Basketball Reference com Selenium e BeautifulSoup para um esquema SQLite de times, partidas e participações por time. Sem lesões, sem escalações, sem odds, sem dias de descanso. Se nove pesos sobre esses números conseguem superar o acaso, isso é uma afirmação sobre os próprios números.

Arquitetura

Um algoritmo genético, e em volta dele apenas o que alimenta um. Um indivíduo são nove pesos sorteados no intervalo [-100, 100], um por métrica. Seu fitness é a fração das partidas da janela de treino cujo primeiro quarto ele acerta. As gerações são elitistas — os melhores indivíduos seguem intactos, o resto é recombinado e sofre mutação — e é por isso que um log de fitness só sobe, e por isso que ele é uma escada, não uma curva.

A linha de comando dirige quatro verbos: coletar, treinar, prever e validar. O último é o que importa. Ele passa o algoritmo e dois controles pela mesma função de fitness, para que uma boa pontuação tenha contra o que ser boa.

Decisões que merecem explicação

  1. 01

    Não existe conjunto de teste separado, e esta página não vai fingir que existe. O fitness é medido nas mesmas 632 partidas contra as quais os pesos são treinados, então 58,84% é um número dentro da amostra. A única evidência fora da amostra que produzimos foi mais tosca e mais pública: previsões de partidas futuras publicadas em uma conta no Twitter conforme os jogos aconteciam.

  2. 02

    Os controles vieram antes das conclusões. Um vetor de pesos totalmente aleatório e um vetor constante de uns passam pela função de fitness idêntica, porque 58% sozinho é um número sem nada com que ser comparado.

  3. 03

    Os parâmetros foram calibrados um de cada vez contra uma configuração fixa, o que os trata como independentes quando eles claramente não são. O artigo diz isso em vez de apresentar a varredura como encerrada — e a única varredura que voltou plana foi relatada como inconclusiva, não maquiada de descoberta.

  4. 04

    Todo eixo de fitness começa em 50%. Um previsor abaixo do acaso não é um previsor ruim, é um previsor útil lido ao contrário; então 50% é o piso que carrega algum significado.

  5. 05

    O requisito pedia 70% de acerto. Nunca foi atingido, e permaneceu no documento como um requisito falhado em vez de ser silenciosamente reescrito para baixo, até o que de fato alcançamos.

Melhor fitness por geração

Duas execuções de treino versionadas no repositório, redesenhadas a partir dos próprios logs. As duas sobem rápido e param: a última melhoria da execução mais longa acontece na geração 405, e as mil gerações seguintes não produzem nada. Nenhuma das duas chega perto dos 70% que os requisitos pediam.

50%55%60%65%70%meta, não atingidaacaso0250500750100012501410geração
  • Execução A — 1.410 gerações, 56,41% a 59,06%
  • Execução B — 515 gerações, 59,06% a 60,47%

Melhor fitness: a porcentagem de primeiros quartos previstos corretamente.

Fonte: src/data/json/gen/teste1.json e teste2.json, amostrados a cada 5 gerações. O algoritmo é elitista, então o melhor fitness nunca cai e a linha é uma escada.

Taxa de acerto por método

Onde o algoritmo fica em relação aos trabalhos correlatos com que o artigo se compara, e em relação a um previsor aleatório. Com 58.84%, ele supera o acaso e a referência do futebol, e fica atrás de todos os métodos de basquete acima dele. As barras não são equivalentes e não devem ser lidas como se fossem: só este projeto prevê um primeiro quarto — os três acima preveem partidas inteiras, dois deles em outra liga.

  1. Regressão linear

    NBB, partida inteira · Michelon et al., 2017

    75%

  2. Rede neural

    NBB, partida inteira · Michelon et al., 2017

    74%

  3. Mineração de dados, aprendizado de máquina

    Playoffs da NBA, partida inteira · Fukano et al., 2018

    73.19%

  4. Este projeto — algoritmo genético

    NBA, primeiro quarto · 2021

    58.84%

  5. Gradient boosting

    Futebol, partida inteira · Fernandes, 2019

    52.78%

  6. Previsor aleatório

    Controle, 50% por construção

    50%

Fonte: o artigo, seção 4.3, figuras 18 e 19.

O vetor de pesos do melhor indivíduo

Os nove multiplicadores em que o cromossomo vencedor parou, aplicados às médias de primeiro quarto do próprio time. A magnitude é o quanto o algoritmo decidiu que a métrica importava; o sinal negativo significa que ele encontrou a métrica mais alta em times que acabam perdendo o quarto. Dois deles contrariam a intuição — a média de pontos do time recebe peso negativo — o que o artigo sinaliza em vez de explicar.

  1. won

    +98.4

  2. points

    -59.5

  3. spread

    +78.5

  4. offensive_rebounds

    +81.3

  5. defensive_rebounds

    +4.9

  6. field_goals_percentage

    -54.9

  7. three_point_field_goals_percentage

    +98.0

  8. turnover

    -36.2

  9. turnover (como impresso)

    +72.5

Fonte: o artigo, seção 5.1. Ele imprime turnover como rótulo dos dois últimos genes; reproduzido aqui como publicado, já que o esquema do banco tem apenas uma coluna com esse nome.

Varredura de calibração

Cinco parâmetros, cada um movido sozinho contra uma configuração fixa, cinco ciclos de cem gerações por valor — entre 12.500 e 15.000 gerações distintas no total. A última linha é a que vale ler: a fração de indivíduos gerados aleatoriamente não fez diferença mensurável em nenhum ponto entre 0 e 40, o que não é um resultado. É uma varredura que não resolveu.

Tabela dos parâmetros do algoritmo genético, os valores testados em cada um e o valor escolhido.
ParâmetroValores testadosEscolhido
mutation_chance1 · 5 · 15 · 30 · 60 · 9515
mutation_magnitude[0, 0] · [-1, 1] · [-10, 10] · [-100, 100] · [-1000, 1000][-1, 1]
population_size15 · 50 · 100 · 200 · 500500
persistent_individuals0 · 5 · 10 · 25 · 405
random_individuals0 · 5 · 10 · 25 · 400 / 25inconclusivo

Fonte: o artigo, seção 4.2, figuras 12 a 16.

Stack

Python
Genetic Algorithms
Selenium
SQLite
NumPy

Situação

Terminado e congelado. Trabalho acadêmico, defendido em 2021 no IFSC Gaspar por Bernardo Pires Mesko, Grégori Sabel, João Vitor Waldrich e Rafael Guilherme Onesko, sob orientação de Thiago Lipinski-Paes e Raphael Silvano Ferreira Silva. O código e o artigo são públicos; nenhum dos dois recebe manutenção.

O resumo honesto é que funcionou e não foi suficiente. 58,84% supera o acaso com folga e fica bem abaixo dos 70% que haviam sido pedidos. Todo número desta página vem do artigo ou de um log de treino versionado no repositório — e os que favorecem o projeto não estão aqui sem os que não favorecem.