






model = Sequential()
model.add(
RepeatVector(
number_of_times # 출력 개수 설정(3개의 캡션을 원하면 3으로 설정)
, input_shape=input_shape
)
)
model.add(
SimpleRNN(
units=output_size
, return_sequences=True # SimpleRNN 신경망이 순환하며 단일 값을 계속 출력
)
)

model = Sequential()
model.add(
SimpleRNN(
units=output_size
, input_shape=(timesteps, features)
)
)


model = Sequential()
model.add(
SimpleRNN(
units=output_size
, input_shape=(timesteps, features)
, return_sequences=True # SimpleRNN 신경망이 순환하며 단일 값을 계속 출력
)
)


시간에 따른 순서를 '기억'하면서 학습한다!
SimpleRNN(units=3, input)_shape=(4,9))
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# hello, apple, hobby, below, wheel
# 단어 사전: 입력되는 하나의 단어 → h, e, l, o, a, p, b, y, w: 9개의 알파벳을 사용해 구축
# 단어 사전 만들기: 각 알파벳을 원핫 인코딩 → 수치화
h = [1,0,0,0,0,0,0,0,0]
e = [0,1,0,0,0,0,0,0,0]
l = [0,0,1,0,0,0,0,0,0]
o = [0,0,0,1,0,0,0,0,0]
a = [0,0,0,0,1,0,0,0,0]
p = [0,0,0,0,0,1,0,0,0]
b = [0,0,0,0,0,0,1,0,0]
y = [0,0,0,0,0,0,0,1,0]
w = [0,0,0,0,0,0,0,0,1]
데이터 분석을 위해 토큰화 결과를 수치로 만드는 방법
원핫 인코딩(One-Hot encoding)

# 입력 특성 데이터 구성
# hell, appl, hobb, belo, whee
# 항상 numpy ndarray로 먼저 만들고 이후 tensor로 변환하기
X_data = np.array(
[
[h, e, l, l]
, [a, p, p, l]
, [h, o, b, b]
, [b, e, l, o]
, [w, h, e, e]
]
)
print(X_data)
[[[1 0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0]]
[[0 0 0 0 1 0 0 0 0]
[0 0 0 0 0 1 0 0 0]
[0 0 0 0 0 1 0 0 0]
[0 0 1 0 0 0 0 0 0]]
[[1 0 0 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0 0]
[0 0 0 0 0 0 1 0 0]
[0 0 0 0 0 0 1 0 0]]
[[0 0 0 0 0 0 1 0 0]
[0 1 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0 0]]
[[0 0 0 0 0 0 0 0 1]
[1 0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0 0]]]

# 정답 (label): o, e, y, w, 1
y_data = np.array([o,e,y,w,l])
y_data = np.argmax(y_data, axis=1)
print(y_data)
[3 1 7 8 2]
# tensor로 변경
X_data = torch.tensor(X_data).float()
y_data = torch.tensor(y_data).long() # 다중분류의 출력(정답 데이터)은 long이어야 함!
# 데이터 크기 확인
print("입력 데이터:",X_data.shape) # (샘플 수, 순환 횟수: 몇 번을 돌 것인가, 단어 사전 단어 수 == 특성 수)
# (5,4,9): (samples, timesteps, feature)
print("출력 데이터:",y_data.shape)
입력 데이터: torch.Size([5, 4, 9])
출력 데이터: torch.Size([5])
[Samples, Timesteps, Features]| 방법 | 메모리 공유 | 복사 발생 여부 | 장점 | 주의사항 |
|---|---|---|---|---|
torch.from_numpy() | ✅ 예 | ❌ 없음 | 매우 빠름, 메모리 공유 | dtype 호환 필요 |
torch.tensor() | ❌ 아님 | ✅ 있음 | 독립된 텐서, 안전 | 느릴 수 있음, dtype 주의 |
torch.as_tensor() | 🔁 상황에 따라 | 조건부 | 유연함 (공유 or 복사) | 내부 처리 파악 어려움 |


tanh 사용


| 데이터 규모 | 추천 hidden_size |
|---|---|
| 매우 단순한 문자 예제 | 2~10 |
| 단어 기반 시퀀스 | 64~256 |
| 문장/문서 처리 | 128~512 이상 가능 |

