


plt.figure(figsize=(12,6))
plt.plot(data["Month"], data[["#Passengers"]])
plt.show()

# 최근 2년 데이터 확인
df_recent=data.tail(24)
plt.figure(figsize=(10,3))
plt.grid()
plt.plot(df_recent["Month"], df_recent[["#Passengers"]])
plt.show()

y = data[["#Passengers"]] # 2차원 구조로 추출해야 함(모델이 2차원만 받는다)
# MinMax스케일링
# RNN, 이미지 데이터에서 많이 활용
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
data_scale = scaler.fit_transform(y)
# 데이터 길이를 기준으로 분할(8:2)
train_ratio = 0.8
train_size = int(len(data_scale)*train_ratio)
# (train) 학습용 데이터
train_data = data_scale[:train_size]
# 최근 1년 간의 데이터를 기반으로 예측
# 과거 기간을 얼마나 참조해서 예측할 것인지 지정해야 함
seq_length = 12
# (valid) 검증용 데이터
# 시계열 데이터의 예측은 과거 데이터를 기반으로 한다
# 시퀀스 연결을 위해 앞쪽에 seq_length 기간 포함해야 함
val_data = data_scale[train_size - seq_length:]
train_data.shape, val_data.shape
((115, 1), (41, 1))

# 데이터(X: 입력 시퀀스, y: 정답값) → 쌍으로 묶어주자
# X: 이전 12개월의 항공 승객 수, y: 예측 승객 수
def create_seq (data, seq_length):
xs, ys = [], []
for i in range(len(data) - seq_length):
x = data[i:i+seq_length] # 입력 시퀀스 → 12개월 승객 수
y = data[i+seq_length] # 정답 시퀀스 →13번째 달 승객 수
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys) # 리스트를 넘파이 배열로 변환 (Tensor 변환을 위함)
X_train, y_train = create_seq(train_data, seq_length)
X_val, y_val = create_seq(val_data, seq_length)
# 데이터 텐서 변환
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.float32)
X_val_tensor = torch.tensor(X_val, dtype=torch.float32)
y_val_tensor = torch.tensor(y_val, dtype=torch.float32)
X_train_tensor.shape, y_train_tensor.shape, X_val_tensor.shape, y_val_tensor.shape
(torch.Size([103, 12, 1]),
torch.Size([103, 1]),
torch.Size([29, 12, 1]),
torch.Size([29, 1]))

# RNN 모델 정의 → 회귀 모델
class RNN(nn.Module):
def __init__(self, input_size=1, hidden_size=32, output_size=1, num_layers=1):
super().__init__()
self.rnn = nn.RNN(input_size=input_size, hidden_size=hidden_size, batch_first=True, num_layers=num_layers)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self,x):
output, h_n = self.rnn(x) # out: 각 time step의 hidden state를 모두 포함(매 시퀀스마다 나오는 값), h_n: 최종 시퀀스의 전체 값(마지막 레이어의 최종 hidden state를 의미)
out = self.fc(output[:,-1])
# 마지막 시점만 사용하겠다는 의미 ← h_m은 num_layers가 많아지면 그걸 다 가지고 옴: squeeze를 쓸 수 없음(dim이 1이 아니게 됨)
# 따라서 h_n.squeeze 아닌 out[:,-1] 사용
return out
# 모델 하이퍼파라미터들 설정 → 객체 생성
model = RNN(input_size=1, hidden_size=32, output_size=1, num_layers=1)
loss_func = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
# 학습에 필요한 변수 설정
n_epochs=100
h1=[]
for epoch in range(n_epochs):
# 학습
y_pred = model(X_train_tensor)
loss = loss_func(y_pred, y_train_tensor)
optimizer.zero_grad()
loss.backward()
optimizer.step() # 가중치 업데이트
h1.append(loss.item())
# 검증
with torch.no_grad():
y_pred = model(X_val_tensor)
val_loss = loss_func(y_pred, y_val_tensor)
# 100회 학습 중 10회 단위로 출력
if (epoch+1) % 10 == 0:
print(f"Epoch: {epoch+1} | train loss: {loss.item():.4f} | val loss: {val_loss.item():.4f}")
Epoch: 10 | train loss: 0.0196 | val loss: 0.0720
Epoch: 20 | train loss: 0.0112 | val loss: 0.0284
Epoch: 30 | train loss: 0.0071 | val loss: 0.0168
Epoch: 40 | train loss: 0.0052 | val loss: 0.0157
Epoch: 50 | train loss: 0.0044 | val loss: 0.0143
Epoch: 60 | train loss: 0.0040 | val loss: 0.0130
Epoch: 70 | train loss: 0.0037 | val loss: 0.0121
Epoch: 80 | train loss: 0.0033 | val loss: 0.0110
Epoch: 90 | train loss: 0.0029 | val loss: 0.0093
Epoch: 100 | train loss: 0.0024 | val loss: 0.0079
# 시각화
plt.figure(figsize = (10,3))
plt.plot(h1)
plt.show()

