본문 바로가기

Minding's Baseball/머신러닝으로 홈런왕 예측하기

[MLB 스탯캐스트] 머신러닝으로 MLB 타자들의 최종 홈런 성적 예측해보기 - 2. Modeling & Prediction

728x90
반응형

작성 코드 및 데이터 (Github, Predict_HR폴더) :

https://github.com/JeongMinHyeok/Handling_MLB_Statcast

 

GitHub - JeongMinHyeok/Handling_MLB_Statcast

Contribute to JeongMinHyeok/Handling_MLB_Statcast development by creating an account on GitHub.

github.com

 

EDA & Data Engineering [이전포스팅]

 

2021.07.27 - [Handling MLB StatCast] - [MLB 스탯캐스트] 머신러닝으로 MLB 타자들의 최종 홈런 성적 예측해보기 - 1. EDA & Data Engineering

 

[MLB 스탯캐스트] 머신러닝으로 MLB 타자들의 최종 홈런 성적 예측해보기 - 1. EDA & Data Engineering

머신러닝 코드 및 데이터자료 (아래 링크의 Predict_HR 폴더) https://github.com/JeongMinHyeok/Handling_MLB_Statcast GitHub - JeongMinHyeok/Handling_MLB_Statcast Contribute to JeongMinHyeok/Handling_MLB..

minding-deep-learning.tistory.com

 


사용할 학습 모델은 Lasso, ElasticNet, RandomForest, GradientBoosting, XGBoost, LightGBM 중

KFold를 통해 오차를 검정하여 3개의 모델을 선정하고,

앙상블 / 보팅 / 스태킹을 사용하여 예측을 진행할 계획을 설정하고 진행했다.

 

RobustScaler

  • 학습을 진행하기 전에, 중앙값과 IQR을 사용한 RobustScaler를 통해 아웃라이어의 영향을 최소화했다.
from sklearn.preprocessing import RobustScaler

rbst_scaler=RobustScaler()
X_rbst=rbst_scaler.fit_transform(x_train)
test_rbst=rbst_scaler.transform(x_test)

 

KFold 검정

kfold = KFold(n_splits=4)

random_state = 1
reg = []

reg.append(Lasso(random_state = random_state))
reg.append(ElasticNet(random_state = random_state))
reg.append(RandomForestRegressor(random_state=random_state))
reg.append(GradientBoostingRegressor(random_state=random_state))
reg.append(XGBRegressor(silent=True,random_state=random_state))
reg.append(LGBMRegressor(verbose_eval=False,random_state = random_state))

# print(reg)

reg_results = []

for regre in reg :
    reg_results.append(np.mean(np.sqrt(-cross_val_score(regre, X_rbst, y = y_data ,scoring = 'neg_mean_squared_error',
                                       cv = kfold, n_jobs=-4))))

# Kfold 검정결과 저장
reg_means = []
reg_std = []
for reg_result in reg_results:
    reg_means.append(reg_result.mean())
    reg_std.append(reg_result.std())
    
    
# 위에서부터 Lasso, ElasticNet, RandomForest, GradientBoosting, XGBoost, LGBM에 대한 교차검증결과
# 평균이 낮을수록 좋음

reg_re = pd.DataFrame({"CrossValMeans":reg_means,"CrossValerrors": reg_std})
reg_re

위에서부터 Lasso, ElasticNet, RandomForest, GradientBoosting, XGBoost, LGBM에 대한 교차검증결과

  • CrossValMeans 확인 : GradeintBoosting, RandomForest, LightGBM 모델에 대해 파라미터 튜닝 결정

GridSearchCV로 모델 파라미터 튜닝

# Gradient boosting 파라미터 튜닝
GBC = GradientBoostingRegressor()
gb_param_grid = {'n_estimators' : [30,50,100],
              'learning_rate': [0.1, 0.01, 0.2],
              'max_depth': [3, 4, 5],
              'min_samples_leaf': [20,30,40],
              'max_features': [0.3, 0.2, 0.15] 
              }
gsGBC = GridSearchCV(GBC,param_grid = gb_param_grid, cv=kfold, scoring="neg_mean_squared_error", n_jobs= 4, verbose = 1)
gsGBC.fit(X_rbst,y_data)
GBC_best = gsGBC.best_estimator_

# 최고 점수
gsGBC.best_score_

>>>
-0.05544372851288073
# RandomForest 파라미터 튜닝
RF = RandomForestRegressor()
rf_param_grid = {'n_estimators' : [10, 20, 30],
              'max_depth': [6, 8, 10, 12, 15],
              'min_samples_leaf': [5, 10, 20, 30],
              'max_features': [0.4, 0.6, 0.8, 1] 
              }
