[PYTHON] Vorhersage für Pferderennen: Wenn Sie der Meinung sind, dass die Wiederherstellungsrate beim maschinellen Lernen (Light GBM) 100% überschritten hat, haben Sie dies getan

Vielen Dank

Hinweis!!!

** Dieser Artikel ist komplett fertig **

Es tut mir leid für die Person, die es auf Lager hat.

Vielen Dank, dass Sie den Punkt von @ hal27 bemerkt haben.

· Was ich getan habe Ich habe beim Schaben einen fatalen Fehler gemacht. Ich dachte, ich hätte die Daten für die letzten drei Rennen ab dem Zeitpunkt des Rennens erfasst, aber tatsächlich habe ich die Informationen für die letzten drei Rennen ab dem Zeitpunkt der Ausführung des Scrapings erfasst.

Als ich jedoch voraussagte, ohne die Informationen des vorherigen Laufs zu verwenden, lag die Wiederherstellungsrate im Durchschnitt bei etwa 90% Selbst wenn ich die richtigen Daten verwende, denke ich, dass sie 100% überschreiten können.

** Von vorn anfangen! **

Bitte lesen Sie diesen Artikel, während Sie denken, dass dies geschehen ist. (Achten Sie besonders auf den Scraping-Teil der vorherigen Laufinformationen.)

Einführung

Vor kurzem bin ich süchtig nach Datenanalyse.

Wenn ich Kaggle, einen Datenanalyse-Wettbewerb, durchführe, denke ich oft ** "Umsatzprognose? Gibt es ein interessanteres Thema? **".

Aus diesem Grund habe ich beschlossen, ein Vorhersagemodell für Pferderennen von Grund auf neu zu erstellen, um es zu studieren. Hoffentlich können Sie Geld verdienen und es ist das beste Analysethema für mich, der Pferderennen liebt.

Ich bin fast ein Anfänger, aber als Ergebnis konnte ich ein Modell mit einer stabilen Wiederherstellungsrate von über 100% ** erstellen. In diesem Artikel werde ich den groben Ablauf bis zur Erstellung des Pferderennmodells und die Details der Simulationsergebnisse beschreiben. Ich werde es weiterhin tun. Wenn etwas mit Ihrer Denkweise nicht stimmt, geben Sie uns bitte eine Anleitung.

Zustandseinstellung

** Vorhersage der Laufzeit des Laufpferdes und Gewinn des schnellsten Pferdes. ** **.

Auf den Straßen gab es viele Modelle, die die Trefferquote des ersten Pferdes erhöhten, aber ich denke, dass es viele Modelle gab, bei denen die Wiederherstellungsrate nicht wie erwartet anstieg. Wenn ja, könnte es schön sein, nach reiner Vorhersage der Zeit zu wetten. Ich dachte ** (Schurke) ** und nahm diese Einstellung vor.

Es gibt tatsächlich andere Gründe .....

Im Pferderennen scheinen mehr Menschen auf beliebte Pferde zu setzen als auf ihren Fähigkeitswert. (Referenz: Theorie, dass Sie nicht gewinnen können, wenn Sie sich auf beliebte Pferde konzentrieren) Mit anderen Worten, anstatt eine Trefferquote auf dem ersten Platz zu verfolgen, kann es möglich sein, die Wiederherstellungsrate zu erhöhen, indem man die Gewinnchancen betrachtet und Vorhersagen trifft. Ich möchte jedoch Wettkarten mit viel Zeit kaufen, daher möchte ich die Quoten, die kurz vor dem Rennen festgelegt wurden, nicht in den Funktionsbetrag einbeziehen.

Was soll ich machen...

Da Pferderennen verschiedene Faktoren mit sich bringt, ist es schwierig, die Laufzeit rein vorherzusagen. Ist es nicht so, dass es schwierig ist, Pferde mit hohen Erwartungen vorherzusagen, auf die die Leute nicht wetten werden? ** (Schurke) **. Okay, lass uns in die Laufzeit gehen.

** Lernziel und Simulationsziel **

