L'intelligence artificielle peut-elle laisser les bookmakers sans travail?

«La victoire de l'intelligence artificielle sur les experts du football» - tel pourrait être le titre de cet article sur les résultats d'une compétition de football. Pourrait, mais, hélas, non.

Pendant la Coupe du monde, dans notre société " NORBIT " a organisé un concours pour les meilleurs matchs de prévision pour le football. Je suis trop superficiellement versé dans le football pour prétendre à quoi que ce soit, mais le désir de participer à la compétition a quand même séduit ma paresse. Under the cut - une histoire sur la façon dont, grâce à l'apprentissage automatique, j'ai réussi à obtenir de bons résultats parmi les experts des équipes de football. Certes, je n'ai pas réussi à décrocher le jackpot, mais j'ai découvert un nouveau monde fascinant de Data Science.


J'ai commencé avec l'hypothèse que, en plus des compétences individuelles des joueurs de l'équipe nationale, il y a encore des facteurs incommensurables mais importants - l'esprit d'équipe + le travail d'équipe (par exemple, une équipe dans un match avec un adversaire plus fort, mais dans le match test et sur son terrain gagne plus souvent). La tâche n'est pas si simple pour une personne, mais tout à fait compréhensible pour l'apprentissage automatique.

J'ai eu un peu d'expérience avec ML (avec la bibliothèque BrainJS), mais cette fois j'ai décidé de vérifier que Python est bien mieux adapté à de telles tâches.

J'ai commencé mon introduction à Python par un excellent cours sur Coursera , et j'ai appris les bases de l'apprentissage automatique à partir d'une série d'articles d'Open Data Science sur Habré .

Assez rapidement trouvé un grand Dataset avec l'histoire de tous les jeux d'équipes internationales du début du XXe siècle. Après l'importation dans la trame de données Pandas:


Au total, la base de données contient des informations sur 39 000 matchs d'équipes internationales.

Pandas facilite l'analyse des données, par exemple, la correspondance la plus productive a été entre l'Australie et les Samoa américaines en 2001, qui s'est terminée par un score de 31: 0 .




Vous devez maintenant ajouter une évaluation objective du niveau de l'équipe l'année du match. Ces évaluations sont gérées par la FIFA.



Mais, malheureusement, le classement FIFA n'a été effectué que depuis 1992. Et, à en juger par le calendrier, les notes des équipes sont très sensibles aux changements, et je ne voudrais vraiment pas faire la moyenne des positions des équipes dans le classement mondial avant cette année.

L'UEFA conserve ses statistiques d'une époque plus ancienne, mais je n'ai pas pu trouver un ensemble de données prêt, donc ce site est venu à la rescousse. Sous Node.js, il y a un Cheerio puissant et pratique pour de telles tâches, mais sous Python, tout s'est avéré non moins simple (pardonnez-moi l'administrateur de ce site).

Classement du scraping Web
from 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) 


Fluctuations de la note après l'ajout de la note de l'UEFA (et une petite révision des noms des pays en fonction des résultats du roque géopolitique):


Mais même cela n'était pas sans baril de goudron - l'UEFA ne classe que les équipes européennes (il faut parfois réfléchir à ce qui est caché sous les abréviations courantes avant de les utiliser). Heureusement, les éliminatoires se sont développées presque «européennes».

Il reste un peu plus pratique de diviser les résultats en jeux séparés et d'ajouter des notes au tableau.

La partie la plus intéressante est la formation des modèles. Google a immédiatement suggéré l'option la plus simple et la plus rapide - il s'agit du MLPClassifier de la bibliothèque Python - Sklearn. Essayons de former un modèle en utilisant l'exemple de la Suède.

 from sklearn.neural_network import MLPClassifier games = pd.read_csv('games.csv') #    SwedenGames = games[(games.teamTitle == 'Sweden')] #   y = SwedenGames['score'] y = y.astype('int') #   X = SwedenGames.drop(['score', 'teamTitle', 'againstTitle'], axis=1) #       X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25) mlp = MLPClassifier() mlp.fit(X_train, y_train); predictions = mlp.predict(X_test) print('Accuracy: {:.2}'.format(   accuracy_score(y_test, mlp.predict(X_test)) )) 

Précision: 0,62

Pas beaucoup plus précis que le lancer de pièces, mais probablement déjà meilleur que mes prévisions "expertes" potentielles. Il serait raisonnable d'essayer d'enrichir les données, de jouer avec des hyperparamètres, mais j'ai décidé d'aller dans l'autre sens et d'essayer la bibliothèque de boosting Yandex gradient Catboost . D'une part, c'est plus patriotique, d'autre part, ils promettent un travail de qualité avec des attributs catégoriels, ce qui est confirmé par de nombreuses comparaisons .

Pris les paramètres de l' exemple :

 #     categorical_features_indices = [1, 2, 4] train_pool = Pool(X_train,  y_train, cat_features=categorical_features_indices) validate_pool = Pool(X_test, y_test, cat_features=categorical_features_indices) #      ,      GridSearchCV.   best_params = {   'iterations': 500,   'depth': 10,   'learning_rate': 0.1,   'l2_leaf_reg': 1,   'eval_metric': 'Accuracy',   'random_seed': 42,   'logging_level': 'Silent',   'use_best_model': True } cb_model = CatBoostClassifier(**best_params) cb_model.fit(train_pool, eval_set=validate_pool) print('Accuracy: {:.2}'.format(   accuracy_score(y_test, cb_model.predict(X_test)) )) 

Précision: 0,73

Déjà mieux, essayez en pratique.

 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}%") 


Résultats de prédiction pour la finale «L'équipe de Crotia perd face à l'équipe de France avec une probabilité de 93,7%»

Bien que cette fois je n'ai pas gagné le concours NORBIT , j'espère vraiment que cet article réduira le niveau de magie pour quelqu'un dans l'utilisation pratique de l'apprentissage automatique, ou peut-être même me motivera à faire mes propres expériences.

Source: https://habr.com/ru/post/fr427273/


All Articles