Recurrent Neural Network (RNN)

5단 분석법

순서
분석
단어
내용
1
일반 명사
Recurrent
되풀이되는, 반복되는
Neural
신경의
Network
RNN
반복되는 신경망?
2
고유 명사
RNN
순차적인 데이터(텍스트, 시간 시계열 데이터 등) 처리(분류 및 예측)를 위해 설계된 인공 신경망
3
사용 이유
RNN
순차적인 데이터에서 이전 시점의 정보를 기억하고 활용해 순차적 패턴과 종속성을 학습하고 처리하기 위해서
4
사용 방법
RNN
순차적인 데이터를 입력으로 받아 시간적 패턴을 학습하고, 다음 값을 예측
5
다른 기술과의 비교
RNN
-

정의

일반 명사

Recurrent
되풀이되는, 반복되는
Neural
신경의
Network
RNN
반복되는 신경망?
Recurrent는 되풀이되는, 반복되는 이라는 뜻을 가진 단어입니다.
Neual은 신경의, Network는 망이라는 뜻을 가지고 있습니다.
그래서, RNN의 뜻을 유추해보면 반복되는 신경망이라고 추측해 볼 수 있는데, 정말인 지 고유 명사를 한 번 확인 해볼까요?

고유 명사

RNN
순차적인 데이터(텍스트, 시간 시계열 데이터 등) 처리를 위해 설계된 인공 신경망
RNN은 순차적인 데이터(텍스트, 시간 시계열 데이터 등) 처리를 위해 설계된 인공 신경망 입니다.
요소
설명
x
RNN에 입력되는 데이터로, 시간의 흐름에 따라 시점마다 다른 값을 가집니다. 예를 들어 x_{t-1}, x_{t}, x_{t+1}은 각각 이전, 현재, 다음 시점에서의 입력을 의미합니다.
y
각 시점에서의 출력 값으로, RNN이 입력 데이터를 기반으로 예측하거나 생성한 결과를 의미합니다. y_{t-1}, y_{t}, y_{t+1}은 각각 이전, 현재, 다음 시점에서의 출력을 나타냅니다.
t
시퀀스 데이터의 현재 시점을 나타내는 인덱스입니다. 예를 들어 t는 현재 시점을, t-1은 이전 시점을, t+1은 다음 시점을 의미합니다.
시그마 f
가중치의 합을 계산하고 활성화 함수 f를 적용하는 노드입니다. 활성화 함수로는 주로 tanh 또는 ReLU가 사용되며, RNN에서 입력을 처리하는 역할을 합니다.
w_{x}
입력 x에 적용되는 가중치로, 입력 데이터와 모델 파라미터 간의 관계를 학습합니다.
w_{h}
이전 숨겨진 상태에서 현재 숨겨진 상태로 정보를 전달하는 가중치로, 시퀀스 데이터의 문맥을 유지하게 합니다.
hidden state
이전 시점의 정보를 현재 시점으로 전달하는 중간 상태로, 시간에 따른 문맥 정보를 RNN 내에서 유지합니다. h_{t-1}, h_{t}, h_{t+1}은 각각 이전, 현재, 다음 시점의 숨겨진 상태입니다.
시간의 흐름
다이어그램의 아래쪽 화살표로 표시되며, 시간 순서에 따라 RNN이 데이터를 처리하는 방향을 나타냅니다.
hidden state가 뭔가요?
은닉층의 셀들이 생성하는 출력 값을 의미합니다.
한 가지 분류 예시를 통해 RNN의 동작 과정을 알아보겠습니다.
I work at startupcode 라는 문장을 RNN을 통해 대명사, 동사, 전치사, 명사로 분류해보려 합니다.
분류 예시
RNN 모델은 위 문장을 받으면 이렇게 분류 하게 됩니다.
1.
첫 번째 입력이 I 일 때, 은닉층은 대명사라고 예측하여 대명사 로 분류
a.
은닉층에서 나온 결과값(출력값)인 대명사를 다음 은닉층으로 보냄
2.
두 번째 입력이 work 이고, 첫 번째 입력 예측 결과는 대명사였기 때문에 대명사 다음은 동사라고 예측하여 동사 로 분류
a.
은닉층에서 나온 결과값(출력값)인 동사를 다음 은닉층으로 보냄
3.
세 번째 입력이 at이고, 두 번째 입력의 예측 결과는 동사였기 때문에 동사 다음은 전치사라고 예측하여 전치사 로 분류
a.
은닉층에서 나온 결과값(출력값)인 전치사를 다음 은닉층으로 보냄
4.
네 번째 입력이 startupcode이고, 세 번째 입력의 예측 결과는 전치사였기 때문에 전치사 다음은 명사라고 예측하여 명사 로 분류
위와 같은 과정으로 분류 과정을 거치게 됩니다.
1.
처음 입력(I)에서 hidden state에 입력에 대한 가중치가 입력됩니다.
2.
두 번째 입력(work)에서 hidden state에 입력에 대한 가중치가 입력됩니다.
3.
첫 번째 hidden state에서 전달받은 가중치와 입력에 대한 가중치를 서로 곱해서 분류를 실시합니다.
위와 같은 순서로 RNN은 분류 과정을 수행하게 됩니다.

