본문 바로가기

텐서플로

임베딩(Embedding) 순환신경망(Recurrent Neural Network, RNN)

1. 임베딩

■ 컴퓨터는 사람이 사용하는 자연어(한글, 영어 등)를 그대로 이해하지 못한다. 그러므로 컴퓨터가 이해할 수 있는 벡터로 변경해야 한다. 이런 변경을 임베딩이라고 말한다. 그러므로 임베딩이 잘 될수록 높은 모델 성능을 기대할 수 있다.

■ 임베딩과 관련된 다양한 방법들이 있는데, 크게는 '단어 수준'의 임베딩(Word2Vec, FastText)과 '문장 수준'의 임베딩(Elmo, Bert, GPT 등)으로 구분한다.

- 단어 수준의 임베딩은 동음이의어(글자의 소리는 같지만 뜻이 다른 단어 - 눈, 차, 배 등)를 구별할 수 없지만,

- 문장 수준의 임베딩은 사람처럼 문장의 '앞뒤'를 보고 의미를 파악할 수 있다. 

■ 단어 수준의 임베딩을 위해 단어, 음절, 형태소 등으로 나누고 이를 수치로 변환한다. 이때 가장 간단한게 토큰을 벡터로 변환할 수 있는 방법은 원-핫(one-hot) 인코딩이다.

■ 다만, 원-핫 인코딩은 [1 0 0 0 0 0 0], [0 1 0 0 0 0 0], ... 처럼 단어가 많아질수록 벡터의 공간(차원)이 커지므로 비효율적이다. 이런 원-핫 인코딩 변환 방법은 행렬의 원소(성분)이 대부분 '0'인 희소행렬(sparse matrix)라고도 부른다.

원-핫 인코딩은 위의 예시처럼 '0'이라는 불필요한 공간이 발생하므로 메모리 낭비가 심하다. 또한, 1 또는 0으로 구성되기 때문에 단어와 단어의 유사도를 계산하기 어렵다는 단점이 있다.

■ 이런 문제로 원-핫 인코딩 대신, 단어를 밀집(dense) 벡터/행렬로 표현한다. 밀집 벡터/행렬의 성분은 0이 아닌 실숫값으로 채워지기 때문에 메모리 낭비가 훨씬 작아지고, 단어와 단어의 유사도를 파악하기 쉽다.

텐서플로 tensorflow.keras.layers에서 제공하는 임베딩 레이어는 밀집 행렬로 구성되어 있다. 이때 두 가지 파라미터 값 설정이 필요하다.

- (1) 첫 번째는 입력 차원(단어의 총 개수)

- (2) 두 번째는 임베딩 차원이다.

- 다음과 같이 \( n \)개의 입력 차원을 3차원 임베딩 공간으로 매핑시키기 위해서이다.

[출처] https://builtin.com/machine-learning/fully-connected-layer

- 위의 예시는 9개의 단어로 구성된 문장을 4개의 원소를 갖는 임베딩 벡터로 변환하는 예시이다.

■ 임베딩 레이어의 input_dim에는 단어의 크기, output_dim에는 출력 벡터의 크기(차원)를 지정한다.

https://www.tensorflow.org/api_docs/python/tf/keras/layers/Embedding

 

tf.keras.layers.Embedding  |  TensorFlow v2.16.1

Turns positive integers (indexes) into dense vectors of fixed size.

www.tensorflow.org

- 여기서 input_dim은 단어의 크기. 즉, 사용할 단어의 개수로 maximum integer + 1이다. 예를 들어 다음과 같이 설정할 수 있다.

[출처] https://aminss.tistory.com/36

그리고 사용할 모델에 따라 입력 (시퀀스) 길이(input_length)를 설정해야 할 수도 있고 그렇지 않을 수도 있다.

- 별도의 시드 고정이 없다면 임베딩 레이어의 초기 가중치는 매번 랜덤하게 초기화된다. 

■ 예를 들어 다음은 임의의 4개의 숫자를 Embedding Layer에 통과시키면 입력 4개(숫자 4개)가 각각 원소(차원) 3개를 갖는 밀집 행렬로 변환되는 것을 볼 수 있다.

import tensorflow as tf
from tensorflow.keras.layers import Embedding

