RNN
앞서 이미지 처리에서 CNN에 대해 배웠다. 또한 잘 작동한다는 것을 알았다. (실습도 많이 해봤고)
그렇다면 언어 처리는 어떨까?


위와 같이 음파를 형태로 만들고, 음절 별로 구분해서 모델을 만들 수는 있을 것이다.
하지만 The sound라고 한다면, 기본적으로 이산적이고 모델에게 The까지인지 Thes까지인지 Theso까지인지 알려주기가 어렵다.
이미지 내에서는 기본적으로 '순서'라는 개념이 모호하다. 앞선 CV를 처리하는 모델들도 이 순서라는 개념이 들어가있지 않았다.
또한 실제 우리 말에는 '맥락'이라는 것도 존재한다. 이렇듯 언어 뿐만 아니라 우리 주위에는 '순서'라는 개념이 들어간 데이터가 많은데 이러한 시계열 데이터와 같은 '순서'가 중요한 데이터들을 가지고 모델을 만들고자 나온 것이 RNN이다.
RNN 파이프라인을 도식화하면 아래와 같다. 이미지를 넣어 언어로 출력하는 모델을 상상해보자.

one-to-many 방식이다. (many to one, many to many, one to one 전부 가능하다. 입력 벡터 대비 출력 벡터에 따른 것)
이미지를 x 벡터로 인코딩하고 이를 h(히든 레이어)로 보내 디코딩하여 I라는 글자를 추론한다.
여기서, 히든 레이어란 히든 스테이트로 만드는 레이어를 뜻하고, 히든 스테이트란, 지금까지 받은 정보를 요약해서 잠시 저장해두는 공간 정도로 이해하면 된다.
이 다음 두 번째 히든레이어로 인코딩한 뒤(이전 '순서'에 대한 정보가 있다고 보면 된다) , 다시 디코딩하여 look이라는 글자를 추론하고,,, 를 반복한다.
이때 각 인코딩 방식은 fc레이어로 생각하면 된다.(거의 fc레이어로 이루어져있다고 함)
fc레이어로 이루어져 있는 이유는, 실제 '시간'이나 '순서'에 대한 정보가 넘어가는게 중요하고 이 레이어를 지나는 과정 자체에서 뭔가를 얻고자 하는게 아니여서 그냥 단순 연산인 fc 레이어를 쓴다는 거 같다.

또한 연산량을 줄이고자 같은 레벨에서는 전부 같은 가중치를 공유한다.
만약 사전의 모든 단어(6만개)를 6만 차원의 embedding vector로 one-hot encoding한다면 출력도 자유로울 텐데, 실제로는 데이터가 너무 방대해서 줄여야 한다. 이 때 두 가지 방법이 있다.
먼저, PCA나 autoencoder로 사이즈를 줄이는 방법이다. 다만 이러한 방법은 의미가 연관된 단어끼리 비슷하게 매핑이 안된다는 단점이 있다.
그래서 a boy has 라는 문장에서 boy를 매핑한다면, 먼저 모래시계와 같은 구조에서 boy를 인코딩한 뒤, 디코딩 과정에서 일부는 a와, 다른 일부는 has와 연관지어 매핑하는 방법이 있다. (매핑 방식은 다양하다. 상황에 맞게 쓰면 됨)
좀 더 간단하게 도식화하면 아래와 같다.

