“A vitória da inteligência artificial sobre os especialistas em futebol” - este poderia ser o título deste artigo sobre os resultados de uma competição de futebol. Poderia, mas, infelizmente, não.
Durante a Copa do Mundo, em nossa empresa "
NORBIT ", houve uma competição pela melhor previsão para jogos de futebol. Sou superficialmente versado no futebol para reivindicar qualquer coisa, mas o desejo de participar da competição ainda conquistou minha preguiça. Sob o corte - uma história sobre como, graças ao aprendizado de máquina, consegui alcançar bons resultados entre os especialistas em times de futebol. É verdade que não consegui ganhar o jackpot, mas descobri um novo mundo fascinante da ciência de dados.
Comecei com a hipótese de que, além das habilidades individuais dos jogadores das seleções nacionais, ainda existem fatores imensuráveis, mas importantes - espírito de equipe + trabalho em equipe (por exemplo, uma equipe em um jogo com um oponente mais forte, mas na partida de teste e em seu campo com mais frequência vence). A tarefa não é tão simples para uma pessoa, mas bastante compreensível para o aprendizado de máquina.
Certa vez, tive uma pequena experiência com ML (com a biblioteca BrainJS), mas desta vez decidi verificar a afirmação de que o Python é muito mais adequado para essas tarefas.
Comecei minha introdução ao Python com um excelente curso sobre o
Coursera e aprendi o básico do aprendizado de máquina com uma série de artigos da Open Data Science on
Habré .
Muito rapidamente encontrou um grande
conjunto de dados com a história de todos os jogos de equipes internacionais desde o início do século XX. Depois de importar para o dataframe do Pandas:
No total, o banco de dados contém informações sobre 39 mil jogos de equipes internacionais.
O Pandas torna muito conveniente analisar os dados, por exemplo, a correspondência mais produtiva foi entre a Austrália e a Samoa Americana em 2001, que terminou com uma pontuação de
31: 0 .
Agora você precisa adicionar uma avaliação objetiva do nível da equipe no ano da partida. Essas avaliações são realizadas pela FIFA.
Infelizmente, a classificação da FIFA é realizada apenas desde 1992. E, a julgar pelo cronograma, as classificações das equipes são altamente suscetíveis a mudanças, e eu realmente não gostaria de calcular a média das posições das equipes no ranking mundial até este ano.
A UEFA mantém suas estatísticas desde os tempos mais antigos, mas eu não consegui encontrar um conjunto de dados pronto, então este
site veio em socorro. No Node.js, existe um
Cheerio poderoso e conveniente para essas tarefas, mas no Python tudo ficou menos simples (perdoe-me o administrador deste site).
Classificação de raspagem da Webfrom requests import get from requests.exceptions import RequestException from contextlib import closing from bs4 import BeautifulSoup def query_url(url): try: with closing(get(url, stream=True)) as resp: if is_good_response(resp): return resp.content else: return None except RequestException as e: log_error('Error during requests to {0} : {1}'.format(url, str(e))) return None def is_good_response(resp): content_type = resp.headers['Content-Type'].lower() return (resp.status_code == 200 and content_type is not None and content_type.find('html') > -1) def log_error(e): print(e) def parse_ranks(raw_html, year): html = BeautifulSoup(raw_html, 'html.parser') ranks = [] for tr in html.select('tr'): tds = tr.select("td") if len(tds) == 10: rank = (year, tds[2].text, tds[7].text) ranks.append(rank) return ranks def get_url(year): if year in range(1960, 1999): method = 1 if year in range(1999, 2004): method = 2 if year in range(2004, 2009): method = 3 if year in range(2009, 2018): method = 4 if year in range(2018, 2019): method = 5 return f"https://kassiesa.home.xs4all.nl/bert/uefa/data/method{method}/crank{year}.html" ranks = [] for year in range(1960, 2019): url = get_url(year) print(url) raw_html = query_url(url) rank = parse_ranks(raw_html, year) ranks += rank with open('team_ranks.csv', 'w') as f: writer = csv.writer(f , lineterminator='\n') writer.writerow(['year', 'country', 'rank']) for rank in ranks: writer.writerow(rank)
Flutuações na classificação após a adição da classificação da UEFA (e uma pequena revisão dos nomes dos países de acordo com os resultados do jogo geopolítico):
Mas mesmo isso não foi sem um barril de alcatrão - a UEFA classifica apenas as equipes europeias (às vezes você precisa pensar no que está oculto sob abreviações comuns antes de usá-las). Felizmente, os playoffs se desenvolveram quase "europeus".
Ainda é um pouco mais conveniente dividir os resultados em jogos separados e adicionar classificações à mesa.
A parte mais interessante é o treinamento de modelos. O Google imediatamente sugeriu a opção mais simples e rápida - este é o MLPClassifier da biblioteca Python - Sklearn. Vamos tentar treinar um modelo usando o exemplo da Suécia.
from sklearn.neural_network import MLPClassifier games = pd.read_csv('games.csv')
Precisão: 0.62Não é muito mais preciso que o lançamento de moedas, mas provavelmente já é melhor do que minhas previsões "especializadas" em potencial. Seria razoável tentar enriquecer os dados, jogar com hiperparâmetros, mas eu decidi seguir o caminho inverso e experimentar a biblioteca de aumento de gradiente Yandex
Catboost . Por um lado, isso é mais patriótico, por outro lado, eles prometem um trabalho de qualidade com atributos categóricos, o que é confirmado por inúmeras
comparações .
Tomou as configurações do
exemplo :
Precisão: 0.73Já melhor, tente na prática.
def get_prediction(country, against): y = SwdenGames['score'] y = y.astype('int') X = SwdenGames.drop(['score', 'againstTitle'], axis=1) train_pool = Pool(X, y, cat_features=[1, 2, 4]) query = [ get_team_rank(country, 2018), 0, 1 if country == 'Russia' else 0, get_team_rank(against, 2018), against] return cb_model.predict_proba([query])[0] team_1 = 'Belgium' team_2 = 'France' result = get_prediction(team_1, team_2) if result[0] > result[1]: print(f" {team_1} {team_2} {result[0]*100:.1f}%") else: print(f" {team_1} {team_2} {result[1]*100:.1f}%")
Resultados da previsão para a final “Equipe Crotia perde para a equipe France com probabilidade de 93,7%”
Embora desta vez eu não tenha vencido o concurso
NORBIT , espero realmente que este artigo reduza o nível de mágica para alguém no uso prático do aprendizado de máquina, ou talvez até me motive a fazer meus próprios experimentos.