머신러닝에서 말하는 Batch의 정의

  • 모델을 학습할 때 한 Iteration당(반복 1회당) 사용되는 example의 set 모임
  • 여기서 iteration은 정해진 batch size를 이용하여 학습(forward - backward)를 반복하는 횟수를 말한다
  • 한 번의 epoch를 위해 여러번의 iteration이 필요하다 
  • training error와 validation error가 동일하게 감소하다가 validation error가 증가하기 시작하는 직전 점의 epoch를 선택해야 함 (이는 overfitting을 방지하기 위함이다)

Batch size의 정의 및 Batch Size를 선택하는 방법
  • Batch 하나에 포함되는 example set의 갯수
  • Batch / Mini-Batch /Stochastic  세 가지로 나눌 수 있다.(아래 그림 참고)


  • SGD(Stochastic Gradient Descent) 은 배치 크기가 1, Mini-Batch는 10~1,000 사이지만 보통 2의 지수승 (32 64 128..)으로 구성

Batch별 특징 및 장단점
Batch
  • 여러 개 샘플들이 한거번에 영향을 주어 합의된 방향으로 smooth하게 수렴, 그러나 샘플 갯수를 전부 계산해야 하므로 시간이 많이 소요된다(한 step을 처리하는데 많은 수의 데이터를 계산해야 한다). 즉 모든 Training data set 사용함.
Stochastic Gradient Descent
  • 데이터를 한 개씩 추출해서(한 개 씩 뽑아서) 처리해보고 이를 모든 데이터에 반복하는 것. 오차율이 크다(수렴 속도는 빠르지만 global minimum을 찾지 못할 가능성이 있다 - 위 그림 참고) . 또한 하나씩 처리하기 때문에 GPU 성능을 제대로 활용하지 못하기 때문에 비효율적이다.
Mini-Batch
  • 전체 학습 데이터를 배치 사이즈로 등분하여(나눠) 각 배치 셋을 순차적으로 수행, 배치보다 빠르고 SGD보다 낮은 오차율 
정리
  • Mini-Batch의 사이즈가 전체 Training data 사이즈와 같으면 Batch Gradient Descent, Mini-Batch의 사이즈가 1이면 Stochastic Gradient Descent)
  • 실제로는 Batch Gradient를 잘 쓸 수 없다. 왜나하면 메모리에 모든 데이터를 한번에 올릴수 없기 때문이다.

Python 코드 예시 


  • Training data set이 5528이라고 가정 


  • 학습 셋이 5528개인데 샘플 코드에서는 Batch Size를 32로 설정
  • 즉 epoch가 173번 돌게 된다(5528/32 = 172.75 --> 반올림해서 173)
  • 마지막 epoch을 돌리지 않으면 172*32 = 5504 --> 24개가 버려짐(배치32로 포함 안되니깐)
  • 이를 방지하기 위해 math.ceil을 이용해 반올림 (172.75 반올림하면 173)하게 되면 모든 데이터 처리 가능(32개씩 172번, 마지막 epoch는 24개) 



이번 포스팅에선 저번 포스팅에서 수집한 비트코인 가격 데이터에 모델 학습을 위한 변수를 추가하기 위해

기술분석에 쓰이는 보조 지표들을 추가해볼 것이다.


본 포스팅에서 필요한 라이브러리들과 설치법은  다음과 같다 


  • Pandas:   cmd창에서 pip install pandas 커맨드 입력후 설치
  • Numpy:   pip install numpy
  • Ta-Lib:    하단에서 설명
  • sklearn:   pip install sklearn


Cmd창은 항상 관리자 권한으로 열어야 한다는 것을 잊지말자.

TA-Lib같은 경우 이상하게 설치가 되지 않으므로 따로 웹사이트를 방문하여 다운받아줘야 한다



여기서 파이썬 버전에 맞는 것을 설치해주면 된다.

잘 모르겠다면 아래와 같이 입력하여 확인해볼수 있다. 

나는 3.5.4이고 (실수로)32비트 아나콘다를 설치하였으므로 cp35~win32.whl를 받으면 된다.

혹시나 이 글 보고 아나콘다를 설치해야 한다면 64비트로 설치하면 된다.


해당 파일을 파이선 워킹디랙토리에 넣고 pip install 파일명을 해주면 잘 설치가 되며, 설치 완료후에는 아래와 같은 화면이 뜬다.



