이번 주 부터는 선행연구와 동일하게 KOSPI 종가 데이터를 사용하여 방향 예측 모델을 제작할 예정이다. 먼저, 변동률과 동일하게 시계열 분석을 위한 검정을 진행하였다.

종가 데이터 자체는 non-stationary 하기 때문에 차분 혹은 로그화를 진행시켜 분석을 진행해야 한다. 그런데, 차분한 데이터 역시 변동률과 동일하게 white noise의 양상을 보이고있다. 즉, 코스피의 변동률 혹은 종가 단일 데이터만으로는 ARIMA 모델을 사용한 분석이 의미가 없다고 판단했다. 이제부터는 원문 자체를 구현하되, 데이터의 양을 현재 시점까지 늘려서 진행해보자. 논문에서는 현재 데이터를 포함해 과거 시차 7일간 변동률에 로그화를 취한 값을 input으로 넣고, output으로 다음날의 코스피 방향을 상승 혹은 하락으로 예측했다. 2012년부터 현재까지 변동률 및 로그값을 확인해보자.

두 가지 그림이 비슷한 양상을 보이고 있음을 확인할 수 있다. 데이터의 시퀀스가 길어졌기 때문에 2012년~2021년 데이터를 train/val data로, 2022년~현재까지의 데이터를 test data로 사용하기로 결정했다. 원문에서 사용했던 모델 중 Random Forest를 사용해서 Accuracy, Precision을 확인해보자.
clf_up = RandomForestClassifier(random_state = 42)
clf_up.fit(x_train, y_up_train)
clf_down = RandomForestClassifier(random_state = 42)
clf_down.fit(x_train, y_down_train)
y_up_pred = clf_up.predict(x_val)
y_down_pred = clf_down.predict(x_val)
accuracy_up = accuracy_score(y_up_val, y_up_pred)
accuracy_down = accuracy_score(y_down_val, y_down_pred)
precision_up = precision_score(y_up_val, y_up_pred)
precision_down = precision_score(y_down_val, y_down_pred)
data = {
'Metric': ['Accuracy', 'Precision'],
'Up (Validation)': [accuracy_up, precision_up],
'Down (Validation)': [accuracy_down, precision_down],
'Up (Test)': [accuracy_score(clf_up.predict(x_test), y_up_test),
precision_score(clf_up.predict(x_test), y_up_test)],
'Down (Test)': [accuracy_score(clf_down.predict(x_test), y_down_test),
precision_score(clf_down.predict(x_test), y_down_test)]
}
# DataFrame으로 변환
df_results = pd.DataFrame(data)
print(df_results)
| Model | Accuracy | Precision |
| Up(Validation) | 0.53 | 0.57 |
| Down(Validation) | 0.53 | 0.49 |
| Up(Test) | 0.51 | 0.59 |
| Down(Test) | 0.47 | 0.32 |
결과를 보면, Random Forest 모델은 상승에 비해 하락 예측에 많이 약함을 확인할 수 있다. 대표적인 배깅 모델을 사용해봤으니, 이제 원문에서 사용한 다른 모델들도 사용해보고 결과를 비교해보자.
from sklearn.svm import SVC
from lightgbm import LGBMClassifier
from xgboost import XGBClassifier
from catboost import CatBoostClassifier
clf_rf = RandomForestClassifier(random_state = 42)
clf_svm = SVC(kernel = 'poly', random_state = 42)
clf_lgbm = LGBMClassifier(random_state = 42)
clf_xgb = XGBClassifier(random_state = 42)
clf_cat = CatBoostClassifier(random_state = 42)
classifiers = {
'RF' : clf_rf,
'SVM' : clf_svm,
'LGBM': clf_lgbm,
'XGB': clf_xgb,
'CAT': clf_cat
}
results_up = {'Model':[], 'Accuracy':[], 'Precision':[]}
results_down = {'Model':[], 'Accuracy':[], 'Precision':[]}
for name, clf in classifiers.items():
# 상승 모델
clf.fit(x_train, y_up_train)
y_up_pred = clf.predict(x_val)
accuracy_up = accuracy_score(y_up_val, y_up_pred)
precision_up = precision_score(y_up_val, y_up_pred)
results_up['Model'].append(name)
results_up['Accuracy'].append(accuracy_up)
results_up['Precision'].append(precision_up)
# 하락 모델
clf.fit(x_train, y_down_train)
y_up_pred = clf.predict(x_val)
accuracy_up = accuracy_score(y_up_val, y_up_pred)
precision_up = precision_score(y_up_val, y_up_pred)
results_down['Model'].append(name)
results_down['Accuracy'].append(accuracy_up)
results_down['Precision'].append(precision_up)
<상승 예측 모델>
| Model | Accuracy | Precision |
| RF | 0.53 | 0.57 |
| SVM | 0.52 | 0.53 |
| LGBM | 0.54 | 0.58 |
| XGB | 0.51 | 0.55 |
| CAT | 0.52 | 0.55 |
상승 예측 모델은 대체적으로 비슷한 성능을 보여주고 있다. 5개의 모델 중 LGBM이 근소한 차이로 가장 좋은 성능을 보이고 있다.
<하락 예측 모델>
| Model | Accuracy | Precision |
| RF | 0.47 | 0.51 |
| SVM | 0.46 | 0.47 |
| LGBM | 0.49 | 0.52 |
| XGB | 0.5 | 0.54 |
| CAT | 0.5 | 0.54 |
하락 예측 모델은 상승 예측 모델에 비해 다소 낮은 성능을 보여준다. 5개의 모델 중 XGBoost와 CatBoost가 가장 좋은 성능을 보이고 있다.
원문에서는 CatBoost가 아닌 MLP를 사용했지만, MLP를 사용한 결과 하락 예측에서 Precision이 0으로 나왔다. 이러한 문제가 발생한 이유는 하이퍼 파라미터 튜닝을 하지 않아서 그렇다고 추정된다. 성능을 제고하기 위해서는 grid search와 같은 튜닝 방법을 사용할 수 있지만, 현재 컴퓨팅 자원으로 제시된 grid search를 돌리는 데 너무 오랜 시간이 걸리기 때문에 부스팅 계열 모델 CatBoost로 대체하여 진행하였다. 5가지 모델의 결과를 확인했으니, 이제 모델을 섞는 하이브리드 모형을 제작해서 결과를 확인해보자. 전체 5가지에서 상승/하락 예측별로 상위 3가지의 모델을 voting과 stacking을 사용해서 앙상블 해보았다.
from sklearn.ensemble import VotingClassifier, StackingClassifier
from sklearn.linear_model import LogisticRegression
hybrid_results_up = {'Model':[], 'Accuracy':[], 'Precision':[]}
hybrid_results_down = {'Model':[], 'Accuracy':[], 'Precision':[]}
# Voting
voting_clf_soft_up = VotingClassifier(estimators=top3_up_classifiers, voting='soft')
voting_clf_soft_down = VotingClassifier(estimators=top3_down_classifiers, voting='soft')
voting_clf_hard_up = VotingClassifier(estimators=top3_up_classifiers, voting='hard')
voting_clf_hard_down = VotingClassifier(estimators=top3_down_classifiers, voting='hard')
# Weighted Voting
weights = [0.4, 0.4, 0.2]
voting_weighted_clf_up = VotingClassifier(estimators=top3_up_classifiers, voting='soft', weights=weights)
voting_weighted_clf_down = VotingClassifier(estimators=top3_down_classifiers, voting='soft', weights=weights)
# Stacking
stacking_clf_up = StackingClassifier(estimators=top3_up_classifiers, final_estimator=LogisticRegression())
stacking_clf_down = StackingClassifier(estimators=top3_down_classifiers, final_estimator=LogisticRegression())
extended_classifiers = {
'Voting_Soft_Up': voting_clf_soft_up,
'Voting_Soft_Down': voting_clf_soft_down,
'Voting_Hard_Up': voting_clf_hard_up,
'Voting_Hard_Down': voting_clf_hard_down,
'Weighted_Voting_Up': voting_weighted_clf_up,
'Weighted_Voting_Down': voting_weighted_clf_down,
'Stacking_Up': stacking_clf_up,
'Stacking_Down': stacking_clf_down
}
for name, clf in extended_classifiers.items():
# Check if the classifier is for upward or downward trend
if "Up" in name:
clf.fit(x_train, y_up_train)
y_pred = clf.predict(x_val)
accuracy = accuracy_score(y_up_val, y_pred)
precision = precision_score(y_up_val, y_pred)
hybrid_results_up['Model'].append(name)
hybrid_results_up['Accuracy'].append(accuracy)
hybrid_results_up['Precision'].append(precision)
else:
clf.fit(x_train, y_down_train)
y_pred = clf.predict(x_val)
accuracy = accuracy_score(y_down_val, y_pred)
precision = precision_score(y_down_val, y_pred)
hybrid_results_down['Model'].append(name)
hybrid_results_down['Accuracy'].append(accuracy)
hybrid_results_down['Precision'].append(precision)
hybrid_results_up_df = pd.DataFrame(hybrid_results_up)
hybrid_results_down_df = pd.DataFrame(hybrid_results_down)
<상승 예측 하이브리드 모델>
| Model | Accuracy | Precision |
| Voting(Soft) | 0.53 | 0.57 |
| Voting(Hard) | 0.53 | 0.56 |
| Voting(Weighted) | 0.53 | 0.57 |
| Stacking | 0.55 | 0.56 |
<하락 예측 하이브리드 모델>
| Model | Accuracy | Precision |
| Voting(Soft) | 0.52 | 0.48 |
| Voting(Hard) | 0.48 | 0.44 |
| Voting(Weighted) | 0.52 | 0.48 |
| Stacking | 0.52 | 0.46 |
상승과 예측 모델을 결합해본 결과, 예상과 다르게 성능이 비슷하거나 오히려 떨어지는 경우가 발생했다. 위에서도 언급했지만, grid search와 같이 모델을 최적화하지 않고 default 값으로 train을 진행시켜서 모델 자체의 성능이 부족하기 때문이라고 생각된다. 우선 grid search에 대해 간단히 알아보고, Random Forest 모델을 사용해서 비교를 진행해보자.
Grid Search
Grid Search는 가능한 모든 하이퍼파라미터 조합에 대해 모델 성능을 평가하고 가장 좋은 성능을 보이는 조합을 찾는 방법이다. 사용자가 하이퍼파라미터의 범위를 지정해주면, 모든 조합에 대해 교차 검증을 수행하여 최적의 조건을 찾는다. 사용자가 직접 조합을 할 필요가 없지만, 범위가 증가할 수록 cost도 함께 증가하여 고성능 자원이 요구될 수 있다.
param_grid = {
'n_estimators': list(range(2, 10, 3)),
'max_depth': list(range(2, 16, 4)),
'min_samples_split': list(range(10, 26, 5)),
'min_samples_leaf': list(range(2, 11, 3))
}
clf_up_grid = GridSearchCV(RandomForestClassifier(), param_grid, cv=5)
clf_up_grid.fit(x_train, y_up_train)
best_params_up = clf_up_grid.best_params_
clf_down_grid = GridSearchCV(RandomForestClassifier(), param_grid, cv=5)
clf_down_grid.fit(x_train, y_down_train)
best_params_down = clf_down_grid.best_params_
print(best_params_up, best_params_down, sep = '\n\n')
- up : {'max_depth': 6, 'min_samples_leaf': 5, 'min_samples_split': 20, 'n_estimators': 8}
- down : {'max_depth': 2, 'min_samples_leaf': 8, 'min_samples_split': 10, 'n_estimators': 5}
<grid search 결과>
| Model | Accuracy | Precision |
| Up | 0.5 | 0.59 |
| Down | 0.5 | 0.25 |
임의의 범위로 조합을 설정하고, grid search 기반 hyper parameter로 훈련시킨 결과이다. 상승 예측 모델은 정밀도가 2%정도 향상되었으나, 하락 예측모델은 절반가까이 떨어진 것을 확인할 수 있다. 위와 같은 결과를 통해 최적의 결과를 찾기 위해서는 보다 폭넓은 범위의 조합을 사용하거나, 다른 하이퍼 파라미터와의 조합을 고민해볼 필요가 있는 것을 확인할 수 있다.
이번주까지 python을 활용해서 학부연구생을 시작하고 처음으로 진행했던 프로젝트를 리뷰해보았다. 과거 실패했던 원인을 알고, 새로운 방향으로 진행하면서 이전과 달리 부족한점과 분석 방향에 대해 다시 한 번 생각해보는 시간을 가질 수 있었다. 다음 주 부터는 No-Code-ML인 KNIME에 대해 소개하고, KNIME으로도 모델을 구현해보도록 하자.
전체 코드는 여기서 확인할 수 있다.
출처
황희수,「코스피 방향 예측을 위한 하이브리드 머신러닝 모델」,한국융합학회,한국융합학회논문지 제12권 제6호, 9-16 쪽(2021)
'데이터 분석 > 주식 선행연구 분석' 카테고리의 다른 글
| [주식] 기계학습을 활용한 주식 데이터 분석 - 8주차 (0) | 2023.09.03 |
|---|---|
| [주식] 기계학습을 활용한 주식 데이터 분석 - 7주차 (0) | 2023.08.19 |
| [주식] 기계학습을 활용한 주식 데이터 분석 - 5주차 (0) | 2023.08.08 |
| [주식] 기계학습을 활용한 주식 데이터 분석 - 4주차 (0) | 2023.08.06 |
| [주식] 기계학습을 활용한 주식 데이터 분석 - 3주차 (2) | 2023.08.05 |