競馬予想AIを作りたい（AI学習編１）

# はじめに
[前回の記事](https://qiita.com/lefirea/items/1a3ef5cf49036e8e73a7)で収集したデータを基に、１位になる馬の番号を予想させる。
上記記事にも書いたが、馬柱が記録され始めたのが2007年の後半からなので、今回の挑戦では2008年以降のデータのみ使用する。

# 要約
最終的な精度は、学習用データで35%程度、テスト用データでは20%程度と結構低い。
学習時には過学習の傾向も見られたので、色々と見直しが必要かも。
過学習の要因を考えると、馬柱が埋まっていないレースが多いことが原因の可能性があるので、その辺を考慮した学習データに整理して再び検証してみる予定。

# 今回のアプローチ
とりあえずディープラーニング使って１位を予想させればええやろｶﾞﾊﾊ

以下の2つを試した。
1. DNNで予想
2. CNNで予想

まずは、入力データを単純に一直線に並べてDNNに入力して精度を評価してみる。
その後、入力データそのまま（2次元構造）をCNNに入力して精度を評価する。

## 1.DNNで予想
DNNの構造は以下のプログラム。
損失関数は`CategoricalCrossentropy`。
最適化関数は`Adam(learning_rate=0.0001, beta_1=0.5, beta_2=0.999)`。

```Python
def CreateModel():
    inputs = Input(shape=(18 * 9,))
    dense = Dense(512, activation="relu")(inputs)
    dense = Dense(256, activation="relu")(dense)
    dense = Dense(128, activation="relu")(dense)
    dense = Dense(64, activation="relu")(dense)
    outputs = Dense(18, activation="softmax")(dense)

    return inputs, outputs
```

これで学習させた結果が以下のグラフ。

- lossの変化
![loss_keras.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/382806/e3627046-f97e-a345-b388-a0aa67292846.png)

- accの変化
![acc_keras.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/382806/3388a84d-4a91-3bf5-4f53-5ffe8ff04b9b.png)

とても分かりやすく過学習してる。
今回のデータの入力形式は、単純に一直線に並べただけなので、その影響もあるかもしれない。

## 2.CNNで予想

kerasでは何故かCNNが動かなかったのでpytorchに変えて実験。

CNNの構造は以下のプログラム。
損失関数は`nn.CrossEntropyLoss`。
最適化関数は`Adam(model.parameters(), lr=0.001, betas=(0.5, 0.999))`。

```Python
def __init__(self):
    super(Model2d, self).__init__()
    bias = True
    
    self.input = nn.Sequential(
        nn.Conv2d(in_channels=1,
                  out_channels=32,
                  kernel_size=(1, 1),
                  padding=(0, 0),
                  stride=(1, 1),
                  bias=bias),
        nn.ReLU(),
    )
    
    self.hidden = nn.Sequential(
        nn.Conv2d(in_channels=32,
                  out_channels=32,
                  kernel_size=(3, 3),
                  padding=(1, 1),
                  stride=(1, 1),
                  bias=bias),
        nn.Dropout(0.2),
        nn.ReLU(),
    
        nn.Conv2d(in_channels=32,
                  out_channels=64,
                  kernel_size=(3, 3),
                  padding=(1, 1),
                  stride=(1, 1),
                  bias=bias),
        nn.Dropout(0.2),
        nn.ReLU(),
    
        nn.Conv2d(in_channels=64,
                  out_channels=64,
                  kernel_size=(3, 3),
                  padding=(1, 1),
                  stride=(1, 1),
                  bias=bias),
        nn.Dropout(0.2),
        nn.ReLU(),
    )
    
    self.output = nn.Sequential(
        nn.Conv2d(in_channels=64,
                  out_channels=1,
                  kernel_size=(1, 1),
                  padding=(0, 0),
                  stride=(1, 1),
                  bias=bias),
        nn.Flatten(),
        nn.Linear(18 * 9, 128),
        nn.ReLU(),
        nn.Linear(128, 64),
        nn.ReLU(),
        nn.Linear(64, 18),
    )

def forward(self, x):
    input = self.input(x)
    middle = self.hidden(input)
    output = self.output(middle)
    return output
```

これで学習させた結果が以下の2つのグラフ。
過学習の気配があるので60epoch程度で中断した。

- lossの変化
![loss_torch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/382806/54d63f56-f134-990a-e710-85c1f0ba85f9.png)

- Accuracyの変化
![acc_torch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/382806/743b7a10-226c-8e07-dc8f-5fd6b2eeff7a.png)

CNNを使う場合でも、やはり学習時はlossが下がり続け、accが上がり続ける結果となった。
傾向はDNNと似たような感じである。
学習時のlossは順調に下り続けるが、テスト時は、最初は減少傾向で途中から上がり始める。
accは、学習時は順調に上がり続け、テスト時は早々に頭打ちになる。
めちゃくちゃ過学習してそう。

# 要因分析

現時点での仮説は以下のとおり。

- 勝つ馬の番号が偏っている
- 馬柱がちゃんと埋まっているデータが2割くらいしか無い

## 勝つ馬の番号が偏っている
2008年～2022年の中央競馬で、1位になった馬の番号を集計したのが以下の棒グラフ。
左から順に1番、2番、……、18番。
縦軸はレース数。

![win_bar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/382806/93c2d233-1a1d-6aae-6cf7-04f61612fbe5.png)

割合にしたのが以下の表。

|馬番|勝率(%)|
|---|---|
|1|7|
|2|7|
|3|7|
|4|8|
|5|7|
|6|7|
|7| 7|
|8| 7|
|9| 7|
|10| 7|
|11| 6|
|12| 6|
|13| 5|
|14| 4|
|15| 3|
|16| 3|
|17| 0|
|18| 0|

という感じで、ある馬番に偏ってるわけではなさそう。（すごい）
なお、後ろの番号ほど割合が減っているのは、単純に少ない頭立てのレースが多いから。
頭立て数の影響もあるだろうが、こんだけ綺麗に分布してるなら影響力は弱い気がする。

よって、「勝つ馬の番号が偏っている」という仮説は否定された。

## 馬柱が埋まっているデータが2割程度

2008年～2022年のデータのレース数は51808。
そのうち、馬柱が5枠全て埋まっていたレースは11615。
割合としては約22.4%となる。

2023年に開催されたレース数は3400。
そのうち、馬柱が5枠全て埋まっていたレースは707。
割合としては約20.8%。

よって、この仮説は正しそう。
馬柱がちゃんと埋まっているレースであれば高い割合で予想できる可能性がある。

# 結論
2008年～2022年の中央競馬の全レースのデータを使った場合、予想精度がかなり低く、過学習してそうな気配があった。
その要因を分析したところ、馬柱のデータがちゃんと埋まっているレースについては正しく予想できている可能性が浮上した。
今後はそのデータだけを使って分析できるか試す予定。