๐Ÿ“ˆ PyTorch๋กœ ์‹œ๊ณ„์—ด ์˜ˆ์ธก์˜ ์„ธ๊ณ„์— ๋น ์ ธ๋“ค๋‹ค

๋ฏธ๋ž˜๋ฅผ ์˜ˆ์ธกํ•˜๋Š” ๊ฒƒ์€ ์–ธ์ œ๋‚˜ ํฅ๋ฏธ๋กœ์šด ์ผ์ด๋‹ค. ์ฃผ์‹ ๊ฐ€๊ฒฉ์˜ ๋“ฑ๋ฝ, ๋‚ด์ผ์˜ ๋‚ ์”จ, ํ˜น์€ ๋‹ค์Œ ๋ถ„๊ธฐ ๋งค์ถœ๊นŒ์ง€. ์ด๋Ÿฐ ์˜ˆ์ธก์˜ ์ค‘์‹ฌ์— ๋ฐ”๋กœ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋‹ค. ์˜ค๋Š˜์€ PyTorch๋ฅผ ์ด์šฉํ•ด ๊ณผ๊ฑฐ์˜ ๋ฐ์ดํ„ฐ๋กœ ๋ฏธ๋ž˜๋ฅผ ์—ฟ๋ณด๋Š” ๋‹ค์–‘ํ•œ ๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ๋“ค์„ ํƒํ—˜ํ•˜๋ฉฐ, ์‹œ๊ณ„์—ด ์˜ˆ์ธก์˜ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์ฐจ๊ทผ์ฐจ๊ทผ ์ •๋ณตํ•ด๋‚˜๊ฐ„ ๊ณผ์ •์„ ๊ธฐ๋กํ–ˆ๋‹ค.

๐Ÿ’ก โ€œ๊ณผ๊ฑฐ๋Š” ๋ฏธ๋ž˜์˜ ๊ฑฐ์šธ์ด๋‹ค.โ€

์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ๋Š” ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๊ธฐ๋ก๋œ ๋ฐ์ดํ„ฐ๋กœ, ๊ทธ ์•ˆ์—๋Š” ํŠธ๋ Œ๋“œ, ๊ณ„์ ˆ์„ฑ, ์ž๊ธฐ ์ƒ๊ด€ ๋“ฑ ๋ฏธ๋ž˜๋ฅผ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ๋Š” ์ค‘์š”ํ•œ ๋‹จ์„œ๋“ค์ด ์ˆจ์–ด์žˆ๋‹ค. ์ด ๋‹จ์„œ๋“ค์„ ์–ด๋–ป๊ฒŒ ์ฐพ์•„๋‚ด๊ณ  ํ™œ์šฉํ•˜๋Š”์ง€๊ฐ€ ์˜ˆ์ธก ๋ชจ๋ธ์˜ ์„ฑํŒจ๋ฅผ ์ขŒ์šฐํ•œ๋‹ค.


๐Ÿ•“ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ, ๋ฌด์—‡์„ ๋‹ด๊ณ  ์žˆ์„๊นŒ?

์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ๋Š” ํฌ๊ฒŒ 4๊ฐ€์ง€ ๊ณตํ†ต ํŠน์ง•์„ ๊ฐ€์ง„๋‹ค. ์ด๋“ค์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ๋ถ„์„์˜ ์ฒซ๊ฑธ์Œ์ด๋‹ค.

  • ํŠธ๋ Œ๋“œ (Trend): ๋ฐ์ดํ„ฐ๊ฐ€ ์žฅ๊ธฐ์ ์œผ๋กœ ์ƒ์Šนํ•˜๊ฑฐ๋‚˜ ํ•˜๊ฐ•ํ•˜๋Š” ๋ฐฉํ–ฅ์„ฑ.
  • ๊ณ„์ ˆ์„ฑ (Seasonality): ํŠน์ • ์ฃผ๊ธฐ๋กœ ๋ฐ˜๋ณต๋˜๋Š” ํŒจํ„ด (e.g., ์›”๋ณ„, ๊ณ„์ ˆ๋ณ„).
  • ์ž๊ธฐ ์ƒ๊ด€ (Autocorrelation): ์ด์ „ ์‹œ์ ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํ˜„์žฌ ๋˜๋Š” ๋ฏธ๋ž˜ ๋ฐ์ดํ„ฐ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ํ˜„์ƒ.
  • ์žก์Œ (Noise): ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ์ž„์˜์˜ ๋ณ€๋™.

์ด๋Ÿฐ ํŠน์ง•๋“ค์„ ์ฝ”๋“œ๋กœ ์ง์ ‘ ๋งŒ๋“ค์–ด๋ณด๋ฉฐ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ์™€ ์นœํ•ด์ง€๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์กŒ๋‹ค.

# ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ ์ƒ์„ฑ์„ ์œ„ํ•œ ํ•จ์ˆ˜ ์ •์˜
import numpy as np
import matplotlib.pyplot as plt

