サッカー勝敗予測 – K近傍法

目的

前回は線形SVMを使って、ブンデスリーガの勝敗予測を試みたが、良い結果が得られなかった。

https://masalabo.blog/2022/02/08/post-268/

なので今回はScikitlearnチートシートに従ってK近傍法を試してみる。

K近傍法とは

Wikipediaによると、

k近傍法は、特徴空間における最も近い訓練例に基づいた分類の手法である。

wikipedia

訓練データと似た対戦チーム組み合わせなら、同じ結果になるだろうと予測する手法である。

実装

コードは以下になる。

線形SVMのときと違うのはKNeighborsClassifierのところだけ。

from sklearn.neighbors import KNeighborsClassifier

# ブンデスリーガデータ読み込み
df = data_catalog.load('match_result')

# チーム名をダミー変数に変換
df_cat = None
for col in ['HomeTeam', 'AwayTeam']:
    tmp = pd.get_dummies(df[col], prefix=col)
    df_cat = pd.concat([df_cat, tmp], axis=1)
cat_col = df_cat.columns
df = pd.concat([df, df_cat], axis=1)

# 勝ち・負け・引き分けをコード値に変換
df['game_result'] = df['game_result'].astype('category')
df['game_result_cd'] = df['game_result'].cat.codes
display(dict(enumerate(df['game_result'].cat.categories)))
"""{0: 'A', 1: 'D', 2: 'H'}"""

df_train, df_test = train_test_split(df, test_size=0.1, random_state=42)

X = df_train[cat_col]
y = df_train['game_result_cd']
X_test = df_test[cat_col]
y_test = df_test['game_result_cd']

neigh = KNeighborsClassifier(n_neighbors=3)
neigh.fit(X, y)

print(f"train accuracy: {accuracy_score(y, neigh.predict(X))}")
"""train accuracy: 0.5028322440087146"""
print(f"test accuracy: {accuracy_score(y_test, neigh.predict(X_test))}")
"""test accuracy: 0.40784313725490196"""

予測モデルを訓練して、テストデータで予測してみたが、正解率は40%となった。

パラメタ最適化

K近傍法のパラメタ Kを最適化して予測精度を上げてみる。
Kは1~20の間で探索してみた。

from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV

# ブンデスリーガデータ読み込み
df = data_catalog.load('match_result')

# チーム名をダミー変数に変換
df_cat = None
for col in ['HomeTeam', 'AwayTeam']:
    tmp = pd.get_dummies(df[col], prefix=col)
    df_cat = pd.concat([df_cat, tmp], axis=1)
cat_col = df_cat.columns
df = pd.concat([df, df_cat], axis=1)

# 勝ち・負け・引き分けをコード値に変換
df['game_result'] = df['game_result'].astype('category')
df['game_result_cd'] = df['game_result'].cat.codes

df_train, df_test = train_test_split(df, test_size=0.1, random_state=42)

X = df_train[cat_col]
y = df_train['game_result_cd']
X_test = df_test[cat_col]
y_test = df_test['game_result_cd']

tuned_parameters = [{'n_neighbors': [x for x in range(1, 20)]}]

neigh = GridSearchCV(KNeighborsClassifier(),
                   tuned_parameters,
                   cv=5,
                   scoring='accuracy')
neigh.fit(X, y)

print(neigh.best_params_)
"""{'n_neighbors': 16}"""

print(f"train accuracy: {accuracy_score(y, neigh.predict(X))}")
"""train accuracy: 0.5291212781408859"""
print(f"test accuracy: {accuracy_score(y_test, neigh.predict(X_test))}")
"""test accuracy: 0.47843137254901963"""

結果としてはK=16で訓練データに対して、精度が最高になった。
テストデータに対しての正解率は48%であり、結果的には前回の線形SVMよりも悪い結果となってしまった。

考察

やはり特徴量がホームチームとアウェイチームのワンホットエンコードベクトルというのが良くないのだろうか。
それとも、K近傍法の相性が良くないのか。
次は線形SVM以外のSVMを試してみようと思う。