개발 프로젝트

머신러닝을 통한 주식 추천 시스템 개발 일지 (3) - 모델 훈련 feat.VertexAi활용

M_군 2025. 3. 31. 21:07

 

2025.03.30 - [개발 프로젝트] - 머신러닝을 통한 주식 추천 시스템 개발 일지 (2) - DB 구성, 데이터 수집

 

머신러닝을 통한 주식 추천 시스템 개발 일지 (2) - DB 구성, 데이터 수집

개발 일지 (1) 머신러닝을 통한 주식 추천 시스템 개발 일지 (1) - 계기, 전체적 설계시스템 개발을 모두 완료한 뒤, 기록을 남기기 위해 작성한다.   시작은 오랜만에 만난 고등학교 친구와의

semicolone-bracket.tistory.com

 

훈련 컨셉

나는 딥러닝이 아니라 머신러닝을 통해 작업하려고 한다. 그 이유는 내가 머신러닝까지만 공부하고 딥러닝은 공부를 안했기 때문이다. 사실 머신러닝도 공부가 얕으니 뭘 선택해도 피차일반이긴 하다. 추가로 이유를 뽑자면, 머신러닝의 경우에는 딥러닝보다 상대적으로 Feature 를 어떻게 정의하느냐에 영향을 더 받는다고 할 수 있기 때문이다. 즉, 작업자의 손을 더 많이 탄다. 트렌디한 경향 상, 작업자가 뭘 더 하지 않고 딥러닝에 맡기는 것이 언제나 더 좋은 결과를 내는 것은 맞으나 Feature를 직접 정의하는 작업, 전처리하는 작업을 해보는게 또 낭만이 있으니 머신러닝을 하는 것도 있다.

 

머신러닝을 통해 주식 데이터를 훈련하고자 하시는 분들을 보면 대부분 회사 별로 별개의 모델을 구성하는 경우가 많다. 사실 이게 좀 더 직관적이다. 결국 머신러닝이라는 것은 어떤 데이터 모집단의 특성을 컴퓨터가 잘 학습하도록 만드는 것인데, 모집단이 서로 다른 (즉, 서로 전혀 다른 특질을 지닌) 데이터를 같이 학습시킨다면 결과가 잘 나올리가 없다. 하지만 여기서 맹점은 한 회사에 대해서만 학습을 시킨다고 해도, 그 거래 데이터들을 같은 모집단이라고 보기 힘들기도 하다는 것이다. 왜냐하면 각기 다른 날짜에 이루어진 거래 내용이고, 그 시점마다 다른 경제 상황, 다른 회사의 상황이 존재하기 때문이다. 즉, 주식 거래 데이터라는 것은 애초에 본질적으로 볼 때 머신러닝의 취지와는 잘 안 맞는 면이 있다. 

 

하지만 기술적 분석을 하시는 분들이 경험칙으로서 가져가는 대전제, ;모든 주식에는 일종의 패턴이 있으며 회사에 무차별하게 적용된다.'는 것을 폭넓게 적용하여 여러 회사의 거래 데이터를 한데 모으되, 전처리는 회사별 주가 흐름 정보를 압축할 수 있도록 따로 하여 일종의 패턴 정보가 담긴 데이터셋을 만들고자 한다. 예를 들어 최근 3일치의 수익률을 input feature로 활용하고자 한다면 회사별로 쪼개서, 정렬한 뒤 데이터 전처리를 해야할 것이다. target feature로 30일 뒤의 수익률을 정했다면 정렬된 상태에서 후반 29일치 데이터는 버려지게 되고, 각 행마다 3일치 수익률의 input feature와 30일 뒤 수익률 target feature가 같이 존재하게 된다. 이렇게 전처리한 뒤에는 회사 정보도, 날짜 정보도 버린채 하나의 데이터셋으로 합친다. 

 

 