Da ich in Kyoto lebe, habe ich mich nur auf Pferderennen in Kyoto konzentriert. Die Daten beziehen sich auf fast alle Rennen von 2009 bis 2019 (siehe Abschnitt Datenvorverarbeitung). Wir haben sie in Trainingsdaten und Testdaten unterteilt und die Testdaten simuliert. Es ist eine Simulation für insgesamt 7 Jahre.

Das Folgende ist der Inhalt der Datenaufteilung.

Trainingsdaten Testdaten
2009~2018 2019
2009~2017 2018
2009~2016 2017
2009~2015 2016
2009~2014 2015
2009~2013 2014
2009~2012 2013

Im Falle eines Lecks werden die Trainingsdaten auf das Jahr vor den Testdaten eingestellt.

Die Details der behandelten Merkmalsmengen werden unten erläutert.

Fluss bis zur Modellerstellung

  1. Datenerfassung (Web Scraping)
  2. Datenvorverarbeitung
  3. Modellierung

1. Datenerfassung

Es scheint, dass Sie die Daten leicht erhalten können, wenn Sie bezahlen, aber ich habe die Daten auch durch Web-Scraping für das Studium erhalten.

Zunächst können Sie HTML / CSS mit Progate problemlos studieren. Ich wusste nicht, wo ich die gewünschten Daten finden konnte, ohne es zumindest zu wissen.

In Bezug auf das WEB-Scraping habe ich es unter Bezugnahme auf den folgenden Artikel erstellt. Um ehrlich zu sein, denke ich, dass dies der schwierigste Teil war. (Zu viele Ausnahmen!)

Die zu kratzende Site ist https://www.netkeiba.com/.

Das Folgende sind die Daten, die durch tatsächliches Schaben erhalten werden. Der Code befindet sich in Anhang. image.png image.png

Die erfassten Daten sind wie folgt

feature Erläuterung feature Erläuterung
race_num Welche Rasse field Shiba oder Dreck
dist Entfernung l_or_r Rechtshänder oder Linkshänder
sum_num Anzahl der Köpfe weather das Wetter
field_cond Baba Staat rank Reihenfolge der Ankunft
horse_num Pferdenummer horse_name Pferdename
gender Sex age Alter
weight Pferdegewicht weight_c Reitergewicht
time Laufzeit sum_score Gesamtergebnisse
odds Gewinnchancen popu Beliebt

Wir haben Daten für die letzten 3 Rennen mit ** + erhalten. ** **.

Von diesen werden ** Laufzeit, Reihenfolge der Ankunft, Gewinnchancen, Beliebtheit und Eigenschaften von Pferdenamen nicht als Trainingsdaten verwendet. ** **.

Außerdem wurden ** Informationen für Pferde gelöscht, die im vorherigen Rennen keine 3 Rennen hatten. ** Wenn jedoch nur ein Pferd in jedem Rennen Informationen hat, werden wir das schnellste Pferd unter ihnen vorhersagen. Wenn die gelöschten Daten ein erstes Pferd enthalten, ist die Erwartung natürlich falsch.

Daten für etwa 450 Rennen bleiben pro Jahr. (Fast alle Rennen)

2. Datenvorverarbeitung

Transformieren Sie die resultierenden Daten für die Aufnahme in ein maschinelles Lernmodell. Ich habe jedoch gerade die kategoriale Variable in ** Beschriftungscodierung ** und ** Zeichentyp in numerischen Typ ** geändert.

Da es sich um eine Art Algorithmusbaummodell des diesmal behandelten Modells handelt, ist es nicht standardisiert.

Außerdem ** mache ich einige neue Funktionen mit den erworbenen Funktionen. ** (Entfernung und Zeit bis zur Geschwindigkeit usw.)

3. Modellierung

Implementiert mit der ** LightGBM ** -Bibliothek des ** Gradientenverstärkungs-Entscheidungsbaum-Algorithmus **. Das ist das, was in letzter Zeit in Kaggle oft verwendet wird.

Unten finden Sie den Implementierungscode.

lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_valid, y_valid, reference=lgb_train)

params = {'objective': 'regression',
          'metric': 'rmse',
          }

model = lgb.train(
    params, lgb_train,
    valid_sets=[lgb_train, lgb_eval],
    verbose_eval=10,
    num_boost_round=1000,
    early_stopping_rounds=10)