embedding_layer = Embedding(input_dim = 100, output_dim = 3) 
result = embedding_layer(tf.constant([10, 3, 4, 5]))
print(result)
```#결과#```
tf.Tensor(
[[-0.03326149 -0.0138962   0.00638118]
 [ 0.00692762  0.02173448  0.00766476]
 [ 0.02313616 -0.01571113  0.02268859]
 [ 0.04769924 -0.00988016 -0.01320392]], shape=(4, 3), dtype=float32)
````````````

 

2. 순환신경망(RNN)

RNN은 시퀀스 데이터(자연어, 음성 데이터처럼 순서가 있는 데이터)를 순차적으로 처리하는 신경망을 의미한다.

- RNN보다 개선된 LSTM도 같은 맥락이기 때문에 LSTM을 RNN으로, 기본 RNN을 Elman RNN으로 부르기도 한다.

https://hyeon-jae.tistory.com/141

 

순환 신경망(RNN) (1)

1. 피드포워드(feed forward) 신경망의 문제점■ 지금까지 살펴본 신경망은 신경망의 기본적인 형태인 피드포워드 신경망이다.- 피드포워드 신경망은 다충 퍼셉트론이라고도 불리며, 은닉칭이 하나

hyeon-jae.tistory.com

https://hyeon-jae.tistory.com/145

 

게이트(gate)가 추가된 RNN - LSTM, GRU

1. RNN의 문제점■ RNN은 순환 경로를 통해 과거 정보를 계승할 수 있어 시퀀스 데이터(순서가 있는 데이터)인 시계열 데이터를 다루기에 적합하다. ■ 하지만, 길이가 긴 시계열 데이터를 사용할

hyeon-jae.tistory.com

■ 순환신경망은 이전의 (은닉)상태가 현 시점에 영향을 주고, 이를 순차적으로 반영해 결과를 계산한다. 즉, 과거의 결과가 현재에 영향을 미친다. 이는 완전연결 신경망(FCN) 및 합성곱 신경망(CNN)과의 큰 차이점이다.

피드포워드(순방향)와 순환 구조의 입출력

■ 순환신경망 유형은 크게 다음과 같은 5가지 유형으로 구분할 수 있다.

[출처] https://karpathy.github.io/2015/05/21/rnn-effectiveness/

- (1) One to One은 가장 기본적인 순환신경망 Simple RNN의 형태이다.

- (2) One to Many는 이미지 캡션(입력으로 이미지를 넣으면 출력으로 문장이 나오는)에 활용된다.

- (3) Many to One은 문장을 입력으로 넣고 긍정 또는 부정을 출력하는 감성 분류에 활용된다.

- (4) Many to Many는 챗봇, 기계 번역, 품사 예측 등 다양하게 활용되고 있다.

■ 이렇게 순환신경망의 입력과 출력의 길이를 다르게 설계하여 다양한 용도로 사용할 수 있다.

 

3. 순환신경망 알고리즘

2.1 SimpleRNN, LSTM, GRU 

■ SimpleRNN, LSTM, GRU 레이어는 tensorflow.keras.layers에서 불러올 수 있으며, Sequential이나 함수형 API, 모델 서브클래싱을 이용해 모델을 설계할 수 있다.

from tensorflow.keras.layers import Embedding, SimpleRNN, LSTM, GRU, Dense
from tensorflow.keras.models import Sequential
model = tf.keras.Sequential()
model.add(Embedding(100, 3, input_length = 14))
model.add(SimpleRNN(units = 16, return_sequences = True)) # activation 기본값은 'tanh'
# model.add(LSTM(units = 16, return_sequences = True)) # activation 기본값은 'tanh' # recurrent_activation 기본값은 'sigmoid'
model.add(Dense(1))

model.summary()
```#결과#```
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 embedding_3 (Embedding)     (None, 14, 3)             300       
                                                                 
 simple_rnn (SimpleRNN)      (None, 14, 16)            320       
                                                                 
 dense (Dense)               (None, 14, 1)             17        
                                                                 
=================================================================
Total params: 637
Trainable params: 637
Non-trainable params: 0
_________________________________________________________________
````````````

■ 여기서 RNN Layer(이 예에서는 simple_rnn) Embedding Layer로부터 (None, 14, 3)크기를 입력으로 받는데, 이는 '(batch_size(배치 크기), input_length(입력 길이), 출력 벡터(밀집 벡터)의 차원)'이다.

 

3. 순환신경망 활용

