작성 코드 및 데이터 (Github, Predict_HR폴더) :
https://github.com/JeongMinHyeok/Handling_MLB_Statcast
EDA & Data Engineering [이전포스팅]
사용할 학습 모델은 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
- 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개 가까이 차이나는 것을 보면, 결과가 조금 이상하다고 볼 수 있다.
홈런갯수와 관련된 것은, 아마 홈런타자들을 제외한 다른 타자들의 영향이 컸을거라고 생각한다.
그래서, 다음번엔 홈런타자들 위주로 데이터를 구성하여 예측을 해 볼 예정이다.
그리고 현재 오타니처럼 홈런왕 경쟁에서 단독으로 치고나갈 경우, 예측이 어려울 수 있다는 것을 배웠다.
머신러닝 데이터는 일반적으로 평균에 가깝게 구성을 하기 때문에, 오타니와 같은 데이터는 이상치라고 생각할 수 있을 것이다.
다음 프로젝트에는 이 또한 정확하게 예측할 수 있도록 방법을 강구해보아야 할 것이다.
'Minding's Baseball > 머신러닝으로 홈런왕 예측하기' 카테고리의 다른 글
[MLB 스탯캐스트] 머신러닝으로 MLB 타자들의 최종 홈런 성적 예측해보기 - 3. 데이터 재전처리하여 예측 (0) | 2021.08.03 |
---|---|
[MLB 스탯캐스트] 머신러닝으로 MLB 타자들의 최종 홈런 성적 예측해보기 - 1. EDA & Data Engineering (5) | 2021.07.27 |