LSTM 다변량 예측하기 exercise (실전 활용)

LSTM 다변량 예측하기 exercise (실전 활용)

LSTM

LSTM은 장기적인 종속성을 학습 할 수 있는 특수한 종류의 RNN입니다.

RNN은 전후의 결과를 기억하고 있지만 긴 기간의 차이에 대한 정보연결이 어렵다.

이 RNN에 장단기 시계열적 특성을 반영한 것이 LSTM이다.

이 글의 목적은 바로 전 날 데이터를 바탕으로 비트코인의 종가를 맞춰보자! 입니다.

여기서 며칠간의 데이터로 다음 날의 종가를 맟추는 것으로 확장을 해보고,

다음 날 가격이 오를지 내릴지 맞추는 모델을 만들어 봅시다. (성능 ㅈㄴ 안나옴.)

우선 데이터셋을 살펴 봅시다.

dataset

논문을 참고해 비트코인 가격에 영향을 미치는 변수들을 모두 포함시킨 데이터셋을 만들었습니다.

이 데이터 셋을 간단히 살펴봅시다.

결측치, 데이터 타입 모두 이상이 없군요.

from matplotlib import font_manager, rc font_path = '/usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf' font_name = font_manager.FontProperties(fname=font_path, size=10) plt.figure(figsize=(15,5),dpi=100) groups = [ i for i in range(11)] i = 1 for group in groups: plt.subplot(len(groups), 1, i) plt.plot(dataset_train.values[:, group]) plt.title(dataset_train.columns[group], y=0.5,loc='right',FontProperties= font_name) i += 1 plt.savefig('savefig_facecolor.png', facecolor='#eeeeee') plt.show()

자 폰트 주소 변수를 만들어주고 font_manager.FontProperties에 주소 변수와 size를 넣어줍니다

이 과정이 없으면 한글 폰트는 깨지게 되거든요~ 그래서 한글이 출력되는 부분인 plt.title에 매개 변수인 FontProperties에 font_name을 넣어 줍니다. plt.savefig는 출력한 그래프를 저장하는 함수 입니다.

자 그래프를 보며 알아서 인사이트를 얻어보자. (알아서)

전 날의 데이터(하루)로 그 다음 날 가격 예측하기.

def series_to_supervised(data, n_in=1, n_out=1, dropnan=True) : n_vars = 1 if type(data) is list else data.shape[1] df = pd.DataFrame(data) cols, names = list(), list() for i in range(n_in,0,-1): cols.append(df.shift(i)) names += [ ('var%d(t-%d)' % (j+1, i)) for j in range(n_vars) ] for i in range(0, n_out): cols.append(df.shift(-i)) if i == 0: names += [('var%d(t)' % (j+1)) for j in range(n_vars)] else: names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)] agg = pd.concat(cols, axis=1) agg.columns = names if dropnan: agg.dropna(inplace=True) return agg

사실 전처리는 끝나지 않았습니다. 저는 이 함수를 보면 정신이 아득해졌습니다. 제가 차근차근 설명 드리겠습니다.

저희는 바로 전날의 데이터로 당일 날의 가격을 맞추려고 하는 겁니다. 그래서 모델이 학습할 수 있게 데이터를 고쳐주어야 합니다. 이게 그 부분에 해당하는 함수 입니다. 이 함수를 거치면 전날의 데이터와 현재의 데이터가 한 row해 존재하게 됩니다. 전날의 데이터와 현재의 데이터 한 행에 존재하게 됩니다. 아무래도 한 줄 씩 실행시켜 보는 것이 좋아요.

암튼 전날 데이터와 그 다음 날 데이터를 한 리스트에 담아서 합쳐주게 되고 열 이름 설정해주고~ 이런 흐름으로 파악하십쇼.

자 이제 함수에 넣어보세요.

자 함수를 통과하고 데이터셋을 보면 이런 식으로 되어 있죠? 아마 저는 정규화를 시키고 넣어서 저런 식으로 나오게 되는데 정규화를 안하시고 넣으시면 값이 다르시겠죠? 뭐 상관 없습니다! 이따가 정규화할게요

.아무튼 저기 (t-1) 보이시죠. 저희는 전날 데이터로 바로 그 다음 날 데이터를 예측하는 것임을 기억해야 합니다.

t-1은 바로 전날 데이터이고 t는 현재 데이터 입니다. 열이 2배가 되었죠! ㅎㅎ

자 데이터를 train과 test로 나누어줍니다. 한 세 달간의 가량을 테스트 데이터로 제가 임의적으로 정하였습니다.

자 데이터를 정규화를 해주어야겠죠? ㅎㅎ 정규화를 해주면 아주 기똥차게 모델이 잘 받아올 수 있답니다.

각 열의 데이터 스케일을 맞춰주거든요~

자 이제 모델에 넣어주기 직전 입니다. LSTM은 3차원 데이터 형태로 받아들입니다. 그렇기 때문에

위 처럼 바꿔줘요. (행 개수, 예측을 위해 필요한 일, 열 개수) 자 잘 나누어졌죠?

만일 n_day를 5로 한다면 (1377, 5, 11)가 되겠쥬? 아마도..

자 대충 모델을 만들어 줍니다. 자 input_shape도 이렇게 잘 설정해주시고 ㅎㅎ 학습시켜봅니다.

음 train, test가 잘 부비적대고 있네요. (이렇게 너무 잘나오는 것도 이 데이터의 특징입니다. 걍 전날 값이랑 비슷하게

때려 맞춰서 그런거임. 이게 한계임. 실제로 오를 지 내릴 지는 잘 예측 못함.)

자 모델 학습이 끝났으니 테스트 데이터 셋으로 predict를 해보아야겠죠! 자 위 코드를 실행!

y_hat 변수에 테스트 데이터의 행 길이 128개 만큼의 길이의 벡터가 나오겠습니다~

이제 predict하면 끝난줄 아셨죠! 근데 그 predict한 값들은 저희가 정규화한 이후의 값이기 때문에

다시 정규화전으로 돌려주어야 합니다. 정규화할 때 sc에 넣었던 데이터의 shape (우리가 fit할 때)

inv_yhat은 predict한 값을 정규화 이전으로 돌려주고

inv_y는 정규화 했던 y값을 다시 이전으로 돌려주는 함수 입니다.

RMSE는 821640만원이군요.. 비트코인이 몇 천만원이기 때문에 상당히 적을 수도 있다고 볼 수 있네요.

(하지만 학습데이터에 시퀀스 값이 있기 때문에 사실 거품임ㅎㅎ)

심지어 R2 score는 1과 매우 가까운 숫자가 나옴.

list_y = [i for i in range(len(inv_yhat))] from matplotlib import pyplot pyplot.figure(num=None, figsize=(15, 12), dpi=200, facecolor='w', edgecolor='k') pyplot.plot(inv_yhat, label='predict') pyplot.scatter(list_y,inv_yhat, label='predict') pyplot.plot(inv_y, label='ground truth') pyplot.scatter(list_y,inv_y, label='ground truth') pyplot.legend() plt.savefig('savefig_facecolor.png', facecolor='#eeeeee') pyplot.show()

자 시각화하는 코드 입니다.

와우 굉장히 그래프가 유사하게 잘나왔죠?

그럼 이 데이터가 실제로 얼마나 효과적인지 파악해 봅시다. 이 모델은 하루 전 날 데이터로 그 다음날 가격을 맞추는 겁니다. 그렇다면 오늘 데이터를 넣었을 때 내일 오를지 말지 판단할 수 있는지 보면 되겠죠?

저희가 predict한 값을 바탕으로 알아 보겠습니다.

자 이렇게 빈 리스트를 만들어주고 안에다 우리가 예측한 값과, lable 데이터를 넣어 줍니다.

df_exp= pd.DataFrame(predict_truth).transpose() df_exp.columns = ['predict', 'lable'] df_exp['predict_next'] = df_exp[['predict']].shift(-1) df_exp['lable_next'] = df_exp[['lable']].shift(-1) df_exp.fillna(method='ffill', inplace=True) df_exp['predict_u'] = df_exp['predict'] < df_exp ['predict_next'] df_exp['lable_u'] = df_exp['lable'] < df_exp['lable_next'] df_exp['result'] = df_exp['predict_u'] == df_exp['lable_u']

자 이것은 위에 저희가 한 행에 오늘 데이터랑 내일 데이터랑 존재하게 만드는 함수와 유사한 코드입니다!

새로운 열로 내일 데이터의 값을 넣어주고 그러면 새로 생긴 열은 한 칸 올린 값들이기 때문에 마지막 값은