사용 이유

RNN
순차적인 데이터에서 이전 시점의 정보를 기억하고 활용해 순차적 패턴과 종속성을 학습하고 처리하기 위해서
RNN은 순차적인 데이터에서 이전 시점의 정보를 기억하고 활용해 순차적 패턴과 종속성을 학습하고 처리하기 위해서 사용합니다.
항목
설명
기억 능력
이전 시점의 정보를 기억하여 현재 시점의 출력에 영향을 미칠 수 있습니다.
시퀀스 데이터 처리
텍스트, 음성, 시계열 데이터 등 순차적인 데이터의 패턴과 종속성을 학습할 수 있습니다.
시간적 패턴 학습
데이터 간의 시간적 관계와 패턴을 학습하여 예측 성능을 향상시킬 수 있습니다.
연속적인 데이터
연속적인 데이터 처리에 적합하여 텍스트 생성, 음성 인식, 기계 번역 등에서 활용됩니다.
종속성 모델링
시간적 종속성을 모델링하여, 앞뒤 문맥을 고려한 자연어 처리 작업에 효과적입니다.
다양한 응용 분야
텍스트 분류, 감정 분석, 시계열 예측, 음악 생성 등 다양한 분야에 적용될 수 있습니다.

사용 방법

RNN
순차적인 데이터를 입력으로 받아 시간적 패턴을 학습하고, 다음 값을 예측
주어진 텍스트 데이터를 기반으로 순환 신경망(RNN)을 학습시켜, 새로운 텍스트 시퀀스를 생성하는 예제코드를 통해 RNN을 이해해보도록 하겠습니다.

TensorFlow (Keras)

PyTorch

알아 두면 좋은 정보

정보1

𝑤: 가중치
𝓧: 입력 값
𝓨: 출력 값
𝑡: 시간
RNN 수식:ht=fW(ht1,xt)여기서,ht: 새로운 상태(현재 시점의 hidden state)fW: 활성화 함수 + 가중치 (퍼셉트론)ht1: 이전 상태(이전 시점의 hidden state)xt: 인풋 벡터 (현재 시점의 입력 값)\text{RNN 수식:} \\ h_t = f_W(h_{t-1}, x_t) \\ \text{여기서,} \\ h_t \text{: 새로운 상태(현재 시점의 hidden state)} \\ f_W \text{: 활성화 함수 + 가중치 (퍼셉트론)} \\ h_{t-1} \text{: 이전 상태(이전 시점의 hidden state)} \\ x_t \text{: 인풋 벡터 (현재 시점의 입력 값)}

RNN의 중요한 문제점