3.1 양방향(Bidirectional) RNN

■ 자연어 데이터는 양방향. 즉, 순서대로 데이터를 처리하고 역순으로 처리할 경우 더 좋은 성능을 발휘한다. 

■ 다음 단어를 예측할 때, 앞에 있는 단어의 정보뿐만 아니라 뒤에 있는 단어의 정보도 사용할 수 있기 때문이다.

https://hyeon-jae.tistory.com/156

 

어텐션(Attention) (2)

1. 양방향 순환 신경망 (Bidirectional RNN)■ 예를 들어 'He always drinks black coffee'라는 문장이 있을 때, 기본적인 RNN/LSTM은 시간축이 왼쪽에서 오른쪽으로, 즉 사람이 보통 글을 왼쪽에서 오른쪽으로 읽

hyeon-jae.tistory.com

 

tensorflow.keras.layers에서 양방향 레이어 Bidirectional를 제공하고 있다. RNN 레이어를 감싸면 양방향 RNN이 된다.

from tensorflow.keras.layers import Bidirectional

model = Sequential()
model.add(Embedding(100, 3, input_length = 14))
model.add(Bidirectional(LSTM(units = 16, return_sequences = True))) 
model.add(Dense(1))

model.summary()
```#결과#```
Model: "sequential_3"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 embedding_7 (Embedding)     (None, 14, 3)             300       
                                                                 
 bidirectional (Bidirectiona  (None, 14, 32)           2560      
 l)                                                              
                                                                 
 dense_9 (Dense)             (None, 14, 1)             33        
                                                                 
=================================================================
Total params: 2,893
Trainable params: 2,893
Non-trainable params: 0
_________________________________________________________________
````````````

■ LSTM(units = 16)으로, LSTM의 유닛(뉴런) 개수를 16개로 지정했지만, summary( )를 보면 유닛의 개수가 2배인 32개인 것을 확인할 수 있다.

■ 이는, LSTM 모델이 순방향과 역방향으로 각각 만들어졌기 때문이다.

- 이렇게 양방향으로 LSTM을 사용하는 것을 Bidirectional LSTM, 줄여서 Bi-LSTM이라고 부른다.

■ 단, 양방향 RNN은 최신 정보가 과거 정보보다 훨씬 더 중요한 task에는 적합하지 않다. 또한 위의 예시에서 유닛 개수가 2배가 된다. 즉, 해당 부분의 네트워크 용량이 2배가 된다는 의미이다.

■ 하지만 앞서 언급한 것처럼 텍스트 데이터 또는 순서가 중요한 종류의 데이터에는 적합하다.

3.2 스태킹(Stacking) RNN

시간 축 방향이 아닌 깊이 방향으로 RNN 레이어 또는 양방향 RNN 레이어를 여러 층 쌓은 것을 스태킹 RNN이라고 한다.

https://hyeon-jae.tistory.com/156

 

어텐션(Attention) (2)

1. 양방향 순환 신경망 (Bidirectional RNN)■ 예를 들어 'He always drinks black coffee'라는 문장이 있을 때, 기본적인 RNN/LSTM은 시간축이 왼쪽에서 오른쪽으로, 즉 사람이 보통 글을 왼쪽에서 오른쪽으로 읽

hyeon-jae.tistory.com

■ 다른 RNN 유형과 다른 점은 다음과 같이 RNN 또는 양방향 RNN 레이어를 쌓기 때문에 각 시점별 은닉 상태 값을 모두 출력해야 한다. 이를 가능하게 하는 파라미터가 return_sequences이다.

return_sequences의 기본값은 False로, 이는 마지막 은닉 상태 값만 출력한다는 의미이다. 하지만, RNN 레이어를 여러 층 쌓을 경우 각 시점별로 연결되야 하기 때문에 True로 설정해야 한다.

- return_sequences = False일 경우 다음 그림과 같이 마지막 은닉 상태만 다음 RNN 층으로 전달되기 때문에 RNN 레이어를 쌓을 수 없다. (에러가 발생한다)

- return_sequences = True로 설정해야 모든 시점의 은닉 상태 값을 다음 RNN 층으로 전달할 수 있다.

- 만약, 최상단에 있는 RNN 레이어가 마지막 계층이라면 더 이상 모든 상태를 전달할 필요가 없으므로 return_sequences를 True로 지정할 필요가 없다.