def trend(time, slope=0):
    return slope * time

def seasonal_pattern(season_time):
    return np.where(season_time < 0.4,
                    np.cos(season_time * 2 * np.pi),
                    1 / np.exp(3 * season_time))

def seasonality(time, period, amplitude=1, phase=0):
    season_time = ((time + phase) % period) / period
    return amplitude * seasonal_pattern(season_time)

def noise(time, noise_level=1, seed=None):
    rnd = np.random.RandomState(seed)
    return rnd.randn(len(time)) * noise_level

# 4๋…„์น˜ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
time = np.arange(4 * 365 + 1, dtype="float32")
baseline = 10
amplitude = 15
noise_level = 6
series = baseline + trend(time, 0.05) + seasonality(time, period=365, amplitude=amplitude)
series += noise(time, noise_level, seed=42)

๐Ÿ› ๏ธ ๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ๋ณ„ ์˜ˆ์ธก ๋„์ „๊ธฐ

1. ์œˆ๋„์šฐ ๋ฐ์ดํ„ฐ์…‹: ์˜ˆ์ธก์„ ์œ„ํ•œ ์žฌ๋ฃŒ ์†์งˆ

๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ์— ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๊ธฐ ์œ„ํ•ด์„  โ€˜์œˆ๋„์šฐโ€™ ๋‹จ์œ„๋กœ ์ž˜๋ผ์ฃผ๋Š” ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค. ํŠน์ • ๊ธฐ๊ฐ„(window)์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅ(X)์œผ๋กœ, ๋ฐ”๋กœ ๋‹ค์Œ ์‹œ์ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ •๋‹ต(y)์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค. PyTorch์˜ Dataset๊ณผ DataLoader๋ฅผ ํ™œ์šฉํ•ด ์ด ๊ณผ์ •์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ–ˆ๋‹ค.

from torch.utils.data import Dataset, DataLoader

class WindowedDataset(Dataset):
    def __init__(self, data, window_size, shift):
        self.data = data
        self.window_size = window_size
        self.shift = shift
        self.windows = [data[i:i+window_size+1] for i in range(0, len(data)-window_size, shift)]

    def __len__(self):
        return len(self.windows)

    def __getitem__(self, idx):
        window = self.windows[idx]
        return window[:-1], window[-1:]

2. DNN (Deep Neural Network) ๋ชจ๋ธ

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ Linear ๋ ˆ์ด์–ด๋ฅผ ์Œ“์•„ ๋งŒ๋“  DNN ๋ชจ๋ธ๋กœ ์ฒซ ์˜ˆ์ธก์„ ์‹œ๋„ํ–ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” MSE 42.65, MAE 5.20. ์ƒ๊ฐ๋ณด๋‹ค ์„ฑ๋Šฅ์ด ์ข‹์ง€ ์•Š์•„ ์•„์‰ฌ์› ๋‹ค. ๐Ÿค”

3. 1D CNN (Convolutional Neural Network) ๋ชจ๋ธ

Conv1d ๋ ˆ์ด์–ด๋ฅผ ์‚ฌ์šฉํ•ด ์‹œ๊ณ„์—ด์˜ ์ง€์—ญ์  ํŒจํ„ด์„ ํฌ์ฐฉํ•˜๋Š” CNN ๋ชจ๋ธ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์—๋งŒ ์“ฐ์ด๋Š” ์ค„ ์•Œ์•˜๋˜ CNN์„ ์‹œ๊ณ„์—ด์— ์ ์šฉํ•˜๋‹ˆ ์‹ ๊ธฐํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ์„ฑ๋Šฅ์€ MSE 44.18, MAE 5.32๋กœ DNN๊ณผ ๋น„์Šทํ–ˆ๋‹ค.

4. RNN (Recurrent Neural Network) ๋ชจ๋ธ

์ˆœ์ฐจ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์— ๊ฐ•์ ์„ ๊ฐ€์ง„ RNN์— ํฐ ๊ธฐ๋Œ€๋ฅผ ๊ฑธ์—ˆ๋‹ค. ์ด์ „ ์Šคํ…์˜ ์ถœ๋ ฅ์„ ๋‹ค์Œ ์Šคํ…์˜ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” โ€˜๋˜๋จน์ž„ ๊ตฌ์กฐโ€™ ๋•๋ถ„์— ์‹œ๊ณ„์—ด์˜ โ€˜ํ๋ฆ„โ€™์„ ํ•™์Šตํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. yfinance๋กœ ์‹ค์ œ AAPL ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ์˜ˆ์ธก์„ ์ง„ํ–‰ํ•˜๋‹ˆ ํ›จ์”ฌ ํฅ๋ฏธ๋กœ์› ๋‹ค. ๐Ÿ“ˆ