# 그래프: 실제 데이터 변화, 예측 데이터 변화
y_pred_val = scaler.inverse_transform(y_pred.numpy())
y_val = scaler.inverse_transform(y_val_tensor.numpy())
# 월 인덱스 생성
val_date = data["Month"][train_size:].reset_index(drop=True)
plt.subplots(figsize=(12,5))
plt.plot(val_date, y_val, label="actual")
plt.plot(val_date, y_pred_val, label="prediction")
plt.legend()
plt.show()

PyTorch RNN 모델의 출력값은 입력 시퀀스의 각 시점에 대한 hidden state와 마지막 시점의 output 을 포함합니다.
출력값은 일반적으로 텐서 형태로 제공되며, 배치 크기, 시퀀스 길이, hidden state 크기 등의 차원을 가집니다.
자세한 설명:
(seq_len, batch, num_directions * hidden_size)(batch, seq_len, num_directions * hidden_size)(num_layers * num_directions, batch, hidden_size)예시:
import torch
import torch.nn as nn
# RNN 모델 정의
rnn = nn.RNN(
input_size=10
, hidden_size=20
, num_layers=2
, batch_first=True
)
# 입력 데이터 (예시)
inputs = torch.randn(5, 3, 10)
# (batch, seq_len, input_size)
# 초기 hidden state (선택 사항)
h0 = torch.randn(2, 5, 20)
# (num_layers * num_directions, batch, hidden_size)
# 출력 및 마지막 hidden state 계산
output, hn = rnn(inputs, h0)
# 출력 및 hidden state shape 확인
print("Output shape:", output.shape)
print("Hidden state shape:", hn.shape)
위 예시에서 output은 (5, 3, 20)의 shape을 가지며, 이는 배치 크기 5, 시퀀스 길이 3, hidden size 20을 의미합니다. hn은 (2, 5, 20)의 shape을 가지며, 2개의 레이어, 배치 크기 5, hidden size 20을 나타냅니다.

# LSTM 모델 설계 → 내부적으로 연산이 추가된 거라 설계 방식은 기존과 동일함
class LSTM(nn.Module):
def __init__(self, input_size, hidden_size, num_layers, output_size):
super(LSTM, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.output_size = output_size
self.num_layers = num_layers
self.istm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
output, _ = self.istm(x) # 사용하지 않을 변수일 경우 이름을 그냥 '_'로 줌
out = self.fc(output[:,-1])
return out
# 객체 생성
model_lstm = LSTM(input_size=1, hidden_size=32, num_layers=1, output_size=1)
# 손실 함수, 최적화 함수 설정
loss_func = nn.MSELoss()
optimizer = torch.optim.Adam(model_lstm.parameters(), lr=0.01)
n_epochs=100
print_interval=10
h1=[]
for epoch in range(n_epochs):
# 학습
y_hat = model_lstm(X_train_tensor)
loss = loss_func(y_hat, y_train_tensor)
optimizer.zero_grad()
loss.backward()
optimizer.step()
h1.append(loss.item()) # 학슴이 잘 되었는지(=loss가 감소했는지) 그래프를 그려 확인하기 위
# 검증
with torch.no_grad():
y_hat = model_lstm(X_val_tensor)
val_loss = loss_func(y_hat, y_val_tensor)
if (epoch+1) % print_interval == 0:
print(f"Epoch: {epoch+1} | train loss: {loss.item():.4f} | val loss: {val_loss.item():.4f}")
Epoch: 10 | train loss: 0.0205 | val loss: 0.0873
Epoch: 20 | train loss: 0.0091 | val loss: 0.0565
Epoch: 30 | train loss: 0.0065 | val loss: 0.0207
Epoch: 40 | train loss: 0.0054 | val loss: 0.0206
Epoch: 50 | train loss: 0.0048 | val loss: 0.0171
Epoch: 60 | train loss: 0.0045 | val loss: 0.0164
Epoch: 70 | train loss: 0.0041 | val loss: 0.0141
Epoch: 80 | train loss: 0.0032 | val loss: 0.0114
Epoch: 90 | train loss: 0.0028 | val loss: 0.0097
Epoch: 100 | train loss: 0.0023 | val loss: 0.0101
# 시각화
plt.figure(figsize = (10,3))
plt.plot(h1)
plt.show()

y_hat_val = scaler.inverse_transform(y_hat.detach().numpy())
y_val = scaler.inverse_transform(y_val_tensor.numpy())
val_date = data["Month"][train_size:].reset_index(drop=True)
plt.subplots(figsize=(12,5))
plt.plot(val_date, y_val, label="actual")
plt.plot(val_date, y_pred_val, label="prediction")
plt.legend()
plt.show()






