nvidia의 tesla t4 16G의 중고가 약 350만원정도에 cuda 프로세의 갯수는 2560개이다. 오로지 머쉰런닝에만 사용되므로 Rtx 시리즈와는 완전히 다르다고 할 수 있습니다. 무엇보다 가격대인데요. tesla t4모델을 다른 이유로 IDC상면가를 알아보다 GPU서버 임대가를 보는데 보통 한달에 몇백이길래 햐 이건 강화학습 따위로 매매프로그램 만드는 회사는 망하겠다고 생각하고 RTX버젼과 RTX A시리즈와 TESLA버젼이 따로 임대가 되고 있길래 가격을 봤더니 TESLA H100 80G모델이 9천에 몇백 빠지는 8천대 가격이 더라고요. 사실 현재 프로그램의 이미지 원본이 500 X 700이라 이걸 돌린거랑 현재처럼 줄인 버젼의 이미지를 돌렸을 경우 어느정도의 차이가 생길까 하는 의문이 계속되고 있습니다. 사실 CUDA 코어 갯수는 현재 정도면 되고 GDDR이라고 크게 메카니즘이 다른것은 아닐거고 메모리만 늘리면 고해상도가 아니어도 충분이 돌것 같은데 왜 GPU보드는 메모리슬롯을 확장가능하게 만들지 않았을까요? ResNet의 경우는 개발자가 인공신경망을 몇겹까지 쌓을 수 있을까 하는 의문에 만들었다고 합니다.
import torch
import torch.nn as nn
import torch.nn.functional as F
def conv_block_1(in_dim, out_dim, act_fn, stride=1):
model = nn.Sequential(
nn.Conv2d(in_dim, out_dim, kernel_size=1, stride=stride),
act_fn,
)
return model
def conv_block_3(in_dim, out_dim, act_fn):
model = nn.Sequential(
nn.Conv2d(in_dim, out_dim, kernel_size=3, stride=1, padding=1),
act_fn,
)
return model
class BottleNeck(nn.Module):
def __init__(self, in_dim, mid_dim, out_dim, act_fn, down=False):
super(BottleNeck, self).__init__()
self.act_fn = act_fn
self.down = down
if self.down:
self.layer = nn.Sequential(
conv_block_1(in_dim, mid_dim, act_fn, 2),
conv_block_3(mid_dim, mid_dim,act_fn),
conv_block_1(mid_dim, out_dim, act_fn),
)
self.downsample = nn.Conv2d(in_dim, out_dim, 1, 2)
else:
self.layer = nn.Sequential(
conv_block_1(in_dim, mid_dim, act_fn),
conv_block_3(mid_dim, mid_dim,act_fn),
conv_block_1(mid_dim, out_dim, act_fn),
)
self.dim_equalizer = nn.Conv2d(in_dim, out_dim, kernel_size=1)
def forward(self, x):
if self.down:
downsample = self.downsample(x)
out = self.layer(x)
out = out + downsample
else:
out = self.layer(x)
if x.size() is not out.size():
x = self.dim_equalizer(x)
out = out + x
return out
class ResNet(nn.Module):
def __init__(self, base_dim, num_classes=2):
super(ResNet, self).__init__()
self.act_fn = nn.ReLU()
self.layer_1 = nn.Sequential(
nn.Conv2d(3,base_dim,7,2,3),
nn.ReLU(),
nn.MaxPool2d(3,2,1),
)
self.layer_2 = nn.Sequential(
BottleNeck(base_dim, base_dim, base_dim*4, self.act_fn),
BottleNeck(base_dim*4, base_dim, base_dim*4, self.act_fn),
BottleNeck(base_dim*4,base_dim,base_dim*4,self.act_fn, down=False),
)
self.layer_3 = nn.Sequential(
BottleNeck(base_dim*4, base_dim*2, base_dim*8, self.act_fn),
BottleNeck(base_dim*8, base_dim*2, base_dim*8, self.act_fn),
BottleNeck(base_dim*8, base_dim*2, base_dim*8, self.act_fn),
BottleNeck(base_dim*8,base_dim*2,base_dim*8,self.act_fn, down=False),
)
self.layer_4 = nn.Sequential(
BottleNeck(base_dim*8, base_dim*4, base_dim*16, self.act_fn),
BottleNeck(base_dim*16, base_dim*4, base_dim*16, self.act_fn),
BottleNeck(base_dim*16, base_dim*4, base_dim*16, self.act_fn),
BottleNeck(base_dim*16, base_dim*4, base_dim*16, self.act_fn),
BottleNeck(base_dim*16, base_dim*4, base_dim*16, self.act_fn),
BottleNeck(base_dim*16,base_dim*4,base_dim*16,self.act_fn, down=False),
)
self.layer_5 = nn.Sequential(
BottleNeck(base_dim*16, base_dim*8, base_dim*32, self.act_fn),
BottleNeck(base_dim*32, base_dim*8, base_dim*32, self.act_fn),
BottleNeck(base_dim*32, base_dim*8, base_dim*32,self.act_fn, down=False),
)
self.avgpool = nn.AvgPool2d(7, 1)
self.fc_layer = nn.Linear(base_dim*3, num_classes)
def forward(self, batch_size, x):
out = self.layer_1(x)
out = self.layer_2(out)
out = self.layer_3(out)
out = self.layer_4(out)
out = self.layer_5(out)
out = self.avgpool(out)
out = out.view(batch_size, -1)
out = self.fc_layer(out)
return out
class ResNetRNN(nn.Module):
def __init__(self, device, h, w, outputs, hdnsize):
super(ResNetRNN, self).__init__()
self.device = device
self.hidden_size = hdnsize
self.resnet = ResNet(h*w, hdnsize)
self.i2h = nn.Linear(54 * hdnsize, hdnsize)
self.h2h = nn.Linear(hdnsize, hdnsize)
self.i2o = nn.Linear(hdnsize, outputs)
self.act_fn = nn.Tanh()
def init_hidden(self):
return torch.zeros(1, self.hidden_size)
def forward(self, batch_size, x):
out = self.resnet(batch_size, x)
hidden = self.init_hidden().to(self.device)
out= self.i2h(out.view(out.size(0), -1))
hidden = self.h2h(hidden)
hidden = self.act_fn(out + hidden)
return self.i2o(hidden)
위 코드는 resnet과 rnn을 합성한 코드 입니다. resnet은 책 '파이토치 첫걸음'의 소스 내용을 응용했고 rnn은 이전 cnnrnn의 소스에서 참조 했습니다. 파일 이름은 ResNet.py입니다.
import random
from collections import deque, namedtuple
from IPython.display import display, Math
from Account import Account
import math
from itertools import count
import os
import sys
import os.path as path
import plotly as plt
import torch
import torch.nn as nn
import torch.optim as optim
from Market import Market
import torchvision
import time
from ResNet import ResNetRNN
from memory import ReplayMemory, Experience
# from transformers import get_cosine_schedule_with_warmup
import transformers
action_kind = 41
max_episode = 5000
screen_height = 100
screen_width = 140
data_size = 250
epsilon = 0.3
dis = 0.9
BATCH_SIZE = 8
# GAMMA = 0.999
GAMMA = 0.99
EPS_START = 0.9
EPS_END = 0.05
EPS_DECAY = 200
TARGET_UPDATE = 10
steps_done = 0
loss = any
WINDOW_START = 0
WINDOW_SIZE = 1500
# for i in range(torch.cuda.device_count()):
# print(torch.cuda.get_device_name(i))
device_str = "cuda"
device = torch.device(device_str)
memory = ReplayMemory(BATCH_SIZE)
converter = torchvision.transforms.ToTensor()
market = Market()
train_net = ResNetRNN(device, screen_height, screen_width, action_kind, 32).to(device)
train_net = nn.DataParallel(train_net, device_ids=[0,1]).to(device)
episode_durations = []
optimizer = optim.RMSprop(train_net.parameters(), lr=0.000001, eps=0.00000001)
# scheduler = get_cosine_schedule_with_warmup(optimizer, 5, base_lr=0.3, final_lr=0.01)
scheduler = transformers.get_cosine_schedule_with_warmup(optimizer,
num_warmup_steps=5,
num_training_steps=25)
def optimize_action(memory):
if len(memory) < BATCH_SIZE:
return None
return loss
# def select_action(df, idx):
# try:
# global steps_done
# sample = random.random()
# eps_threshold = EPS_END + (EPS_START - EPS_END) * math.exp(-1. * steps_done / EPS_DECAY)
# steps_done += 1
# if sample > eps_threshold:
# if df.loc[idx, "closemax"] == df.loc[idx, "close"]:
# action = 2
# return action
# elif df.loc[idx, "closemin"] == df.loc[idx, "close"]:
# action = 1
# return action
# else:
# return 0
# else:
# action = random.randrange(action_kind)
# return action
# except Exception as ex:
# exc_type, exc_obj, exc_tb = sys.exc_info()
# fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
# print("`select_action -> exception! %s : %s %d" % (str(ex) , fname, exc_tb.tb_lineno))
# return 0
def select_action(df, idx):
return ((df.loc[idx, "close"] - df.loc[idx, "closemin"]) * (action_kind-1) // (df.loc[idx, "closemax"] - df.loc[idx, "closemin"]))
def get_chart(market, idx, max_data):
img = market.get_chart(idx, max_data=max_data)
if img is None:
return None
# img = Image.fromarray(np.uint8(cm.gist_earth(plt.io.to_image(fig, format='png')*255)))
# im = Image.fromarray(img, bytes=True)
# im = Image.fromarray(np.uint8(cm.gist_earth(img))/255)
# im = Image.fromarray(np.uint8(img)/255)
# img = img.resize((700, 500), resample=Image.BICUBIC)
# img = Image.fromarray(cm.gist_earth(plt.io.to_image(fig, format='png'), bytes=True))
# display(img)
# chart = converter(img).unsqueeze(0).to(device)
chart = converter(img).unsqueeze(0)
return chart
def plot_durations(last_chart, curr_chart):
plt.figure()
# plt.subplot(1,2,1)
img = plt.imshow(last_chart.cpu().squeeze(0).permute(1, 2, 0).numpy(), interpolation='none')
plt.title('Example extracted screen')
plt.figure(2)
# plt.subplot(1,2,2)
plt.clf()
durations_t = torch.tensor(episode_durations, dtype=torch.float)
plt.title('Training...')
plt.xlabel('Episode')
plt.ylabel('Duration')
plt.plot(durations_t.numpy())
# plt.show()
# Take 100 episode averages and plot them too
if len(durations_t) >= 100:
means = durations_t.unfold(0, 100, 1).mean(1).view(-1)
means = torch.cat((torch.zeros(99), means))
plt.plot(means.numpy())
img.set_data(curr_chart.cpu().squeeze(0).permute(1, 2, 0).numpy())
plt.pause(0.01) # pause a bit so that plots are updated
display.clear_output(wait=True)
display.display(plt.gcf())
def main():
if path.exists("pt/train_resnetrnn_{}.pt".format(device_str)):
train_net.load_state_dict(torch.load("pt/train_resnetrnn_{}.pt".format(device_str)))
for _ in range(10):
df = market.get_data()
# df = df.head(WINDOW_SIZE)
for epoch in range(max_episode):
account = Account(df, 50000000)
account.reset()
for idx,_ in enumerate(df.index, start=data_size):
try:
since = time.time()
curr_chart = get_chart(market, idx, data_size)
reward = 0
num_action = select_action(df, idx)
reward, real_action = account.exec_action(2 if num_action == (action_kind - 1) else (1 if num_action == 0 else 0) , idx)
print("idx:%d==>action:%d,real_action==>%d price:%.2f"%(idx, num_action, real_action, df.loc[idx, 'close']))
# reward = torch.tensor([reward], device=device)
action = torch.tensor([[num_action]], device=device, dtype=torch.int64)
memory.push(curr_chart, action, reward)
if len(memory) >= BATCH_SIZE:
epsode = memory.pop(BATCH_SIZE)
batch = Experience(*zip(*epsode))
state_batch = torch.cat(batch.state)
action_batch = torch.cat(batch.action)
train_net.train()
state_action_values = train_net(state_batch).gather(1, action_batch)
# optimizer = optim.RMSprop(train_net.parameters(), 0.01)
criterion = nn.SmoothL1Loss()
loss = criterion(state_action_values, action_batch)
# Optimize the model
# optimizer.zero_grad()
optimizer.zero_grad()
loss.backward()
for param in train_net.parameters():
param.grad.data.clamp_(-1, 1)
optimizer.step()
if loss is not None:
print("epoch[%d:%d] epsode is next loss[%.10f]" % (epoch, idx, loss.item()))
if idx % TARGET_UPDATE == 0:
torch.save(train_net.state_dict(),"pt/train_resnetrnn_{}.pt".format(device_str))
spend = time.time() - since
print("idx:%d price [%.4f] unit[%.4f] used time[%.2f] agent rate:%.05f remind money:%.02f"
% (idx, df.loc[idx, 'close'], account.unit, spend, account.rate, account.balance + account.unit * df.loc[idx, 'close']))
if account.is_bankrupt():
break
if idx == df.index.max():
break
except Exception as ex:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
print("`recall_training -> exception! %s : %s %d" % (str(ex) , fname, exc_tb.tb_lineno))
scheduler.step()
print("end training DQN")
print('Complete Training')
if __name__ == "__main__":
main()
위는 ResNet.py의 ResNetRNN클래스를 활용하여 학습을 진행하는 main_resnetrnn.py입니다.
글을 마치며
훈련이 잘 되어 거의 0에 수렴한 결과를 가지고 백테스팅을 진행해 보니 매수 자체를 안하는 결과가 나왔습니다 초반의 학습률이 굉장히 낮을 때보다 결과가 좋지 않았습니다. 그래서 지금은 전체를 여러 단계로 나누고 그 중 (결과값은 인덱스 이므로 언제나 0부터 액션의 가지수 -1 만큼 발생 합니다.) 0과 가장 큰값을 매수와 매도로 설정 나머지는 매매를 안하고 관망 하게 소스를 수정 했습니다. 문제는 액션 수를 늘리니 이번에는 학습률이 굉장히 떨어지는 문제가 발생합니다. 이제 모델은 거의 다 만든것 같습니다 .지금부터는 개인의 삽질만이 방법이 겠죠?
'python > 자동매매 프로그램' 카테고리의 다른 글
강화학습을 이용한 비트코인 매매프로그램(13) - 강화학습 최적화 (1) | 2023.02.14 |
---|---|
강화학습을 이용한 비트코인 매매프로그램(12) - 실거래 적용 (5) | 2023.01.30 |
강화학습을 이용한 비트코인 매매프로그램(7)-Back Test (1) | 2022.12.24 |
강화학습을 이용한 비트코인 매매프로그램(10) - CNN+RNN 모델 확장 (0) | 2022.12.21 |
강화학습을 이용한 비트코인 매매프로그램(9) - CNN+RNN 모델 (0) | 2022.12.20 |