9-2. 머신러닝으로 FIFA 19 선수 포지션 예측하기_2 [SVM]

SVM알고리즘을 사용하여 FIFA 19데이터의 포지션을 예측해본다.

[main code]

머신러닝으로 FIFA 19 선수 포지션 예측하기[SVM]

import pandas as pd
import pickle
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

문제 정의

FIFA 19의 선수 스텟을 바탕으로, 그 선수의 포지션을 예측하라

데이터 수집 및 전처리

# 데이터를 수집합니다
df = pd.read_csv("../data/fifa_data.csv")
# 수집된 데이터 샘플을 확인합니다
df.head()
Unnamed: 0IDNameAgePhotoNationalityFlagOverallPotentialClub...ComposureMarkingStandingTackleSlidingTackleGKDivingGKHandlingGKKickingGKPositioningGKReflexesRelease Clause
00158023L. Messi31https://cdn.sofifa.org/players/4/19/158023.pngArgentinahttps://cdn.sofifa.org/flags/52.png9494FC Barcelona...96.033.028.026.06.011.015.014.08.0€226.5M
1120801Cristiano Ronaldo33https://cdn.sofifa.org/players/4/19/20801.pngPortugalhttps://cdn.sofifa.org/flags/38.png9494Juventus...95.028.031.023.07.011.015.014.011.0€127.1M
22190871Neymar Jr26https://cdn.sofifa.org/players/4/19/190871.pngBrazilhttps://cdn.sofifa.org/flags/54.png9293Paris Saint-Germain...94.027.024.033.09.09.015.015.011.0€228.1M
33193080De Gea27https://cdn.sofifa.org/players/4/19/193080.pngSpainhttps://cdn.sofifa.org/flags/45.png9193Manchester United...68.015.021.013.090.085.087.088.094.0€138.6M
44192985K. De Bruyne27https://cdn.sofifa.org/players/4/19/192985.pngBelgiumhttps://cdn.sofifa.org/flags/7.png9192Manchester City...88.068.058.051.015.013.05.010.013.0€196.4M

5 rows × 89 columns

df1 = pd.DataFrame({'Name': df.Name,'Club':df.Club,'Position':df.Position,}) #선수정보데이터
df2 = df.iloc[:,54:88].astype(float) #스텟데이터

td = pd.concat([df1,df2],axis=1)
td
NameClubPositionCrossingFinishingHeadingAccuracyShortPassingVolleysDribblingCurve...PenaltiesComposureMarkingStandingTackleSlidingTackleGKDivingGKHandlingGKKickingGKPositioningGKReflexes
0L. MessiFC BarcelonaRF84.095.070.090.086.097.093.0...75.096.033.028.026.06.011.015.014.08.0
1Cristiano RonaldoJuventusST84.094.089.081.087.088.081.0...85.095.028.031.023.07.011.015.014.011.0
2Neymar JrParis Saint-GermainLW79.087.062.084.084.096.088.0...81.094.027.024.033.09.09.015.015.011.0
3De GeaManchester UnitedGK17.013.021.050.013.018.021.0...40.068.015.021.013.090.085.087.088.094.0
4K. De BruyneManchester CityRCM93.082.055.092.082.086.085.0...79.088.068.058.051.015.013.05.010.013.0

18207 rows × 37 columns

tmp = td.columns
  • 분류에 필요없는 스텟을 제거한다.
del td[tmp[9]]
del td[tmp[10]]
del td[tmp[11]]
del td[tmp[15]]
del td[tmp[16]]
del td[tmp[17]]
del td[tmp[18]]
del td[tmp[19]]
del td[tmp[20]]
del td[tmp[21]]
del td[tmp[22]]
del td[tmp[23]]
del td[tmp[28]]
td.columns
Index(['Name', 'Club', 'Position', 'Crossing', 'Finishing', 'HeadingAccuracy',
       'ShortPassing', 'Volleys', 'Dribbling', 'BallControl', 'Acceleration',
       'SprintSpeed', 'Interceptions', 'Positioning', 'Vision', 'Penalties',
       'Marking', 'StandingTackle', 'SlidingTackle', 'GKDiving', 'GKHandling',
       'GKKicking', 'GKPositioning', 'GKReflexes'],
      dtype='object')
