Instructions to use OneclickAI/RNN_test_Model with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Keras
How to use OneclickAI/RNN_test_Model with Keras:
# !pip install -U keras tensorflow huggingface_hub # Keras needs TensorFlow installed to read "hf://" paths, so the tensorflow backend is selected here; # "jax" and "torch" also work for computation once TensorFlow is installed. import os os.environ["KERAS_BACKEND"] = "tensorflow" import keras model = keras.saving.load_model("hf://OneclickAI/RNN_test_Model") - Notebooks
- Google Colab
- Kaggle
for new ver
Browse files
README.md
DELETED
|
@@ -1,332 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
license: apache-2.0
|
| 3 |
-
---
|
| 4 |
-
|
| 5 |
-
안녕하세요 Oneclick AI 입니다!!
|
| 6 |
-
오늘은, RNN(순환 신경망, Recurrent Neural Network) 모델에 대해서 알아보는 시간을 가져볼까 합니다.
|
| 7 |
-
|
| 8 |
-
딥러닝이 문장, 음성, 주가 예측과 같은 순서가 있는 데이터(Sequential Data)를 다룰 수 있게 된 것은 전적으로 RNN 덕분입니다.
|
| 9 |
-
단순한 신경망이 '순서'라는 개념을 이해하지 못하는 반면,
|
| 10 |
-
RNN은 마치 사람처럼 시간의 흐름에 따라 정보를 기억하고, 다음을 예측할 수 있는 능력을 갖추고 있습니다.
|
| 11 |
-
|
| 12 |
-
오늘은 이 RNN이라는 신경망이 어떻게 과거의 정보를 기억하며 작동하는지,
|
| 13 |
-
그리고 어떻게 문장의 숨겨진 문맥과 의미를 파악할 수 있는지 알아봅시다.
|
| 14 |
-
|
| 15 |
-
---
|
| 16 |
-
|
| 17 |
-
## 목차
|
| 18 |
-
1. RNN 핵심 원리 파악하기
|
| 19 |
-
- 왜 순차 데이터에 RNN을 사용해야만 할까?
|
| 20 |
-
- RNN의 심장 : 순환 구조와 은닉 상태의 역학
|
| 21 |
-
- RNN을 시간에 따라 펼쳐보기
|
| 22 |
-
- RNN의 주요 구성 요소 상세 분석
|
| 23 |
-
2. 아키텍처를 통한 내부 코드 들여다 보기
|
| 24 |
-
- Keras로 구현한 RNN 모델 아키텍처
|
| 25 |
-
- model.summary()로 구조 확인하기
|
| 26 |
-
3. 직접 RNN 구현해 보기
|
| 27 |
-
- 1단계 : 데이터 로드 및 전처리
|
| 28 |
-
- 2단계 : 모델 컴파일
|
| 29 |
-
- 3단계 : 모델 학습 및 평가
|
| 30 |
-
- 4단계 : 학습된 모델 저장 및 재사용
|
| 31 |
-
- 5단계 : 나만의 문장으로 모델 테스트하기
|
| 32 |
-
4. 나만의 RNN 모델 업그레이드하기
|
| 33 |
-
- 기초 체력 훈련 : 하이퍼파라미터 튜닝
|
| 34 |
-
- RNN의 치명적 약점 : 장기 의존성 문제
|
| 35 |
-
- 기억력 강화 : LSTM과 GRU의 등장
|
| 36 |
-
- 과거와 미래를 동시에 : 양방향 RNN
|
| 37 |
-
5. 결론
|
| 38 |
-
---
|
| 39 |
-
|
| 40 |
-
## 1. RNN 핵심원리 파악하기
|
| 41 |
-
가장 먼저, RNN이 왜 순차적인 데이터를 이해하는 데 필수적인 도구인지 그 근본적인 이유부터 살펴보겠습니다.
|
| 42 |
-
|
| 43 |
-
**왜 순차 데이터에 RNN을 사용할까?? with MLP, CNN의 한계**
|
| 44 |
-
가장 기본적인 신경망인 MLP(다층 퍼셉트론)에 "나는 학교에 간다"라는 문장을 입력한다고 상상해 봅시다.
|
| 45 |
-
MLP는 각 단어를 독립적인 특징으로 보기 때문에, "학교에 나는 간다"라는 문장과 거의 동일하게 받아들입니다.
|
| 46 |
-
단어의 '순서'가 가진 중요한 의미, 즉 문맥을 완전히 잃어버리는 것입니다.
|
| 47 |
-
이미지 처리에 특화된 CNN 역시 마찬가지입니다.
|
| 48 |
-
CNN은 공간적인 특징(픽셀 주변 관계)을 추출하는 데는 뛰어나지만, 시간적인 순서나 흐름을 파악하도록 설계되지 않았습니다.
|
| 49 |
-
반면, RNN은 설계 자체가 '순서'를 기억하기 위해 만들어졌습니다.
|
| 50 |
-
이전 단계의 처리 결과를 다음 단계의 입력으로 재사용하는 '순환' 구조를 통해, 마치 우리가 문장을 앞에서부터 차례로 읽으며 내용을 머릿속에 축적하는 것과 같은 방식으로 작동합니다.
|
| 51 |
-
|
| 52 |
-
**RNN의 심장 : 순환 구조와 은닉 상태의 역학**
|
| 53 |
-
RNN의 핵심 아이디어는 바로 순환 구조(Recurrent Structure)와 은닉 상태(Hidden State) 입니다.
|
| 54 |
-
- 순환 구조(Recurrent Structure)
|
| 55 |
-
신경망 내부에 '루프'가 존재하여, 정보가 계속해서 순환할 수 있는 구조를 말합니다.
|
| 56 |
-
각 타임스텝 $t$에서 모델은 입력 $x_t$와 이전 타임스텝의 정보 요약본인 $h_{t-1}$을 함께 받아 처리합니다.
|
| 57 |
-
|
| 58 |
-
- 은닉 상태(Hidden State, $h_t$)
|
| 59 |
-
은닉 상태는 '메모리' 또는 '문맥 벡터'라고 불리며, RNN의 모든 것을 담고 있습니다.
|
| 60 |
-
특정 타임스텝 t에서의 은닉 상태 $h_t$는 다음과 같은 수식으로 계산됩니다.
|
| 61 |
-
$h_t=tanh(W_hhh_t−1+W_xhx_t+b_h)$여기서 $W_hh$, $W_{xh}$는 학습을 통해 최적화되는 가중치 행렬이며, $b_h$는 편향입니다.
|
| 62 |
-
중요한 점은 모든 타임스텝에서 동일한 가중치($W$)와 편향($b$)이 공유된다는 것입니다.
|
| 63 |
-
이는 모델이 시간과 관계없이 일관된 패턴을 학습하게 하며, 파라미터 수를 크게 줄여줍니다.
|
| 64 |
-
tanh와 같은 활성화 함수는 계산된 값을 특정 범위(-1에서 1 사이)로 압축하는 역할을 합니다.
|
| 65 |
-
|
| 66 |
-
**RNN을 시간에 따라 펼쳐보기**
|
| 67 |
-
아래 그림처럼 시간에 따라 네트워크를 길게 펼쳐서 표현하면, 쉽게 이해할 수 있습니다.
|
| 68 |
-
```markdown
|
| 69 |
-
시간 흐름 ───▶
|
| 70 |
-
입력 시퀀스: x₁ x₂ x₃ ... xₜ
|
| 71 |
-
↓ ↓ ↓ ↓
|
| 72 |
-
┌────┐ ┌────┐ ┌────┐ ... ┌────┐
|
| 73 |
-
h₀ ───▶ │RNN │▶│RNN │▶│RNN │ ▶ ... ▶│RNN │
|
| 74 |
-
└────┘ └────┘ └────┘ └────┘
|
| 75 |
-
│ │ │ │
|
| 76 |
-
▼ ▼ ▼ ▼
|
| 77 |
-
h₁ h₂ h₃ hₜ
|
| 78 |
-
```
|
| 79 |
-
RNN은 내부적으로 **같은 셀** 을 매 시점마다 반복해서 사용하는 구조 입니다.
|
| 80 |
-
펼쳐서 보면, 위 그림처럼 하나의 RNN셀이 시간이 흐름에 따라 여러 번 복제된 것 처럼 보입니다.
|
| 81 |
-
순서대로 보자면,
|
| 82 |
-
1. 각 시점의 $X_t$가 RNN 셀에 들어가고,
|
| 83 |
-
2. 이전 시점의 은식 상태의 $h_(t^-1)$도 함께 들어가서,
|
| 84 |
-
3. 새로운 은닉상태 $h_t$를 출력합니다.
|
| 85 |
-
4. 이 $h_t$는 다음 시점으로 전달되어 메모리 역할을 합니다.
|
| 86 |
-
|
| 87 |
-
위 그림 속 여러 셀은 같은 RNN 셀을 시간에 따라 복제한 것이고, 모든 셀은 가중치를 공유하기 때문에 시점이 달라도 같은 규칙으로 계산이 이루어 집니다.
|
| 88 |
-
|
| 89 |
-
보충설명 하자면,
|
| 90 |
-
`나는 학교에 간다`
|
| 91 |
-
를 입력하면,
|
| 92 |
-
$X_1$이 나는, $X_2$가 학교에, $X_3$ 가 간다가 되면서 시간에 따른 연산이 이루어 진다고 생각하시면 됩니다.
|
| 93 |
-
|
| 94 |
-
## 2. 아키텍처를 통한 내부 코드 들여다 보기
|
| 95 |
-
이제 이론을 바탕으로, TensorFlow Keras 를 통해 직접 RNN을 구현해 봅시다.
|
| 96 |
-
Keras로 구현한 RNN 모델 아키텍처 심층 분석다음은 IMDB 영화 리뷰 감성 분석을 위한 간단한 RNN 모델입니다.
|
| 97 |
-
|
| 98 |
-
```python
|
| 99 |
-
import tensorflow as tf
|
| 100 |
-
from tensorflow import keras
|
| 101 |
-
|
| 102 |
-
# 모델 아키텍처 정의
|
| 103 |
-
model = keras.Sequential([
|
| 104 |
-
# 1. 단어 임베딩 층
|
| 105 |
-
# input_dim: 전체 단어 집합의 크기 (가장 빈번한 1만개 단어)
|
| 106 |
-
# output_dim: 각 단어를 표현할 벡터의 차원 (32차원)
|
| 107 |
-
keras.layers.Embedding(input_dim=10000, output_dim=32),
|
| 108 |
-
|
| 109 |
-
# 2. RNN 층
|
| 110 |
-
# units: 은닉 상태 벡터의 차원 (32차원)
|
| 111 |
-
keras.layers.SimpleRNN(32),
|
| 112 |
-
|
| 113 |
-
# 3. 최종 분류기(Classifier)
|
| 114 |
-
# units: 출력 뉴런의 수 (긍정/부정 1개)
|
| 115 |
-
# activation: 출력 값을 0~1 사이 확률로 변환 (이진 분류)
|
| 116 |
-
keras.layers.Dense(1, activation="sigmoid"),
|
| 117 |
-
])
|
| 118 |
-
|
| 119 |
-
# 모델 구조 요약 출력
|
| 120 |
-
model.summary()
|
| 121 |
-
```
|
| 122 |
-
레이어를 자세히 들어다 봅시다.
|
| 123 |
-
|
| 124 |
-
- **임베딩 층(Embedding)**
|
| 125 |
-
```python
|
| 126 |
-
keras.layers.Embedding(input_dim=10000, output_dim=32)
|
| 127 |
-
```
|
| 128 |
-
컴퓨터는 '영화', '재미' 같은 단어를 직접 이해하지 못합니다.
|
| 129 |
-
Embedding 층은 각 단어에 부여된 고유한 정수 인덱스를 output_dim 차원의 의미론적 벡터로 변환합니다.
|
| 130 |
-
이 과정에서 '슬픔'과 '비극' 같은 단어들은 벡터 공간상에서 가까운 위치에, '행복'과는 먼 위치에 표현되도록 학습됩니다.
|
| 131 |
-
|
| 132 |
-
- **순환 계층(SimpleRNN)**
|
| 133 |
-
```python
|
| 134 |
-
keras.layers.SimpleRNN(32),
|
| 135 |
-
```
|
| 136 |
-
이 층이 순환 신경망의 본체입니다.
|
| 137 |
-
입력된 단어 임베딩 벡터 시퀀스를 순서대로 하나씩 처리하며 은닉 상태를 계속해서 업데이트합니다.
|
| 138 |
-
기본적으로 마지막 단어까지 처리한 후의 최종 은닉 상태만을 다음 층으로 전달합니다.
|
| 139 |
-
이 최종 은닉 상태 벡터는 전체 문장의 문맥을 압축한 결과물입니다.
|
| 140 |
-
|
| 141 |
-
- **완전 연결 계층(Dense)**
|
| 142 |
-
```python
|
| 143 |
-
keras.layers.Dense(1, activation="sigmoid")
|
| 144 |
-
```
|
| 145 |
-
최종 은닉 상태 벡터를 받아, 리뷰가 긍정(1에 가까운 값)인지 부정(0에 가까운 값)인지 최종 판단을 내립니다.
|
| 146 |
-
model.summary()로 파라미터 수 계산 원리 이해하기위 코드에서 model.summary()를 실행하면 다음과 같은 결과가 나옵니다.
|
| 147 |
-
|
| 148 |
-
```bash
|
| 149 |
-
Model: "sequential"
|
| 150 |
-
_________________________________________________________________
|
| 151 |
-
Layer (type) Output Shape Param #
|
| 152 |
-
=================================================================
|
| 153 |
-
embedding (Embedding) (None, None, 32) 320000
|
| 154 |
-
|
| 155 |
-
simple_rnn (SimpleRNN) (None, 32) 2080
|
| 156 |
-
|
| 157 |
-
dense (Dense) (None, 1) 33
|
| 158 |
-
|
| 159 |
-
=================================================================
|
| 160 |
-
Total params: 322,113
|
| 161 |
-
Trainable params: 322,113
|
| 162 |
-
Non-trainable params: 0
|
| 163 |
-
_________________________________________________________________
|
| 164 |
-
```
|
| 165 |
-
|
| 166 |
-
각 층의 파라미터 수는 어떻게 계산되는지 알아보자면,
|
| 167 |
-
1. Embedding: input_dim * output_dim = 10,000 * 32 = 320,000 개. (1만 개 단어 각각에 대한 32차원 벡터)
|
| 168 |
-
2. SimpleRNN:
|
| 169 |
-
- 입력 가중치(W_xh): input_shape * units = 32 * 32 = 1024
|
| 170 |
-
- 은닉 상태 가중치(W_hh): units * units = 32 * 32 = 1024
|
| 171 |
-
- 편향(b_h): units = 32
|
| 172 |
-
- 총합: 1024 + 1024 + 32 = 2,080 개.
|
| 173 |
-
3. Dense: input_shape * units + bias = 32 * 1 + 1 = 33 개.
|
| 174 |
-
|
| 175 |
-
이처럼 summary를 통해 모델의 구조뿐만 아니라, 각 층이 얼마나 많은 파라미터를 학습해야 하는지 정확히 파악할 수 있습니다.
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
## 3. 직접 RNN 구현해 보기 (실전 예제)
|
| 179 |
-
이제 전체 코드를 단계별로 실행하며 직접 모델을 학습시켜 보겠습니다.
|
| 180 |
-
|
| 181 |
-
**1단계 : 데이터 로드 및 전처리 (패딩의 중요성)**
|
| 182 |
-
RNN은 고정된 길이의 시퀀스를 입력으로 받습니다.
|
| 183 |
-
하지만 영화 리뷰는 길이가 제각각이므로, **패딩(Padding)**을 통해 모든 리뷰의 길이를 동일하게 맞춰줘야 합니다.
|
| 184 |
-
```python
|
| 185 |
-
import numpy as np
|
| 186 |
-
import tensorflow as tf
|
| 187 |
-
from tensorflow import keras
|
| 188 |
-
from keras import layers
|
| 189 |
-
|
| 190 |
-
# 가장 빈도가 높은 1만개 단어만 사용하여 데이터셋 로드
|
| 191 |
-
(x_train, y_train), (x_test, y_test) = keras.datasets.imdb.load_data(num_words=10000)
|
| 192 |
-
|
| 193 |
-
print(f"패딩 전 첫 번째 리뷰 길이: {len(x_train[0])}")
|
| 194 |
-
|
| 195 |
-
# 모든 시퀀스의 길이를 256으로 통일
|
| 196 |
-
# maxlen보다 길면 잘라내고, 짧으면 앞부분을 0으로 채움 (pre-padding)
|
| 197 |
-
x_train = keras.preprocessing.sequence.pad_sequences(x_train, maxlen=256)
|
| 198 |
-
x_test = keras.preprocessing.sequence.pad_sequences(x_test, maxlen=256)
|
| 199 |
-
|
| 200 |
-
print(f"패딩 후 첫 번째 리뷰 길이: {len(x_train[0])}")
|
| 201 |
-
```
|
| 202 |
-
|
| 203 |
-
**2단계 : 모델 컴파일**
|
| 204 |
-
모델을 어떻게 학습시킬지 학습 방법을 설정합니다.
|
| 205 |
-
```python
|
| 206 |
-
model.compile(
|
| 207 |
-
# 손실 함수: 예측이 정답과 얼마나 다른지 측정.
|
| 208 |
-
# 이진 분류(0 또는 1) 문제이므로 binary_crossentropy가 가장 적합.
|
| 209 |
-
loss="binary_crossentropy",
|
| 210 |
-
|
| 211 |
-
# 옵티마이저: 손실을 최소화하기 위해 모델의 가중치를 업데이트하는 알고리즘.
|
| 212 |
-
# Adam은 현재 가장 널리 쓰이고 성능이 좋은 옵티마이저 중 하나.
|
| 213 |
-
optimizer="adam",
|
| 214 |
-
|
| 215 |
-
# 평가지표: 훈련 과정을 모니터링할 지표. 정확도를 사용.
|
| 216 |
-
metrics=["accuracy"]
|
| 217 |
-
)
|
| 218 |
-
```
|
| 219 |
-
|
| 220 |
-
**3단계 : 모델 학습 및 평가 (배치, 에포크, 그리고 과적합)**
|
| 221 |
-
model.fit() 함수로 실제 학습을 시작합니다.
|
| 222 |
-
batch_size: 한 번에 처리할 데이터 샘플의 수. 메모리 효율성과 학습 속도에 영향을 줍니다.
|
| 223 |
-
epochs: 전체 데이터셋을 몇 번 반복하여 학습할지 결정합니다.
|
| 224 |
-
|
| 225 |
-
```python
|
| 226 |
-
batch_size = 128
|
| 227 |
-
epochs = 10
|
| 228 |
-
|
| 229 |
-
# 모델 학습 실행
|
| 230 |
-
# validation_data를 지정하여 매 에포크마다 테스트 데이터로 성능을 검증
|
| 231 |
-
history = model.fit(
|
| 232 |
-
x_train, y_train,
|
| 233 |
-
batch_size=batch_size,
|
| 234 |
-
epochs=epochs,
|
| 235 |
-
validation_data=(x_test, y_test)
|
| 236 |
-
)
|
| 237 |
-
|
| 238 |
-
# 학습 완료 후 최종 성능 평가
|
| 239 |
-
score = model.evaluate(x_test, y_test, verbose=0)
|
| 240 |
-
print(f"\nTest loss: {score[0]:.4f}")
|
| 241 |
-
print(f"Test accuracy: {score[1]:.4f}")
|
| 242 |
-
```
|
| 243 |
-
|
| 244 |
-
학습 과정을 지켜볼 때, 훈련 데이터의 정확도(accuracy)는 계속 오르는데 검증 데이터의 정확도(val_accuracy)가 어느 순간부터 정체되거나 떨어진다면, 모델이 훈련 데이터에만 과하게 적응하는 **과적합(Overfitting)**이 발생하고 있다는 신호입니다.
|
| 245 |
-
이럴 때는, 드롭아웃의 비율을 높이거나 학습율을 조정하는 방법으로 해결할 수 있습니다.
|
| 246 |
-
|
| 247 |
-
**4단계 : 학습된 모델 저장 및 재사용**
|
| 248 |
-
학습에 오랜 시간이 걸리는 모델은 그 결과를 저장해두고 필요할 때마다 불러서 사용하는 것이 효율적입니다.
|
| 249 |
-
|
| 250 |
-
```python
|
| 251 |
-
# 모델의 구조, 가중치, 학습 설정을 모두 '.keras' 파일 하나에 저장
|
| 252 |
-
model.save("my_rnn_model_imdb.keras")
|
| 253 |
-
|
| 254 |
-
# 저장된 모델 불러오기
|
| 255 |
-
loaded_model = keras.models.load_model("my_rnn_model_imdb.keras")
|
| 256 |
-
```
|
| 257 |
-
|
| 258 |
-
**5단계 : 나만의 문장으로 모델 테스트하기 (실전 예측)**
|
| 259 |
-
실제 문장을 예측하려면, 훈련 데이터와 동일한 전처리 과정을 거쳐야 합니다.
|
| 260 |
-
|
| 261 |
-
```python
|
| 262 |
-
# IMDB 데이터셋의 단어-인덱스 사전 로드
|
| 263 |
-
word_index = keras.datasets.imdb.get_word_index()
|
| 264 |
-
|
| 265 |
-
# 새로운 리뷰 문장
|
| 266 |
-
review = "This movie was fantastic and wonderful"
|
| 267 |
-
|
| 268 |
-
# 1. 소문자 변환 및 단어 분리 -> 2. 단어 인덱스로 변환
|
| 269 |
-
tokens = [word_index.get(word, 2) for word in review.lower().split()]
|
| 270 |
-
|
| 271 |
-
# 3. 패딩 처리
|
| 272 |
-
padded_tokens = keras.preprocessing.sequence.pad_sequences([tokens], maxlen=256)
|
| 273 |
-
|
| 274 |
-
# 4. 예측
|
| 275 |
-
prediction = loaded_model.predict(padded_tokens)
|
| 276 |
-
print(f"리뷰: '{review}'")
|
| 277 |
-
print(f"긍정 확률: {prediction[0][0] * 100:.2f}%")
|
| 278 |
-
```
|
| 279 |
-
|
| 280 |
-
4. 나만의 RNN 모델 업그레이드하기
|
| 281 |
-
기본 RNN도 좋지만, 더 복잡한 문제를 해결하기 위해서는 몇 가지 한계를 극복해야 합니다.
|
| 282 |
-
|
| 283 |
-
**RNN의 치명적 약점 : 장기 의존성 문제 (Vanishing Gradients)**
|
| 284 |
-
"나는 프랑스에서 태어나 자랐다. ... (중략) ... 그래서 나는 ___를 유창하게 구사한다."
|
| 285 |
-
빈칸에 들어갈 말은 '프랑스어'입니다.
|
| 286 |
-
사람은 문장 맨 앞의 '프랑스'라는 단어를 기억하여 쉽게 답을 찾습니다.
|
| 287 |
-
하지만 기본 RNN은 시퀀스가 길어질수록, 역��파 과정에서 그래디언트(기울기)가 계속 곱해지면서 0에 가까워져 사라지는 그래디언트 소실(Vanishing Gradient) 문제가 발생합니다.
|
| 288 |
-
이로 인해 문장 앞부분의 중요한 정보를 학습하지 못하게 됩니다.
|
| 289 |
-
이를 장기 의존성 문제라고 합니다.
|
| 290 |
-
|
| 291 |
-
**기억력 강화 : LSTM과 GRU의 등장**
|
| 292 |
-
이 문제를 해결하기 위해 **LSTM(Long Short-Term Memory)**과 **GRU(Gated Recurrent Unit)**가 등장했습니다.
|
| 293 |
-
이들은 RNN 내부에 **게이트(Gate)**라는 정교한 장치를 추가하여 정보의 흐름을 제어합니다.
|
| 294 |
-
- LSTM: '셀 상태(Cell State)'라는 별도의 기억 컨베이어 벨트를 두고, 망각 게이트(Forget Gate), 입력 게이트(Input Gate), 출력 게이트(Output Gate) 3개의 게이트를 통해 어떤 정보를 버리고, 어떤 정보를 새로 기억하고, 어떤 정보를 출력할지 학습합니다.
|
| 295 |
-
장기 기억에 매우 효과적입니다.
|
| 296 |
-
- GRU: LSTM을 더 단순화한 모델로, **리셋 게이트(Reset Gate)**와 업데이트 게이트(Update Gate) 2개의 게이트만 사용합니다.
|
| 297 |
-
파라미터 수가 적어 계산 효율성이 높고, 많은 경우 LSTM과 비슷한 성능을 보입니다.
|
| 298 |
-
|
| 299 |
-
```python
|
| 300 |
-
# LSTM을 2개 층으로 쌓은 모델
|
| 301 |
-
model_lstm = keras.Sequential([
|
| 302 |
-
layers.Embedding(input_dim=10000, output_dim=64),
|
| 303 |
-
# return_sequences=True: 다음 LSTM 층으로 전체 시퀀스를 전달
|
| 304 |
-
layers.LSTM(64, return_sequences=True),
|
| 305 |
-
layers.LSTM(32),
|
| 306 |
-
layers.Dense(1, activation='sigmoid')
|
| 307 |
-
])
|
| 308 |
-
```
|
| 309 |
-
|
| 310 |
-
**과거와 미래를 동시에 : 양방향 RNN (Bidirectional RNN)**
|
| 311 |
-
"오늘 새로 온 ___ 선생님은 정말 멋지다."
|
| 312 |
-
빈칸에 들어갈 '영어'라는 단어는 뒷부분의 '선생님'이라는 단어를 봐야 더 정확히 유추할 수 있습니다.
|
| 313 |
-
이처럼 문맥은 순방향뿐만 아니라 역방향의 정보도 중요합니다.
|
| 314 |
-
**양방향 RNN(Bidirectional RNN)**은 시퀀스를 정방향으로 한번, 역방향으로 한번 독립적으로 처리한 후, 두 결과를 합쳐서 최종 출력을 만듭니다.
|
| 315 |
-
이를 통해 문맥을 훨씬 더 풍부하게 이해할 수 있습니다.
|
| 316 |
-
|
| 317 |
-
```python
|
| 318 |
-
model_bidirectional = keras.Sequential([
|
| 319 |
-
layers.Embedding(input_dim=10000, output_dim=64),
|
| 320 |
-
# LSTM 레이어를 Bidirectional 래퍼로 감싸기만 하면 됨
|
| 321 |
-
layers.Bidirectional(layers.LSTM(64)),
|
| 322 |
-
layers.Dropout(0.5), # 과적합 방지를 위한 드롭아웃 추가
|
| 323 |
-
layers.Dense(1, activation='sigmoid')
|
| 324 |
-
])
|
| 325 |
-
```
|
| 326 |
-
|
| 327 |
-
## 5. 결론
|
| 328 |
-
오늘은 순차 데이터 처리의 근간이 되는 RNN의 핵심 원리부터 시작하여, 실제 코드로 모델을 구현하고, LSTM, GRU, 양방향 RNN과 같은 고급 기법을 통해 성능을 개선하는 방법까지 상세하게 알아보았습니다.
|
| 329 |
-
RNN은 그 자체로도 강력하지만, 자연어 처리 분야의 발전에 엄청난 기여를 한 기념비적인 모델입니다.
|
| 330 |
-
특히 RNN의 한계를 극복하려는 시도 속에서 탄생한 어텐션(Attention) 메커니즘은 이후 **트랜스포머(Transformer)**라는 혁신적인 모델의 기반이 되었습니다.
|
| 331 |
-
다음에는 어텐션 기법과 트랜스포머 모델, 인코더, 디코더로 돌아오겠습니다!!
|
| 332 |
-
오늘도 좋은하루 보내세요!!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|