본문 바로가기
Project/Predict Stock price Project

2. 파이썬 멀티프로세싱 (feat.주식 데이터) - 주가예측 프로젝트

by inhovation97 2022. 3. 8.

학부 연구생으로서 진행한 주가 예측 프로그램의 일련의 과정을 차례로 포스팅합니다. 

위 그림처럼 어떤 종목이든 10일간의 데이터로

다음날 종가 상승 여부를 예측하는 머신러닝 프로젝트입니다.

 

주식 데이터는 여기저기 예제들도 굉장히 많이 있고,

비교적 얻기 쉬운 빅데이터이기 때문에 여러가지 프로젝트를 하시는 분들은 굉장히 많을 거라고 생각합니다. 

 

하지만 어려운 도메인인만큼 유의미한 모델링을 한 사례는 굉장히 적습니다. 

특히 주식을 잘 모르시는 분들에게는 정말 어려운 데이터죠...

제가 얻은 인사이트를 기록합니다.

 

데이터 수집 - EDA - 전처리 - 모델링 - 성능 개선 

 

저번 포스팅에서는 한국 거래소에서 상장 회사 약 2,000개 가량의 종목코드를 얻었고, fdr 라이브러리를 통해 데이터를 얻었습니다.

이번 포스팅에서는 멀티 프로세싱을 이용하여 2,000개 가량의 회사들의 데이터를 ​빠르게 불러와 병합하는 방법에 대해서 다룹니다.

멀티 프로세싱만 필요하신 분들은 해당 부분만 보시면 될 것 같습니다.

저는 서버PC를 이용중이라 리눅스 환경이신 분들은 더욱 유용할 것 같습니다.

 

1. 2,500개 가량의 주식 데이터 병합하기.
2. 멀티 프로세싱 이용

 

1. 약 2,000개 종목의 주식 데이터 병합하기

 

종목 코드 리스트

저번 포스팅에서 얻은 1,890개 기업에 대해 2017년 ~ 현재 기간의 주가 데이터를 전부 가져와봅시다.

from tqdm import tqdm
import time
start_time = time.time()

merge_stock_list =[]
for code in tqdm(code_list): # code_list는 1,890개의 종목 코드가 담긴 리스트입니다.
    stock_list = fdr.DataReader(code, '2017').reset_index().values.tolist() # fdr로 불러온 주가 데이터프레임을 리스트로 변형
    
    for row in stock_list:                   # 불러온 주가 데이터를 1줄씩 불러옴
        row.append(code)                      # 주가 데이터에 기업 코드를 추가    
        merge_stock_list.append(row)          # 모든 기업의 데이터를 병합
        
end_time = time.time()
print('--- 걸린시간: {} ---'.format(end_time - start_time))

멀티 x 결과

코드를 설명하자면,

1,890개의 for문을 돌기 때문에 fdr 함수를 1,890번 호출해야합니다. 

일단 fdr로 가져온 데이터는 데이터 프레임인데, 데이터 프레임은 다룰 때 속도가 굉장히 오래걸리기 때문에 핸들링할 때에는 되도록 지양해야합니다.

따라서 불러온 데이터 프레임을 values.tolist()로 리스트 타입으로 바꿔줍니다. 

그렇게 2중 for문으로 append해가며 모든 종목의 데이터를 stack 시켜줍니다.

2중 for문이 되었지만, 아마 이 정도만으로도 시간절약이 굉장히 많이 될겁니다.

 

해당 라이브러리는 기간 보다는 fdr함수를 1번 호출하는 것에 대해 시간이 많이 들었습니다.
즉, 한 종목에 대해 2년의 데이터를 불러오는 것과 1년의 데이터를 불러오는 것은 시간 차이가 없음.
2종목을 각각 1년기간의 데이터를 불러오는게 시간이 오래걸림.
fdr 함수의 호출 횟수가 시간 복잡도를 많이 잡아 먹습니다.

 

총 240만 row정도 되며,

멀티 프로세싱을 이용하지 않은 시간은 2분 31초가 나왔습니다.

컴퓨터 성능마다 시간은 다릅니다.

 

결과

2. 멀티 프로세싱 이용하기.

 

import multiprocessing
multiprocessing.cpu_count()

먼저 본인 PC의 cpu코어 개수가 몇개인지 위 코드로 확인합니다.