# 현재 가지고 있는 데이터에서, 포지션의 갯수를 확인한다
td.Position.value_counts()
ST     2152
GK     2025
CB     1778
CM     1394
LB     1322
RB     1291
RM     1124
LM     1095
CAM     958
CDM     948
RCB     662
LCB     648
LCM     395
RCM     391
LW      381
RW      370
RDM     248
LDM     243
LS      207
RS      203
RWB      87
LWB      78
CF       74
LAM      21
RAM      21
RF       16
LF       15
Name: Position, dtype: int64
  • 비슷한 역할을 하는 포지션끼리는 합쳐서 포지션별 데이터의 수를 늘린다.
td.loc[td['Position']=='LF', ['Position']] = 'ST'
td.loc[td['Position']=='RF', ['Position']] = 'ST'
td.loc[td['Position']=='CF', ['Position']] = 'ST'
td.loc[td['Position']=='LS', ['Position']] = 'ST'
td.loc[td['Position']=='RS', ['Position']] = 'ST'
td.loc[td['Position']=='LAM', ['Position']] = 'CAM'
td.loc[td['Position']=='RAM', ['Position']] = 'CAM'
td.loc[td['Position']=='LCM', ['Position']] = 'CM'
td.loc[td['Position']=='RCM', ['Position']] = 'CM'
td.loc[td['Position']=='RDM', ['Position']] = 'CDM'
td.loc[td['Position']=='LDM', ['Position']] = 'CDM'
td.loc[td['Position']=='LW', ['Position']] = 'WF'
td.loc[td['Position']=='RW', ['Position']] = 'WF'
td.loc[td['Position']=='LB', ['Position']] = 'WB'
td.loc[td['Position']=='RB', ['Position']] = 'WB'
td.loc[td['Position']=='LWB', ['Position']] = 'WB'
td.loc[td['Position']=='RWB', ['Position']] = 'WB'
td.loc[td['Position']=='LM', ['Position']] = 'WM'
td.loc[td['Position']=='RM', ['Position']] = 'WM'
td.loc[td['Position']=='LCB', ['Position']] = 'CB'
td.loc[td['Position']=='RCB', ['Position']] = 'CB'
# 현재 가지고 있는 데이터에서, 포지션의 갯수를 확인한다
td.Position.value_counts()
CB     3088
WB     2778
ST     2667
WM     2219
CM     2180
GK     2025
CDM    1439
CAM    1000
WF      751
Name: Position, dtype: int64
td = td.dropna() #null값 제거

데이터 나누기 (학습 데이터, 테스트 데이터)

# sklearn의 train_test_split을 사용하면 라인 한줄로 손쉽게 데이터를 나눌 수 있다
from sklearn.model_selection import train_test_split

# 다듬어진 데이터에서 20%를 테스트 데이터로 분류합니다
train, test = train_test_split(td, test_size=0.2)
# 학습 데이터의 갯수를 확인합니다, 14565개의 데이터가 있습니다.
train.shape[0]
14334
# 테스트 데이터의 갯수를 확인합니다. 3642개의 데이터가 있습니다.
test.shape[0]
3584

다듬어진 데이터를 파일로 저장하기

다듬어진 데이터를 파일로 저장하여, 머신러닝 분류 알고리즘 실습 시에 사용하도록 하겠습니다.

with open('../data/fifa_train.pkl', 'wb') as train_data:
    pickle.dump(train, train_data)
    
with open('../data/fifa_test.pkl', 'wb') as test_data:
    pickle.dump(test, test_data)

데이터 불러오기 (학습 데이터, 테스트 데이터)

학습 데이터 및 테스트 데이터를 로드합니다.

with open('../data/fifa_train.pkl', 'rb') as train_data:
    train = pickle.load(train_data)
    
with open('../data/fifa_test.pkl', 'rb') as test_data:
    test = pickle.load(test_data)

2. SVM 최적의 파라미터 찾기

SVM의 파라미터는 두가지가 있습니다.

  1. C : 비용 (cost), 결정경계선의 마진을 결정하는 파라미터입니다.
  2. gamma: 커널의 데이터포인트의 표준편차를 결정하는 파라미터입니다.