# RNN ๋ชจ๋ธ ์ •์˜
class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, seq_size):
        super(RNN, self).__init__()
        self.rnn = nn.RNN(input_size, hidden_size, num_layers, batch_first=True)
        self.fc1 = nn.Linear(seq_size * hidden_size, 64)
        self.output = nn.Linear(64, 1)
        self.relu = nn.ReLU()

    def forward(self, x, h0=None):
        # ... (forward pass logic)
        return torch.flatten(x)

5. LSTM (Long Short-Term Memory) ๋ชจ๋ธ

RNN์˜ ์žฅ๊ธฐ ์˜์กด์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ LSTM์€ ์—ญ์‹œ ๊ฐ•๋ ฅํ–ˆ๋‹ค. โ€˜์…€ ์ƒํƒœ(Cell State)โ€™์™€ 3๊ฐœ์˜ ๊ฒŒ์ดํŠธ(๋ง๊ฐ, ์ž…๋ ฅ, ์ถœ๋ ฅ)๋ฅผ ํ†ตํ•ด ์ •๋ณด์˜ ํ๋ฆ„์„ ์ •๊ตํ•˜๊ฒŒ ์ œ์–ดํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ์ธ์ƒ ๊นŠ์—ˆ๋‹ค. RNN๋ณด๋‹ค ๋ณต์žกํ–ˆ์ง€๋งŒ, ๊ทธ๋งŒํผ ์žฅ๊ธฐ์ ์ธ ํŒจํ„ด์„ ์ž˜ ํ•™์Šตํ•ด๋‚ด๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

LSTM์˜ ํ•ต์‹ฌ์€ ์ •๋ณด๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ธฐ์–ตํ•˜๊ณ  ์žŠ๋Š” ๋Šฅ๋ ฅ์— ์žˆ๋‹ค. ๋•๋ถ„์— ๋ฉ€๋ฆฌ ๋–จ์–ด์ง„ ๊ณผ๊ฑฐ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํ˜„์žฌ ์˜ˆ์ธก์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ํฌ์ฐฉํ•  ์ˆ˜ ์žˆ๋‹ค.

# LSTM ๋ชจ๋ธ ์ •์˜
class LSTM(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, seq_size):
        super(LSTM, self).__init__()
        self.lstm = nn.LSTM(input_size=input_size,
                            hidden_size=hidden_size,
                            num_layers=num_layers,
                            batch_first=True)
        self.fc1 = nn.Linear(in_features=seq_size * hidden_size, out_features=64)
        self.fc2 = nn.Linear(in_features=64, out_features=1)
        self.relu = nn.ReLU()

    def forward(self, x, h0, c0):
        x, (hn, cn) = self.lstm(x, (h0, c0))
        x = torch.reshape(x, (x.shape[0], -1))
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return x

โœจ ์˜ค๋Š˜์˜ ํšŒ๊ณ 

์˜ค๋Š˜์€ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ์˜ ํŠน์ง•๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด DNN, CNN, RNN, ๊ทธ๋ฆฌ๊ณ  LSTM์— ์ด๋ฅด๊ธฐ๊นŒ์ง€ ๋‹ค์–‘ํ•œ ๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ๋กœ ์˜ˆ์ธก์„ ์ˆ˜ํ–‰ํ•ด๋ดค๋‹ค. ๊ฐ ๋ชจ๋ธ์˜ ๊ตฌ์กฐ์™€ ์žฅ๋‹จ์ ์„ ์ฝ”๋“œ๋กœ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ฉฐ ๋น„๊ตํ•ด๋ณด๋‹ˆ ์ด๋ก ์œผ๋กœ๋งŒ ๋ฐฐ์šฐ๋˜ ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๋ช…ํ™•ํ•˜๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ํŠนํžˆ RNN๊ณผ LSTM์ด ์™œ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ์— ์ ํ•ฉํ•œ์ง€, ๊ทธ ๊ตฌ์กฐ์  ์ฐจ์ด๊ฐ€ ์„ฑ๋Šฅ์— ์–ด๋–ค ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š”์ง€ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๊ฒฝํ—˜์ด์—ˆ๋‹ค.

๋‹ค์Œ์—๋Š” Transformer ๊ธฐ๋ฐ˜์˜ ๋ชจ๋ธ(e.g., Informer, Autoformer)์„ ์‚ฌ์šฉํ•ด ์‹œ๊ณ„์—ด ์˜ˆ์ธก์— ๋„์ „ํ•ด๋ณด๊ณ  ์‹ถ๋‹ค. LSTM์„ ๋›ฐ์–ด๋„˜๋Š” ์„ฑ๋Šฅ์„ ๋ณด์—ฌ์ค€๋‹ค๊ณ  ํ•˜๋‹ˆ, ์–ผ๋งˆ๋‚˜ ๋” ์ •ํ™•ํ•œ ์˜ˆ์ธก์ด ๊ฐ€๋Šฅํ• ์ง€ ๊ธฐ๋Œ€๋œ๋‹ค. ๐Ÿ˜„