Wie Sie sehen, haben wir nichts getan (lacht)

Ich habe versucht, die Parameter mit Optuna usw. abzustimmen, aber da die Bewertungsfunktion und die Wiederherstellungsrate unterschiedlich sind, führte dies nicht zu einer wesentlichen Verbesserung der Wiederherstellungsrate.

Simulation

Nachfolgend sind die Simulationsergebnisse für 7 Jahre aufgeführt

** Horizontale Achse **: Welches Rennen ** Vertikal **: Gewinnchancen bei Treffer (0 bei Fehlschlag) ** Trefferquote **: Trefferquote ** back_rate **: Wiederherstellungsrate Der Titelzug und der Test repräsentieren den Zeitraum der jeweils verwendeten Daten. (09 ist 2009)

09-10-11-12,13.jpg 09-10-11-12-13,14.jpg 09-10-11-12-13-14,15.jpg 09-10-11-12-13-14-15,16.jpg 09-10-11-12-13-14-15-16,17.jpg 09-10-11-12-13-14-15-16-17,18.jpg 09-10-11-12-13-14-15-16-17-18,19.jpg

Die Ergebnisse sind unten zusammengefasst

Simulationsjahr Anzahl der Treffer Trefferquote Erholungsrate
2013 116/460 25.2% 171.9%
2014 89/489 18.5% 155.3%
2015 120/489 24.5% 154.7%
2016 101/491 20.6% 163.6%
2017 131/472 27.8% 263.5%
2018 145/451 32.2% 191.8%
2019 136/459 29.6% 161.7%
durchschnittlich ------ 25.5% 180.4%

Es ist zu gut.

Die Trefferquote ist in Ordnung, aber was mich überraschte, war, dass ich von Zeit zu Zeit Pferde mit hohen Chancen schlug.

** 2017 schlage ich 250 Mal mehr Pferde! ** **.

Ich bin neugierig, also schauen wir uns den Inhalt an Nachfolgend finden Sie den Inhalt des Rennens des Tages. (Sortieren in der Reihenfolge von time_pred)

image.png

image.png

Ich rate ernsthaft.

Ich habe Angst, dass etwas nicht stimmt. Ist es in Ordnung, 100% so leicht zu überschreiten?

Worauf sollte ich in einem solchen Fall achten? .....

** Du musst es im wirklichen Leben versuchen! ** **.

Folgendes habe ich gesucht. ・ ** Verwenden Sie nur die Funktionen, die am Tag verwendet werden können **? ・ ** Ist die Formel zur Berechnung der Wiederherstellungsrate korrekt? ** ・ ** Gibt es Unstimmigkeiten mit den Informationen im Internet? ** **. ・ ** Können Sie die Person mit der frühesten erwarteten Zeit auswählen **? ・ ** Spielen Sie das erstellte Modell aus verschiedenen Richtungen ab **

Ich habe mit dem Modell gespielt

Es ist eine gute Idee, also werde ich verschiedene Dinge ausprobieren.

1 Versuchen Sie, das Pferd zu reiten, von dem Sie vorhergesagt haben, dass es das langsamste ist

aaa.png

Ich habe es nur 6 Mal getroffen.

2 Bedeutung der Merkmalsmenge

Unten finden Sie die LightGBM-Funktionen zum Importieren von Funktionen (2019). "a", "b", "c" sind die Geschwindigkeiten (dist / time) der Pferde im vorherigen Lauf, im vorherigen Lauf bzw. im vorherigen Lauf.

aaa.png

Ich verstehe, dass "dist (Race Distance)" wichtig ist, weil ich die Zeit vorhersage, aber welche Bedeutung hat "race_id (welches Rennen des Jahres)"?

Ist die Jahreszeit wichtig für die Vorhersage der Zeit?

Darüber hinaus ist die Umgebung von großer Bedeutung, z. B. "Race_Cond (Baba State)", "Race_Num (welches Rennen des Tages)".

** * Ich habe Addition geschrieben. (2020/05/28) **

3 Was passiert, wenn Sie weiterhin der n-te beliebteste sind?

Es hat nichts mit dem Modell zu tun, das ich erstellt habe (lacht)

