果たして自作競艇予測モデルは進歩したのか ~t検定~


はじめに

この記事は、自身で製作しWeb公開中のボートレース3連単予測サイト「きょう、ていの良い予想は当たるだろうか」の解説記事となります。その他のまとめ記事は以下リンクにまとめております。
・機械学習とFlaskを使ってボートレース3連単予測サイトを立ち上げるまで

今回は初めて統計に関して触れます。t検定の考え方が間違っている、怪しいところがあればご指摘いただけると大変助かります。

自作予測モデルは果たして成長しているのか?

運営を開始してから約2ヶ月。少しずつ見てもらえる人も増えており、嬉しい限りです。
そして、さらなるWebサイト成長のため、7月に予測モデルをマイナーアップデートしておりました。今回は、6月15日〜7月7日の予測結果と7月8日以降の予測結果をt検定にかけ、予測モデルが成長しているか否かを統計的に判断したいと思います。


↑7月8日にモデルアップデートしたときのツイート。その後幾度も「アップデートやるやる詐欺」をかまし今に至る。8月にやります。..たぶん。

t検定について

「統計WEB」にはいつも大変お世話になっております。t検定に関しての解説はこちらが参考になります。

今回は①6月15日〜7月7日の予測結果(平均的中率、平均回収率)と②7月8日~31日の予測結果、の2つを比較します。

2つの異なる標本①②に対して、それぞれ別の予測モデルを適用するので、若干怪しさはあるのですが、ここでは”標本①、②の間にレース傾向の有意な差はない”と仮定し、あくまで予測モデルによって、予測精度に有意な差が生まれたかどうかを判定したいと思います。

(補足:別例を挙げると、同じ学力を持つ(と仮定できる)1組、2組にそれぞれ違うテストを受けてもらい、その平均点の差からテストの難易度の差を統計的に判断する、ということをやっています。本当ならば1組に2種類のテストを試した方が良さそうですよね..。)

的中率の平均値、標準偏差

見辛くてすみませんが、最後の平均、標準偏差が肝になります。
標本①:6月15日〜7月7日の予測結果
標本②:7月8日~31日の予測結果

標本①__ 的中率 標本①__ 回収率 標本②__ 的中率 標本②__ 回収率
Day1 11.27 112.54 9.8 69.8
Day2 10.29 82.5 8.62 48.45
Day3 13.16 106.84 10.87 100.87
Day4 12.9 101.13 10.2 78.57
Day5 5.56 38.06 11.11 94.17
Day6 5 30.67 17.86 130.36
Day7 8.22 69.86 6.15 37.08
Day8 12.07 118.97 14.29 119.71
Day9 12.73 119.64 8 55.2
Day10 6.06 61.06 5.17 33.97
Day11 15.38 95.23 19.44 196.94
Day12 7.81 56.88 9.43 103.77
Day13 8.7 54.78 11.36 66.14
Day14 13.4 128.14 13.73 78.43
Day15 12.35 106.05 11.11 89.33
Day16 6.67 42.67 16.67 92.86
Day17 15.79 129.12 7.84 58.63
Day18 7.14 57.32 14 120.8
Day19 8.89 53.56 6.98 46.98
Day20 11.39 66.58 13.11 98.85
Day21 9.64 77.59 14.04 84.04
Day22 2.86 20 5.66 39.25
Day23 7.35 71.47
Average 9.8 78.3 11.2 83.8
Standard 3.51 32.34 4.00 38.62

ぱっと見は、モデルをマイナーアップデートしたことで的中率や回収率が上がっているようにも見えます。
(回収率が100%超えていないのは、突っ込まないでください。笑 苦手競艇場に引っ張られている傾向があります。)

scipyでウェルチのt検定を行う

scipyによる記述はこちらのページを参考にさせて頂きました。↓
【Python】2群間での統計検定手法まとめ

帰無仮説を”標本①と標本②の的中率、回収率に差はない”とし、有意水準5%で棄却することとします。

import numpy as np
from scipy import stats

#的中率のリスト
Hit_1 = np.array([11.27,10.29,13.16,12.9,5.56,5,8.22,12.07,12.73,6.06,15.38,\
              7.81,8.7,13.4,12.35,6.67,15.79,7.14,8.89,11.39,9.64,2.86,7.35])
Hit_2 = np.array([9.8,8.62,10.87,10.2,11.11,17.86,6.15,14.29,8,5.17,19.44,9.43,\
              11.36,13.73,11.11,16.67,7.84,14,6.98,13.11,14.04,5.66])

#回収率のリスト
Payoff_1 = np.array([112.54,82.5,106.84,101.13,38.06,30.67,69.86,118.97,119.64,\
                     61.06,95.23,56.88,54.78,128.14,106.05,42.67,129.12,57.32,\
                     53.56,66.58,77.59,20.0,71.47])
Payoff_2 = np.array([69.8,48.45,100.87,78.57,94.17,130.36,37.08,119.71,55.2,\
                     33.97,196.94,103.77,66.14,78.43,89.33,92.86,58.63,120.8,\
                     46.98,98.85,84.04,39.25])

#t検定の結果
print(stats.ttest_ind(Hit_1, Hit_2, equal_var=False))
print(stats.ttest_ind(Payoff_1, Payoff_2, equal_var=False))

scipyってすごいですね。

結果...

8月、しっかりと機械学習モデルアップデートします、ぜったい..。(真顔)

7月の方が性能が上がっている様にみえるのは、まだ”バラツキ”の範囲内と判断され、帰無仮説は棄却できませんでした。↓

さいごに

こうやって、主観に囚われず判断できるのが統計のいいところですよね。
胸を張って「予測性能改善しました!」と言える様になるまで、頑張ります。

また統計に詳しい方で、今回のt検定の考え方が間違っている様でしたらご指摘いただけると大変助かります。