class RNN(nn.Module):
def __init__(
self
, input_size=9
, hidden_size=2 # 사용할 units 수
, output_size=9 # 알파벳 9개 중에 1개를 예측
):
super().__init__()
# RNN 층(위 그림 초록색 박스)
self.rnn = nn.RNN(
input_size=input_size
, hidden_size=hidden_size
, batch_first=True # batch_first=True를 통해서 입력 텐서의 첫 번째 차원이 배치 크기(samples)임을 알려줍니다
)
# 우리가 입력하는 데이터 형식: (samples, timestamps, features)
# RNN에서 데이터를 입력받고자 하는 형식(데이터 형식): (timestamps, samples, features)
# 분류층(위 그림 살구색 박스)
self.fc = nn.Linear(hidden_size, output_size)
# nn.RNN은 내부적으로 활성화 함수 tanh를 기본으로 사용함(별도 명시 X여도 알아서 씀)
# 기본 설정은 nonlinearity="relu" → 변경하고 싶을 때 nonlinearity="relu" 등으로 파라미터 직접 입력
def forward(self, x):
# output: 모든 시점의 출력값, h_n: 마지막 스텝의 결과 → 보통의 분류 분제에서는 마지막 값만 사용
output, h_n = self.rnn(x)
out = self.fc(h_n.squeeze(0)) # RNN:[1, batch, hidden] 출력 → fc는 반드시 [batch, hidden]을 원한다! → sqeeze 사용해 형태 맞춰줌
return out # RNN에서는 y보다 out이라고 많이 함
활성화 함수 설정?

중요 포인트

model = RNN()
loss_func = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

n_epochs = 200
his = []
for i in range(n_epochs):
optimizer.zero_grad()
y_pred = model(X_data)
loss = loss_func(y_pred, y_data)
# 손실값 저장
his.append(loss.item()) # float(loss)도 가능
loss.backward()
optimizer.step()
if (i+1) % 10 == 0:
print(f"epoch {i+1}, loss: {loss.item():.4f}")
epoch 10, loss: 1.9364
epoch 20, loss: 1.7201
epoch 30, loss: 1.5534
epoch 40, loss: 1.4093
epoch 50, loss: 1.2743
epoch 60, loss: 1.1322
epoch 70, loss: 0.9659
epoch 80, loss: 0.8073
epoch 90, loss: 0.6850
epoch 100, loss: 0.5967
epoch 110, loss: 0.5329
epoch 120, loss: 0.4851
epoch 130, loss: 0.4474
epoch 140, loss: 0.4165
epoch 150, loss: 0.3901
epoch 160, loss: 0.3671
epoch 170, loss: 0.3466
epoch 180, loss: 0.3283
epoch 190, loss: 0.3117
epoch 200, loss: 0.2966
plt.figure(figsize = (10,3))
plt.plot(his, label = 'loss')
plt.show()

# hell을 넣으면 어떤 값이 출력될까?
X_test = torch.tensor([[h,e,l,l]], dtype=torch.float)
alphabet = ['h', 'e', 'l', 'o', 'a', 'p', 'b', 'y', 'w']
with torch.no_grad():
out = model(X_test)
idx_pred = torch.argmax(out, dim=1)
result = alphabet[idx_pred]
print(f"예측 결과: {result}")
예측 결과: o
✅ 데이터 개요:
| 항목 | 내용 |
|---|---|
| 데이터 내용 | 1949년부터 1960년까지의 월별 항공 승객 수 |
| 샘플 수 | 약 144개 (12년 × 12개월) |
| 목적 | 예측 모델링 (ex. 다음 달 수요 예측) |
data=pd.read_csv("./data/AirPassengers.csv")
# 월 데이터 타입 변경 (datetime): 텍스트 데이터가 아닌 날짜 데이터로써 활용하기 위함
data["Month"] = pd.to_datetime(data["Month"])
# 승객 수 컬럼 데이터 타입 변경 (float)
data["#Passengers"] = data["#Passengers"].values.astype(float)
Month #Passengers
0 1949-01-01 112.0
1 1949-02-01 118.0
2 1949-03-01 132.0
3 1949-04-01 129.0
4 1949-05-01 121.0
... ... ...
139 1960-08-01 606.0
140 1960-09-01 508.0
141 1960-10-01 461.0
142 1960-11-01 390.0
143 1960-12-01 432.0
144 rows × 2 columns
y = data[["#Passengers"]] # 2차원 구조로 추출해야 함(모델은 2차원만 받는다)
# MinMax스케일링
# RNN, 이미지 데이터에서 많이 활용
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
data_scale = scaler.fit_transform(y)