Nachfolgend sind die Ergebnisse von ** 2019 ** aufgeführt

n-ten beliebtesten Trefferquote Erholungsrate
Am beliebtesten 30.9% 71.1%
2. beliebteste 17.3% 77.2%
3. am beliebtesten 15.3% 90.3%
4. am beliebtesten 10.1% 81.3%
5. am beliebtesten 8.4% 100.5%
6. am beliebtesten 6.2% 92.4%
7. am beliebtesten 3.2% 64.2%
8. am beliebtesten 2.4% 52.1%
9. am beliebtesten 1.5% 48.2%
10. am beliebtesten 1.3% 59.1%
11. am beliebtesten 1.5% 127.6%
12. am beliebtesten 1.3% 113.9%
13. am beliebtesten 1.5% 138.6%
14. am beliebtesten 0.4% 77.8%

Es ist ein sehr interessantes Ergebnis. Wenn Sie sich erholen möchten, können Sie weiterhin unbeliebte Pferde kaufen. Es scheint keinen Spaß zu machen, es zu sehen, weil es kaum trifft (lacht)

Es ist interessant, also habe ich mir den ** Durchschnitt 2013-2019 ** angesehen.

n-ten beliebtesten Trefferquote Erholungsrate
Am beliebtesten 31.7% 73.4%
2. beliebteste 20.0% 83.7%
3. am beliebtesten 13.2% 80.2%
4. am beliebtesten 9.9% 81.9%
5. am beliebtesten 7.8% 89.1%
6. am beliebtesten 5.5% 89.8%
7. am beliebtesten 4.2% 86.0%
8. am beliebtesten 2.4% 64.8%
9. am beliebtesten 2.1% 64.8%
10. am beliebtesten 1.7% 80.9%
11. am beliebtesten 1.1% 98.2%
12. am beliebtesten 1.0% 69.4%
13. am beliebtesten 1.1% 113.2%
14. am beliebtesten 0.2% 35.4%

interessant

Zusammenfassung

Die Wiederherstellungsrate kann fast ohne Einfallsreichtum 100% überschreiten lightGBM super

Blinddarm

Bitte beachten Sie, dass dies ein schmutziger Code ist.

・ ** Scraping (Erfassung der Renninformationen und der URL jedes Pferdes) **


def url_to_soup(url):
    
    time.sleep(1)
        
    html = requests.get(url)
    html.encoding = 'EUC-JP'
    return BeautifulSoup(html.text, 'html.parser')


