LightGBM 학습 파이프라인 구축 - #14
Open
cfcromn wants to merge 6 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
✨ 작업 내용
스텁이던 트레이딩 모델을 실제 LightGBM 모델로 교체했습니다. 시세를 수집해 피처와 라벨을 만들고, 시계열 홀드아웃으로 검증한 모델을 학습합니다.
app/mlops/features.py) — 학습과 추론이 공유하는 계약 원본app/mlops/dataset.py) — Yahoo Finance, 키 불필요app/mlops/train.py) —python -m app.mlops.trainpredictor버그 수정 — 피처를 이름 순서대로 재배열🔍 리뷰 시 참고사항
LightGBM Booster는 피처 이름이 아니라 열 순서로 매칭합니다.
pd.DataFrame을 넘겨도 이름을 보지 않습니다. 실제로 확인한 동작:0.9721820.907763— 에러 없이 다른 값volumeChange→volume_change오타0.972182— 조용히 통과LightGBMError(개수만 검사)즉 기존
predict()는 Spring이 보내는 JSON 필드 순서에 의존했습니다. 스텁이0.5를 반환하는 동안은 드러나지 않지만, 실모델을 붙이는 순간 순서가 어긋나면 에러 없이 틀린 매매 결정이 됩니다. 그래서 실모델 투입과 같은 PR에서 고쳤습니다.수정:
booster.feature_name()순서로 명시적으로 재배열하고 누락된 이름은ValueError. 회귀 테스트 5개가 재발을 막습니다.머지 전에 판단이 필요한 부분입니다. 학습된 모델의 확률 분포는
0.382 ~ 0.755(중앙값 0.548)에 몰려 있는데, 현재THRESHOLDS는 그 바깥을 보고 있습니다:SELL 은 구조적으로 발생하지 않습니다. SELL 임계값(0.25
0.45)이 모델의 최소 확률(0.382)보다 낮습니다. 라벨이 "5일 후 상승"이고 데이터의 상승 비율이 0.520.56이라 모델 출력이 0.5 위쪽으로 편향되는데, 이건 임계값 조정만으로는 완전히 풀리지 않습니다.THRESHOLDS재튜닝은 계획에서 범위 밖으로 뒀기 때문에 값을 건드리지 않고 그대로 뒀습니다. 백테스트 결과(models/metrics.json의 임계값별 정밀도/커버리지)가 튜닝 근거로 나오니, 별도 작업으로 진행하는 게 맞다고 봅니다. 다르게 생각하시면 말씀해 주세요.피처 정의를 백엔드와 합의해야 합니다
ma5/ma20/macd는 종가로 나눈 비율입니다. 절대값으로는 한 모델이 두 시장을 학습할 수 없습니다:ma20절대값ma20 / close이름과 개수가 맞아도 정의가 다르면 모델은 조용히 틀린 예측을 냅니다. 위 순서 버그를 고쳐도 이건 안 잡힙니다. CLAUDE.md 의 Feature Contract 표와
app/mlops/features.py가 계약 원본입니다.데이터 소스가 Yahoo Finance 하나뿐입니다
이 환경에서 Binance·Coinbase·Kraken·stooq 는 전부 연결이 막혀 있었습니다(
curl이code=000). Yahoo Finance 만 열려 있고 키 없이 US·COIN 10년치를 줍니다.urllib이 시스템 CA 를 못 찾아certifi를 명시해야 하는 함정도 코드에 반영했습니다.지표 계산에
ta같은 라이브러리를 넣지 않았습니다 — 6개 지표는 각각 두세 줄이고, 새 의존성은 Spring 이 재현해야 할 공식을 한 겹 감춥니다. 추가한 의존성은scikit-learn(AUC) 하나입니다.검증 결과
학습 — 12종목 10년치, 32,201행:
홀드아웃 AUC 0.5394 는 금융 시계열에서 정상 범위입니다. 0.7 이상이면 오히려 누수를 의심해야 하는데, 학습 스크립트가 두 경우 모두 경고를 냅니다. 시계열 분할이라 랜덤 split 의 미래 정보 누수는 없고, 라벨 꼬리
horizon행도 잘라냅니다(테스트로 검증).실모델 엔드포인트:
{"probability":0.6306689733244655,"modelVersion":"trading_lgbm","action":"HOLD"}0.6306689733244655— 소수점 16자리까지 동일action":"BUY"(임계값 0.55 통과)500probability: 0.4808decision_logs에0.5가 아닌 실제 확률이 남는 것도 확인했습니다:모델 파일은 git 에 넣지 않았습니다
models/를 gitignore 했습니다. 학습을 돌려야 생기고, 없으면 기존 스텁으로 폴백하므로 CI 와 로컬 기동은 그대로 됩니다. MLflow 를 붙이면 그때 아티팩트 저장소가 생깁니다.✅ 체크리스트
.env.example등) 변경이 필요한 경우 작성 또는 수정했나요?📎 관련 이슈(선택)