본문 바로가기

Data & AI

PatchTST 모델을 이용한 딥러닝 트레이딩 (ICLR 2023)

반응형

 

안녕하세요

 

오늘은 ICLR 2023 PatchTST 모델을 리뷰하고 

 

트레이딩에 활용하기 위해 훈련시키고 성과를 측정해 보도록 하겠습니다. 

 

그리고 만들어진 모든 시계열 분석 모델은 트레이딩, 혹은 투자 의사결정을 위한 데이터로

 

추후 출시될 JeTech Lab에 업데이트 될 예정입니다. 

 

 

PatchTST Review

 

PatchTST는 Transformer를 이용한 시계열 예측문제에서 

 

기존의  Transformer를 이용한 방법들에 Patching과 Channel-Independence를 통해

 

성능을 개선을 제시하고있습니다. 

 

기존의 Transformer 기반의 모델들은 

 

Transformer가 주목받았던 이유이기도 한 Self-Attention에 중점을 두고 있습니다. 

 

Attention을 통해서 입력 시퀀스의 토큰 간의 상관관계를 파악하는데 강점을 두어서

 

언어모델의 성장에 크게 기여했지만, 자연어와는 다르게 시계열데이터에서는

 

"토큰들의 위치" 자체도 중요한 정보들이기 때문에

 

구조상 permutation-invariant 특성을 가지는 attention은 시계열 데이터의 처리에서 

 

데이터들의 순서가 가지는 정보를 잃게 됩니다. 

 

실제로 논문에서도 여러 Transformer 기반 시계열 모델의 입력 데이터를 Shuffle 하면서 

 

성능이 어떻게 변화하는지 체크하면서 서술됩니다. 

 

PatchTST의 핵심은

 

Patching과 Channel-Independence 입니다. 

 

Patching은 기존의 transformer 처럼 point-wise input token들 간의 분석 대신

 

지역적 sub-series 간의 비교를 통해 Local semantic information 들을 찾아나갑니다. 

 

Patching이 사실 처음 제안된건 아닙니다. 


BERT가 문자 기반 토큰화 대신 sub-word based tokenization을 진행하는 것,

 

CV의 Vision Transformer에서 Transformer 모델 입력 전에 16x16 patch들로 데이터를 나누는 작업이 있습니다. 

 

Patching 도식화 (출처 : DSBA 연구실 PatchTST Paper Review 발표 자료)

 

Channel-Independence의 핵심은 

 

Transformer의 다변량 시계열 입력에서 각 입력 토큰이 single-channel information만 포함하는 것을 의미합니다. 

 

아래 사진과 같이 다변량 시계열을 여러 개의 단변량 시계열로 분할하여 Transformer Backbone model로 넣게 됩니다. 

 

 

 

 

 

Backbone model 에선 입력된 시계열에 대해 Patching 된 데이터들을 Position Embedding 이후

 

Transformer Encoder로 보내주면서 Sub-Series들 간의 local semantic information을 학습하도록 합니다. 

 

그리고 각 단변량 시계열의 미래 데이터를 예측하도록 합니다. 

 

class PatchEmbedding(nn.Module):
    def __init__(self, d_model, patch_len, stride, padding, dropout):
        super(PatchEmbedding, self).__init__()
        # Patching
        self.patch_len = patch_len
        self.stride = stride
        self.padding_patch_layer = nn.ReplicationPad1d((0, padding))

        # Backbone, Input encoding: projection of feature vectors onto a d-dim vector space
        self.value_embedding = nn.Linear(patch_len, d_model, bias=False)

        # Positional embedding
        self.position_embedding = PositionalEmbedding(d_model)

        # Residual dropout
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        # do patching
        n_vars = x.shape[1]
        x = self.padding_patch_layer(x)
        x = x.unfold(dimension=-1, size=self.patch_len, step=self.stride)
        x = torch.reshape(x, (x.shape[0] * x.shape[1], x.shape[2], x.shape[3]))
        # Input encoding
        x = self.value_embedding(x) + self.position_embedding(x)
        return self.dropout(x), n_vars

 

실제 구현된 PatchEmbedding을 보면

 

patch_len, d_model의 value_embedding과 position_embedding을 backbone으로 forward 하는 것을 볼 수 있습니다. 

 

이전 사진에서 L=12의 시계열의 경우 12개의 point-wise 시계열 데이터들을 입력 토큰으로 넣어주어야 하지만

 

patching에서는 (L-P) / 2 + padding으로  L -> L/S로 토큰 수가 감소하게 됩니다. 

 

토큰 수가 감소한다는 게 성능저하라고 생각할 수 있지만

 

앞서 이야기했듯 attention 연산을 최적화하고, 

 

point-wise 시계열입력대비 더 많은 local semantic information을 반영하는 구조라고 볼 수 있습니다. 

 

 

Training 

학술적인 이야기들을 좋아하실 수 있지만

 

제 채널은 엄연히 투자/트레이딩 채널이고 이런 지식을 가치로 만들 수 있는가 실험을 해봐야 합니다.

 

저는 앞으로 여러분들이 투자하는 여러 상품들에 대해 투자 분석 데이터를 공유드릴 예정입니다. 

 

국내외 주식과 지수, ETF, 암호화폐들이 포함된 모든 시장데이터를 분석해서 공유드려야 하지만

 

이 포스트에서 모든 것들을 공유할 순 없으니

 

대표적으로 많은 분들이 투자하고 계시는 나스닥 지수 추종 ETF 인 QQQ 학습시켜 보았습니다. 

 