RNN(Recurrent Neural Network)의 가장 큰 단점 중 하나는 학습 과정에서 발생하는 기울기 소실(Vanishing Gradient) 문제입니다.
이 문제는 주로 노드의 깊이가 깊어지거나, 시퀀스 데이터의 길이가 길어질 때 발생합니다.
기울기 소실 문제는 모델이 긴 시퀀스 데이터 내의 장기 종속성(Long-Term Dependency)을 학습하는 데 어려움을 겪게 만듭니다.

원인

RNN의 학습 과정에서 역전파 알고리즘을 통해 기울기가 전파됩니다.
그러나 각 시점에서 기울기를 계산할 때, 기울기가 연속적으로 작아지거나 커질 수 있습니다.
특히, 시퀀스가 길어질수록 기울기가 지수적으로 감소하는 경향이 있어, 초기 단계의 정보가 뒤쪽 단계에 도달할 때 거의 무시되게 됩니다.
이로 인해 모델은 초기에 입력된 데이터의 영향을 거의 받지 않게 되고, 이는 긴 시퀀스의 패턴을 학습하는 데 큰 장애가 됩니다.
말이 어려우니 쉽게 말하자면, 현재 hidden layer를 기준으로 이전 hidden state 값을 알기 어려워져서 기울기 소실이 됩니다.
즉, RNN에서 발생하는 기울기 소실(vanishing gradient) 문제는, 시간이 지남에 따라 초기 시점의 정보가 사라지면서 신호가 약해지는 현상입니다. 이는 네트워크가 긴 시퀀스에 걸쳐서 역전파(Backpropagation Through Time)를 진행할 때, 시간이 오래된 정보에 대한 기울기가 점점 작아지면서 발생합니다. 이 문제는 주로 첫 번째 hidden state (h_{t-2})**와 같이 과거의 정보가 현재 상태(h_{t-2})에 충분히 전달되지 않기 때문에 일어납니다.

영향

기울기 소실 문제로 인해 RNN은 긴 시퀀스 데이터를 처리하는 데 있어 성능이 떨어집니다.
특히 텍스트를 생성하거나 음성 인식 작업을 수행할 때 의도치 않은 예측 결과가 나올 확률이 높아집니다.
영향
내용
텍스트 생성
장문의 텍스트를 생성할 때, 문맥을 유지하기 어려움
음성 인식
긴 음성 데이터를 처리할 때, 앞부분의 음성 정보가 손실됨

정보2

참고 하면 좋은 사이트

미니퀘스트

1번 미니퀘스트 - 텍스트 생성기 만들기

주어진 텍스트 데이터를 기반으로 RNN 모델을 만들어 텍스트를 생성하는 프로그램을 구현해 보세요.
텍스트 데이터는 간단한 영어 문장으로 구성되어 있으며, 이를 학습하여 주어진 시작 텍스트로부터 이어지는 텍스트를 생성해야 합니다.

문제 설명

1.
라이브러리 임포트
2.
데이터셋 클래스 정의
3.
데이터 준비
4.
RNN 모델 정의
5.
모델 생성 및 학습 준비
6.
모델 훈련
7.
텍스트 생성 함수
8.
텍스트 생성

데이터셋

text = "The quick brown fox jumps over the lazy dog. This is a sample text for the RNN model. The purpose is to generate text based on the learned patterns."
Python
복사

요구사항

1.
주어진 텍스트 데이터셋을 처리할 수 있는 TextDataset 클래스를 작성하세요.
2.
RNN 모델을 정의하고, 손실 함수와 옵티마이저를 설정하세요.
3.
모델을 학습시키고, 학습된 모델을 사용하여 주어진 시작 텍스트로부터 이어지는 텍스트를 생성하세요.

코드