t가 시간이라면, t 시간에 x 벡터가 RNN으로 넘어가며 ht가 되고, 이전 t-1 시간에 인코딩 되었던 벡터가 들어오며 h(h(t-1) + h(t)가 될 것이다. 이로부터 디코딩 과정을 거쳐 y벡터가 나온다.
새로운 상태 ht는 h(t-1)과 이전에서 넘어온 '순서'가 포함된 정보 x(t)를 어떤 가중치의 레이어에 통과시킨 값이 된다는 것이다.
여기서 activation func으로 tanh를 쓰는데, 그 이유는 글자라는 모든 값을 -1~1로 매핑하여 버려지지 않게 하기 위해서라고 한다.
Seq to Seq
여기서 좀 더 복잡하게 만든 것이 seq2seq이다. RNN의 문제는 한글을 영어로 번역한다고 했을 때 실제 입력한 글자 수와 출력 글자 수가 다르거나, 입력의 첫 단어가 번역 시 동일하게 첫 어순으로 매칭되지 않는 상황에서 성능을 제대로 내지 못한다는 단점이 있다. 그래서 기존의 RNN 자체를 many to one과 one to many 두 개의 RNN으로 이어 붙여 각각 인코더와 디코더로서 하도록 만든 모델이다.

당연히 한글 -> 벡터화의 인코딩의 가중치가 있고, 벡터 -> 영어로의 디코딩의 가중치를 각각 학습하여 성능을 더 높일 수 있었을 것이다.
학습시키는 하나의 예시를 보면 다음과 같다.

실제로 어느 정도 성능이 잘 나왔다. 셰익스피어 글을 학습시킨건 너무나도 유명하고 코딩과 같은 영역에서도 잘 만들어냈다.
하지만 아직도 여러 문제가 있었다.
너무 긴 문장을 넣게 되면 메모리를 엄청 잡아먹는다는 문제가 있었다. 또한 길이가 고정된 하나의 context 벡터로 인코딩하는 데엔 문장의 길이가 길면 한계가 있었다. 문장의 길이가 너무 길면 context vector에서 정보가 희미해지고 성능이 떨어졌다.
이를 해결하려고 단락을 나누어 학습시킨다거나 이런 방법을 썼는데, 이러다가 나온게 바로 그 유명한 attention 메커니즘이다. attention은 다음 글에서 정리하도록 하고 CNN과 RNN으로 만든 Image captioning 모델만 보여주고 다음 내용으로 넘어가겠다.
Image Captioning

CNN 모델을 가지고 context vector로 만든 뒤 이를 RNN 디코더에 넣는 구조이다.

RNN 구조를 조금 더 뜯어보면 사실 학습이 잘 안되는 구조라고 한다.(물론 이건 attention이랑 비교해서 하는 말이다.. ㅠㅠ)
동일한 가중치가 글자 수에 따라 계속 곱해지는 형태라 w가 계속 곱해져 값이 점점 커지게 되고, 이를 잡아주기 위해 activation func으로 tanh를 쓸 수밖에 없다.(LeRU 안쓰는 이유..)
그래서 나온게 LSTM이다.
LSTM
LSTM은 Long Short Term Memory로, 흔히 우리 인간의 뇌를 생각했을 때 장기기억 어쩌구~ 하는거를 본따 만들었다고 한다. 이 RNN의 구조를 일부 그대로 채용하고 있는데, 도식을 보면서 설명하겠다.

i,f,o,g는 스위치이다. on/off 스위치라고 생각하면 된다. 시그모이드 함수를 거쳐 on/off 여부를 결정한다.
g는 우리가 앞서 배운 그 RNN이다.
c가 앞서 말한 장기기억 cell인데, 수식을 보면 저 식의 결과를 토대로 f,c 식에서는 장기기억을 얼만큼 지울지 정하고, 뒤의 i,g에서 현재 정보를 쓸지 말지를 정한다고 한다.
h는 히든 스테이트로 이 장기기억 셀 c를 얼마만큼 꺼내쓸지 결정하는 구조이다.
사실 진짜 포인트는 '내부 구조가 더 중요해졌다'는 것이다. CNN에서도 복잡하면 기울기 소실 문제가 있어 학습이 되지 않았다. 그래서 skip connection(resnet)으로 해결하였다. 얘도 같은 원리로 RNN에서 발전된 형태인 것이다. 다음 시간엔 개인적으로 ML이 대중적이게 되는데 큰 역할을 한, ML의 꽃이라 생각하는 attention에 대해 알아보겠다.