model = Sequential()
model.add(Embedding(100, 3, input_length = 14))

model.add(LSTM(units = 32, return_sequences = True)) # 모든 시점의(전체 시퀀스의) 은닉 상태를 출력
model.add(LSTM(units = 32, return_sequences = False)) # 마지막 시점의 은닉 상태만 출력

model.add(Dense(1))

model.summary()
```#결과#```
Model: "sequential_4"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 embedding_8 (Embedding)     (None, 14, 3)             300       
                                                                 
 lstm_1 (LSTM)               (None, 14, 32)            4608      
                                                                 
 lstm_2 (LSTM)               (None, 32)                8320      
                                                                 
 dense_10 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 13,261
Trainable params: 13,261
Non-trainable params: 0
_________________________________________________________________
````````````

- 위의 모델 구조를 보면 최상단 RNN 레이어의 return_sequences 는 False이며 출력값은 FC 레이어에 전달되는 것을 볼 수 있다. 이 모델 구조를 도식화하면 다음과 같다.

3.3 순환 드롭아웃

■ 일반적인 '드롭아웃(dropout)'이 아닌 '순환 드롭아웃(recurrent dropout)'을 구분해야 한다.

일반적인 '드롭아웃'은 훈련 데이터를 층에 주입할 때 데이터에 있는 우연한 상관관계를 깨기 위해 입력 층의 유닛을 랜덤하게 비활성화 시키는 기법이다.

■ 하지만, 순환 신경망에 일반적인 드롭아웃 기법을 적용하는 것은 쉽지 않다. 시퀀스 데이터를 사용하기 때문이다.

■ 시퀀스 데이터를 사용하는 순환 신경망에 일반적인 드롭아웃 기법을 적용하면, 오히려 학습을 방해하는 요인으로 작용될 수 있다. 

위의 그림처럼 각 시점별 정보가 전달되야 하는데, 학습 과정에서 랜덤으로 연결을 비활성화하면 모든 시점의 정보가 제대로 전달될 수 없다. 

■ 이 문제를 해결하기 위해 제안된 것이 바로 '순환 드롭아웃'이다.

■ 순환 드롭아웃은 타임스텝마다 랜덤하게 드롭아웃 마스크를 바꾸는 것이 아니라, 동일한 드롭아웃 마스크를 모든 타임스텝에 적용하는 방법이다. 즉, 랜덤이 아닌 동일한 패턴으로 유닛을 드롭아웃한다.

■ LSTM이나 GRU 같은 순환 게이트에 의해 만들어지는 표현을 규제하려면 순환 층(LSTM이나 GRU 레이어) 내부 계산에 사용되는 활성화 함수에 타임스텝마다 동일한 드롭아웃 마스크를 적용해야 한다.

이렇게 하면 학습 오차를 타임스텝에 걸쳐 적절하게 전파할 수 있다. 

텐서플로 케라스에 있는 모든 순환 레이어는 2개의 드롭아웃 매개변수를 가지고 있다. 예를 들어 tf.keras.layers.LSTM은 dropout과 recurrent_dropout 매개변수가 있으며 기본값은 모두 0.0으로 설정되어 있다.

- dropout은 층의 입력에 대한 드롭아웃 비율, recurrent_dropout은 순환 상태의 드롭아웃 비율을 의미한다.

- 두 드롭아웃 매개변수 모두 0과 1사이의 부동 소수점 값을 가진다.

model = Sequential()
model.add(Embedding(100, 3, input_length = 14))
model.add(LSTM(units=32, dropout=0.0, recurrent_dropout=0.0))
model.add(Dense(1))

model.summary()
```#결과#```
Model: "sequential_9"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 embedding_13 (Embedding)    (None, 14, 3)             300       
                                                                 
 lstm_7 (LSTM)               (None, 32)                4608      
                                                                 
 dense_15 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,941
Trainable params: 4,941
Non-trainable params: 0
_________________________________________________________________
````````````

 

 

'텐서플로' 카테고리의 다른 글

텐서플로 합성곱 신경망(CNN) (2)  (0) 2024.11.22
텐서플로 합성곱 신경망(CNN) (1)  (0) 2024.11.15
케라스(Keras) (2)  (2) 2024.08.20
케라스(Keras) (1)  (0) 2024.08.18
텐서(Tensor) (1)  (0) 2024.08.10