엄밀한 설계는 아니긴 하지만... 굳이 의미를 부여하자면! 최종적으로 나온 데이터셋은 '임의의 회사의 주가 데이터를 임의의 날짜를 기준으로 하여 과거 데이터와 미래 수익률 데이터를 한번에 표현한 것의 집합'이 수 없이 나열되어 있는 것이다. 다시 말하지만 어떤 회사인지 정확히 어떤 시점이었는지는 무시한다. 나는 이런 데이터 전처리가 기술적 분석을 하고자 하는 사람들의 분석 태도와 유사한 면이 있다고 본다. 그렇기에 그들의 경험칙을 근거로 이러한 형태의 전처리를 구상하였다.

훈련 Feature, 종류 정의하기

다시 한번 말하자면, 이 모델은 거시경제적 지표나 회사의 재정 정보 등, 주식을 분석할 때 응당 같이 분석해주어야 하는 것들에 대해서는 무시하고 가격, 거래량의 패턴만을 활용한다. 작업을 좀 더 간소화시키려는 것도 있고 앞서 말한 컨셉대로 기술적 분석을 하고자 하는 사람들의 스탠스를 모델로 녹이고 싶었다.

 

일단 수치 정보로만 구성되는 기술적 지표에는 어떤 것들이 있나 좀 공부했다. 이에 더해 퀀트가 쓴 책도 대강 훑어보고, 비슷한 프로젝트에서는 어떤 식으로 Feature를 구성했는지를 참고하여 Feature 리스트를 구성했다. 공식적인 지표를 참조한 것도 있지만, 내 입맛에 따라 뇌피셜로 정의한 지표들도 있다.

 

담백하게 거래량과 종가를 정규화하거나 최근 수익률을 log화한 Feature도 사용했고 SMA, EMA, MACD, 볼린저밴드, ATR이라는 기술적 지표를 활용한 Feature도 적절히 정의해 사용했다.


Input Feature를 어떻게 구성할지 정했으니 이제 좀 더 중요한 결정을 해야 했다. 바로 Classification 모델을 만들지, Regression 모델을 만들지다. 일단 Regression을 하기에는 변동성이 너무 큰 데이터라 큰 의미는 없어보였고 변동성을 적당히 갈무리할 수 있도록 Classification 모델로 만드는 것이 더 적절하겠다는 생각을 했다. 그렇다면 '무엇'을 Classification하는 모델로 만들 것인가가 그 다음 문제다. 그 답은 간단하다. 매수를 할 것이냐 말 것이냐를 추천하는 모델이니 심플하게 n일 뒤 수익률을을 기준 잡아서 나눠 Target Label로 만드는 것이다. 

 

몇개의 class로 나눌지와 몇일 뒤의 수익률을 타깃으로 할지에 대해서는 실제 데이터로 작업을 하면서, 훈련 데이터의 분포를 보고 적당히 정하도록 하자.

실제 데이터로 Feature 검증 - Plot

어차피 포레스트 기반 모델을 사용할 예정이기에 스케일링에 대해 각 잡고 신경써줄 필요는 없었지만 내가 정의한 각 지표와 주가 그래프 사이에서 미약하나마 상관관계를 찾을 수 있는지 plot을 그려보며 확인하는 과정도 거쳤다. 

임의로 정의한 이산 변수가 잘 작동하는지 체크해보기 위해 plot을 그려 확인했다.

이를 통해서, 조금씩 Feature 산출식을 조정해 나갔다. 물론, 각잡고 엄밀히 하지는 않았고 회사 1,2개 갖다가 구색만 맞췄다고 볼 수 있다.... 그러니 사실 큰 의미는 없다. 다만 의도와는 달리 너무 민감하게 작동하는 feature를 시각적으로 확인해 개선하는데 도움이 되었다. 

 

모델 학습 우여곡절

첫 학습은 랜덤포레스트 모델을 사용하는 것으로 정했다. 가장 무난한 모델이라는 생각이 들었기 때문이다. 맨 처음 시도에서는 2일 뒤의 수익률을 5개 class로 나눠서 훈련을 시켜보았다. 각 클래스 별로 데이터 수의 차이가 너무 나지 않도록 적당하게 class를 가르는 값을 조정했다. 테스트 결과를 보니 처참했다. 어느 정도 예상은 했지만 막상 보니 아쉽긴 했다. 물론, 전체 데이터를 당장 뽑기엔 좀 무거워서 적은 수의 데이터만 사용한 것이긴 했다.