gsRF = GridSearchCV(RF,param_grid = rf_param_grid, cv=kfold, scoring="neg_mean_squared_error", n_jobs= 4, verbose = 1)
gsRF.fit(X_rbst,y_data)
RF_best = gsRF.best_estimator_

# 최고 점수
gsRF.best_score_

>>>
-0.05524458001657022
#LGBMClassifier 파라미터 튜닝
LGB = LGBMRegressor()
lgb_param_grid = {
    'n_estimators' : [30, 50, 70],
    'learning_rate': [0.1],
    'max_depth': [5, 10, 15],
    'num_leaves': [10, 30, 50],
    'min_split_gain': [0.1, 0.2, 0.3],
}
gsLGB = GridSearchCV(LGB,param_grid = lgb_param_grid, cv=kfold, scoring="neg_mean_squared_error", n_jobs= 4, verbose = 1)
gsLGB.fit(X_rbst,y_data)
LGB_best = gsLGB.best_estimator_

# 최고 점수
gsLGB.best_score_

>>>
-0.05724827980153845

 

예측

  • 앙상블
test_Survived_GBC = pd.Series(GBC_best.predict(test_rbst), name="GBC")
test_Survived_RF = pd.Series(RF_best.predict(test_rbst), name="RF")
test_Survived_LGB = pd.Series(LGB_best.predict(test_rbst), name="LGB")

ensemble_results = pd.concat([test_Survived_RF,test_Survived_LGB,
                              test_Survived_GBC],axis=1)
g= sns.heatmap(ensemble_results.corr(),annot=True)

ensemble = np.expm1(0.1*test_Survived_GBC + 0.8*test_Survived_RF + 0.1*test_Survived_LGB)
prediction = pd.DataFrame({
    "player_id" :player_id,
    "name" : name,
    "HR": ensemble
})

 

  • 보팅 (Voting)
from sklearn.ensemble import VotingRegressor

votingC = VotingRegressor(estimators=[('RF', RF_best), ('LGB', LGB_best), ('GBC',GBC_best)], n_jobs=4)
votingC = votingC.fit(X_rbst, y_data)

test_HR = pd.Series(votingC.predict(test_rbst), name="HR")

predict_voting = pd.DataFrame({
    "player_id" :player_id,
    "name" : name,
    "HR": np.expm1(test_HR)
})

 

  • 스태킹(stacking)
from mlxtend.regressor import StackingRegressor
from sklearn.linear_model import LogisticRegression
from sklearn.utils.testing import ignore_warnings

params = {'meta_regressor__C': [0.1, 1.0, 10.0, 100.0],
          'use_features_in_secondary' : [True, False]}
clf1 = RF_best
clf2 = LGB_best
clf3 = GBC_best

lr = LogisticRegression()
st_re= StackingRegressor(regressors=[clf1, clf2, clf3], meta_regressor=RandomForestRegressor())
st_mod = st_re.fit(X_rbst, y_data)
st_pred = st_mod.predict(test_rbst)
predict_stacking = pd.DataFrame({
    "player_id" :player_id,
    "name" : name,
    "HR": np.expm1(st_pred)
})

 

예측결과

  • [이름 / 예측홈런갯수 / 연도 / 실제시즌 홈런갯수(7/28일 기준)]
  • 앙상블 (Ensemble)

 

  • 보팅 (Voting)

 

  • 스태킹 (Stacking)

 


생각치도 못한 결과다...

앙상블, 보팅, 스태킹 중 두 모델이 보스턴 레드삭스의 라파엘 데버스 선수가 홈런왕에 오를거라는 예측을 내놓았다.

홈런갯수는 아직 데이터 전처리가 부족했다고 쳐도, 홈런 순위는 비슷할거라 생각했는데 큰 오산이었다.

 

물론, 라파엘 데버스 선수도 파워를 강점으로 가지고 있는 강타자이다.

하지만 현재 홈런 1위를 달리고 있는 오타니가 35개로 10개 가까이 차이나는 것을 보면, 결과가 조금 이상하다고 볼 수 있다.

 

홈런갯수와 관련된 것은, 아마 홈런타자들을 제외한 다른 타자들의 영향이 컸을거라고 생각한다.

그래서, 다음번엔 홈런타자들 위주로 데이터를 구성하여 예측을 해 볼 예정이다.

 

그리고 현재 오타니처럼 홈런왕 경쟁에서 단독으로 치고나갈 경우, 예측이 어려울 수 있다는 것을 배웠다.

머신러닝 데이터는 일반적으로 평균에 가깝게 구성을 하기 때문에, 오타니와 같은 데이터는 이상치라고 생각할 수 있을 것이다.

다음 프로젝트에는 이 또한 정확하게 예측할 수 있도록 방법을 강구해보아야 할 것이다.

728x90