Kann künstliche Intelligenz Buchmacher ohne Arbeit lassen?

    „Der Sieg der künstlichen Intelligenz über Fußballexperten“ - so könnte der Titel dieses Artikels über die Ergebnisse eines Fußballwettbewerbs lauten. Konnte aber leider nicht.

    Während der Weltmeisterschaft gab es in unserer Firma " NORBIT " einen Wettbewerb um die beste Prognose für Fußballspiele. Ich bin zu oberflächlich mit Fußball vertraut, um etwas zu behaupten, aber der Wunsch, an dem Wettbewerb teilzunehmen, hat mich immer noch überzeugt. Unter dem Strich - eine Geschichte darüber, wie ich dank maschinellem Lernen gute Ergebnisse bei Experten von Fußballmannschaften erzielen konnte. Es stimmt, ich habe den Jackpot nicht geknackt, aber ich habe eine neue faszinierende Welt der Data Science entdeckt.


    Ich begann mit der Hypothese, dass es neben den individuellen Fähigkeiten der Nationalspieler noch unermessliche, aber wichtige Faktoren gibt - Teamgeist + Teamwork (zum Beispiel ein Team in einem Spiel mit einem stärkeren Gegner, das aber im Testspiel und auf seinem Feld häufiger gewinnt). Die Aufgabe ist für einen Menschen nicht so einfach, aber für maschinelles Lernen durchaus verständlich.  

    Ich hatte einmal ein wenig Erfahrung mit ML (mit der BrainJS-Bibliothek), aber dieses Mal habe ich beschlossen, die Aussage zu überprüfen, dass Python für solche Aufgaben viel besser geeignet ist.

    Ich begann meine Einführung in Python mit einem ausgezeichneten Kurs über Coursera und lernte die Grundlagen des maschinellen Lernens aus einer Reihe von Artikeln von Open Data Science über Habré .

    Ziemlich schnell fand ich einen tollen Datensatzmit der Geschichte aller Spiele internationaler Mannschaften seit Beginn des 20. Jahrhunderts. Nach dem Import in Pandas DataFrame:


    Insgesamt enthält die Datenbank Informationen zu 39.000 Spielen internationaler Mannschaften.

    Pandas macht es sehr bequem, die Daten zu analysieren. Das produktivste Spiel fand beispielsweise 2001 zwischen Australien und Amerikanisch-Samoa statt und endete mit einem Ergebnis von 31: 0 .




    Jetzt müssen Sie eine objektive Bewertung der Teamstärke im Jahr des Spiels hinzufügen. Solche Bewertungen werden von der FIFA durchgeführt.



    Leider wird die FIFA-Wertung erst seit 1992 durchgeführt. Und nach dem Zeitplan sind die Teambewertungen sehr anfällig für Änderungen, und ich möchte die Positionen der Teams in der Weltrangliste erst in diesem Jahr mitteln.

    Die UEFA bewahrt ihre Statistiken aus früheren Zeiten auf, aber ich konnte keinen fertigen Datensatz finden, sodass diese Seite zur Rettung kam . Unter Node.js gibt es ein leistungsfähiges und praktisches Cheerio für solche Aufgaben , aber unter Python stellte sich heraus, dass alles nicht weniger einfach war (der Administrator dieser Site wird mir vergeben).

    Web-Scraping-Ranking
    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)
    


    Schwankungen in der Bewertung nach dem Hinzufügen der UEFA-Bewertung (und einer kleinen Überarbeitung der Ländernamen gemäß den Ergebnissen der geopolitischen Rochade):


    Aber auch das war nicht ohne Teer - die UEFA zählt nur europäische Teams (manchmal muss man sich überlegen, was unter den üblichen Abkürzungen verborgen ist, bevor man sie einsetzt). Glücklicherweise haben sich die Playoffs fast „europäisch“ entwickelt.

    Es bleibt ein wenig bequemer, die Ergebnisse in separate Spiele zu unterteilen und der Tabelle Bewertungen hinzuzufügen.

    Der interessanteste Teil ist das Modelltraining. Google schlug sofort die einfachste und schnellste Option vor - dies ist der Klassifikator MLPClassifier aus der Python-Bibliothek - Sklearn. Versuchen wir, ein Modell am Beispiel Schwedens zu trainieren.

    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))
    ))
    
     
    Genauigkeit: 0,62

    Nicht viel genauer als das Werfen von Münzen, aber wahrscheinlich schon besser als meine potenziellen "Experten" -Vorhersagen. Es wäre ratsam zu versuchen, die Daten anzureichern, mit Hyperparametern zu spielen, aber ich entschied mich, in die andere Richtung zu gehen und die Catboost- Gradienten- Boost- Bibliothek von Yandex zu testen . Dies ist einerseits patriotischer, andererseits versprechen sie Qualitätsarbeit mit kategorialen Attributen, wie zahlreiche Vergleiche belegen .

    Übernahm die Einstellungen aus dem Beispiel :

    
    # Индексы столбцов категориальных признаков
    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))
    ))
    

    Genauigkeit: 0,73

    Bereits besser, versuchen Sie es im Geschäft.

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


    Prognoseergebnisse für das Finale „Crotia-Team verliert gegen Frankreich-Team mit einer Wahrscheinlichkeit von 93,7%“

    Obwohl ich diesmal den NORBIT- Wettbewerb nicht gewonnen habe , hoffe ich, dass dieser Artikel das Ausmaß der Magie für jemanden im praktischen Einsatz des maschinellen Lernens verringern wird, und Vielleicht motiviert es mich sogar, selbst zu experimentieren.

    Jetzt auch beliebt: