12-1. RNN / SimpleRNN

2020. 8. 19. 02:10AI/모두를 위한 딥러닝

이 포스트는 허민석님의 유튜브 [딥러닝]RNN 기초 강의와 시작하세요! 텐서플로2.0프로그래밍을 참고하여 작성했습니다.
누구나 이해할 수 있는 수준으로 설명하고자 하였으며 LAB의 코드는 TF2.0에 맞추어 재작성되었습니다.

CNN에 이어 이번에는 순환 신경망(RNN, Recurrent Neural Network)과 RNN의 가장 기초인 SimpleRNN에 대해 알아보겠습니다. 먼저 RNN은 음악, 자연어, 주식 시장 등 시간의 흐름에 따라 변화하는 시퀀스(순서가 있는) 데이터를 입력받아 처리할 때 사용됩니다. 하나의 예로 여러 단어들이 주어졌을 때, 이 단어들을 조합해서 만든 문장의 품사를 맞추는 프로그램을 생각해보겠습니다. 예시를 들어 i, google, at, work의 네 단어가 주어졌을 때, 'i work at google(나는 구글에서 일한다)'와 'i google at work(나는 직장에서 구글링을 한다)'의 두 문장을 만들 수 있습니다. 이는 google과 work 단어 둘 다 명사의 뜻과 동사의 뜻이 있으며, 서로 동사-명사, 명사-동사로 쓰여도 전혀 어색하지 않기 때문입니다. 따라서 두 문장에서 google과 work가 명사인지 동사인지는 앞 뒤 단어의 품사 혹은 문장 성분이 중요합니다. 아래 그림을 보겠습니다. I work at google에서 work는 I라는 대명사 뒤에 나왔으므로 동사로 사용되었습니다. 반면 google은 at 뒤에 나와 구글이라는 장소를 의미하는 명사로 사용되었습니다.

i뒤에 나온 work, at뒤에 나온 google이 나온 것으로 보아 work는 동사, google은 명사로 사용되었음을 알 수 있습니다.

 

이번에는 반대로 google이 동사로, work가 명사로 사용되었습니다.

work와 google의 품사를 구분하는 것은 주어진 단어 앞의 단어인 i와 at입니다. 따라서 이전 단어의 품사 정보를 기억하는 새로운 형식의 네트워크 구조가 필요해짐에 따라 나온 것이 바로 RNN입니다. 이 때 기존 네트워크와 달리 이전의 정보를 기억하는 공간을 hidden state라고 하며, 입출력의 형태에 따라 아래와 같이  다양한 문제에 대해 다양한 구조의 네트워크를 만들 수 있습니다. 아래에서 각 네트워크의 예시로는 다음과 같습니다. 이미지를 입력했을 때 이미지에 대한 설명을 하는 이미지 설명 생성(Image captioning, one to many), 문장의 긍/부정을 판단하는 문장 감성 분석기(Sentiment classification, many to one), 한영/영한 번역 등의 언어 자동번역기(Machine Translation, many to many), 문장에서 다음에 나올 단어를 예측하는 모델(Word predictor, many to many).

다음 포스트에서 LSTM을 설명할 때 마지막의 문장에서 다음에 나올 단어를 예측하는 모델을 예로 들어 설명할 것이므로, 지금은 많은 문제들을 RNN으로 해결할 수 있다는 것에 대해서만 알고 넘어가겠습니다. 이제 RNN에서 가장 기초적인 구조인 SimpleRNN를 예로 입력이 hidden state에서 계산되어 출력되는 과정에 대해 알아보겠습니다.

출처: 허민석님의 유튜브 [딥러닝] RNN 기초: https://www.youtube.com/watch?v=PahF2hZM6cs

선을 따라 h_t에서의 출력을 살펴보면, 이전 네트워크들과는 달리 h_t-1의 출력을 어느 정도 반영할지 정하는 weight인 Whh과 곱한 값을 h_t의 입력에 weight을 곱한 값과 더하는 것을 볼 수 있습니다. 이를 아래처럼 수식으로도 표현할 수 있는데, 많이 복잡해 보이지만 기존의 네트워크에서 "출력을 정하는 데에 있어 이전의 정보를 얼마만큼 기억할 것인지를 고려"하는 수식이 추가되었을 뿐입니다. 

활성화 함수 tanh를 통과하지 않은 h_t에서의 출력

이후 모델은 softmax를 통해 one-hot encoding된 값을 통해 차례대로 대명사, 동사, 전치사, 명사라는 답을 예측합니다. 사실 이 문장에서 i와 at는 대명사와 전치사임을 모델도 이미 알고 있기 때문에 이 단어들이 work와 google에 미치는 영향(각 단계에서의 Whh값)이 어느 정도 유의미해야만 정확한 예측을 할 수 있었을 것입니다. 이후에는 모델의 예측값(아래 그림에서의 pred)을 실제값(target)과 비교하며 w_xh, bias, w_hh를 최적화하여 둘의 차이를 줄여나가도록 학습을 합니다. 이때 주의할 점으로는 RNN은 셀 하나를 가지고 재사용하는 방식이기 때문에 w_xh와 bias, w_hh는 모든 네트워크에서 하나의 동일한 값입니다.

one-hot encoding된 예측값 pred를 실제값 target과 비교하며 weight과 bias를 업데이트합니다.

이번에는 문장의 긍정/부정을 판단하는 감성 분석의 알고리즘을 보겠습니다. 역시 단어들의 앞 뒤 관계를 파악해(문맥이라는 표현이 정확하겠습니다) 긍정/부정을 판단하였으며, 주목할 점은 분류할 항목이 긍정/부정의 2개이기 때문에 binary classification을 사용하며 학습 시에는 역전파(BackPropagation Throuht Time, BPTT) 방법을 사용했습니다.

이제 SimpleRNN에 대해 알아보겠습니다. 사실 RNN에서는 LSTM이나 GRU 등이 더 자주 사용되긴 하지만, SimpleRNN은 RNN의 가장 기초적인 레이어로서 RNN을 쉽게 이해하는데 도움이 될 것 입니다. SimpleRNN의 구조는 위의 문장 품사 맞추는 프로그램의 네트워크 구조처럼 단순한 네트워크들이 이어져있는 모양입니다. 이제 다음의 childult-programmer github에서 예제 문제와 코드를 보겠습니다. 문제는 다음과 같습니다.

그렇다면 TF2.0버전에 맞추어 Keras에서 SimpleRNN을 구현한 코드는 다음과 같습니다. 실제 keras.layers.SimpleRNN에는 아래의 많은 인자들을 필요로 하지만, 지금 필요로 하는 인자는 반복 횟수와 입력 벡터의 크기를 담은 인자 input_shape와 반환 시 시퀀스 형식으로(one to many, many to many, LSTM 등에서는 True로 사용됩니다) 반환할 것인지를 표현하는 인자 return_sequences만이 필요합니다. 블록 형태로 표현하면 왼쪽이 return_sequences=False일 때, 오른쪽이 return_sequences=True일 때의 모양입니다. 바로 밑에는 출력을 위해 Dense레이어를 연결한 모습을 볼 수 있습니다. 

return_sequences= False/True, 출처: https://tykimos.github.io/2017/04/09/RNN_Getting_Started/

나머지 코드들은 링크의 Github에서 확인할 수 있습니다. 사실 SimpleRNN은 다음 포스트에서 알아볼 LSTM, GRU 레이어들에 비해 복잡한 현실 문제를 푸는데에는 한계점이 많습니다. 따라서 다음 포스트에서는 그 한계점은 무엇이고 LSTM, GRU 레이어들은 무엇인지에 대해 알아보겠습니다.