while (1): study();

Ch7. RNN을 사용한 문장 생성 본문

독서

Ch7. RNN을 사용한 문장 생성

전국민실업화 2021. 7. 31. 19:06
728x90

 이번 장에서는 RNN 아키텍처를 이용한 생성 모델의 끝판왕인 Seq2Seq을 간단하게 구현해봅니다. 일전에 Seq2seq 기계번역기를 수업을 들으며 꽤 그럴싸하게 구현한 적이 있었는데, 당시엔 파이토치를 사용하여 구현했기 때문에, 원리도 알아볼겸 천천히 책을 따라가봤습니다.

 일전에 구현했던 기계번역기 모듈은 다음 깃허브 레포에 있습니다.

https://github.com/jeongchanyoung-1234/Neural_Machine_Translation

 

GitHub - jeongchanyoung-1234/Neural_Machine_Translation: Neural Machine Translation with Attention (Experiment result will be up

Neural Machine Translation with Attention (Experiment result will be uploaded later) - GitHub - jeongchanyoung-1234/Neural_Machine_Translation: Neural Machine Translation with Attention (Experiment...

github.com

 

 Seq2seq 모델은 말 그대로 시계열 데이터를 받아, 대응하는 시계열 데이터를 출력합니다. 입력으로 주어지는 시계열 데이터를 고정 길이 벡터(은닉 상태)로 변환하는 부분을 인코더, 인코더가 출력한 은닉 상태를 다시 받아 대응하는 시계열 데이터로 출력하는 부분을 디코더라고 합니다.

 책에서는 덧셈 문제를 문자열로 받아서, 정답을 문자열로 뱉는 과제를 수행하는 모델을 만들고 있습니다. 저도 이 문제에 국한해서 실험해보도록 하겠습니다.

 

입력 문장 반전

 다만 실험 과정에서 입력 문장 반전(Reversing the source target)이라는 트릭을 사용합니다. 이는 입력 데이터의 어순을 완전히 반대로 뒤집어 인코더에 입력하는 것입니다. 예를 들어 '나는 어제 그녀를 보았습니다'라는 문장을 'I saw her yesterday'로 번역하는 문제라면, 입력을 '보았습니다 그녀를 어제 나는'으로 주는 것입니다. 실제로 입력 문장 반전을 사용할 시 좋은 성적을(여기서는 정확도) 거두는 것을 확인했습니다. 조금 더 이론적인 근거를 찾아보기 위해서 논문을 찾아보았습니다만, 정확한 근거는 논문에서도 제시하지 않고 있습니다. 다만 Minimal time lag을 줄일 수 있다는 관점에서 유효하다고 추론하고 있습니다.

 위에서 예시로 든 '나는 어제 그녀를 보았습니다'라는 문장에서 'I saw her yesterday'로 번역하는 같은 문제를 가정하겠습니다. 이때 각각의 문장에 대응하는 요소들의 거리는 최소의 시간차(Minimal time lag)이 있습니다. '나'에서 'I'에 이르기까지 인코더는 '어제', '그녀를', '보았습니다'를 순차적으로 거쳐야합니다. 대부분의 문장이 주어로 시작하는 것을 감안하면, 소스 문장의 주어와 타겟 문장의 주어 간 거리는 언제나 (소스 문장의 토큰 수 - 1)만큼의 거리가 존재한다고 가정할 수 있겠습니다. 그러나 입력 문장을 뒤집게되면 '나'와 'I' 간의 거리가 크게 감소합니다. 따라서 대응하는 단어 간의 전체 평균 거리는 유지하면서, 주어와 같이 앞에 나오는 단어들간의 거리는 상대적으로 짧아져서 디코더는 조금 더 유용한 정보를 가지고 첫 단어를 생성할 수 있게 됩니다. 문장 생성이 자기회귀적(Auto-regressive)인 속성을 가진다는 점에서 첫 단추를 잘 꿰는 것은 더욱 중요하다고 할 수 있겠습니다.

 우선 가장 기본적인 Seq2seq을 이용하여 덧셈 문제를 풀어보겠습니다. 파라미터는 다음과 같이 설정했습니다.

배치 사이즈 256
에포크 25
옵티마이저 SGD
학습율 1.
기울기 클리핑 5
단어 벡터 크기 16
은닉 벡터 크기 128

Seq2seq - acc
Seq2seq - loss

 손실은 확실히 줄어들고 있지만, 정확도는 거의 좋아지지 않은 모습입니다. 단순히 입력 데이터만 반전시킨다고 해서 모든 게 잘 풀릴거라고 기대하는 것은 너무 욕심이 컸죠.

 

Regularized with Cell state

기존 LSTM 계층에서 정보를 전달하기 위해 고안되었던 Cell state는 실제로는 다른 계층에 전달하는 일이 많이 없다고 합니다. 조금 더 Cell state를 활용할만한 방안이 없을까 고민하던 찰나, 인코더와 디코더를 구성하는 cell state의 유사도를 손실로 사용해보면 어떨까 싶었습니다.

 배경은 이렇습니다. A문장에서 B문장으로 번역하는 과정에서 각각의 LSTM계층은 대응하는 문장 A, B를 입력으로 받을 것입니다. 만약 두 문장이 완벽하게 대응한다면 A문장과 B문장이 가지는 정보량은 같을 것이고, 또한 같은 맥락을 공유할 것입니다. 따라서 Cell state가 일종의 '기억'의 역할을 한다면 인코더와 디코더의 Cell state는 결과적으로 유사한 크기(놈)에, 유사한 벡터가 나와야한다는 것이 저의 가설입니다. 따라서 간단하게 다음과 같은 식을 만들어 손실에 추가하려고 했습니다. 즉 일종의 정규화 방안으로 Cell state를 이용하는 것입니다.

$$reg = cosine similarity(C_{enc}, C_{dec})|$$

 단, 이렇게만 하면 reg가 기존 손실에 비해 월등히 커지기 때문에, 일명 배보다 배꼽이 커지게 됩니다. 따라서 간단하게 하이퍼볼릭 탄젠트 함수를 씌우고 반으로 나누어, -0.5에서 0.5사이의 값을 가지도록 했습니다. 절대값을 씌워서 reg는 언제나 양수이므로, 결과적으로 두 Cell state가 비슷하면 비슷할수록 0에 가까운 값을, 그렇지 않다면 0.5에 수렴하는 값을 반환할 것입니다.

$$L = L + tanh(reg)$$

 사실 손실 자체가 학습 초기와 학습 후반의 편차가 큰 편이므로 단순히 0에서 0.5 사이의 값으로 압축하는 것은 썩 좋지 않은 생각입니다만, 실험의 목적에서 문제를 단순화하였습니다. 이대로 코드를 구현하고 실제로 학습을 진행해보았습니다. 파라미터는 이전과 같습니다. 또한 이 모델을 RC Seq2seq이라고 명명하겠습니다.

RC Seq2seq - accuracy

  이전에 심플한 Seq2seq으로 학습시켰던 것보다 훨씬 좋은 성과를 거두고 있습니다! 사실 기계번역 태스크에서나 먹힐 것 같았고, 덧셈 문제에 있어서 성과를 거둘수 있을지 의문이었는데, 생각보다 높은 점수가 나와서 오히려 저도 놀랐습니다. 훈련 세트에 대해서는 84%정도의 정확도를, 검증 세트에 대해서는 77% 정도의 정확도를 달성했습니다.

RC seq2seq - loss

 손실도 지속적으로 하강하고 있는 모습입니다. 아무래도 방향성을 잘 잡은 듯 합니다. 이번에는 교재에서 제시한 Peeky(엿보기)까지 적용시켜 훈련해보겠습니다.

RC Seq2seq with peeky - acc

 테스트 세트의 정확도는 거의 100%이고, 검증 세트에 대한 정확도도 최대 96%로 지금까지 중 최고 성적을 거두었습니다.

RC Seq2seq with peeky - loss

 20 에포크에 도달할 무렵 손실이 완만하게 하강하는 경향이 보입니다. 따라서 peeky를 사용하면 연산량은 늘어나지만, 최적점에 도달하는 속도는 훨씬 빠르다고 할 수 있겠습니다. 다만 교재에서는 Peeky seq2seq을 사용했을 때 약 10에포크만에 90% 이상의 정확도에 도달하고 있는데, 이것은 교재의 실험환경에서 Adam을 사용했기 때문이라고 생각합니다. 우선 꽤 좋은 성적을 거두었으므로, 실험은 여기에서 마무리하겠습니다.

728x90

'독서' 카테고리의 다른 글

Part1. 들어가며  (0) 2021.12.05
Ch8. 어텐션  (0) 2021.08.01
Ch6. 게이트가 추가된 RNN  (0) 2021.07.29
Ch5. 순환 신경망(RNN)  (0) 2021.07.28
Ch4. Word2Vec 속도 개선  (0) 2021.07.27
Comments