LM(4) Sequence to Sequence 이론
이번 포스팅에서는 Sequence to Sequence의 방법에 대해 알아본다.
1. 시퀀스-투-시퀀스(Sequence-to-Sequence)
앞서 알아본 NNLM은 다음 단어 예측을 위해 window size만큼을 지정해주었기에 유연성에 한계가 있었다. 따라서 단어의 개수에 무관하게 처리할 수 있는 네트워크가 필요했고, 그것이 RNN 그리고 LSTM의 아이디어로 이어졌다. LSTM은 RNN 계열의 모델이기에 내부 구성의 차이가 있을 뿐, 동작 방식은 동일하다. 이에 대한 내용은 아래 링크에서 확인할 수 있으니 이번 포스트를 읽기 전 반드시 확인이 필요하다.
RNN 계열의 모듈이 사용된 시퀀스 투 시퀀스(=seq2seq)는 단어 시퀀스를 입력으로 받아 또 다른 단어 시퀀스를 출력하는 특징을 가지고 있다. 대표적인 예시는 챗봇과 기계번역인데, 입력 시퀀스와 출력 시퀀스를 질문-답으로 구성하면 챗봇으로 만들 수 있고, 영어-한국어 등 번역 문장으로 구성하면 번역기로 만들 수 있다. 따라서 이번 포스트는 seq2seq에 대한 이해를 위해 번역기를 예시로 한다.
1.1 Model Architecture
Seq2seq은 크게 인코더와 디코더라는 두 개의 모듈로 구성되며, 이를 번역기의 예시로 도식화 하면 위와 같다. 단어의 시퀀스(=문장)가 입력되면 인코더안의 RNN 계열 모듈들은 순차적 연산을 통해 모든 단어가 압축된 하나의 벡터(=마지막 timestep의 출력)를 만드는데, 이를 context 벡터라고 한다. 이렇게 입력 문장의 정보가 하나의 context 벡터로 압축되면, 인코더는 이를 디코더로 전송한다. 전송된 context 벡터는 디코더의 첫번째 hidden state로 첫번째 입력과 함께 연산된다. 디코더 또한 RNN 계열 모듈로 이루어져있기 때문에 인코더와 같은 연산을 진행하나, 각 timestep의 출력을 모두 사용한다는 것이 인코더와의 차이점이다.
1.2 Word Embedding
언어 모델의 연산을 위해서 기계에게 텍스트가 아닌 숫자로 이루어진 벡터를 입력으로 주어야한다. 이를 위한 방법으로 word embedding을 사용하며, 보통은 framework에서 지원하는 embedding layer를 사용하곤 한다. 이는 텍스트를 숫자에 매핑하고 lookup table을 읽어오는 단순한 구조이며, 결국 텍스트를 벡터로 변환하게 된다. 이러한 embedding 과정은 인코더와 디코더 모두 적용되어야 한다. 아래는 연산하기 전, 임베딩 레이어를 거치는 과정을 도식화 한 것이다.
Word embedding의 기법은 embedding layer 뿐만 아니라, word2vec, glove 등등 다양하게 존재한다. 만약 높은 성능의 기계 번역 혹은 언어 모델을 구축하고 싶다면 초기값을 가진 embedding layer가 아닌 pre-trained된 모델들을 적용시키는 방법들도 가능한다. 이에 대한 자세한 내용은 추후 포스팅하도록 할 것이다.
1.3 Predict
앞서 말했듯, 디코더는 인코더의 마지막 출력인 context 벡터를 첫번째 hidden state로, sos(=start of sentence)를 첫번째 입력으로 사용한다. 이 두가지 요소가 RNN 계열 모듈을 통해 연산되면 하나의 벡터 값이 나오게 되고, 이것이 seq2seq 언어 모델의 첫번째 timestep(t=0)의 예측 값이 된다. 하지만 이는 텍스트가 아닌 숫자 벡터이기에 이를 다시 텍스트로 변환시키기 위한 softmax 연산이 필요하다. 이를 도식화하면 아래와 같다. 예시에서는 RNN 계열 모듈 중 LSTM을 사용하였고, softmax 함수를 통해 텍스트 변환을 하기 전 dense layer를 거치도록 구성하였다.
해당 과정을 통해 나온 첫번째 timestep(t=0)의 예측은 je이다. 위 그림에서는 예측한 je가 다음 LSTM에 들어가는 것으로 보이지만, 실상 LSTM은 하나이며, sos와 똑같은 연산(embedding -> lstm -> dense -> softmax)이 수행된다. 단, 연산에 필요한 hidden state는 contex 벡터가 아닌 첫번째 timestep(t=1)의 연산 결과인 벡터로 바뀌게 된다. 이러한 과정이 각 timestep(t=1, 2, 3…)마다 반복되며 결국 LSTM의 예측 단어가 eos가 되면 언어 모델의 번역이 끝나게 된다.
1.3 Teacher forcing (=교사 강요)
모델을 설계하고 훈련 코드를 작성하기 앞서 teacher forcing(=교사 강요)의 개념이 필요하다. 앞서 predict 과정에서 현재 timestep의 디코더는 이전 timestep의 출력을 입력으로 사용한다고 하였다. 이로인해 만약 문장의 첫 단어가 잘못된 경우 이후 모든 출력들이 연쇄적으로 잘못 예측 될 수 있게 되고, 이러한 상황이 반복되면 훈련 시간이 느려진다. 따라서 학습시에는 모델이 정확한 방향으로 학습할 수 있도록 각 timestep의 입력은 실제값(ground-truth)을 받도록 하고, 테스트 혹은 배포 후 운영 환경에서는 이전 timestep의 예측값을 받도록 하여야 한다. 이처럼 모델 학습시 각 timestep에 예측값이 아닌 실제값을 입력으로 주는 방법을 교사 강요라고 한다.
2. More Learn!
해당 부분은 모델 구성 및 학습에 엄청나게 중요하지 않으나 향후 개념을 확장할 때 도움되는 것이라 생각하여 정리하였다. 언어 모델을 처음 본다면 지금 볼 필요는 없을 듯 하다.
2.1 Conditional Language Model
Conditional language model이란 주어진 입력 시퀀스라는 조건을 기반으로 출력 시퀀스를 생성하는 모델이다. 즉, seq2seq 번역기에선 입력 시퀀스를 기반으로 번역된 출력 시퀀스를 생성한다는 것인데, 이때의 입력 시퀀스는 인코더의 최종 출력인 context 벡터로 볼 수 있다. 앞의 Model Architecture에서는 이를 디코더의 첫번째 hidden state로 사용한다고 하였는데, 이외로도 활용 방법은 다양하다. 예를들어 디코더의 입력을 단순히 출력 시퀀스만을 주는 것이 아닌 context 벡터와의 contatenate된 새로운 벡터를 입력으로 설정 할 수 있다. 이를 도식화 하면 아래와 같다.
이러한 구조는 우리가 영어를 번역할때 번역될 문장을 힐끔힐끔 보는 것으로 쉽게 비유할 수 있다.
2.2 Generator
Seq2seq 모델은 teacher forcing을 통해 ground-truth 기반으로 학습을 진행한다. 그렇기에 앞서 단어가 잘못되었어도 걱정할 필요가 없이 계속 학습을 반복하면 된다. 하지만 테스트 및 운영 환경에서는 이를 민감하게 보아야 할 필요가 있다. 아무리 훈련이 잘 된 모델이라도, 문장의 첫 단어가 틀려 모든 문장이 잘못될 가능성은 무시할 수 없다. 따라서 각 timestep에서 가능한 단어들을 여러개 생성하는 generator 방법론들이 존재한다. 이에대한 예시는 아래와 같으며 방법론의 유형 및 자세한 내용은 추후 포스팅 하도록 할 것이다.
2.3 Backpropagation
모델을 학습하고 가중치를 개선하기 위해서는 loss 계산 및 backpropagation이 이루어져야 한다. 일반적으로 loss의 계산은 한번(batch 단위)의 연산 후, 예측값과 groud-truth의 비교를 통해 구하게된다. 또한 backpropagation은 앞서 구한 loss를 통해 모델의 가중치를 update한다. 그런데 RNN 계열의 모델은 예측값이 단어의 시퀀스로 여러개가 생성된다. 또한 update를 위한 weight는 크게 Encoder와 Decoder 두개 뿐이다. 이러한 상황에서 단어의 시퀀스를 예측하고 실제 문장과 비교를 통해 loss를 계산한다고 하였을 때, 만약 시퀀스의 앞 단어는 맞았으나 뒷 단어가 틀릴 경우 디코더의 가중치를 어떻게 update 해야할까? RNN 계열은 backpropagation through time(=BPTT)라는 알고리즘을 사용하여 이를 해결한다고 한다. 사실 BPTT에 대한 정확한 이론을 알지 못해 ChatGPT를 통해 정의 정도 알아보았다. 추후 정확한 정의를 보고 포스팅 하도록 할 것이다.
👀 BPTT
BPTT는 각 시간 단계에 대한 gradient를 계산한 다음 hidden state의 전체 시퀀스를 통해 해당 gradient를 역전파 하는 방식으로 작동한다. 그런 다음 SGD와 같은 최적화 알고리즘을 통해 모델의 가중치를 업데이트 한다. BPTT는 모든 hidden state를 메모리에 저장하고 시퀀스와 각 시간 단계에 대해 별도의 연산을 수행해야 하기 때문에 계산 비용이 많이 들 수 있다. 그러나 이는 RNN이 장기적인 종속성을 학습하고 순차 데이터에서 복잡한 패턴을 캡처할 수 있게 해준다.
참조
https://wikidocs.net/24996
https://heegyukim.medium.com/keras-embedding%EC%9D%80-word2vec%EC%9D%B4-%EC%95%84%EB%8B%88%EB%8B%A4-619bd683ded6
https://wikidocs.net/33793
https://ebbnflow.tistory.com/154
https://jiho-ml.com/weekly-nlp-22/
댓글남기기