大阪とかで働くスタッツ厨

バスケットボールのスタッツとかが好きな人。 Run and gunが大好きなサンズファン。 懐古厨。

昨日の結果から今日の結果を予測できるか  ――機械学習を用いた予測:ロジスティック回帰・ランダムフォレスト・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が解析したりと,何かとバスケットと人工知能との関りが最近アツイのでこうした記事をもっと書いていきたいと思います。

 

それでは。