JBCクラシックの予想モデルをPytorchで作ってみる（1）

pytorchの学習がてら、JBCクラシックの予想モデルを作ってみました
pytorchで競馬予想作ってみた系の記事が見当たらなかったので記事を書いてみます。

# 概要
JBCの1着馬をpytorchで予想するプログラムを作成する。

# 作成方針
ワインの品種を推測する記事があったので、こちらの記事を参考に1着馬になる確率を推測するように応用する。
各出走馬の1着になる確率を出し、最も高い数値を出した馬を◎にする。

https://qiita.com/__Rossi__/items/a0ea3a8d6e24d0755737

# データの収集
[netkeiba](https://www.netkeiba.com/ "netkeiba")からスクレイピングしてデータ収集しました。
具体的なコードはネット上に多数転がっているので当記事では記載しませんが、結構面倒な作業となります。

# 入力

・説明変数
| ラベル          | 内容         | 
|:---------------|------------:|
|horse_id        |馬id
|sex             |性別
|age             |年齢
|dateforda       |日付
|track           |競馬場
|weather         |天候
|race_num        |レース番号 
|horse_num_anim  |頭数 
|horse_num       |馬番
|odds            |オッズ
|popularity      |人気
|jockey          |騎手
|weight          |馬体重
|dirt_grass      |芝かダートか
|distance        |距離
|condition       |馬場コンディション
|same_track      |同一競馬場かどうか |
|g1_age3         |3歳G1かどうか     |
|g2_age3         |3歳G2かどうか     |
|g3_age3         |3歳G3かどうか     |
|g1_age4         |古馬G1かどうか     |
|g2_age4         |古馬G2かどうか     |
|g3_age4         |古馬G3かどうか     |

目的変数
| ラベル          | 内容         | 
|:---------------|------------:|
|order           |順位

これらを1800〜2100mのダートG1を対象にデータ収集しました。
・訓練データ：過去5戦
・テストデータ：ダートG1レース
としています


# 予測モデル

```ruby:mlp_pytorch.rb
import pandas as pd
import numpy as np

# pytorch
import torch
import torch.nn as nn
import torch.optim as optim

#参考
#https://qiita.com/__Rossi__/items/a0ea3a8d6e24d0755737

#ここに大量のレースをベタ書きする
lists =[
    "CHAMPIONSCUP_2018",
]

jbc_all_lists_train = pd.DataFrame()
jbc_all_lists_test = pd.DataFrame()

for train_or_test in range (0,2):
    if(train_or_test == 0):
        jbc_all_lists_tmp = jbc_all_lists_train
        TRAIN_OR_TEST = 'train_'
    else:
        jbc_all_lists_tmp = jbc_all_lists_test
        TRAIN_OR_TEST = 'test_'
    for list in lists:
        try:
            horse_info_new_path ='/home/xxx/workspace/vscode/horserace/python_ci/csv/horse_id_new/JBC2022/'+TRAIN_OR_TEST+list+'.csv'
            jbc_2021_df = pd.read_csv(horse_info_new_path,index_col=0)
            #print(jbc_2021_df)

            jbc_all_lists_tmp = jbc_all_lists_tmp.append(jbc_2021_df)
        except:
            print("file not found")

    horse_info_new_path ='/home/xxx/workspace/vscode/horserace/python_ci/csv/horse_id_new/JBC2022/JBC_CATALL.csv'

    #jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='horse_id')
    #jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='jockey')
    jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='date')
    jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='weight_incdec')
    jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='3furlong')
    #jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='distance')
    jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='horse_weight')

    #print(jbc_all_lists_tmp)

    f_horsediv = lambda x: x / 10000000
    jbc_all_lists_tmp['horse_id'] = jbc_all_lists_tmp['horse_id'].apply(f_horsediv)

    f_distancediv = lambda x: x / 100
    jbc_all_lists_tmp['distance'] = jbc_all_lists_tmp['distance'].apply(f_distancediv)

    f_jockey = lambda x: x / 100
    jbc_all_lists_tmp['jockey'] = jbc_all_lists_tmp['jockey'].apply(f_jockey)

    f_money = lambda x: x / 100
    jbc_all_lists_tmp['money'] = jbc_all_lists_tmp['money'].apply(f_money)

    f_dateda = lambda x: x / 100
    jbc_all_lists_tmp['dateforda'] = jbc_all_lists_tmp['dateforda'].apply(f_dateda)

    get_odd_even = lambda x: 10000 + x if x % 2 == 0 else x
    f_dateda = lambda x: x / 100
    jbc_all_lists_tmp['jockey'] = jbc_all_lists_tmp['jockey'].apply(f_dateda)

    #jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='horse_id')
    jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='money')
    jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='time')
    #jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='weight')
    #jbc_all_lists_tmp = jbc_all_lists_tmp.drop(columns='jockey')

    jbc_all_lists = jbc_all_lists_tmp

    print(jbc_all_lists)

    if train_or_test == 0:
        t_train = torch.Tensor(jbc_all_lists['order'].values.astype(np.int64))
        x_train = torch.Tensor(jbc_all_lists.drop('order', axis=1).values.astype(np.float32))
    else:
        t_test = torch.Tensor(jbc_all_lists['order'].values.astype(np.int64))
        x_test = torch.Tensor(jbc_all_lists.drop('order', axis=1).values.astype(np.float32))

# numpyからtensorに変換
x_train = torch.tensor(x_train, dtype=torch.float32)
t_train = torch.tensor(t_train, dtype=torch.int64) 
x_test = torch.tensor(x_test, dtype=torch.float32)
t_test = torch.tensor(t_test, dtype=torch.int64) 

# シードを固定
torch.manual_seed(100)

net = nn.Sequential(
    nn.Linear(23 ,128),  
    nn.ReLU(),
    nn.Linear(128 ,128),  
    nn.ReLU(),
    nn.Linear(128 ,128),  
    nn.ReLU(),  
    nn.Linear(128 ,32),  
    nn.ReLU(),          
    nn.Linear(32, 8),
    nn.ReLU(),
    nn.Linear(8, 2)
)
net.train() #学習モードに切り替え

# 交差エントロピー誤差関数
loss_fnc = nn.CrossEntropyLoss()

# SGD
optimizer = optim.SGD(net.parameters(), lr=0.1)  # 学習率は0.01

# 損失のログ
record_loss_train = []
record_loss_test = []

# 1000エポック学習
for i in range(2000):

    # 勾配を0に
    optimizer.zero_grad()
    
    # 順伝播
    y_train = net(x_train)
    y_test = net(x_test)
    
    # 誤差を求める
    loss_train = loss_fnc(y_train, t_train)
    loss_test = loss_fnc(y_test, t_test)
    record_loss_train.append(loss_train.item())
    record_loss_test.append(loss_test.item())

    # 逆伝播（勾配を求める）
    loss_train.backward()
    
    # パラメータの更新
    optimizer.step()

    if i%100 == 0:
        print("Epoch:", i, "Loss_Train:", loss_train.item(), "Loss_Test:", loss_test.item())

y_test = net(x_test)
#print(y_test)
count = (y_test.argmax(1) == t_test).sum().item()
print("正解率:", str(count/len(y_test)*100) + "%")

sample = torch.tensor([
    [ 202.0104376    ,3   ,3     ,85.00,45       ,6      ,11.0             ,15         ,2    ,1.0         ,1.0        ,54.04    ,55.0          ,2      ,20.0         ,4            ,1       ,0       ,0       ,0       ,1       ,0       ,0],     
    ],dtype=torch.float32)

net.eval() #推論モードに切り替え
for i in range (0,1):
    y_test2 = net(sample[i])
    y_test2 = y_test2.softmax(dim=0)
    print(y_test2)

```
訓練データとテストデータで事前に学習し、それを基に推論するプログラムを作成しました。
まだ絶賛作成中でベタ書きなのと実験繰り返し状態ですので、今後改修を進めます。