def race_info_df(url):
    
    df1 = pd.DataFrame()
    HorseLink = []
    
    try:
        # year = '2018'
        # url = 'https://race.sp.netkeiba.com/?pid=race_result&race_id=201108030409&rf=rs'
        soup = url_to_soup(url)
        
        if soup.find_all('li',class_='NoData') != []:
            return df1,HorseLink
            
        else:
            
            race_cols = ['year', 'date', 'place', 'race_num' ,'race_class', 'field', 'dist', 'l_or_r',\
                        'sum_num','weather', 'field_cond', 'rank', 'horse_num', 'horse_name', 'gender', 'age',\
                        'weight', 'weight_c', 'time', 'jackie', 'j_weght', 'odds', 'popu']
            
            #Gemeinsame Gegenstände#
            # Year = year
            Date = soup.find_all('div', class_='Change_Btn Day')[0].text.split()[0]
            Place = soup.find_all('div', class_="Change_Btn Course")[0].text.split()[0]

            RaceClass = soup.find_all('div', class_="RaceDetail fc")[0].text.split()[0][-6:].replace('、','')

            RaceNum = soup.find('span', id= re.compile("kaisaiDate")).text
            RaceData = soup.find_all('dd', class_="Race_Data")[0].contents
            Field  = RaceData[2].text[0]
            Dist = RaceData[2].text[1:5]

            l_index = RaceData[3].find('(')
            r_index = RaceData[3].find(')')
            LOrR = RaceData[3][l_index+1:r_index]

            RD = RaceData[3][r_index+1:]
            SumNum = RD.split()[0]
            Weather = RD.split()[1]
            FieldCond =  soup.find_all('span',class_= re.compile("Item"))[0].text

            #Nicht üblich#
            HorseLink = []
            for m in range(int(SumNum[:-1])):
                HN = soup.find_all('dt',class_='Horse_Name')[m].contents[1].text
                HL = soup.find_all('dt',class_='Horse_Name')[m].contents[1].get('href')
                HorseLink.append(HL if HN!='' else soup.find_all('dt',class_='Horse_Name')[m].contents[3].get('href'))

            HorseName = []
            for m in range(int(SumNum[:-1])):
                HN = soup.find_all('dt',class_='Horse_Name')[m].contents[1].text
                HorseName.append(HN if HN!='' else soup.find_all('dt',class_='Horse_Name')[m].contents[3].text)
            #     print(soup.find_all('dt',class_='Horse_Name')[m].contents[3])

            Rank = [soup.find_all('div',class_='Rank')[m].text for m in range(int(SumNum[:-1]))]

            #Holen Sie sich die Informationen, die Sie von hier erhalten können
            HorseNum = [soup.find_all('td', class_ = re.compile('Num Waku'))[m].text.strip() for m in range(1,int(SumNum[:-1])*2+1,2)]

            Detail_Left = soup.find_all('span',class_='Detail_Left')
            Gender = [Detail_Left[m].text.split()[0][0] for m in range(int(SumNum[:-1]))]
            Age =  [Detail_Left[m].text.split()[0][1] for m in range(int(SumNum[:-1]))]
            Weight = [Detail_Left[m].text.split()[1][0:3] for m in range(int(SumNum[:-1]))]
            WeightC = [Detail_Left[m].text.split()[1][3:].replace('(','').replace(')','') for m in range(int(SumNum[:-1]))]

            Time = [soup.find_all('td', class_="Time")[m].contents[1].text.split('\n')[1] for m in range(int(SumNum[:-1]))]

            Detail_Right = soup.find_all('span',class_='Detail_Right')
            Jackie = [Detail_Right[m].text.split()[0] for m in range(int(SumNum[:-1]))]
            JWeight = [Detail_Right[m].text.split()[1].replace('(','').replace(')','')for m in range(int(SumNum[:-1]))]
            Odds = [soup.find_all('td', class_="Odds")[m].contents[1].text.split('\n')[1][:-1] for m in range(int(SumNum[:-1]))]
            Popu = [soup.find_all('td', class_="Odds")[m].contents[1].text.split('\n')[2][:-2] for m in range(int(SumNum[:-1]))]

            Year = [year for a in range(int(SumNum[:-1]))]
            RaceCols =  [Year, Date, Place, RaceNum ,RaceClass, Field, Dist, LOrR,\
                      SumNum,Weather, FieldCond, Rank, HorseNum, HorseName, Gender, Age,\
                      Weight, WeightC, Time, Jackie, JWeight, Odds, Popu]
            for race_col,RaceCol in zip(race_cols,RaceCols):
                df1[race_col] = RaceCol

            return df1,HorseLink
        
    except:
        return df1,HorseLink

・ ** Scraping (bisherige Renninformationen für jedes Pferd) **


def horse_info_df(HorseLink, df1):
    
    df2 = pd.DataFrame()
    # print(HorseLink)

    for n,url2 in enumerate(HorseLink):

        try: 
            soup2 = url_to_soup(url2)

            horse_cols = ['sum_score',\
                          'popu_1','rank_1','odds_1','sum_num_1','field_1','dist_1','time_1',\
                          'popu_2','rank_2','2','sum_num_2','field_2','dist2','time_2',\
                          'popu_3','rank_3','odds_3','sum_num_3','field_3','dist_3','time_3']

            sec = 1
            ya = soup2.find_all('section',class_="RaceResults Sire")
            #ya = soup.find_all('div',class_="Title_Sec")
            if ya !=[]:
                sec = 2
            tbody1 = soup2.find_all('tbody')[sec] 
            SomeScore = tbody1.find_all('td')[0].text
            # print(SomeScore)

            tbody3 = soup2.find_all('tbody')[2+sec]

            HorseCols = [SomeScore]

            for late in range(1,4):
                HorseCols.append(tbody3.contents[late].find_all('td')[2].text)  # Popu
                HorseCols.append(tbody3.contents[late].find_all('td')[3].text)  # Rank
                HorseCols.append(tbody3.contents[late].find_all('td')[6].text)  # Odds
                HorseCols.append(tbody3.contents[late].find_all('td')[7].text)  # SumNum
                HorseCols.append(tbody3.contents[late].find_all('td')[10].text[0]) # Field
                HorseCols.append(tbody3.contents[late].find_all('td')[10].text[1:5]) # Dist
                HorseCols.append(tbody3.contents[late].find_all('td')[14].text) # Time


            dfplus = pd.DataFrame([HorseCols], columns=horse_cols)
            dfplus['horse_name'] = df1['horse_name'][n]

            df2 = pd.concat([df2,dfplus])
        except:
            pass

    return df2