이제 준비를 끝냈으니 TA-Lib와 보조지표들에 대해서 간단히 설명해 보겠다.

비트코인 가격을 분석할때 많은 트레이더들은 다양한 보조 지표들을 사용하며, 

차트는 심리의 반영이기 때문에 이러한 보조 지표들이 (일정부분) 맞아 떨어지는 경우가 많다.


비트코인 3시간 봉 - RSI를 볼때 과매수 영역으로 진입하고 있음을 알 수 있으며 RSI와 MACD가 상승하고 있다는 것은 매수가 붙고 있다는 것을 뜻한다


(더 많은 차트분석은 제 트레이딩뷰 퍼블리시에서 보실 수 있습니다)

이러한 이유로 보조지표가 유의미한 변수가 될 수 있다고 생각하였으며, 선택한 보조지표 리스트는 아래와 같다.

  • SMA5

  • SMA20

  • SMA120

  • EMA12

  • EMA26

  • MACD

  • RSI

  • BBANDS

그 외 다른 보조지표들은 깃허브 참고


해당 지표들을 추가하기 위한 파이선 코드는 아래와 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import csv
import numpy as np 
import talib
import pandas as pd
 
 
df = pd.read_csv("./duplication_eliminated_bitcoin_data.csv")
df.head(20)
 
close = np.array(df[['close']])
close
 
##################################지표추가#######################################
 