# 라이브러리 임포트 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, Dataset # 데이터셋 클래스 정의 class TextDataset(Dataset): def __init__(self, text, sequence_length): self.text = text self.sequence_length = sequence_length self.char_to_idx = {ch: i for i, ch in enumerate(sorted(set(text)))} self.idx_to_char = {i: ch for ch, i in self.char_to_idx.items()} self.data = [self.char_to_idx[ch] for ch in text] def __len__(self): return len(self.data) - self.sequence_length def __getitem__(self, idx): return ( torch.tensor(self.data[idx:idx+self.sequence_length], dtype=torch.long), torch.tensor(self.data[idx+1:idx+self.sequence_length+1], dtype=torch.long) ) # 데이터 준비 text = "The quick brown fox jumps over the lazy dog. This is a sample text for the RNN model. The purpose is to generate text based on the learned patterns." sequence_length = 10 dataset = TextDataset(text, sequence_length) dataloader = DataLoader(dataset, batch_size=2, shuffle=True) # RNN 모델 정의 class SimpleRNN(nn.Module): def __init__(self, vocab_size, embed_dim, hidden_dim, output_dim): super(SimpleRNN, self).__init__() self.embedding = nn.Embedding(vocab_size, embed_dim) self.rnn = nn.RNN(embed_dim, hidden_dim, batch_first=True) self.fc = nn.Linear(hidden_dim, output_dim) def forward(self, x): x = self.embedding(x) out, _ = self.rnn(x) out = self.fc(out) return out vocab_size = len(dataset.char_to_idx) embed_dim = 10 hidden_dim = 20 output_dim = vocab_size model = SimpleRNN(vocab_size, embed_dim, hidden_dim, output_dim) # 손실 함수 및 옵티마이저 정의 criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) # 모델 훈련 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") model = model.to(device) num_epochs = 20 for epoch in range(num_epochs): model.train() running_loss = 0.0 for inputs, targets in dataloader: inputs, targets = inputs.to(device), targets.to(device) outputs = model(inputs) outputs = outputs.view(-1, outputs.size(-1)) targets = targets.view(-1) loss = criterion(outputs, targets) optimizer.zero_grad() loss.backward() optimizer.step() running_loss += loss.item() * inputs.size(0) epoch_loss = running_loss / len(dataset) print(f'Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}') # 텍스트 생성 함수 def generate_text(model, start_text, length): model.eval() chars = [dataset.char_to_idx[ch] for ch in start_text] input_seq = torch.tensor(chars, dtype=torch.long).unsqueeze(0).to(device) generated_text = start_text with torch.no_grad(): for _ in range(length): output = model(input_seq) _, predicted = torch.max(output[:, -1, :], 1) next_char = dataset.idx_to_char[predicted.item()] generated_text += next_char input_seq = torch.cat([input_seq[:, 1:], predicted.unsqueeze(0)], dim=1) return generated_text # 텍스트 생성 start_text = "The quick" generated_text = generate_text(model, start_text, 20) print(f'Generated Text: {generated_text}')
Python
복사

코드 설명

미니퀘스트 2: 시계열 데이터 예측

주어진 시계열 데이터를 기반으로 RNN 모델을 만들어 미래의 값을 예측하는 프로그램을 구현해 보세요.
시계열 데이터는 간단한 정수 배열로 제공되며, 이를 학습하여 주어진 데이터의 패턴을 기반으로 다음 값을 예측해야 합니다.

문제 설명

1.
라이브러리 임포트
2.
데이터셋 클래스 정의
3.
데이터 준비
4.
RNN 모델 정의
5.
모델, 손실 함수 및 옵티마이저 정의
6.
모델 훈련
7.
시계열 예측 함수
8.
시계열 예측

데이터셋

time_series = [i for i in range(100)]
Python
복사

요구사항

1.
주어진 시계열 데이터셋을 처리할 수 있는 TimeSeriesDataset 클래스를 작성하세요.
2.
RNN 모델을 정의하고, 손실 함수와 옵티마이저를 설정하세요.
3.
모델을 학습시키고, 학습된 모델을 사용하여 주어진 시계열 데이터의 다음 값을 예측하세요.

코드

