lightGBMでボート予想AIを作ってみた③|モデル作成編

Boat race

今回は取得したデータから、modelを作っていきます。

前回の記事はこちらから。

モデルの作成方法と特徴量の選定

lightGBMは『教師あり学習』に分類される、決定木をベースとしているアルゴリズムのため、目的変数と、説明変数(特徴量)を定義する必要があります。

まずは、モデルの作成方法ですが、前回作成した”personal_data.csv”と”data_result_csv”のデータを1行を1レースとしてデータフレームを作っていきます。

def make_data_for_ai():
    import pandas as pd
    import os
    import numpy as np
    from sklearn.metrics import confusion_matrix
    
    df_personal = pd.read_csv('/users/tom/boat_pred/data_csv/personal_data.csv')

    csv_rsult_dir = '/Users/tom/boat_pred/data_csv/data_result_csv'
    csv_result_file = os.listdir(csv_rsult_dir)
    csv_result_file.sort()
    csv_result_file.remove('.ipynb_checkpoints')
    
    df_r = []
    for i in csv_result_file[-8:]:
        d = pd.read_csv(f'/Users/tom/boat_pred/data_csv/data_result_csv/{i}')
        df_r.append(d)
    
    df_results = pd.concat(df_r)
    df_results = df_results.reset_index(drop=True)
    
    df_results['1着'] = df_results['着順'].apply(lambda x : 1 if x == 1 else 0)
    df_results['2連対'] = df_results['着順'].apply(lambda x : 1 if x <= 2 else 0)
    df_results['3連対'] = df_results['着順'].apply(lambda x : 1 if x <= 3 else 0)
    
    mix_data = pd.merge(df_results,df_personal, on='選手登番')
    
    id = df_results['id'].unique()
    
    one_race = []
    jyogai = []
    for i in id:
        w = mix_data[mix_data['id'] == i]
        if len(w) == 6:
            w.sort_values('艇番',inplace=True)
            one_race.append(w)
        else:
            jyogai.append(w)
    
    white = mix_data.columns+'1号艇'
    black = mix_data.columns+'2号艇'
    red = mix_data.columns+'3号艇'
    blue = mix_data.columns+'4号艇'
    yello = mix_data.columns+'5号艇'
    green = mix_data.columns+'6号艇'
    teiban = np.concatenate([white,black,red,blue,yello,green])
    
    one_race_list = []
    for i in one_race:
        no1 = i[:1].values
        no2 = i[1:2].values
        no3 = i[2:3].values
        no4 = i[3:4].values
        no5 = i[4:5].values
        no6 = i[5:6].values
        
        one = np.concatenate([no1,no2,no3,no4,no5,no6])
        one_array = one.flatten()
        df = pd.DataFrame([one_array])
        one_race_list.append(df)
    
    axis1_data = pd.concat(one_race_list)
    
    axis1_data = axis1_data.reset_index(drop=True)
    
    axis1_data.columns = teiban
    
    axis1_data.to_csv('/users/tom/boat_pred/ai_predict/data_for_ai.csv', index=False)
    print('ai用csv完了')

if __name__ == '__main__':
    make_data_for_ai()

これで1レースを、1行にすることができました。

今回は二値分類でのモデルづくりをしていくので、22行目で1着を”1”、それ以外を”0”として新しいカラムを作っています。これを今回は目的変数としています。特徴量に関しては、明らかに影響のなさそうな、選手の登録番号や支部などを除外しています。また、展示タイムは直前にならないと分からないことから、運用する上で好ましくないのでこれも対象外としました。実際に使った特徴量は次の項目の”train_df”になります。

学習開始