(제 개인 노트북은 그램 2019 기종이며, 8개라고 나옵니다.)

 

def merging_stock_data(code):
    
    merge_stock_list =[]
    stock_list = fdr.DataReader(code, '2017').reset_index().values.tolist()
    for row in stock_list:                                # 불러온 주가 데이터를 1줄씩 불러옴
        row.append(code)                                  # 주가 데이터에 기업 코드를 추가    
        merge_stock_list.append(row)                      # 모든 기업의 데이터를 병합    
    
    return merge_stock_list

위 함수는 종목코드 1개를 받아 리턴하는 함수입니다.

원리를 간략히 설명하자면, 멀티 프로세싱할 코어를 20개로 정했다고 합시다. 

현재 제가 처리해야할 주식 종목 코드는 1,890개입니다.

위 코드처럼 단일 값을 받아 리턴하는 함수를 만들어주면, 20개의 코어가 1,890번 함수를 병렬 처리하게 만들어주는 원리입니다.

 

 

<멀티 프로세싱 코드>

from multiprocessing import Pool
import time
start_time = time.time()
result=[]

#### 멀티 프로세싱 ####
p = Pool(20) # 몇개의 코어를 이용할 것인지 설정
for row in p.map(merging_stock_data, code_list): # 각 코어에 입력값들을 병렬 처리
    result+=row
p.close() # 멀티 프로세싱 종료
p.join()
        
end_time = time.time()
print('--- 걸린시간: {} ---'.format(end_time - start_time))

결과

이제 멀티프로세싱을 설정하여 실행해봅시다. 

Pool( 이용할 코어 개수 )을 선언한 뒤, map함수로 처리해줄 함수와 입력값 리스트를 전달합니다.

code_list의 1,890개의 입력값을 20개의 코어가 병럴처리하게 됩니다.

혹시 저는 1개의 인자를 입력으로하는 함수를 이용했지만 여러개의 인자를 입력으로 받는 함수를 구현하고 싶으시면, map이아닌 starmap 함수를 이용하시면 됩니다.  참고 자료

 

 

이중 리스트를 풀어주지 않은 결과

출력값 result를 for문으로 돌리는 이유는 merging_stock_data는 code_list에서 1개의 입력값을 받아 처리하는 함수입니다.

따라서 멀티프로세싱을 한 결과물은 1,890개의 결과값이 각각 리스트로 합쳐진 결과물이라 위 이미지처럼 이중리스트가 되더라구요...

 

p.close(), p.join() 함수는 병렬처리 후 꼭 선언해줍시다. 종료해주지 않으면 계속해서 메모리를 잡아먹어 굉장히 느려질 수 있습니다.

 

신경써야할 점

출력값 result가 none 값을 포함할 수 있습니다. 

조건문을 걸어서 None값이 포함되지 않도록 for문을 한 번 수행해주어야 할 겁니다. 딥러닝 모델은 None값이 있으면 loss를 계산할 수 없는 문제가 발생하기 때문에 데이터를 한 번 검토해보시는게 좋을 것 같습니다.

 

결과적으로 20개의 코어로 멀티프로세싱을 했더니 위 처럼 16초가 걸리더군요!

 

멀티프로세싱하지 않은 경우 : 216초

20 코어 멀티프로세싱 : 16초

 

 

리눅스 환경 cmd에서 htop 명령어를 입력하면, 이렇게 컴퓨터를 모니터링할 수 있습니다. 

저는 아까 사용가능한 코어가 128개라고 나왔는데, 여기 128개가 다 보이네요.

가시적으로 보기위해 코어 50개로 멀티 프로세싱을 진행하여 모니터링을 해보면,

멀티 프로세싱

이렇게 여러개의 코어가 바쁘게 일을합니다. 

이게 꼭 50개 쓴다고 25개 쓸 때보다 1/2의 시간복잡도가 걸리는 건 아닙니다!

아마 최소한의 수행시간 같은 것도 생각하시면 될 것 같습니다.

서버 PC 이용자라면, 멀티 프로세싱을 수행할 경우 다른 분들이 이용할 수 있는 자원이 줄어들기 때문에 너무 남발하면 안됩니다. 

이용 후에 close는 반드시 필수입니다!

댓글