본문 바로가기
NLP

[NLP Study] - RNN

by Tiabet 2024. 2. 6.

트랜스포머가 무엇이 대단한지를 이해하려면, Seq2Seq부터 이해해야 하고, 결국엔 그 전의 자연어 처리가 어떠한 식으로 이루어졌는지를 완전히 이해해야 할 것 같다. 그래서 한 달 동안 RNN부터 쭉 공부를 해보고자 한다.

 

나중에는 관련 논문들을 읽어보고 코드로 구현할 수 있을 수준까지 실력을 올리고 싶기 때문에 논문 베이스 + 관련 자료 공부하고 정리하는 형식으로 포스팅이 이어질 것 같다.

 

참고자료

https://www.youtube.com/watch?v=Hn3GHHOXKCE&ab_channel=%EB%A9%94%ED%83%80%EC%BD%94%EB%93%9CM

이번에 RNN을 공부하면서 참고한 영상이다.

https://arxiv.org/abs/1808.03314

 

Fundamentals of Recurrent Neural Network (RNN) and Long Short-Term Memory (LSTM) Network

Because of their effectiveness in broad practical applications, LSTM networks have received a wealth of coverage in scientific journals, technical blogs, and implementation guides. However, in most articles, the inference formulas for the LSTM network and

arxiv.org

RNN과 LSTM의 개념이 정리된 논문이다. 사실 너무 길어서 다 읽진 못했다.

참고로 RNN의 개념이 처음 등장한 논문은 https://www.nature.com/articles/323533a0 라고 하는데, 30년도 넘게 전에 나온 논문이라 가독성이 떨어진다.

 

https://wikidocs.net/48558 

 

08. 순환 신경망(Recurrent Neural Network)

앞서 배운 피드 포워드 신경망은 입력의 길이가 고정되어 있어 자연어 처리를 위한 신경망으로는 한계가 있었습니다. 결국 다양한 길이의 입력 시퀀스를 처리할 수 있는 인공 신경망이 …

wikidocs.net

자연어 처리 공부하는 데에 많은 도움을 얻고 있는 위키독스의 딥 러닝을 이용한 자연어 처리 입문도 참고했다.

 

ANN (Artificial Neural Network) 과 DNN (Deep Neural Network)

ChatGPT가 그려준 ANN과 RNN의 모형화. 사실 뭔 소린지 잘 모르겠다.

가장 기본적인 NN을 ANN이라 부른다. 

출처 : https://www.researchgate.net/figure/Artificial-neural-network-architecture-ANN-i-h-1-h-2-h-n-o_fig1_321259051

 

사람의 뇌가 작동하는 원리를 본따서 만들었다고 하여 Nerual Network 라는 이름이 붙었다. Hidden layer의 개수가 2개 이하면 ANN, 2개 이상이면 DNN이라고 부르는데 이들을 통틀어서 Feed Forward NN (FFNN) 으로 부른다. 이들이 Feed Forward라는 이름을 갖게 된 이유는 Input으로 들어온 값들이 Output layer를 향해 앞으로 쭉 이동하기 때문이다. 입력층에서 출발한 값들은 은닉층(Hidden layer)을 거치면서 여러 계산(가중치 곱 및 활성화 함수 등)을 거친 끝에 출력층에 도착, 출력값은 원래값과의 차이를 계산하는 데에 사용된다. 이때 발생한 오차는 loss라고 불리며, 이는 다시 은닉층들의 가중치를 업데이트 하는 데에 사용된다.

 

RNN (Recurrent Neural Network)

그렇다면 RNN은 FFNN 과 어떤 것이 다른 걸까? 내가 제일 궁금했던 부분이다. RNN을 간단하게 설명하자면 내부적으로 순환하면서 가중치를 업데이트 해나가는 신경망인데, 선뜻 와닿지가 않는다. 그래서 정말 많은 책들을 찾아보면서 공부했고 간신히 나만의 개념으로 잡는 데에 성공했다.

 

우선 위에서 언급했듯이 FFNN은 입력층에 들어온 값이 출력층을 향해서 일직선으로 죽 나아가지만, RNN은 그렇지가 않다. RNN의 은닉층에선 출력층으로 값을 보내는 것 외에, 다음 입력값에 대해 은닉층이 계산할 때 같이 계산하라고 값을 보낸다. 사실 써보고 보니 이 말도 잘 이해가 되지 않는다.

 

출처 : 참고자료 위키독스

