[NLP] 트랜스포머 디코더 구조
시작하며
지금까지 트랜스포머의 기초 개념과 인코의 구조에 대해 자세히 알아보았다. 이번 포스트에서는 트랜스포머의 나머지 구조인 디코더에 대해서 자세히 알아보도록 하자. 기본적으로 트랜스포머의 개념과 인코더의 구조에 대해서 알고 있다는 전제하에 작성되는 글이니, 이것들에 대한 기초 개념이 부족하면 아래의 포스트에서 공부하고 보는 것을 추천한다.
트랜스포머 기초 개념 공부하러 가기
트랜스포머 인코더 구조 공부하러 가기 (1)
트랜스포머 인코더 구조 공부하러 가기 (2)
인코더에서 디코더로(From Encoder To Decoder)
이렇게 구현된 인코더는 총 $\text{num_layers}$만큼의 층 연산을 순차적으로 한 후에 마지막 층의 인코더의 출력을 디코더에게 전달한다. 인코더 연산이 끝났으면 디코더 연산이 시작되어 디코더 또한 $\text{num_layers}$만큼의 연산을 하는데, 이때마다 인코더가 보낸 출력을 각 디코더 층 연산에 사용한다. 위 그림에선 디코더가 하나밖에 없지만, 실제로는 여러개의 디코더가 있어 각 디코더마다 인코더가 보낸 출력을 연산에 사용하는 것이다. 이제 디코더에 대해서 자세히 이해해보자.
디코더의 첫번째 서브층 : 셀프 어텐션과 룩-어헤드 마스크
위 그림과 같이 디코더도 인코더와 동일하게 임베딩 층과 포지셔널 인코딩을 거친 후의 문장 행렬이 입력된다. 트랜스포머 또한 seq2seq와 마찬가지로 교사 강요(Teacher Forcing)을 사용하여 훈련되므로 학습 과정에서 디코더는 번역할 문장에 해당되는 ‘
여기서 문제가 있다. seq2seq의 디코더에 사용되는 RNN 계열의 신경망은 입력 단어를 매 시점마다 순차적으로 입력받으므로 다음 단어 예측에 현재 시점을 포함한 이전 시점에 입력된 단어들만 참고할 수 있다. 반면, 트랜스포머는 문장 행렬로 입력을 한 번에 받으므로 현재 시점의 단어를 예측하고자 할 때, 입력 문장 행렬로부터 미래 시점의 단어까지도 참고할 수 있는 현상이 발생한다. 가령, suis를 예측해야 하는 시점이라고 해봅시다. RNN 계열의 seq2seq의 디코더라면 현재까지 디코더에 입력된 단어는
이를 위해 트랜스포머의 디코더에서는 현재 시점의 예측에서 현재 시점보다 미래에 있는 단어들을 참고하지 못하도록 룩-어헤드 마스크(look-ahead mask)를 도입했다. 직역하면 ‘미리보기에 대한 마스크’이다.
룩-어헤드 마스크(look-ahead mask)는 디코더의 첫번째 서브층에서 이루어진다. 디코더의 첫번째 서브층인 멀티 헤드 셀프 어텐션 층은 인코더의 첫번째 서브층인 멀티 헤드 셀프 어텐션 층과 동일한 연산을 수행한다. 오직 다른 점은 어텐션 스코어 행렬에서 마스킹을 적용한다는 점만 다르다. 우선 다음과 같이 셀프 어텐션을 통해 어텐션 스코어 행렬을 얻는다.
이제 자기 자신보다 미래에 있는 단어들은 참고하지 못하도록 다음과 같이 마스킹한다.
마스킹 된 후의 어텐션 스코어 행렬의 각 행을 보면 자기 자신과 그 이전 단어들만을 참고할 수 있음을 볼 수 있다. 그 외에는 근본적으로 셀프 어텐션이라는 점과, 멀티 헤드 어텐션을 수행한다는 점에서 인코더의 첫번째 서브층과 같다.
디코더의 두번째 서브층 : 인코더-디코더 어텐션
디코더의 두번째 서브층에 대해서 이해해보자. 디코더의 두번째 서브층은 멀티 헤드 어텐션을 수행한다는 점에서는 이전의 어텐션들(인코더와 디코더의 첫번째 서브층)과는 공통점이 있으나 이번에는 셀프 어텐션이 아니다.
셀프 어텐션은 Query, Key, Value가 같은 경우를 말하는데, 인코더-디코더 어텐션은 Query가 디코더인 행렬인 반면, Key와 Value는 인코더 행렬이기 때문이다. 다시 한 번 각 서브층에서의 Q, K, V의 관계를 정리해보자.
인코더의 첫번째 서브층 : Query = Key = Value
디코더의 첫번째 서브층 : Query = Key = Value
디코더의 두번째 서브층 : Query : 디코더 행렬 / Key = Value : 인코더 행렬
디코더의 두번째 서브층을 확대해보면, 다음과 같이 인코더로부터 두 개의 빨간색 화살표가 그려져 있다.
두 개의 화살표는 각각 Key와 Value를 의미하며 이는 인코더의 마지막 층에서 온 행렬로부터 얻는다. 반면 Query는 디코더의 첫번째 서브층의 결과 행렬로부터 얻는다는 점이 다르다. Query가 디코더 행렬, Key가 인코더 행렬일 때, 어텐션 스코어 행렬을 구하는 과정은 다음과 같다.
그 외에 멀티 헤드 어텐션을 수행하는 과정은 다른 어텐션들과 같다.
이외에 잔차 연결과 층 정규화, FFNN층은 인코더와 똑같이 동작하니 이 부분에 대한 설명은 생략하도록 한다.
마치며
이번 포스팅에선 트랜스포머의 인코더와 디코더 중 디코더에 대해 자세히 알아보았다. 디코더의 다른 부분은 인코더의 구조와 비슷하게 동작하지만, 첫 번째 층에서 미래의 단어가 현재 예측에 영향을 주는것을 방지하기 위해 룩-어헤드 마스크를 사용한다는 점과, 두 번째 서브층의 어텐션은 인코더와 디코더의 첫 번째 층과는 다르게 셀프 어텐션이 아니라는 점이다. 이번 포스팅을 끝으로 트랜스포머에 대한 포스팅은 끝이 날 것 같다. 처음 볼 때는 굉장히 어려웠는데 계속 반복해서 보다보니 몇 몇 수식 빼고는 이해가 다 된 것 같다. 다음 포스팅에선 이 트랜스포머의 인코더를 이용한 Bert모델에 대해서 알아보도록 하겠다. 마지막으로 트랜스포머에 대한 자세한 설명과 그림들을 담은 글을 공유하면서 이 포스팅을 마치도록 하겠다.
댓글남기기