# 라이브러리 임포트 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, Dataset # 데이터셋 클래스 정의 class TimeSeriesDataset(Dataset): def __init__(self, series, sequence_length): self.series = series self.sequence_length = sequence_length def __len__(self): return len(self.series) - self.sequence_length def __getitem__(self, idx): return ( torch.tensor(self.series[idx:idx+self.sequence_length], dtype=torch.float), torch.tensor(self.series[idx+self.sequence_length], dtype=torch.float) ) # 데이터 준비 time_series = [i for i in range(100)] sequence_length = 10 dataset = TimeSeriesDataset(time_series, sequence_length) dataloader = DataLoader(dataset, batch_size=2, shuffle=True) # RNN 모델 정의 class SimpleRNN(nn.Module): def __init__(self, input_dim, hidden_dim, output_dim): super(SimpleRNN, self).__init__() self.rnn = nn.RNN(input_dim, hidden_dim, batch_first=True) self.fc = nn.Linear(hidden_dim, output_dim) def forward(self, x): out, _ = self.rnn(x) out = self.fc(out[:, -1, :]) return out input_dim = 1 hidden_dim = 20 output_dim = 1 model = SimpleRNN(input_dim, hidden_dim, output_dim) # 손실 함수 및 옵티마이저 정의 criterion = nn.MSELoss() optimizer = optim.Adam(model.parameters(), lr=0.001) # 모델 훈련 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") model = model.to(device) num_epochs = 20 for epoch in range(num_epochs): model.train() running_loss = 0.0 for inputs, targets in dataloader: inputs = inputs.unsqueeze(-1).to(device) targets = targets.to(device) outputs = model(inputs) loss = criterion(outputs, targets.unsqueeze(-1)) optimizer.zero_grad() loss.backward() optimizer.step() running_loss += loss.item() * inputs.size(0) epoch_loss = running_loss / len(dataset) print(f'Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}') # 시계열 예측 함수 def predict(model, start_sequence, length): model.eval() input_seq = torch.tensor(start_sequence, dtype=torch.float).unsqueeze(0).unsqueeze(-1).to(device) predictions = start_sequence with torch.no_grad(): for _ in range(length): output = model(input_seq) next_value = output.item() predictions.append(next_value) new_input = torch.tensor([next_value], dtype=torch.float).unsqueeze(0).unsqueeze(-1).to(device) input_seq = torch.cat([input_seq[:, 1:], new_input], dim=1) return predictions # 시계열 예측 start_sequence = [i for i in range(10)] predicted_series = predict(model, start_sequence, 10) print(f'Predicted Series: {predicted_series}')
Python
복사

코드 설명

결과 (예시)

Predicted Series: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15.772008895874023, 16.848833084106445, 16.890918731689453, 16.892337799072266, 16.892383575439453, 16.89238739013672, 16.89238739013672, 16.89238739013672, 16.89238739013672, 16.89238739013672]
Python
복사

결과 설명

모델의 학습이 완료된 후, start_sequence로 주어진 초기 값 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]를 바탕으로 시계열 데이터를 예측했습니다.
예측된 시계열 데이터는 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15.7720, 16.8488, 16.8909, 16.8923, 16.8923, 16.8923, 16.8923, 16.8923, 16.8923, 16.8923]입니다.
모델이 처음에는 적절히 학습되어 첫 번째 예측 값인 15.7720을 생성했으나, 그 이후의 예측 값은 16.8488, 16.8909 등 비슷한 값으로 수렴하는 모습을 보였습니다.
이는 모델이 시계열 데이터의 증가 패턴을 학습하는 데 어려움을 겪었거나, 시계열 데이터의 특성을 제대로 반영하지 못했음을 나타냅니다.
이를 개선하기 위해선 데이터의 정규화 또는 표준화를 통해 모델이 더 나은 학습을 할 수 있도록 하거나,
단순한 RNN 대신 LSTM(Long Short-Term Memory) 또는 GRU(Gated Recurrent Unit) 같은 더 복잡한 모델을 사용하여 장기적인 종속성을 더 잘 학습하도록 할 수 있습니다.

미니퀘스트 답안지

ⓒ 2024 startupcode. 모든 권리 보유. 무단 복제 금지.