df['sma5'= talib.SMA(np.asarray(df['close']), 5)
 
df['sma20'= talib.SMA(np.asarray(df['close']), 20)
 
df['sma120'= talib.SMA(np.asarray(df['close']), 120)
 
df['ema12'= talib.SMA(np.asarray(df['close']), 12)
 
df['ema26'= talib.SMA(np.asarray(df['close']), 26)
 
upper, middle, lower = talib.BBANDS(np.asarray(df['close']), timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
df['dn'= lower
df['mavg'= middle
df['up'= upper
df['pctB'= (df.close - df.dn)/(df.up - df.dn)
 
rsi14 = talib.RSI(np.asarray(df['close']), 14)
df['rsi14'= rsi14
 
macd, macdsignal, macdhist = talib.MACD(np.asarray(df['close']), 12269)  
df['macd'= macd
df['signal'= macdsignal
 
 
df.to_csv('./중복제거최종데이터_전처리전.csv', sep=',',na_rep='NaN')
 
cs

한 파이선 코드는 아래와 같

모든 보조지표는 종가를 매개변수로 받고 있으므로 우리는 종가(close)를 활용하여 보조지표를 추가해 준다



추가를 한 후 csv를 열어보면 잘 들어온 것을 확인할 수 있다. 

NaN은 아직 지표를 계산하기에 충분하지 못한 일수(ex 12일 이동평균선인데 12일보다 적게 데이터가 모여있음)이기 때문에 발생한 것이므로 

추후 분석시엔 NaN이 없는 지점부터 사용하게 된다.


다음 포스팅에선 해당 데이터들을 최종적으로 정리하여 전처리를 마무리하게 된다.

오늘부터 진행해볼 실습은 딥러닝을 통한 비트코인 가격 예측이다.

이를 위해서는 아래와 같은 프로세스를 통해 진행해 보겠다. 



따라서 오늘 진행할 것은 API를 통하여 비트코인의 과거 가격 데이터를 수집해보도록 하겠다 


우리가 API를 통해 받아올 거래소는 미국의 "Coinbase" 거리소이다. 

해당 거래소를 선택한 이유는 아래와 같다.


Coinbase (Gdax)

  • USDT가 아닌 USD를 사용 
  • 미국 내 거래량 1위 


Bitfinex와 Binance같은 거래소를 선택하지 않은 이유는 테더로 거래를 진행하기 때문이다. 

테더가 사실 우리가 진행할 예측에 크게 영향을 주진 않지만 때마침 테더 관련 이슈가 터진 상황이기 때문에 

향후에도 예측모델을 사용하기 위해 혹시모를 사태에 대비해 USD를 이용하는 Coinbase(Gdax)를 선택하였다. 


코인베이스의 API 설명 사이트는 이곳이며 클릭시 이동하게 된다.

해당 페이지에서 과거 데이터 수집을 위한 방법을 찾아보자.



대략적인 파라미터는 start(시작시간) 과 end(종료시간), 그리고 granulariy(몇시 봉으로 할건지)를 넣어줄 수 있다.

여기서 보여준 예시는 아래와 같다 


즉 종목과 TImeslice를 정해주면 지정해준 시간부터 지정해준 시간까지에 대한 데이터를 수집할 수 있다는 것이다.


Gdax는 UnixTIme을 쓰는데, 이는 우리가 쓰고있는 2018-02-16 이런 시간대신, 뭔가 다른 계산 프로세스를 통해서 시간을 표시하는 것이다.

해당 프로세스의 장점은 현실에서 2월은 30일까지 있고 3월은 31일까지 있고 이런 귀찮은 프로세스를 처리하지 않아도 자연스럽게 타임라인을

표시할 수 있다는 것이지만, 직관성이 매우 떨어진다는 단점을 가진다. 


자세한 시간계산 프로세스는 모르겠고, 우선 우리가 쓰는 시간을 Unixtime으로 바꿔줄 수 있는 코드는 아래와 같다. 


1
2
3
4
5
6
from datetime import datetime
 
def unixtime(t): #우리가 쓰는 시간을 유닉스타임으로 바꿈 
    temp = t.split('-')
    return int(time.mktime(datetime(int(temp[0]), int(temp[1]), int(temp[2])).timetuple()))
 
cs

활용 예시는 아래에서 다시 확인하겠다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import gdax
from datetime import datetime
import csv
import time
 
#########데이터 수집################
public_client = gdax.PublicClient()
= open("./get_bitcoin_data.csv"'a', newline='')
wr = csv.writer(o)
 
granularity = 3600*6#3600*6 #6시간봉
interval = 3600*24*50
start_time = '2015-02-28'
last_time = '2018-01-17'
 
result = []
#for i in range(unixtime('2015-02-28'), unixtime('2018-01-17'), interval): #i는 15-02-28 ~ 18-01-17 이지만 (i, i+777600)(2015-02-28~2018-01-25까지) 뽑으니깐 오늘인 18-01-18까지 다 뽑아짐.
for i in range(unixtime(start_time), unixtime(last_time), interval): #i는 15-02-28 ~ 18-01-17 이지만 (i, i+777600)(2015-02-28~2018-01-25까지) 뽑으니깐 오늘인 18-01-18까지 다 뽑아짐.
    start = str(datetime.fromtimestamp(i)).split(' ')[0]
    if i + interval > unixtime(last_time): 
        end = str(datetime.fromtimestamp(unixtime(last_time) + 3600*24)).split(' ')[0# 이렇게 하면 새벽3시까지만 나오므로 18시간 뒤까지 뽑게 24시간의 초를 더해줌
    else:
        end = str(datetime.fromtimestamp(i+interval)).split(' ')[0]
    
    r = public_client.get_product_historic_rates('BTC-USD', start=start, end=end, granularity=granularity)
    for k in r:
        result.append([str(datetime.fromtimestamp(k[0])), k[1], k[2], k[3], k[4], k[5]])
 
    last_r_size = len(r) #마지막 넣을때 사이즈 - 예를들어 마지막에 12만 저장되면 
    print(last_r_size)
 
 
popped = result.pop(-1*last_r_size) #그냥 17일까지로 뽑으면 17일 새벽3시까지만 뽑힘. 그래서 하루 더 뽑은다음 제일 마지막 시간만 빼게 되면 17일 저녁 9시까지 뽑을수 있음 
print('del',popped)
for i in result:
    wr.writerow(i)
 
o.close()
##########################
cs

위 코드는 Gdax API를 활용하여 비트코인 과거 데이터를 뽑아온 것이다.

자세한 과정을 라인별로 설명하면 다음과 같다. 


우선 Gdax API를 사용하기 위하여 gdax를 import 해주고, 이를 csv 파일로 저장해주기 위한 코드 역시 삽입한다.


Granularity 는 3600 (한시간은 3600초) * 6을 하여 여섯시간 봉을 뽑아오게 된다.

Interval에 준 3600*24*50의 뜻은 한번에 뽑을때 50일치를 가지고 오는데 거기서 *4를 하니깐 200일치를 가지고 온다는 것이다.

즉 하루 4개(24시간 = 6시간 *4) * 50일치는 200개 봉.


그 아래 포문은 계속 뽑아오는 과정이고, 이 과정에서 한가지 짚고 넘어가야 할 것이 있다.

유닉스 타임을 사용해서인지 어떤건지 우리가 지정한 last_time이 전부 받아오는 것이 아닌, 해당 일의 새벽 3시까지 봉만 받아오는 문제점이 발생하였다.


따라서 last_time에서 하루를 더 받아온다음, 제일 마지막에 받아온 봉을 날리는 방법으로 우리가 원하는 last_time까지 데이터를 수집하기로 하였다.

해당 코드는 주석이 달린 부분에서 확인할 수 있다.


따라서 마지막까지 데이터를 다 받아온 후, 이에 대한 사이즈를 구해야 삭제하는 코드를 진행하였다. (result.pop~) 


해당 코드를 돌려보면 아래와 같이 뜬다. 



위에서 설명한 것처럼 200개씩 받아온 후, 마지막 행을 del을 프린트하며 삭제하도록 한다. 


받아온 csv파일을 열어보니 데이터 순서도 뒤죽박죽이고 무엇보다 중복된 데이터들이 있었다.

정확한 이유는 모르겠다. 아마 gdax api가 구려서 그런것 같은데.. 


여튼 이를 해결하기 위해 파이썬 딕셔너리를 사용하였다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
############ 중복제거##################   
= open("./get_bitcoin_data.csv"'r', newline='')
rdr = csv.reader(f)
dic = {}
for i in rdr: # 한줄씩 보겠다 
    key = str(i[0]) #0번인덱스 = 날짜 / 이걸 키로 설정함 
    value = str(i[1]) + ' ::: ' + str(i[2]) + ' ::: ' + str(i[3]) + ' ::: ' + str(i[4]) + ' ::: ' + str(i[5])
    #temp = str(i[0]) +' ::: ' + str(i[1]) + ' ::: ' + str(i[2]) + ' ::: ' + str(i[3]) + ' ::: ' + str(i[4]) + ' ::: ' + str(i[5])
    dic[key] = value
 
f.close()
 
= open("./duplication_eliminated_bitcoin_data.csv"'w', newline=''# 저장 
wr = csv.writer(o)
wr.writerow(['time','low','high','open','close','volume']) # 필터 자리 놔둠
 
for i in dic: # i 는 키 
    temp = dic[i].split(' ::: '# 딕셔너리는 랜덤하게 긁어옴 - 그래서 엑셀에서 정렬해줘야 
    temp.insert(0, i) #맨앞에 날짜 추가
    wr.writerow(temp)
    #wr.writerow(i.split(' ::: '))
 
 
o.close()
###########################
cs


파이선 자료형중 하나인 딕셔너리에 대해 간단하게 설명하면, 인덱스별로 짝을 맞게 삽입하여 인덱스를 입력하면 짝이 튀어나오도록 하는 것이다.

이 과정에서 중복되는 인덱스가 있으면(키) 둘 중 하나가 랜덤으로 삭제되게 된다.


csv파일을 보았는데 우리가 가진 데이터의 중복 튜플들은 소수점을 올림했냐 버림했냐 반올림했냐 등의 사소한 문제였기 때문에 뭐가 삭제되어도 

딱히 상관이 없으므로 그냥 딕셔너리에 삽입후 꺼내도록 한다. 


key는 0번 인덱스로 설정 - CSV파일을 TIme 칼럼을 뜻한다. 

그 뒤 해당 행의 가격 값들 (high, low, close, volum 등등등) 을 짝을 맞추어 딕셔너리에 삽입한다.(for i in rdr문) 

":::" 를 경계로 각 값을 붙였기 때문에 해당 값을 기준으로 다시 나눠주게 되고, 해당 행의 0번자리(제일 앞자리)에 i(날짜)를 추가하게 된다. (for i in dic문) 


이렇게 저장된 csv파일을 확인해보면 


이렇게 중복 데이터가 삭제된 것을 확인할 수 있다. 

물론 정렬은 안 된 상태여서 그냥 액셀에서 필터를 활용하여 오름차순 정렬을 하였다. 


이렇게 해서 API를 활용한 비트코인 가격 데이터 수집을 마치고, 

이제 모델 학습에 활용할 변수 획득을 위한 보조지표(기술분석을 위한 보조지표)를 추가하여 보겠다. 


+ Recent posts