결측치겠죠? 저는 그냥 대충 앞의 값으로 채워주겠습니다. 그리고 가격이 올랐으면 true를 내렸으면 False를 가진

열을 또 새로 만들어 줍니다. 이렇게 잘 맞췄는지 result 열에 저장을 해주고 나면!!!

자 맞춘 데이터가 110개이고 틀린 데이터가 18개 입니다. 즉 내일 가격이 오를지 내릴지 맞춘 정확도가

무려 86퍼센트가량 높게 나오고 있습니다!!! 우와 대박.. 나 이제 부자되는거야??

그 전 날들의 데이터 (5일)로 그 다음 날 예측하기.

자 열 수와 날짜의 수를 변수에 저장을 해줍니다.

스케일러에 걍 넣어주고 정규화된 데이터를 넣어주면 이러한 데이터의 모양이 나옵니다.

저희가 처음에 1506행이었는데 1501로 줄었죠? 5일간의 데이터로 해야하기 때문에 5일 이전의 데이터가 없는 경우에는

없어질 수 밖에 없겠죠. 그리고 저희가 넣어준 날짜만큼 커져야겠죠 ㅎ

자 train 데이터와 test 데이터를 나눠주고요~

자 우리가 예측할 현재는 레이블 데이터만 필요하겠죠? 그러면 레이블 데이터만 남기고 삭제해줍니다~

자 3차원으로 바꿔줍니다

자 노드 개수만 64개로 바꿔주고 돌려보았습니다. 별 이유는 없습니다. 보통 2의 배수로 많이 한다고 하는 얘기를 들어서 바꿔보았습니다.

자 이러한 그래프가 나왔습니다.

예측을 해주고

자 스케일 하기 전으로 돌아와라~얍 test_X(11개의 열(우리가 종가를 맞추기 위해 가져온 종속변수)을 가져와야

원래 형태가 복원됨.)

RMSE는 오히려 조금 늘어났군요? 20만원 정도니 별 차이 없는 것 같기도 합니다.

자 이렇게 R2값도 잘 나오고 있죠?

자 테스트 데이터가 다음날 등락을 할지 맞추는 정도는 어느정도인지 봅시다.

0.02 정도가 낮군요! 허허 그냥 하루로 측정하는게 나을까요? ㅎㅎ

그래프 또한 의례적으로 그려줘야겠죠..

내일 등락할 지 말지 맞추는 모델 만들어보기

자 똑같이 함수에서부터 시작합니다. 이번엔 걍 뚝 잘라줄게요

자 이렇게 va1(t)가 오늘의 종가이니까 저 열까지만 남겨주면 되용 ㅎㅎ (걍 위에서 쓰던 방식으로 하셔도 상관 없습니다.)

자 저희는 등락을 맞추는 모델을 만들려고 했죠? 그러니까 위에 하루전날의 종가와 당일의 종가와 비교해서 새로운 열('var0(t)')을 추가해줍니다. 당일 가격이 올랐다면 True이고 내렸다면 False가 됩니다.

이진분류로 만들기 때문에 이렇게 True와 False를 1과 0으로 바꿔줍니다.

자 이제 필요 없어진 이 종가열을 삭제 해줍니다. 저희는 등락을 맞추는 문제로 바꾸었기 때문에 필요 없쥬 ㅎㅎ

자 이렇게 training, test 데이터와 바꿔주고 input 데이터와 lable 데이터를 나눠줍니다.

삼차원 데이터로 바꿔주고

모델도 만들어주고

plot을 그려줍니다. 자 이렇게 0.5 이하의 값도 안나옵니다.. ㅠㅠ 그러니까 저희가 이진분류 문제이니까

0.5이상은 올라간다로 판단을 합니다. 그런데 0.5 이하의 값은 안나오니까 전부 오를 것이다라고 예측을 한 것입니다.

종가, 시가 열을 삭제해보고 해볼까요? 좋습니다.

자 삭제 해보았습니다!

점점 멀어지는 너.. 더 성능이 안나오네요! 전부 오를 것이다라고 예측하고 있습니다.. 혹시 비트코인 떡상의 신호"? 호호호 ㅋㅋㅋ

수고링! 이 방법은 기각입니다! 사실 원래 이게 정상이 아닌 가 싶습니다. 위에 시도한 방법은 이상하리만큼 잘 먹히네요.

머지..

from http://gt40766.tistory.com/61 by ccl(A) rewrite - 2021-12-13 05:27:19