Offline RL with Decision Transformer (GPT2Decision Transformer)
안녕하세요
오늘은 제가 학습하는 트레이딩 모델의 훈련과
그중 OfflineRL기반 모델을 중점으로 살펴보고
훈련 모델의 어떤 요소들을 트레이딩 외에 서비스에 활용할 수 있는지 알려드리겠습니다.
사실 과거 저의 영상들을 뒤져보면 중복되는 내용들이 있을 텐데,
새로 유입되는 분들이 꾸준히 늘고있어서 아무래도 반복되는 이야기를 해야 하는 점 양해 부탁드립니다.
모델 종류
예전에도 말씀드린 적 있지만
모델로 활용할 수 있는 종류는 여러가지가 있지만
저는 퀀트 전략에 기반한 모델부터
예측모델과 강화학습 모델까지 활용하고 있습니다.
퀀트 모델들은 흔히 우리가 아는 기술적 지표가 어느 수치 이상이면 매수, 매도를 결정하는
인간중심적인 모델입니다.
다른 모델들에 비해 간단하지만 무시할 수 없는 모델입니다.
두 번째는 예측모델입니다.
예측모델은 제가 주로 다뤄왔던 모델들이고
주로 시계열을 분석하여 가격의 예측에 중점을 둔 모델입니다.
가격의 예측을 기반으로 매수, 매도의 의견을 낼 수도 있겠지만
손실함수 자체가 예측의 MSE를 보다보니 트레이딩 모델로 활용하기에 완벽하지 않은 점이 조금 있었습니다.
매수나 매도에 대한 의견과 투자 수익을 학습 과정에 반영할 수 있다면 더 좋은 모델이 될 수 있을 겁니다.
마지막은 강화학습 모델입니다.
강화학습은 주로 online learning 영역의 알고리즘이 많이 널려있고
학습에 대한 방법들도 주로 online learning RL 중심이 많습니다.
하지만 저는 시장데이터를 수집해서 학습시키기 때문에
offline learning RL을 이용한 학습을 합니다.
이번에 서비스를 개편하면서 강화학습 모델도 여러분들께 선보일 수 있도록 시스템을 좀 수정중인데요
과정에서 일어났던 일들을 조금씩 기록해보려고 합니다.
Decision Transformer forward
DecisionTransformerModel을 살펴보도록 하겠습니다.
이전에도 Decision Transformer에 대해 다룬 적이 있는데요.
간단히 Decision Transformer 그리고 offline-rl에 대해 이야기해보겠습니다.
오프라인 강화학습
우선 오프라인 강화학습은
에이전트가 실시간 환경 상호작용 없이
고정된 데이터셋으로 학습하는 방법으로 실제 환경에서 학습하기엔 적절하지 않은 모델의 경우 이런 방식의 학습을 진행합니다.
대신 미리 수집된 데이터셋을 이용해 최적의 정책을 학습하는 방법입니다.
트레이딩 환경의 경우 제가 이전에 공유했던 것처럼 합성데이터를 이용해서 훈련을 시키는 방법도 있지만
아직 실험적 단계인 수준입니다.
로보틱스, 의료, 트레이딩 등 환경 상호작용이 위험하거나 비용이 높은 경우에 유용합니다.
대신 주어진 학습 데이터 분포 내의 학습만 이루어지고, 새로운 데이터와 상호작용 하다 보면 더 많은 시행착오를 겪어야 할지도 모릅니다.
하지만 시장은 알 수 없는 변동성이 넘치는 것 같다가도 가격은 인간의 심리로 만들어가기 때문에
현재 보이는 패턴들을 과거에도 보이는 모습을 많이 찾아볼 수 있습니다.
갑작스러운 변동성에 알고리즘의 알파가 무너질 순 있지만,
결국 확률우위의 추세를 이용한 모델은 시장 이상의 성과를 낼 수 있으리라 믿습니다.
Decision Transformer
요즘엔 동네 할머니 할아버지들도 ChatGPT를 아시더라구요
그만큼 인간에게 필요한 서비스가 나오게 되면 최전선의 기술이 닿기 어려운 사람들까지도 손쉽게 전달되는 것 같습니다.
GPT도 Decision Transformer와 큰 부류에는 Transformer라는 같은 소속을 가집니다.
Transformer는 시퀀스의 어텐션으로 중요한 포인트들을 잘 짚어내고 이를 이용해 오프라인 RL을 시퀀스 모델링 문제로 재구성한 모델이
Decision Transformer입니다.
전통적인 RL 방식인 가치함수 학습이나 정책경사와 같은 방법들 대신에
과거 상태, 행동, 그에 따른 보상등을 입력받아 다음 행동을 예측합니다.
기존 시계열 예측 모델이 시계열데이터 자체를 예측했다면
Decision Transformer에서는 시계열 데이터인 상태, 그에 따른 행동과 보상자체를 일련의 시계열로 보는 것입니다.
트랜스포머의 병렬 처리와 장거리 의존성 학습 능력을 활용해 긴 시퀀스의 특징들을 포착합니다.
입출력
강화학습 자체가 환경의 상태를 보고 행동을 하는 모델인데
입력도 states, actions를 입력받습니다.
하지만 trajectory 데이터로 학습하는 offline rl 이기에 trajectory에서 볼 수 있는 rewards, returns_to_go 등이 보입니다.
정리해 보면 입력은
- states: 상태 (예: 트레이딩의 기술적 지표, [batch_size, seq_length, state_dim]).
- actions: 행동 (예: 매수/매도, [batch_size, seq_length, act_dim]).
- rewards: 보상 (당신의 코드에서는 사용 안 함, [batch_size, seq_length, 1]).
- returns_to_go: 예상 누적 보상 (R_hat, [batch_size, seq_length, 1]).
- timesteps: 타임스텝 인덱스 (예: [0, 1, ..., K-1], [batch_size, seq_length]).
- attention_mask: 어텐션 마스크 (유효 토큰 표시, [batch_size, seq_length]).
그리고 출력인 DecisionTransformerOutput은
- state_preds: 다음 상태 예측.
- action_preds: 다음 행동 예측 (당신의 코드에서 주로 사용).
- return_preds: 다음 Returns-to-Go 예측.
- DecisionTransformerOutput: last_hidden_state, hidden_states, attentions 포함 (옵션).
위와 같습니다
제가 예전버전 라이브러리를 썼어서 그런 건 진 모르겠는데
model.device를 gpu로 해주어도
attention_mask는 gpu로 처리되지 않는 이슈가 있었는데 (클래스 내 attention_mask 지정해 주는 코드가 device 처리 로직이 없는..)
attention_mask를 별도로 지정하지 않고 실행 시 에러가 날 수 있는데
학습 시 입력에 넣어줄 때 attention_mask를 별도로 지정해서 넣어줬습니다.
_batch['attention_mask'] = torch.ones(batch_size, seq_len, dtype=torch.long).to(device)
클래스 내 attention_mask 지정코드는 아래와 같습니다.
if attention_mask is None:
# attention mask for GPT: 1 if can be attended to, 0 if not
attention_mask = torch.ones((batch_size, seq_length), dtype=torch.long)
임베딩
# embed each modality with a different head
state_embeddings = self.embed_state(states)
action_embeddings = self.embed_action(actions)
returns_embeddings = self.embed_return(returns_to_go)
time_embeddings = self.embed_timestep(timesteps)
# time embeddings are treated similar to positional embeddings
state_embeddings = state_embeddings + time_embeddings
action_embeddings = action_embeddings + time_embeddings
returns_embeddings = returns_embeddings + time_embeddings
- 각 입력(states, actions, returns_to_go)은 별도의 임베딩 레이어를 거쳐 고정된 차원(hidden_size)으로 변환.
- timesteps는 위치 인코딩처럼 처리되어 각 임베딩에 추가
시퀀스 구성
# this makes the sequence look like (R_1, s_1, a_1, R_2, s_2, a_2, ...)
# which works nice in an autoregressive sense since states predict actions
stacked_inputs = (
torch.stack((returns_embeddings, state_embeddings, action_embeddings), dim=1)
.permute(0, 2, 1, 3)
.reshape(batch_size, 3 * seq_length, self.hidden_size)
)
stacked_inputs = self.embed_ln(stacked_inputs)
- 입력은 [R_1, s_1, a_1, R_2, s_2, a_2, ..., R_K, s_K, a_K] 순서로 쌓임.
- stacked_inputs: [batch_size, 3 * seq_length, hidden_size] (예: [1, 60, hidden_size] for K=20).
- embed_ln: LayerNorm으로 정규화.
어텐션 마스크
# to make the attention mask fit the stacked inputs, have to stack it as well
stacked_attention_mask = (
torch.stack((attention_mask, attention_mask, attention_mask), dim=1)
.permute(0, 2, 1)
.reshape(batch_size, 3 * seq_length)
)
- 어텐션 마스크도 [R, s, a] 순서에 맞게 3배 확장 ([batch_size, 3 * seq_length]).
트랜스포머 인코더
# we feed in the input embeddings (not word indices as in NLP) to the model
encoder_outputs = self.encoder(
inputs_embeds=stacked_inputs,
attention_mask=stacked_attention_mask,
position_ids=torch.zeros(stacked_attention_mask.shape, device=device, dtype=torch.long),
output_attentions=output_attentions,
output_hidden_states=output_hidden_states,
return_dict=return_dict,
)
x = encoder_outputs[0]
- 트랜스포머 인코더(GPT-2 based)가 시퀀스를 처리하여 문맥 정보를 학습
- 출력 x: [batch_size, 3 * seq_length, hidden_size].
출력 재구성
# reshape x so that the second dimension corresponds to the original
# returns (0), states (1), or actions (2); i.e. x[:,1,t] is the token for s_t
x = x.reshape(batch_size, seq_length, 3, self.hidden_size).permute(0, 2, 1, 3)
- x를 [batch_size, 3, seq_length, hidden_size]로 재구성.
- 인덱스: x[:, 0, :, :] (Returns-to-Go), x[:, 1, :, :] (States), x[:, 2, :, :] (Actions).
예측 생성
# get predictions
return_preds = self.predict_return(x[:, 2]) # predict next return given state and action
state_preds = self.predict_state(x[:, 2]) # predict next state given state and action
action_preds = self.predict_action(x[:, 1]) # predict next action given state
- action_preds: 상태(s_t)를 기반으로 다음 행동 예측
- return_preds, state_preds: 보통 훈련에서만 사용
훈련
간단하게 이번 하락장에 훈련 모델들을 테스트해보았는데요
모델의 파라미터수를 변화시켜 가며 트레이딩 시뮬레이션을 동작했고
보수적인 행동들을 쌓아가면서 결국 하락장 시장 수익을 이겨내는 모습을 보였습니다.
근데 이번 케이스는 너무 안정적으로 수익이 쌓여가서
아마 일부 데이터에 look ahead bias성 데이터가 들어갔는지 살펴봐야 할 것 같습니다.
두 번째 차트는 모델의 행동 그래프인데요
보시는 바와 같이 상태들을 보고 최적의 행동들을 선택하고 있는데
중간씩 행동들이 주파수모양처럼 변동이 심한 모습을 보이고 있는데
학습데이터에서 저런 행동이 유의미하게 작용했기 때문에 저런 행동을 하는 것으로 보입니다.
사실 저렇게 행동을 마구 바꿔가면 거래비용에 드는 슬리피지나 수수료를 생각해서 좋은 행동들은 아니기 때문에
이런 부분들까지 에이전트 행동의 보상으로 반영해 주면 더 좋은 모델이 될 것 같습니다.
모델의 생애주기
제가 데이터를 수집하고 가공하는 과정과
여러분들께 서비스로 제공되는 시점까지의 간단한 흐름을 그려보았습니다.
데이터의 수집과 모델 훈련을 위한 가공, 훈련과 배포
자동화된 데이터 배치와 추론 스케줄링
그리고 서비스에 제공되기 위한 영역의 개발까지 제가 모두 구현하고 있습니다.
더 세부적으로 이야기하자면
처음에 이야기했던 모델의 종류마다 서비스에서 보이는 데이터와 역할도 다를 것 같은데요
예측모델은 현재 시장의 예측을,
알고리즘 모델들과 강화학습 모델들은
현재 시장의 통계적인 기법을 이용하여 확률적으로 수익을 위한 유리한 포지션을 실시간으로 공유해 줄 수 있을 겁니다.
그리고 그런 포지션들의 과거 수익률과 현재 시장에 어울리는 전략들과
혹은 여러분들이 직접 여러분들에게 맞는 모델의 전략을 추려서 과거데이터에 백테스트해 보고 상호작용할 수 있는 요소들을 고민하고 있습니다.
일련의 과정에는 시스템을 유지하고 데이터를 사들이고 유지하기 위한 비용이 들어가고 있습니다.
지금은 제가 트레이딩 시스템에서 불리는 돈으로 충당하고 있지만
트레이딩 시스템은 제가 스스로의 레버리지를 일으켜서 하는 돈벌이라면
서비스는 제가 여러분들의 관심을 레버리지 삼아서 일으키는 돈벌이이기 때문에
어서 서비스를 출시하고 싶은 마음이기도 합니다.
사업의 현황에 대해 공유드리자면
아직 금감원과 관련 절차에 대한 과정에 있고
조만간 심사 결과가 나올 것 같습니다.
심사 결과가 나오면 유튜브 채널에 회원제를 도입해서 아이디를 발급하거나
인증된 회원분들에게만 서비스에서 부가적인 기능을 더 제공하는 방안으로 논의가 되었습니다.
올해도 많은 시간이 흐르고 슬슬 더워지기 시작하는 것 같은데요
저도 올 한 해 많은 것을 이루고 싶은 마음이 커서 급하기도 하지만
차근차근 조금씩 일들을 처리해가고 있습니다.
앞으로 더 자주 이야기드릴 수 있도록 노력하겠습니다.
감사합니다.