너무 처참해서 마음이 아팠다. 하지만 찍은 것보단 나은 수준이니 효과가 있다고 봐야할까?

이 과정에서 어떻게 모델을 개선할 수 있을지, 또 랜덤포레스트 말고 내 데이터셋에 적용하기에 적당한 다른 모델에는 무엇이 있는지를 공부했다. 사실 기존에 공부했다던 머신러닝도 입문서로 얕게 실습을 따라간 것이 다였기 때문에 피상적으로만 이해하고 있었는데 직접 데이터 넣어보고 값을 개선하려고 노력하다보니 이해도가 좀 깊어졌다. 고민과 수많은 뻘짓 끝에 다음과 같이 결정했다.

 

  • 여러 조건이 다른 예측 모델을 동시에 사용해 일종의 자체 앙상블을 구성하고, 이를 통해 도출된 종합점수로 추천 순위를 결정한다.
    • 여기서 다른 조건이란? 모델의 종류, 몇일 뒤의 수익률을 훈련시키고 예측할 것인가, 몇 개의 class로 수익률을 나누어 예측할 것인가 총 3개의 변수이다.
      • 모델 -> Catboost, RandomForest 총 2개
      • 몇일 뒤의 수익률? -> 5, 7, 21, 30 총 4개
      • 몇 개의 class로 나누어 예측할 것인가? -> 2, 3, 4 총 3개
      • 즉, 각 예측 때마다 총 24개의 모델을 사용하는 것이다!
  • 클래스 별 데이터 불균형이 있을 때 Class Weight 를 조정하는 파라미터를 쓰는게 보통 일반적이나, 의도적으로 쓰지 않는다.
    • 이렇게 하면 소수 클래스에 대해 Precision이 증가하고 Recall이 감소한다. 즉, 놓치는 정답이 있더라도 더 조심스러운 예측을 한다는 의미이다. 보통 제일 높은 수익률에 해당하는 클래스가 소수라서, 아예 해당 클래스를 예측하지 않는 경우가 발생할 수도 있으나, 어차피 24개 모델을 동시에 사용해 점수 합산을 낼 것이고 3-5개 주식만 추천할 것이기 때문에 적은 수를 예측하는 것을 의도적으로 활용하는 것이 좋다고 판단했다.
  • 중요한 건 결국 데이터다. 극단적인 케이스의 데이터, 너무 하락세 위주인 데이터를 과감하게 삭제했다. 이를 통해 거의 60% 이상의 데이터를 날렸다.
    • 저 정도로 날려도, 미국 주식의 훈련 데이터는 90만 정도, 한국 주식의 훈련 데이터는 30만 정도 나왔기 때문에 큰 문제는 없다고 판단했다.
    • 어차피 모든 주식의 분포를 예측하는 모델을 만든다기보다는, 추천할 만하고 오를만한 주식을 예측해내는 것이 목표이기 때문에 이렇게 데이터를 필터링하는 것이 모델 성능 상에서도 이점이 있다고 판단했다. (그리고 실제 그랬다.) 
    • 미국 주식의 경우에는 극단적인 상승세를 보이는 회사도 몇개 있었기 때문에 그런 회사들도 삭제했다.
  • 하이퍼 파라미터 조정에 큰 의미는 없다. 여러 모델을 사용하기로 했으니, 트리 개수(n_estimator)도 줄이고 전체적으로 모델을 가볍게 가져가자.
    • 랜덤포레스트의 경우에는 욕심을 내면 거의 모델 당 2GB에 육박할 정도의 크기가 되었다. 모델을 결국 Cloud Run 상에서 불러와서 예측을 해야하는데, 이렇게 되면 Cloud Run instance의 실행시간도 길어지고 메모리 부담도 커지게 된다. 
    • 훈련을 여러 파라미터로 반복하다보니 어차피 데이터가 이 모양이면, 욕심을 낸다고 상승하는 1,2퍼센트가 크게 의미가 없다는 생각을 했다. 그래서 과감히 포기하고 담백한 파라미터로 설정해 모델 당 용량이 2,300 MB 수준이 되게끔 조정했다.
      • (Catboost의 경우에는 모델 크기가 다 10MB 이하고, 학습 속도도 비교도 안될 정도로 빠르다... 역시 신삥은 다른가보다.)

