昨日の結果から今日の結果を予測できるか ――機械学習を用いた予測:ロジスティック回帰・ランダムフォレスト・SVM――
どうもaoiです。ついに昨日Bリーグが開幕しましたね。
BSで三河と栃木の試合観ましたが,栃木ではチケット完売だったそうでバスケット文化が根付きつつあるのかなと感じました。
フリースロー時のブーイングもNBAのようで驚きました。日本人はああいうの苦手だと思っていたので。
さて,今回は試合の勝敗をどうにか予想してみたいと思います。
予測というからにはティップオフの前に勝敗予想を立てたいところですが,如何せん試合前に手に入れられる情報は限られます。例えば,対戦相手,HOME/AWAY,過去の対戦成績,けが人の数,くらいのものだと思います。
これだけでも,予測できなくはないと思いますが,もう少し情報が欲しいところです。
現実的にどれほど意味を為すかという,メカニズム的な部分は一旦置いておいて,事前に入手できる情報として,今回は前の試合のスタッツを用いて試合の結果を予測したいと思います。
方法としては,Four Factorsの時と同様にデータからモデルを作成し,別のデータ当てはめることで予測するというプロセスになります。今回は(昨今流行りの)機械学習を用いて予測していきたいと思います。手法としてはランダムフォレストとサポートベクターマシーン(SVM)を用いました。レギュラーシーズンのデータからモデルを作成し,プレイオフの試合を予測しました。
間違い等多々ある可能性が大なのでお気づきの方はご指摘頂けますと幸いです。(これも流行りの炎上ラーニング)
なお,データは全てBasketball-Reference.comの16-17シーズンゲームログから入手しております。ホーム,対戦チームの事前にわかるデータについては当該試合のデータを用いています。3p成功率などの事前に手に入らないデータは一試合まえのデータを使用しています。データが欲しい方はTwitterから連絡いただければエクセルファイルでお送りいたします。
また,本稿を執筆するにあたって以下のサイト等を参考にしました。
パッケージユーザーのための機械学習(3):サポートベクターマシン(SVM)(http://tjo.hatenablog.com/entry/2013/12/03/183055)
SVMについて(https://www.slideshare.net/mknh1122/svm-13623887)
SVM のチューニングのしかた(2)(http://d.hatena.ne.jp/hoxo_m/20110325/p1)
1-ロジスティック回帰
まずは王道を征くロジスティック回帰で予測モデルを作ってみます。すべてRを用いて分析を行っています。
コードは以下の通り。
#データの読み込み 上がレギュラーシーズンのデータ
> reg = read.csv("pred.game.csv")
> playoff = read.csv("pred.game.playoff.csv")
>
> #学習データとテストデータに分ける
> n = nrow(reg)
> A = sample(n,n*0.5)
> data.L = reg[A,]
> data.T = reg[-A,]
>
> #回帰分析
> m1 = glm(W1L0~., data.L, family = binomial)
>
> #変数選択
> m2 = step(m1)
中略
> summary(m2)
Call:
glm(formula = W1L0 ~ home1 + FG + FTA + TRB + AST + FG_opp +
X3P_opp + X3PA_opp + X3P_p_opp + STL_opp + BLK_opp + TOV_opp +
ORtg + X3PAr + TS_p + AST_p + ORB_p + eFG_p_opp + TOV_p_opp +
FT.FGA_opp + BRK + CHI + CHO + CLE + DAL + DET + GSW + HOU +
LAL + MIA + NOP + NYK + ORL + PHI + PHO + SAC + SAS + UTA +
ATL_opp + BOS_opp + BRK_opp + CLE_opp + DAL_opp + GSW_opp +
HOU_opp + LAC_opp + LAL_opp + MEM_opp + MIL_opp + PHO_opp +
SAS_opp + UTA_opp, family = binomial, data = data.L)
Deviance Residuals:
Min 1Q Median 3Q Max
-2.4751 -0.9196 -0.2959 0.9621 2.3364
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 4.22022 3.77880 1.117 0.264073
home1 -1.01548 0.13521 -7.510 5.90e-14 ***
FG -0.26199 0.08709 -3.008 0.002626 **
FTA -0.05586 0.01642 -3.401 0.000671 ***
TRB 0.04319 0.02246 1.923 0.054523 .
AST 0.23679 0.13145 1.801 0.071637 .
FG_opp 0.18347 0.06668 2.752 0.005931 **
X3P_opp 0.23751 0.12109 1.961 0.049823 *
X3PA_opp -0.07052 0.04234 -1.666 0.095793 .
X3P_p_opp -4.40852 2.93767 -1.501 0.133437
STL_opp 0.04989 0.03309 1.508 0.131676
BLK_opp -0.08288 0.02868 -2.890 0.003856 **
TOV_opp -0.32420 0.20461 -1.584 0.113087
ORtg 0.08887 0.02553 3.480 0.000501 ***
3PAr -2.89855 1.33738 -2.167 0.030209 *
TS_p -8.08222 3.51759 -2.298 0.021581 *
AST_p -0.09325 0.05059 -1.843 0.065301 .
ORB_p -0.04814 0.01673 -2.878 0.004006 **
eFG_p_opp -10.39052 5.50168 -1.889 0.058944 .
TOV_p_opp 0.47531 0.25190 1.887 0.059175 .
FT.FGA_opp 5.09996 1.67204 3.050 0.002287 **
BRK -1.13407 0.39227 -2.891 0.003839 **
CHI -0.68178 0.37539 -1.816 0.069343 .
CHO -0.79222 0.35937 -2.204 0.027491 *
CLE 1.02564 0.46070 2.226 0.025997 *
DAL -0.80441 0.36176 -2.224 0.026175 *
DET -0.72568 0.38782 -1.871 0.061316 .
GSW 1.66595 0.49671 3.354 0.000797 ***
HOU 1.39149 0.44012 3.162 0.001569 **
LAL -1.12874 0.41194 -2.740 0.006143 **
MIA -0.59414 0.36439 -1.630 0.102999
NOP -0.63590 0.37434 -1.699 0.089373 .
NYK -0.68804 0.38943 -1.767 0.077268 .
ORL -1.15863 0.37535 -3.087 0.002023 **
PHI -0.97424 0.37220 -2.618 0.008857 **
PHO -0.94938 0.40102 -2.367 0.017914 *
SAC -0.59027 0.34591 -1.706 0.087924 .
SAS 0.71962 0.45582 1.579 0.114396
UTA 1.07460 0.40343 2.664 0.007730 **
ATL_opp -0.76288 0.37384 -2.041 0.041287 *
BOS_opp -1.39995 0.41000 -3.415 0.000639 ***
BRK_opp 1.06124 0.39330 2.698 0.006970 **
CLE_opp -0.82108 0.35758 -2.296 0.021663 *
DAL_opp 0.51514 0.33983 1.516 0.129546
GSW_opp -1.73876 0.41910 -4.149 3.34e-05 ***
HOU_opp -0.85714 0.38750 -2.212 0.026969 *
LAC_opp -1.10474 0.40115 -2.754 0.005888 **
LAL_opp 0.64615 0.36544 1.768 0.077036 .
MEM_opp -1.43564 0.43687 -3.286 0.001016 **
MIL_opp -0.68822 0.37076 -1.856 0.063422 .
PHO_opp 0.73751 0.39060 1.888 0.059003 .
SAS_opp -1.88128 0.41503 -4.533 5.82e-06 ***
UTA_opp -0.82797 0.39484 -2.097 0.035997 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 1684.3 on 1214 degrees of freedom
Residual deviance: 1383.2 on 1162 degrees of freedom
AIC: 1489.2
Number of Fisher Scoring iterations: 4
> exp(m2$coefficients) #オッズ比
home1 0.362228518
FG 0.769518719
FTA 0.945671521
TRB 1.044136262
AST 1.267174983
FG_opp 1.201378924
X3P_opp 1.268087678
X3PA_opp 0.931909101
X3P_p_opp 0.012173181
STL_opp 1.051155463
BLK_opp 0.920461596
TOV_opp 0.723105607
ORtg 1.092938565
3PAr 0.055103062
TS_p 0.000308984
AST_p 0.91096573
ORB_p 0.953000358
eFG_p_opp 3.07224E-05
TOV_p_opp 1.608512759
FT.FGA_opp 164.0153466
BRK 0.321721183
CHI 0.505716016
CHO 0.452838377
CLE 2.788879773
DAL 0.447351786
DET 0.48399534
GSW 5.290697025
HOU 4.020836638
LAL 0.323440535
MIA 0.552037114
NOP 0.529458761
NYK 0.502560122
ORL 0.313915951
PHI 0.377479129
PHO 0.386980877
SAC 0.554177637
SAS 2.053652674
UTA 2.928821138
ATL_opp 0.466321485
BOS_opp 0.246609294
BRK_opp 2.889952309
CLE_opp 0.439956245
DAL_opp 1.673872828
GSW_opp 0.175738181
HOU_opp 0.424374058
LAC_opp 0.331297008
LAL_opp 1.908180175
MEM_opp 0.237963019
MIL_opp 0.50246967
PHO_opp 2.090723127
SAS_opp 0.152394915
UTA_opp 0.436935365
> #テストデータへの当てはめ> p = predict(m2, newdata = data.T)> pred = exp(p)> pred1 = ifelse(pred > 1, 1, 0)> table(pred1 ,data.T$W1L0) pred1 0 1 0 359 232 1 246 378 > sum(diag(table(pred1 ,data.T$W1L0)))/nrow(data.T) #精度[1] 0.6065844 #約60%の正解率
指標についてですが,_p=%,_opp = 相手チーム 例 eFG_p_opp = 相手チームのeFG%です。(分かりにくくて済みません)
ロジスティック回帰では約60%の精度で一試合前のスタッツから勝敗を予測することが出来ました。ランダムでも50%で勝敗を当てられると考えると全然精度高くないですね…
結果の中ではこの辺が面白いですね。
GSW 5.290697025 オッズ比
GSW_opp 0.175738181
PHO_opp 2.090723127
GSWは他のチームに比べ勝つ確率が5.2倍!!
逆に相手チームがGSWだと自チームの勝つ確率が0.17倍になってしまいます。。。
一方,相手チームがPHOだと勝つ確率が2倍になるみたいです(泣き笑い)。
また,
FG 0.769518719 オッズ比
FTA 0.945671521
一試合前のFGが一本増えると次の試合の勝率が減るという常識的にはおかしい状態が起きていますが,この辺りが,一試合前のデータから勝敗を予想するというアイデアの限界なのかもしれません。
2-ランダムフォレスト
ランダムフォレストってなんだよ!!!という方はこちら(https://www.slideshare.net/teppeibaba5/ss-37143977?qid=2c2640ec-805a-44b7-a082-cc57143a2d70&v=&b=&from_search=1)をご覧ください(丸投げ)
さて早速結果を見てみましょう。
> reg = read.csv("pred.game.csv")
playoff = read.csv("pred.game.playoff.csv")
n = nrow(reg)
A = sample(n,n*0.5)
data.L = reg[A,]
data.T = reg[-A,]
tuneRF(data.L[,-3], factor(data.L[,3]), doBest = T)
mtry = 10 OOB error = 44.28% Searching left ...mtry = 5 OOB error = 47.57% -0.07434944 0.05 Searching right ...mtry = 20 OOB error = 45.68% -0.03159851 0.05 Call: randomForest(x = x, y = y, mtry = res[which.min(res[, 2]), 1]) Type of random forest: classification Number of trees: 500No. of variables tried at each split: 10 OOB estimate of error rate: 42.63%Confusion matrix: 0 1 class.error0 361 246 0.40527181 272 336 0.4473684> forest = randomForest(data.L[,-3], factor(data.L[,3]), + mtry = 10,+ ntree = 1000,+ importance = T)> forest Call: randomForest(x = data.L[, -3], y = factor(data.L[, 3]), ntree = 1000, mtry = 10, importance = T) Type of random forest: classification Number of trees: 1000No. of variables tried at each split: 10 OOB estimate of error rate: 43.79%Confusion matrix: 0 1 class.error0 355 252 0.41515651 280 328 0.4605263> pred.test = predict(forest, data.T)> table(pred.test, data.T$W1L0) pred.test 0 1 0 356 279 1 253 327> sum(diag(table(pred.test, data.T$W1L0)))/nrow(data.T)[1] 0.5621399> > pred.playoff = predict(forest, playoff)> table(pred.playoff, playoff$W1L0) pred.playoff 0 1 0 42 26 1 29 45> sum(diag(table(pred.playoff, playoff$W1L0)))/nrow(playoff)[1] 0.6126761 #約61%
OOBエラー43%の時点で大分やっちまったな感が否めません。
つまり精度が低いってことです。パラメータを調整すると多少の改善は見られましたが,そもそもの予測パラダイムの無理さ加減が出てきていると思います。
結果的には,ロジスティック回帰とほぼ同じ精度になりました。
ここまでくると,やはり無理だったか感が濃厚ですね。。。
3-SVM
サポートベクターマシーンって(以下略)こちら(https://www.slideshare.net/mknh1122/svm-13623887)をご覧ください。
さてさて,どうなるでしょう。
> reg = read.csv("pred.game.csv")> playoff = read.csv("pred.game.playoff.csv")> m_svm = ksvm(factor(W1L0)~., data = reg, cross = 12)> m_svmSupport Vector Machine object of class "ksvm" SV type: C-svc (classification) parameter : cost C = 1 Gaussian Radial Basis kernel function. Hyperparameter : sigma = 0.00454899390363163 Number of Support Vectors : 2043 Objective Function Value : -1665.372 Training error : 0.239506 Cross validation error : 0.379857> pred_svm = predict(m_svm,playoff)> table(pred_svm,playoff$W1L0)
pred_svm 0 1
0 43 17
1 28 54
sum(diag(table(pred_svm, playoff$W1L0)))/nrow(playoff)
[1] 0.6830986 #精度
ここで,約70%弱の精度まで上がりました。
大分期待薄だったのでデフォルトでただ突っ込んでいただけなので,パラメータを調整すれば70%も夢ではなさそうです。
まとめ
以上3つのモデルだとSVMが最も精度が出ました。ただ,一試合前のスタッツから試合の勝敗を予測するというアイデアがあんまり巧いものではなかったことが分かりました(知ってたって人も少なくないでしょう)。
NBAのスカウトがIBMのワトソンが代行したり,トラッキングシステムをAIが解析したりと,何かとバスケットと人工知能との関りが最近アツイのでこうした記事をもっと書いていきたいと思います。
それでは。