결과적으로 C가 클수록, 결정경계선과 서포트 벡터의 간격(마진)이 작아집니다.
결과적으로 gamma가 클수록, 결정경계선이 데이터포인트와 더욱 가까워집니다.

from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
from sklearn.metrics import accuracy_score
from sklearn.svm import SVC

import numpy as np

sklearn에서 제공하는 gridsearch를 사용하시면, 손쉽게 최적의 C, gamma를 구하실 수 있습니다.

def svc_param_selection(X, y, nfolds):
    svm_parameters = [
                        {'kernel': ['rbf'],
                         'gamma': [0.00001,0.0001, 0.001, 0.01, 0.1, 1],
                         'C': [0.01, 0.1, 1, 10, 100, 1000]
                        }
                       ]
    
    clf = GridSearchCV(SVC(), svm_parameters, cv=10)
    clf.fit(X_train, y_train.values.ravel())
    print(clf.best_params_)
    
    return clf
X_train = train.iloc[:,3:24]
y_train = train[['Position']]
# 최적의 파라미터를 sklearn의 gridsearch를 통해 구합니다.
clf = svc_param_selection(X_train, y_train.values.ravel(), 10)
{'C': 10, 'gamma': 0.0001, 'kernel': 'rbf'}
clf
GridSearchCV(cv=10, error_score='raise',
       estimator=SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False),
       fit_params=None, iid=True, n_jobs=1,
       param_grid=[{'kernel': ['rbf'], 'gamma': [1e-05, 0.0001, 0.001, 0.01, 0.1, 1], 'C': [0.01, 0.1, 1, 10, 100, 1000]}],
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring=None, verbose=0)

2. SVM

sklearn의 gridsearch로 얻어진 최적의 파라미터로 학습된 clf를 이용하여,
테스트를 진행합니다.

# 테스트에 사용될 특징을 지정합니다
X_test = test.iloc[:,3:24]

# 특징으로 예측할 값 (포지션)을 지정합니다
y_test = test[['Position']]

# 최적의 파라미터로 완성된 SVM에 테스트 데이터를 주입하여, 실제값과 예측값을 얻습니다.
y_true, y_pred = y_test, clf.predict(X_test)

print(classification_report(y_true, y_pred))
print()
print("accuracy : "+ str(accuracy_score(y_true, y_pred)) )
             precision    recall  f1-score   support

        CAM       0.52      0.46      0.49       188
         CB       0.87      0.87      0.87       602
        CDM       0.53      0.44      0.48       258
         CM       0.62      0.75      0.68       439
         GK       1.00      1.00      1.00       399
         ST       0.87      0.88      0.88       521
         WB       0.82      0.87      0.85       607
         WF       0.00      0.00      0.00       151
         WM       0.55      0.66      0.60       419

avg / total       0.73      0.76      0.74      3584


accuracy : 0.7575334821428571


C:\Users\study\Anaconda3\lib\site-packages\sklearn\metrics\classification.py:1135: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples.
  'precision', 'predicted', average, warn_for)
# 실제값(ground truth)과 예측값(prediction)이 어느 정도 일치하는 눈으로 직접 비교해봅니다
comparison = pd.DataFrame({'prediction':y_pred, 'ground_truth':y_true.values.ravel()}) 
comparison
predictionground_truth
0WBWB
1GKGK
2CMCAM
3WBCB
4CBCB
5GKGK
6CDMCDM
7STST
8CAMWM
9CBWB
10CDMCDM

3584 rows × 2 columns

결론

  • WB, ST, GK, CB의 예측률이 매우 높게 나왔고 미드필더의 세부예측률이 낮게 나왔다.

  • WF는 아예 예측을 못한것으로 보아 해당 방법에 문제점이 존재한다고 알 수 있다.

  • 전체적인 정확도는 약 76%로 k-NN알고리즘보다 약간 높은 정도로 나왔다. 분류에 필요없는 column을 제거한 것과 svm의 더 높은 예측력이 이유인것 같다.


© 2020. All rights reserved.