여담) Google Vertex AI에서 모델 학습시키기!

맨 처음에는 코랩으로 슥 해봤다가 너무 느려서, 그냥 내 맥에서 랜덤포레스트 모델을 훈련시켜봤다. 훈련 데이터가 거의 200만개에 육박했고 맥에서는 한 2-30분 걸렸다. (코랩에서는 돌리다가 메모리 부족하다고 터져버렸다) 코랩 GPU활용도 못하는게, 랜덤포레스트의 경우에는 CPU만 사용한다고 한다. 

 

어떤 하이퍼 파라미터로 할지 결정이 된 상태면 내 맥에서 2-30분 걸려서 훈련하는 것이 그렇게 큰 부담은 아니긴 했다.(하지만 노트북 소리리가 너무 크게 웽웽 거려서 터지는게 아닌가 걱정은 됐다..ㅋㅋ) 하지만 초반에 감을 못 잡고 있으니 하이퍼 파라미터 최적화를 하기 위해 RandomizedSearchCV를 돌리려고 했고, 유의미한 값을 얻기 위해서 search의 이터레이션을 적어도 100, crossValidate 세팅도 5-fold로 해야했끼 때문에 단순 계산으로도 500배 시간이 걸리는 셈이었다. 여기에 n_estimator도 좀 늘려서 테스트해보려고 했으니 그 이상의 시간이 걸릴 것으로 예상되었다. 

 

나도 노트북을 이동하면서 계속 써야 하는 상황이고, 그렇게 사용하다가는 맥에 영구적 부하를 줄까봐 무서워져 다른 대안을 찾아보려 했다. 고민해보니 친구네 연구실 서버에 몰래 잠입해서 돌리고 나오는게 사실 상 제일 좋은 해결책이긴 했다 ㅋㅋㅋ. 대학원 생활 하는 친구들 말을 들어보니 훈련용 서버 스펙이 어마어마하더라. 하지만 그건 말이 안되니 포기했다.

하지만 어째튼 내 컴퓨터가 아니라, 좀 더 스펙이 좋은 서버 컴퓨터에서 돌리는 것을 목표로 이것저것 찾아봤다. 찾아보니 나 같은 사람들을 위해 Google에서 제공하는 서비스가 있었다. 바로 Vertex Ai 훈련 서비스~!! 별로 특별한 건 아니고 서비리스 환경, 도커 환경에서 훈련을 시키는 것이다. 쉽게 말하면 고사양 컴퓨터 스펙을 사용할 수 있고 모델 관리에 약간의 편의성이 있는 Cloud Run Job이라고 할 수 있다.

실제 사용한 기록. 실패로 기록되어 있긴 하지만, 다 완료된 뒤에 발생한 사소한 실수 였고 RandomizedSearch는 잘 수행했다. 32코어 vcpu 컴퓨팅을 활용해서 10시간이 걸렸다...! ㄷㄷ
재밌는건 내가 세팅한 컴퓨팅 스펙이 적절했는지 알 수 있게끔 이렇게 작업이 돌아가는 동안의 CPU, RAM 사용률을 보여준다. 아마 GPU를 적극 활용하는 모델의 경우에는 GPU 리소스도 보여줄 것이다.

 

가격도 그렇게 부담되지 않는 선이었고 여러모로 유용하게 사용하긴 했다. 이걸로 여러 세팅의 모델을 동시에 훈련시켜보면서 결과를 빠르게 확인할 수 있었다. 물론, 위에서 언급했듯이 훈련하기 부담스럽고 용량이 큰 모델이 아니라 하이퍼 파라미터 부담을 줄인 가벼운 모델을 사용하기로 해서, 마무리 작업할 때는 그냥 다 내 로컬에서 훈련시키긴 했지만 초반에 여러 세팅으로 훈련시켜보면서 감 잡는데 큰 도움이 됐다.