Nachtrag

Berücksichtigung der Bedeutung von Merkmalsmengen (2020/05/28)

Betrachten wir die Bedeutung von race_id für Feature-Wichtigkeiten. Erstens können Sie sehen, dass die Wichtigkeit von race_id aus den folgenden zwei Schritten hoch geschätzt wird.

** 1 Vergleiche mit und ohne race_id **

Wenn die Merkmalsmenge von'race_id 'gelöscht wurde, betrug die Wiederfindungsrate ungefähr 10% und die Testrate war in allen 7 Jahren ungefähr 0,1 schlechter. Daraus können wir ersehen, dass die Feature-Menge von race_id erforderlich ist.

** 2 Machen Sie dasselbe wie 1 mit dem zweitwichtigsten dist ** aaa.png

Sie können sehen, dass sich die Vorhersagegenauigkeit erheblich verschlechtert hat.

Ab ** 1.2 können Sie sehen, dass die Bedeutung von race_id überschätzt wird. ** **.

Dieses Mal werden "gültig" und "Zug" wie folgt zufällig aufgeteilt.

X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.3,\
                                                      random_state=0)

Dies gibt Ihnen eine ungefähre Zeit, wenn Sie die race_id kennen.

Das Folgende ist das Ergebnis des Versuchs, shuffle = False zu setzen. aaa.png

Auf diese Weise wird race_id weniger wichtig. Es ist jedoch eine andere Dimension als die Verbesserung der Wiederherstellungsrate und der RMSE. Es wird tatsächlich schlimmer.

Recommended Posts

Vorhersage für Pferderennen: Wenn Sie der Meinung sind, dass die Wiederherstellungsrate beim maschinellen Lernen (Light GBM) 100% überschritten hat, haben Sie dies getan
Ich habe mit LightGBM einen Code geschrieben, der die Wiederherstellungsrate von 100% bei der Vorhersage von Pferderennen überschreitet (Teil 2).
Eine Geschichte über das Erreichen einer Wiederherstellungsrate von Pferderennen von über 100% durch maschinelles Lernen
Eine konkrete Methode zur Vorhersage von Pferderennen und zur Simulation der Wiederherstellungsrate durch maschinelles Lernen
Mit Deep Learning können Sie die Erholungsrate von 100% im Pferderennen überschreiten
[Verifikation] Nur weil es tiefes Lernen gibt, bedeutet dies nicht, dass die Wiederherstellungsrate bei Pferderennen leicht 100% überschreiten kann.
Ein Anfänger des maschinellen Lernens versuchte, mit Python ein Vorhersagemodell für Pferderennen zu erstellen
Implementierung eines Modells, das Wechselkurse (Dollar-Yen-Kurs) durch maschinelles Lernen vorhersagt
Einführung in das Buch "Erstellen einer profitablen KI mit Python", mit dem Sie in kürzester Zeit maschinelles Lernen erlernen können
MALSS (Einführung), ein Tool, das maschinelles Lernen in Python unterstützt
Ein Beispiel für einen Mechanismus, der eine Vorhersage von HTTP aus dem Ergebnis des maschinellen Lernens zurückgibt
Wenn Sie der Meinung sind, dass die Person, die Sie mit pip eingegeben haben, nicht funktioniert → Verwenden Sie python3 zufällig?
Wenn Sie neu in der Programmierung sind, warum machen Sie nicht vorerst ein "Spiel"? Die Geschichte