그리고 다변량 데이터로 준비하기 위해 여러 자산군들의 가격들도 특성 데이터로 추가해서

 

자산들 간의 상관관계를 QQQ 트레이딩에 활용하기 위한 모델로 학습시키기로 했습니다. 

 

트레이딩을 비롯해, 시계열에선 각 시계열마다 가지는 고유한 특징들이 있기 때문에

 

훈련한 모델을 다른 시계열에 적용하기 어렵다는 단점이 있습니다. 

 

그래서 모든 자산들에 대해 학습을 시켜야 하기 때문에 모든 자산들에 대해 훈련을 진행하고 

 

여러분들에게 제공되기까지 시간이 걸릴 수 있음을 이해해 주시기 바랍니다. 

 

우선 PatchTST 자체가 Long-term Forecasting을 위해 고안되긴 했지만

 

channel-independence 모델이기에 다변량 시계열에서 looback window를 분할하여 shared-network로 활용한다는 점

 

그리고 patching을 통해 연산량과 메모리 사용량을 감소시키고 local semantic information을 강화한다는 점에서

 

굳이 long-term forecasting이 아니어도 잘 수행해 내지 않을까 생각했고

 

(트레이딩에선 long-term forecasting이 의미 없기도 해서)

 

20-timesteps sequence를 입력받아 5-timesteps sequence를 예측하는 task에 대해서 훈련을 시켰습니다. 

 

 

테스트 데이터 구간에 대해서 예측을 수행한 결과입니다. 

 

파란 선은 GroundTruth / 주황선은 Prediction입니다. 

실제 데이터는 급변하게 변하지만, 예측치는 아무래도 MSE Loss를 거쳐 학습된 모델이 주는 값이기 때문에 소극적인 예측을 하긴 합니다. 

 

그래도 현재 시장 상태를 기준으로 5일 이후 거래일의 상승, 하락에 대한 예측에 의미를 두고 백테스트를 수행해 보았습니다. 

 

 

batch_size를 24로 학습했어서 

 

24개의 profit bar가 나타나있고, 각 배치별 평균 수익을 빨간 수평선으로 그려놓았습니다. 

 

모든 배치들에 대한 수익을 보여드리기엔 너무 많은 데이터들이라 일부만 보여드렸는데 

 

전체 테스트 셋에 대한 상승 / 하락에 대한 예측 정확도는 52% 정도로 기대되고 있습니다. 

 

매번 예측을 잘하는 모델은 아니지만 예측이 틀릴 경우 손해가 적어 손익비 좋은 모델로 활용할 수 있을 것 같습니다.

 

 

모델을 만들었으면 이제 추론을 통해 시장을 분석하고 실시간 예측 데이터들을 제공하는 함수를 만들어서 활용해야 합니다. 

 

사실 백테스트 / 테스트 데이터에 대해 실험을 진행하면서 

 

마지막 입력 데이터에 대해서 모델의 forward 함수를 호출시켜 주면 현재 시점의 데이터에 대한 예측값들을 제공해 줍니다. 

 

글을 적고 있는 시점은 한국시간 9월 30일 오후 8시인데 아래와 같은 예측을 제공하고 있습니다. 

 

 

 

 

 

 

 

마무리

 

이렇게 PatchTST에 대한 학습과 백테스트, 그리고 실제 모델 투입을 위한 추론까지 진행해 보았습니다. 

 

그렇다고 하더라도 바로 서비스에 적용하는 건 불가능합니다. 

 

모델이 만들어낸 데이터를 우리가 그래프로 볼 순 있지만 

 

JeTech Lab에서 원활하게 데이터를 보여주고, 변화하는 시장에 따라 기민한 데이터 제공이 필요하기에 

 

Model Serving API 작업이 추가로 필요합니다. 

 

 

한동안 JeTech  Lab 개발거리를 다루느라 새로운 모델에 대한 연구가 뜸했었죠.

 

오늘은 JeTech Lab에 보이는 하나의 데이터를 위해

 

이 데이터가 어떤 지식으로부터 시작되었는지

 

이 데이터가 여러분들의 투자 의사결정을 위해 어떻게 응용해야 하는지

 

이 데이터가 가치가 될 수 있는지 검증하고, 여러분들에게 전달되기까지 어떤 개발을 설계하고 구현해야 하는지 이야기해 보았습니다. 

 

뭐 지표팔이라고 생각하실 수 있는데

 

주관적인 이유로 차트에 선을 그으면서 롱 숏 외치는 사람들이나

 

검색식으로 이것저것 넣어서 조잡하게 만든 지표들보다

 

더 객관적이고 논리적이고 설명가능한 근거가 있는 통계적인 방법의 지표들이라고 생각합니다. 

 

그리고 JeTech Lab에서 대부분의 데이터를 무료로 공개할 예정이고

 

차츰 유료화를 진행하면서도 일부 프리미엄 데이터들을 제외하고는 

 

제가 시장에서 여러 시장참여자들에게 가치를 줄 수 있다는 자아실현을 위한 목적으로 무료로 제공하고 싶습니다. 

 

(제가 사기꾼이라면 이렇게 공들여서 사기치진 않을 것 같습니다.)

 

이제 4분기가 시작되었네요. 올 해는 100일도 남지 않았습니다. 

 

남은 한 분기도 건강히 잘 보내시고

 

앞으로 더 좋은 데이터로 찾아뵙겠습니다. 

 

감사합니다. 

 

 

 

 

 

 

 

 

 

반응형