Optunaを用いたWC予選突破条件探索


# 初めに
2022 FIFA WC予選もあと２試合を残す飲みですが、今回、Optunaを用いて、日本のWC予選突破条件を探索してみます。

# 今後の試合予定
参照：[2022 FIFAワールドカップ、日本がワールドカップに出場できる確率（2022年2月2日時点）](https://qiita.com/eclipse-hunter-usa/items/b80646e46b5761c5a84d)
2022年3月24日 第一試合、オーストラリア 対 日本
2022年3月24日 第二試合、中国 対 サウジアラビア
2022年3月24日 第三試合、ベトナム 対 オマーン
2022年3月29日 第一試合、日本 対 ベトナム
2022年3月29日 第二試合、サウジアラビア 対 オーストラリア
2022年3月29日 第三試合、オマーン 対 中国

#Optunaとは
Optunaは自動ハイパーパラメータ最適化ソフトウェアフレームワークです。使い方はマニュアルを参照してください。
https://optuna.readthedocs.io/en/stable/index.html

```
import optuna
```

# 各試合での勝ち点からの検索
日本が勝ち点で3位になるケース（アジアプレーオフに回る場合）について検索します。

```
def objective1(trial):
    # point for home team
    m1 = trial.suggest_categorical('m1', [0,1,3]) # 2022年3月24日 第一試合、オーストラリア 対 日本
    m2 = trial.suggest_categorical('m2', [0,1,3]) # 2022年3月24日 第二試合、中国 対 サウジアラビア
    m3 = trial.suggest_categorical('m3', [0,1,3]) # 2022年3月24日 第三試合、ベトナム 対 オマーン
    m4 = trial.suggest_categorical('m4', [0,1,3]) # 2022年3月29日 第一試合、日本 対 ベトナム
    m5 = trial.suggest_categorical('m5', [0,1,3]) # 2022年3月29日 第二試合、サウジアラビア 対 オーストラリア
    m6 = trial.suggest_categorical('m6', [0,1,3]) # 2022年3月29日 第三試合、オマーン 対 中国   
    
    mpg={0:3,1:1,3:0} # point for away team
    
    # Total Victory Point
    jp=18+mpg[m1]+m4
    sa=19+mpg[m2]+m5
    au=15+m1+mpg[m5]
    om=8+mpg[m3]+m6
    cn=5+m2+mpg[m6]
    vn=3+m3+mpg[m4]
    
    MV=[m1,m2,m3,m4,m5,m6]
    VP=[sa,jp,au,om,cn,vn] 
    VPs=sorted(VP)[::-1]  
    jp_rank=VPs.index(jp)+1 # 勝ち点での日本の順位
    
    #検索条件設定、条件に適合する場合にプリント  
    if jp_rank==3:  # 日本が勝ち点で3位になるケース
        print(VP,MV)
    
    return jp_rank

#　検索目的に応じて、maximize、　minimize、設定なし、を使い分けます。
study1 = optuna.create_study(direction=None)
study1.optimize(objective1, n_trials=100)

```

探索の過程で、日本が勝ち点で3位になるケースがあれば、プリントします。

```
# 日本2敗で、サウジがオーストラリアと引き分ける場合、１位サウジ、２位オーストラリア、３位日本
[23, 18, 19, 9, 6, 9] [3, 0, 3, 0, 1, 1]
[20, 18, 19, 12, 8, 7] [3, 3, 1, 0, 1, 3]

# 日本2敗で、サウジがオーストラリアに負け、中国と引き分ける場合、１位オーストラリア、２位サウジ、３位日本
[20, 18, 21, 8, 9, 9] [3, 1, 3, 0, 0, 0]

# 日本2敗で、サウジがオーストラリアに負け、中国にも負ける場合、１位オーストラリア、２位サウジ、３位日本
[19, 18, 21, 11, 8, 9] [3, 3, 3, 0, 0, 3]
```

# 各試合での得点を考慮した検索　
ここでは、日本が、オーストラリアまたはサウジと勝ち点で２位で並ぶ条件について検索します。各チームの得点範囲は0から4の範囲に設定しています。設定は試合毎に自由に変更出来ます。

```
def objective2(trial):
    # goals in each game
    m1_au = trial.suggest_int('m1_au', 0, 4) # 2022年3月24日 第一試合、オーストラリア 対 日本
    m1_jp = trial.suggest_int('m1_jp', 0, 4) # 2022年3月24日 第一試合、オーストラリア 対 日本
    m2_cn = trial.suggest_int('m2_cn', 0, 4) # 2022年3月24日 第二試合、中国 対 サウジアラビア
    m2_sa = trial.suggest_int('m2_sa', 0, 4) # 2022年3月24日 第二試合、中国 対 サウジアラビア
    m3_vn = trial.suggest_int('m3_vn', 0, 4) # 2022年3月24日 第三試合、ベトナム 対 オマーン
    m3_om = trial.suggest_int('m3_om', 0, 4) # 2022年3月24日 第三試合、ベトナム 対 オマーン
    m4_jp = trial.suggest_int('m4_jp', 0, 4) # 2022年3月29日 第一試合、日本 対 ベトナム
    m4_vn = trial.suggest_int('m4_vn', 0, 4) # 2022年3月29日 第一試合、日本 対 ベトナム
    m5_sa = trial.suggest_int('m5_sa', 0, 4) # 2022年3月29日 第二試合、サウジアラビア 対 オーストラリア
    m5_au = trial.suggest_int('m5_au', 0, 4) # 2022年3月29日 第二試合、サウジアラビア 対 オーストラリア
    m6_om = trial.suggest_int('m6_om', 0, 4) # 2022年3月29日 第三試合、オマーン 対 中国 
    m6_cn = trial.suggest_int('m6_cn', 0, 4) # 2022年3月29日 第三試合、オマーン 対 中国  

    def home_away_goal(x,y):
        if x==y:
            home_vp=1
            away_vp=1
        elif x>y:
            home_vp=3
            away_vp=0
        elif x<y:
            home_vp=0
            away_vp=3

        home_df=x-y
        away_df=y-x
        home_tp=x
        away_tp=y 
            
        return home_vp,away_vp,home_df,away_df,home_tp,away_tp
    
    m1_home_vp,m1_away_vp,m1_home_df,m1_away_df,m1_home_tp,m1_away_tp=home_away_goal(m1_au,m1_jp)
    m2_home_vp,m2_away_vp,m2_home_df,m2_away_df,m2_home_tp,m2_away_tp=home_away_goal(m2_cn,m2_sa)
    m3_home_vp,m3_away_vp,m3_home_df,m3_away_df,m3_home_tp,m3_away_tp=home_away_goal(m3_vn,m3_om)
    m4_home_vp,m4_away_vp,m4_home_df,m4_away_df,m4_home_tp,m4_away_tp=home_away_goal(m4_jp,m4_vn)
    m5_home_vp,m5_away_vp,m5_home_df,m5_away_df,m5_home_tp,m5_away_tp=home_away_goal(m5_sa,m5_au)
    m6_home_vp,m6_away_vp,m6_home_df,m6_away_df,m6_home_tp,m6_away_tp=home_away_goal(m6_om,m6_cn)
        
    vp_sa=19+m2_away_vp+m5_home_vp
    vp_jp=18+m1_away_vp+m4_home_vp
    vp_au=15+m1_home_vp+m5_away_vp
    vp_om=8+m3_away_vp+m6_home_vp
    vp_cn=5+m2_home_vp+m6_away_vp
    vp_vn=3+m3_home_vp+m4_away_vp

    df_sa=5+m2_away_df+m5_home_df   
    df_jp=6+m1_away_df+m4_home_df    
    df_au=9+m1_home_df+m5_away_df
    df_om=-2+m3_away_df+m6_home_df   
    df_cn=-8+m2_home_df+m6_away_df
    df_vn=-10+m3_home_df+m4_away_df
 
    tp_sa=10+m2_away_tp+m5_home_tp    
    tp_jp=9+m1_away_tp+m4_home_tp    
    tp_au=15+m1_home_tp+m5_away_tp
    tp_om=8+m3_away_tp+m6_home_tp
    tp_cn=8+m2_home_tp+m6_away_tp
    tp_vn=7+m3_home_tp+m4_away_tp
    
    VP=[vp_sa,vp_jp,vp_au,vp_om,vp_cn,vp_vn] 
    DF=[df_sa,df_jp,df_au,df_om,df_cn,df_vn]
    TP=[tp_sa,tp_jp,tp_au,tp_om,tp_cn,tp_vn]
    GL=[m1_au,m1_jp,m2_cn,m2_sa,m3_vn,m3_om,m4_jp,m4_vn,m5_sa,m5_au,m6_om,m6_cn]
    
    VPs=sorted(VP)[::-1]  
    jp_vp_rank=VPs.index(vp_jp)+1 # 勝ち点での日本の順位
    au_vp_rank=VPs.index(vp_au)+1 # 勝ち点でのオーストラリアの順位
    sa_vp_rank=VPs.index(vp_sa)+1 # 勝ち点でのサウジの順位
    DFs=sorted(DF)[::-1]  
    jp_df_rank=DFs.index(df_jp)+1 # 得失点差での日本の順位
    au_df_rank=DFs.index(df_au)+1 # 得失点差でのオーストラリアの順位
    sa_df_rank=DFs.index(df_sa)+1 # 得失点差でのサウジの順位    
    TPs=sorted(TP)[::-1]  
    jp_tp_rank=TPs.index(tp_jp)+1 # 得点での日本の順位
    au_tp_rank=TPs.index(tp_au)+1 # 得点でのオーストラリアの順位
    sa_tp_rank=TPs.index(tp_sa)+1 # 得点でのサウジの順位    
    
    #日本が、オーストラリアまたはサウジと勝ち点で並ぶ場合はprintする
    if jp_vp_rank==sa_vp_rank==2 or jp_vp_rank==au_vp_rank==2 :  
        print(VP,DF,TP,GL)
    
    return jp_df_rank

#　検索目的に応じて、maximize、　minimize、None、を使い分ける
study2 = optuna.create_study(direction='minimize') 
study2.optimize(objective2, n_trials=100)

```

このプログラムでは日本の得失点差のランキングが上位になるように探索して行きます。その過程で、条件に合った結果がプリントされます。勝ち点が同じ場合でも、得失点差、総得点の結果から、総合順位が決定します。

```
# 日本が、オーストラリアまたはサウジと勝ち点で並ぶ場合
#　勝ち点２位でオーストラリアと並ぶ場合
[22, 21, 21, 8, 8, 6] [6, 6, 13, -6, -9, -10] [15, 13, 21, 11, 13, 11] [3, 1, 1, 4, 3, 1, 3, 1, 1, 3, 2, 4]

# 2022年3月24日 第一試合、オーストラリア 対 日本 = 3:1
# 2022年3月24日 第二試合、中国 対 サウジアラビア = 1:4
# 2022年3月24日 第三試合、ベトナム 対 オマーン = 3:1
# 2022年3月29日 第一試合、日本 対 ベトナム = 3:1
# 2022年3月29日 第二試合、サウジアラビア 対 オーストラリア = 1:3
# 2022年3月29日 第三試合、オマーン 対 中国 = 2:4

その他
[20, 19, 19, 11, 11, 4] [2, 5, 10, -2, -4, -11] [15, 12, 23, 10, 13, 8] [4, 3, 4, 1, 1, 2, 0, 0, 4, 4, 0, 1]
[23, 19, 19, 9, 8, 5] [8, 5, 10, -3, -10, -10] [15, 14, 20, 13, 13, 11] [4, 3, 1, 4, 2, 2, 2, 2, 1, 1, 3, 4]
[25, 18, 18, 8, 8, 9] [9, 3, 9, -6, -7, -8] [17, 11, 19, 11, 14, 12] [3, 1, 2, 4, 3, 2, 1, 2, 3, 1, 1, 4]
いずれも得失点差でオーストラリアに負け、総合３位確定

#勝ち点２位でサウジと並ぶ場合
[19, 19, 21, 14, 8, 4] [0, 4, 12, 2, -7, -11] [11, 9, 19, 13, 12, 8] [2, 0, 4, 0, 1, 2, 0, 0, 1, 2, 3, 0]
得失点差でサウジに勝ち、総合2位確定

```
全てのケースを網羅しているわけではないので、一度の実行で、条件に合致するケースが見つからない場合もあり得ます。

# 終わりに
よりマニアックな設定を考えてみて、動かしてみると楽しいと思います。Optunaの使い方に慣れる意味でも、いい題材かと思います。Optunaは、高速に条件に合うケースを見つけてくれますので、モデルのパラメータtuningだけでなく、工夫次第で色々な場面で使えそうな気がします。

## 参考
[2022 FIFAワールドカップ、日本がワールドカップに出場できる確率（2022年2月2日時点）](https://qiita.com/eclipse-hunter-usa/items/b80646e46b5761c5a84d)
[【Optuna】ハイパーパラメータの自動最適化ライブラリの使い方例](https://qiita.com/nokomoro3/items/2fd19412f8d41c8971bf)