이 모든 것은 RNN의 입력값 처리가 순차적으로 이루어지기 때문에 발생하는 현상이다. 예를 들어보자.

 

'철수가 사과를 먹고 있다.' 같은 짧은 문장을 보자. RNN은 입력값을 순차적으로 처리하기 때문에, 우선 '철수가' 에 대해 은닉층에서 처리를 쭉 할 것이다. 그럼 은닉층을 거쳐서 나온 결과를 출력층으로 보냄과 동시에 저장해둔다. 그리고 다음 입력값인 '사과를'에 대해 처리할 때, '철수가'에 대한 은닉층의 계산값이 더해져서 처리가 되는 것이다. 

 

위 그림을 보면 조금 더 쉽게 이해할 수 있다. 한 층에 두 개의 은닉 노드가 존재하는데, 각각의 은닉 노드에서 두 개씩 계산값을 다음 time 에서의 은닉 노드로 보내는 것을 알 수 있다.

 

그렇다면 왜 time이란 용어를 쓸까? 이 부분도 상당히 헷갈릴 수밖에 없는데, 우선 1. 문장에서 단어가 순차적으로 들어오기 때문에 time 이란 말이 틀린 것은 아니다. 2. 원래 RNN은 NLP, 즉 자연어에서 사용된 것이 아닌 시계열 데이터 처리에서 사용됐을 것이다. 라고 생각해볼 수 있다. 

 

그렇다면 대체 이전 단어에 대한 은닉층 계산값이 어떻게 다음 단어의 은닉층 계산값에 영향을 미치는지 살펴보자.

 

RNN에서의 수식과 RNN의 활용

출처 : 참고자료 위키독스

수식은 생각보다 간단했다. 위에서 은닉층의 계산값이 서로 더해진다고 표현했는데, 정말로 덧셈을 진행한다. 이전 은닉층의 계산값에 대해서도 가중치가 존재하는데, 이 가중치는 특별히 다른 것이 아니고 FFNN에서도 계산하는 은닉 노드에 대한 가중치와 같은 것이다. 그리고 f는 tanh, Relu 같은 비선형 활성화 함수를 의미한다. 위에서 계산된 ht 는 다시 다음 은닉층으로 보내질 것이다. FFNN과의 차이가 더욱 명확해지는 순간이다.

 

RNN의 구조가 명확해지니 RNN을 NLP에서 어떻게 사용하는지 감이 온다. 문장 하나를 주고 이 문장을 분류하는 작업 (이전 단어들에 대한 계산값을 쭉 저장하므로 문장 전체에 대한 이해가 쌓임), 문장을 번역하는 작업(단어 하나하나 마다 출력층으로 값을 보내고, 또 다음 은닉층으로도 보내기 때문에 한 언어의 단어와 일치하는 다른 언어의 단어 탐색 가능), 문장을 생성하는 작업 (문장이 중간에 끊어져도 다음 단어로 무엇이 올 지 판단 가능) 등이 가능할 것이다. 문득 시계열 데이터와 자연어가 비슷한 맥락으로 분석이 가능하다는 것이 좀 신기하다.

 

간단한 코드

끝으로 ChatGPT로 생성한 RNN 예제를 소개하고자 한다.

 

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import SimpleRNN, Dense

# Assuming you have already preprocessed your input data and it's ready to be fed into the model
# For example, your input data could be something like this:
# X_train shape: (number_of_sequences, sequence_length, features_per_timestep)
# y_train shape: (number_of_sequences, number_of_classes)

# Define the model
model = Sequential()

# Add a SimpleRNN layer
model.add(SimpleRNN(50, activation='tanh', return_sequences=False, input_shape=(sequence_length, features_per_timestep)))

# Add a Dense layer for output
model.add(Dense(number_of_classes, activation='softmax'))

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Summary of the model
model.summary()

# Train the model
model.fit(X_train, y_train, epochs=10, batch_size=32)

# Replace 'sequence_length', 'features_per_timestep', and 'number_of_classes' with actual numbers based on your dataset.

 

Keras API를 사용하여 모델에 층을 쌓는 방식으로 RNN을 구현한 것이다. 텐서플로에 SimpleRNN이라는 이름으로 RNN이 구현되어 있어 이를 불러와서 간단하게 사용할 수 있다.

 

 

사실 이 포스팅을 쓰는 데에만 굉장히 오랜 시간이 걸렸다. 쓰면서 이미 알고 있는 트랜스포머와의 차이도 더 명확하게 다가왔다. 다음 포스팅은 또 얼마나 걸릴지...