def make_model():

    import pandas as pd
    import numpy as np
    from sklearn.metrics import confusion_matrix
    from sklearn.model_selection import train_test_split
    import lightgbm as lgb
    from sklearn.metrics import accuracy_score
    import seaborn as sns
    import japanize_matplotlib
    import pickle
    
    df = pd.read_csv('/users/tom/boat_pred/ai_predict/data_for_ai.csv')
    t_list = ['1着1号艇','1着2号艇','1着3号艇','1着4号艇','1着5号艇','1着6号艇',
              '2連対1号艇','2連対2号艇','2連対3号艇','2連対4号艇','2連対5号艇','2連対6号艇',
              '3連対1号艇','3連対2号艇','3連対3号艇','3連対4号艇','3連対5号艇','3連対6号艇']
    for i in t_list:
        #予測ターゲットの格納
        target_df = df[[i]]
        #特徴量の格納
        train_df = df[['艇番1号艇','全国勝率1号艇','1枠1着率1号艇', '1枠2連対率1号艇','1枠3連対率1号艇','平均ST順位1号艇', '1枠ST順位1号艇',
                   'モーター2連率1号艇',
               '艇番2号艇','全国勝率2号艇','2枠1着率2号艇', '2枠2連対率2号艇','2枠3連対率2号艇','平均ST順位2号艇', '2枠ST順位2号艇',
                   'モーター2連率2号艇',
               '艇番3号艇','全国勝率3号艇','3枠1着率3号艇', '3枠2連対率3号艇','3枠3連対率3号艇','平均ST順位3号艇', '3枠ST順位3号艇',
                   'モーター2連率3号艇',
               '艇番4号艇','全国勝率4号艇','4枠1着率4号艇', '4枠2連対率4号艇','4枠3連対率4号艇','平均ST順位4号艇', '4枠ST順位4号艇',
                   'モーター2連率4号艇',
               '艇番5号艇','全国勝率5号艇','5枠1着率5号艇', '5枠2連対率5号艇','5枠3連対率5号艇','平均ST順位5号艇', '5枠ST順位5号艇',
                   'モーター2連率5号艇',
               '艇番6号艇','全国勝率6号艇','6枠1着率6号艇', '6枠2連対率6号艇','6枠3連対率6号艇','平均ST順位6号艇', '6枠ST順位6号艇',
                  'モーター2連率6号艇']]
        
        # trainとtestに分割
        X_train, X_test, y_train, y_test = train_test_split(train_df, target_df, test_size=0.3,random_state=0)
        # trainをtrainとvalに分割
        X_train, X_eval, y_train, y_eval = train_test_split(X_train, y_train,test_size=0.2,random_state=1,stratify=y_train)
        
        # データを格納する
        # 学習用
        lgb_train = lgb.Dataset(X_train, y_train)
        # 検証用
        lgb_eval = lgb.Dataset(X_eval, y_eval)
            
        params = {'objective':'multiclass',
                  'num_class': 2, # クラスの数
                  'metric':'multi_logloss',
                  'verbose': -1,
                  'learning_rate': 0.05,         # 学習率
                  'num_leaves': 21
                 }
        verbose_eval = 0  
        
        model = lgb.train(params,                                   # 設定したパラメータ
                          lgb_train,                                # 使用するデータセット
                          valid_names=['train', 'valid'],           # 学習経過で表示する名称
                          valid_sets=[lgb_train, lgb_eval],         # モデル検証のデータセット
                        num_boost_round=1000,                      
                        callbacks=[lgb.early_stopping(stopping_rounds=20, 
                                        verbose=True), # early_stopping用コールバック関数
                                   lgb.log_evaluation(verbose_eval)]
                         )
        with open(f'/users/tom/boat_pred/ai_predict/model/model{i}.pickle', mode='wb') as f:
            pickle.dump(model, f)

if __name__== '__main__':
    make_model()

とりあえずこんな感じで学習させました。

ここが、本題でありゴールであるはずなのに割とあっさりmodelが完成しました。

2連対率、3連対率ものちのち使えるかもと、forループでモデルとして残しています。

今回のやり方(1レース1行にまとめる)で実装しようと思った理由が、実際のレースでは相手関係によってに1着率が変わるのは当然で、『1号艇の勝率教えて、他の選手はこんな選手ですけど』と言う対比をはっきりさせたかったからです。

兎にも角にもこれで1号艇から6号艇の、それぞれの1着率を計算するmodelが出来上がりました。

次回modelの検証編に続く。

タイトルとURLをコピーしました