女子卓球レーティング

囲碁将棋などで有名な実力計算方式である"イロレーティング"を卓球に応用し、ITTFランキングでは測れない「実力」を試算しました。イロレーティングをもとに、トーナメントドローに応じたモンテカルロシミュレーションを加えた勝敗予想を提供します。さらにイロレーティングを超える「AIレーティング」を開発しています。

【テクニカル】AIレーティング活用開始・その前に

2月の試合データが揃ったので、AIレーティングを実際に活用開始してみます。その前に、ここ1週間でいろいろ学んだ要素をAIレーティングに追加してみました。

追加要素 内容
検証データのランダム抽出 前回は最終月のみ検証データとしましたが、通常通りデータ全体からランダムに抽出することにしました。scikit-learnのtrain_test_split関数を使います。デフォルトは、全データの25%を検証データとして抽出します。
正則化項の追加 モデルが極端なウエイトになって不安定になることを防止するため、正則化項を追加しました。
バッチサイズの見直し バッチサイズは2の累乗がよく、データが数万ある場合は1024がよい、とのことなので修正しました。
アーリーストッピング モデルが過学習になる段階を自動判別して、学習を早期終了させるようにしました。
最良モデルのセーブ アーリーストッピングはモデルが劣化しはじめたことを検出するため、最終モデルが最良とは限りません。そのため、最良モデルをセーブするようにしました。

また、モデルについてもいろいろ試した結果、安定かつ成績もそれなりな、隠れ層なしモデルにしました。

最終的なソースは以下のようになりました。

import tensorflow as tf
import numpy as np
from tensorflow_core.python.keras.metrics import binary_accuracy
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 前処理は省略
# match_featuresは試合ごとの特徴量のリスト
# match_lablesは試合ごと正解ラベル(どちらが勝ったかを1または0で表す)のリスト
# 対象試合は2018年以降の有効試合(試合成立、レーティングやランキングが計測可能など)

ds_features = np.array(match_features)
ds_labels = np.array(match_labels)
ms = MinMaxScaler()
ds_features = ms.fit_transform(ds_features)  # 正規化

# データセットを訓練用と検証用に分割
training_features, validation_features, training_labels, validation_labels  = train_test_split(ds_features, ds_labels)

# モデル準備 (正則化項を追加)
INPUT_FEATURES = ds_features.shape[1]   # 特徴量の次元
LAYER_NEURONS = INPUT_FEATURES * 2   # 入力次元より少し広げる
OUTPUT_RESULTS = 1  # 出力は一次元
ACTIVATION = 'tanh'
model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(input_shape=(INPUT_FEATURES,), units=OUTPUT_RESULTS, activation='sigmoid',
                          kernel_regularizer=tf.keras.regularizers.l2(0.001)),
# 隠れ層を有効にする際は下記のコードを使う
#    tf.keras.layers.Dense(input_shape=(INPUT_FEATURES,), units=LAYER_NEURONS, activation=ACTIVATION,
#                          kernel_regularizer=tf.keras.regularizers.l2(0.001)),
#    tf.keras.layers.Dense(units=OUTPUT_RESULTS, activation='sigmoid'),
])
LOSS = 'binary_crossentropy'
OPTIMIZER = tf.keras.optimizers.Adam   # 典型的な最適化手法
LEARNING_RATE = 0.03   # 学習係数のよくある初期値
model.compile(optimizer=OPTIMIZER(lr=LEARNING_RATE), loss=LOSS, metrics=[binary_accuracy])

# 学習(アーリーストッピングを行い、それまでの最良のモデルをセーブする)
BATCH_SIZE = 1024
EPOCHS = 200
model_path = './model'
es_cb = EarlyStopping(monitor='val_loss', patience=20, verbose=1, mode='auto')
cp_cb = ModelCheckpoint(filepath=model_path, monitor='val_loss', verbose=1,
                        save_best_only=True, save_weights_only=False, mode='auto')
result = model.fit(x=training_features, y=training_labels,
                   validation_data=(validation_features, validation_labels),
                   batch_size=BATCH_SIZE, epochs=EPOCHS, verbose=1, callbacks=[es_cb, cp_cb], shuffle=True)

# 表示
number_of_epochs_it_ran = len(result.history['loss'])
plt.plot(range(1, number_of_epochs_it_ran+1), result.history['binary_accuracy'], label="training")
plt.plot(range(1, number_of_epochs_it_ran+1), result.history['val_binary_accuracy'], label="validation")
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

これを何回か実行して、よさそうな結果を採用することにします。

最終的な学習曲線はこのようになりました。

f:id:miumima:20200303104856p:plain

この時の検証データ精度は、イロレーティング72.5%、AIレーティング74.7%となっています。AIレーティングの方が約2ポイント優っており、予想結果にどう影響するかを見ていきたいと思います。