【第3回】Python で競馬予想してみる　~ モデルの学習・検証 ~

前回の第2回までで、データの前処理まで完了
Python に関しては完全な初心者だが、Google 検索だけで何とかなるもんだ

今回は、とりあえず　__モデルの学習・予測__　まで一気に進めてみるのだ

#開発環境
###Anaconda3 のインストール
#データの取得
###ターゲットフロンティアの出力データを使用
####ターゲットフロンティアのレース検索で CVS 出力
※第1回はここまで終了-------------------------------
#データの前処理
###CVS の読込みについて
####20年分の全データを読み込んでみたら　Wall time: 41.3 s
####20年分の使うかもしれないデータだけを読込み
### n 走前までのデータを連結
### 欠損地処理、データ型処理、目的変数の処理
※第2回はここまで終了-------------------------------
#モデルの学習
機械学習の種類や選び方を、よく分かってないのだが、
他の方のコードでよく使われている sklearn のロジスティック回帰を使用

###

```Python:build.ipynb
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

# 説明変数をdataXに格納
dataX = rd.drop(['3着以内'], axis=1)

# 目的変数をdataYに格納
dataY = rd['3着以内']

# データの分割を行う（学習用データ 0.8 評価用データ 0.2） stratify:層化抽出
X_train, X_test, y_train, y_test = train_test_split(dataX, dataY, test_size=0.2, stratify=dataY)

# 分類器を作成（ロジスティック回帰）
clf = LogisticRegression()

# 学習
clf.fit(X_train, y_train)
```

これだけで __モデルの学習__ は完了。簡単

#モデルの評価
とりあえず、学習した結果から予測をしてみます
予測結果を、様々な指標で評価してみます

詳しくは ↓ を参考に

- 参考：[機械学習の初心者がpythonで競馬予測モデルを作ってみた](https://qiita.com/kazuktym/items/7804a63338113653b04f)

```Python:build.ipynb
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score

# 予測
y_pred = clf.predict(X_test)

# 正解率を表示
print('正解率', accuracy_score(y_test, y_pred))
# 混同行列を表示
print('混同行列\n', confusion_matrix(y_test, y_pred, labels=[1, 0]))
# 適合率を表示
print('適合率', precision_score(y_test, y_pred))
# 再現率を表示
print('再現率', recall_score(y_test, y_pred))
# F値を表示
print('F値', f1_score(y_test, y_pred))
```

```text:Out
正解率 0.7828058218782488
混同行列
 [[   707  36705]
 [   900 134828]]
適合率 0.4399502177971375
再現率 0.01889767988880573
F値 0.03623875547810041
```
正解率は`0.7828058218782488`
すごい！！と思ったが、これは4着以下と予想して4着以下になったのも含んでいるのだ

「この馬はこない！」と予想して「予想が、当たった！当たった！」と喜んでいるのと同じなのだ

再現率は`0.01889767988880573`
再現率は、実際に 3着以内 だった中で、どの程度正しく予測できているかの割合
混合行列の数字から ↓ の数式で算出される

```math
\frac{707}{707+36705}
```
98％以上の取りこぼしがあるので、使い物にならん💦

###3着以内のデータ数と 4着以下のデータ数の不均衡を修正
####アンダーサンプリングの最適化
#####4着以下のデータ比率を 2倍
学習データの 3着以内と 4着以下のデータ比率を調整して改善させよう（アンダーサンプリングというらしい）
アンダーサンプリングを最適化する方法があるのかもしれないが、わからないのでマニュアルで検証
とりあえず、4着以下のデータ比率を 2倍にすると

```Python:build.ipynb
from imblearn.under_sampling import RandomUnderSampler

# 学習データをアンダーサンプリング
f_count = y_train.value_counts()[1] * 2
t_count = y_train.value_counts()[1]
rus = RandomUnderSampler(sampling_strategy={0:f_count, 1:t_count})
X_train_rus, y_train_rus = rus.fit_sample(X_train, y_train)

# 学習
clf.fit(X_train_rus, y_train_rus)
# 予測
y_pred = clf.predict(X_test)

# 正解率を表示
print('アンダーサンプリング')
print('正解率', accuracy_score(y_test, y_pred))
# 混同行列を表示
print('混同行列\n', confusion_matrix(y_test, y_pred, labels=[1, 0]))
# 適合率を表示
print('適合率', precision_score(y_test, y_pred))
# 再現率を表示
print('再現率', recall_score(y_test, y_pred))
# F値を表示
print('F値', f1_score(y_test, y_pred))
```

```text:Out
アンダーサンプリング　1：2
正解率 0.7591140117823727
混同行列
 [[ 13926  23486]
 [ 18221 117507]]
適合率 0.4331974989890192
再現率 0.3722335079653587
F値 0.40040828649060506
```

正解率は`0.7591140117823727`、僅かに下がったが、
再現率は`0.3722335079653587`、かなり良化


#####4着以下のデータ比率を 1倍

```text:Out
アンダーサンプリング 1:1
正解率 0.6473085364444958
混同行列
 [[27155 10257]
 [50808 84920]]
適合率 0.34830624783551173
再現率 0.7258366299583021
F値 0.47072589382448543
```
正解率は`0.6473085364444958`、かなり低下、
適合率は`0.34830624783551173`、かなり低下
再現率は`0.7258366299583021`、かなり良化
これは、取りこぼしは少なくなるが負ける回数も増えるということか？
実際に馬券を買ったときのシミュレーションしてみないとなんとも言えないが、比率は 2倍のほうが良い感じ
馬券シミュレーションの段階で再検証ということで･･･

とりあえず 2倍で続行

###説明変数の標準化
今後、値が大きな説明変数などを使用することも考え標準化を実施

```Python:build.ipynb
from sklearn.preprocessing import StandardScaler

# 説明変数を標準化
sc = StandardScaler()
X_train_rus_std = pd.DataFrame(sc.fit_transform(X_train_rus), columns=X_train_rus.columns)
X_test_std = pd.DataFrame(sc.transform(X_test), columns=X_test.columns)

# 学習
clf.fit(X_train_rus_std, y_train_rus)
# 予測
y_pred = clf.predict(X_test_std)

# 正解率を表示
print('アンダーサンプリング,標準化')
print('正解率', accuracy_score(y_test, y_pred))
# 混同行列を表示
print('混同行列\n', confusion_matrix(y_test, y_pred, labels=[1, 0]))
# 適合率を表示
print('適合率', precision_score(y_test, y_pred))
# 再現率を表示
print('再現率', recall_score(y_test, y_pred))
# F値を表示
print('F値', f1_score(y_test, y_pred))
```

```text:Out
アンダーサンプリング,標準化
正解率 0.7581668014323669
混同行列
 [[ 14143  23269]
 [ 18602 117126]]
適合率 0.43191326920140477
再現率 0.37803378595103176
F値 0.40318143592228856
```
今回、標準化してもしなくても結果に影響はなし
今後、どのような説明変数を追加するかわからないので標準化は組み込んでおくのだ。

第3回はここまで:wave: