Home 트랜스포머-디코더
Post
Cancel

트랜스포머-디코더

영어 “I am good”을 입력하면 프랑스어 “Je vais bien”을 생성하는 번역기를 만든다고 가정하자. 번역기를 만들려면 먼저 입력 문장인 “I am good”을 인코더에 입력해야 한다. 인코더는 입력 문장의 표현을 학습한다. 앞에서 인코더가 입력 문장을 학습하는 방법을 상세히 다뤘다. 이제 이 인코더의 결괏값을 가져와서 디코더에 입력값으로 사용한다. 디코더는 다음 그림과 같이 인코더의 표현을 입력값으로 사용하고 타깃 문장인 “Je vais bein”을 생성한다.

그림 1-35 트랜스포머의 인코더와 디코더

인코더 부분을 다룰 때 인코더 $N$개를 누적해서 쌓을 수 있다는 것을 배웠다. 인코더와 유사하게 디코더 역시 $N$개를 누적해서 쌓을 수 있다. $N=2$로 예를 들어보자. [그림 1-36]에 표시된 것처럼 하나의 디코더 출력값은 그 위에 있는 디코더의 입력값으로 전송된다. 또한 인코더의 입력 문장 표현 (인코더의 출력값)이 모든 디코더에 전송된다. 즉, 디코더는 이전 디코더의 입력값과 인코더의 표현(인코더의 출력값), 이렇게 2개를 입력 데이터로 받는다.

그림 1-37 시간 스텝 t=1 경우 디코더 예측

시간 스텝 t=2 경우 현재까지의 입력값에 이전 단계 (t-1) 디코더에서 생성한 단어를 추가해 문장의 다음 단어를 생성한다. 즉 [그림 1-38]처럼 디코더는 <sos>와 “Je”를 입력받아 타깃 문장의 다음 단어를 생성한다.

그림 1-38 시간 스텝 t=2 경우 디코더 예측

위의 방법과 마찬가지로 모든 단계에서 디코더는 이전 단계에서 새로 생성한 단어를 조합해 입력값을 생성하고 다음 단어를 예측하는 방법을 진행한다. 따라서 $t=4$의 경우 <sos>, “Je”, “vais”, “bien”을 입력하고 다음 단어를 예측한다.

그림 1-39 시간 스텝 t=4 경우 디코더 예측

[그림 1-40]을 통해 알 수있듯이 디코더에서 <eos> 토큰을 생성할 때 타깃 문장의 생성이 완료된다.

인코더의 경우, 입력 문장을 임베딩 행렬로 변환한 후 여기에 위치 인코딩을 더한 값을 입력한다. 마찬가지로 디코더 역시 입력값을 바로 입력하는 것이 아니라 위치 인코딩을 추가한 값을 디코더의 입력값으로 사용한다.

예를 들어 [그림 1-41]처럼 각 시간 단계의 입력을 임베딩으로 변환한다고 할 때 위치 인코딩 값을 추가한 다음 디코더에 입력한다.

그림 1-41 위치 인코딩이 적용된 인코더와 디코더

하나의 디코더 블록은 다음과 같은 요소들로 구성된다.

그림 1-42 디코더 블록

디코더 블록은 서브레이어 3개로 구성된 인코더 블록과 유사한 구조다.

  • 마스크된 멀티 헤드 어텐션 (masked multi-head attention)
  • 멀티 헤드 어텐션 (multi-head attention)
  • 피드포워드 네트워크 (feedforward network)

디코더 블록은 인코더 블록과 유사하게 서브레이어에 멀티 헤드 어텐션과 피드포워드 네트워크를 포함한다. 하지만 인코더와 다르게 두 가지 형태의 멀티 헤드 어텐션을 사용한다. 그 중 하나는 어텐션 부분인 마스크된 형태이다.

1.3.1 마스크된 멀티 헤드 어텐션

영어를 프랑스어로 번역하는 태스크가 있고, 학습 데이터가 다음과 같이 준비되어 있다고 가정하자.

그림 1-43 학습 데이터 예제

위의 데이터를 통해 번역 태스크의 입력과 출력 형태를 이해할 수 있다. 앞에서 번역 모델에 대한 테스트를 수행할 때 디코더에서 타깃 문장을 어떻게 생성하는지 알아봤다.

모델을 학습할 때는 이미 타깃 문장을 알고 있어서 디코더에 기본으로 타깃 문장 전체를 입력하면 되지만 수정 작업이 조금 필요하다. 디코더에서 문장을 입력할 때 처음에는 <sos> 토큰을 입력하고 <eos> 토큰이 생성될 때까지 이전 단계에서 예측한 단어를 추가하는 형태로 입력을 반복한다. 따라서 타깃 문장의 시작 부분에 <sos> 토큰을 추가한 다음 디코더에 입력한다.

“I am good”을 “Je vais bien”으로 번역한다고 가정해보자. 타깃 문장 시작 부분에 <sos> 토큰을 추가한 “<sos> je vais bien”을 디코더에 입력하면 디코더에서 “Je vais bien <eos>”를 출력한다.

그림 1-44 트랜스포머의 인코더, 디코더

그렇다면 세부적으로 어떤 방식으로 작동하는 것인가? 왜 타깃 문장 전체를 입력하고 디코더에서는 한 단계 이동한 형태의 문장을 출력하는 것인가? 이 부분을 좀 더 자세히 알아보자.

디코더에 입력 문장을 입력할 때 입력 문장을 임베딩 (출력 임베딩 행렬)으로 변환한 후 위치 인코딩을 추가해 디코더에 입력하는 것은 알고 있다. 디코더의 입력 행렬을 $X$라고 하자.

그림 1-45 입력 행렬

행렬 $X$를 디코더에 입력하면 첫 번째 레이어는 마스크된 멀티 헤드 어텐션이 된다. 인코더에서 사용한 멀티 헤드 어텐션과 기본 원리는 같지만 다른 점이 한 가지 있다.

셀프 어텐션을 구현하면 처음에 $Q, K, V$행렬을 생성한다.멀티 헤드 어텐션을 계산하면 $h$개의 $Q, K, V$행렬을 생성한다. 헤드 $i$의 경우 행렬 $X$에 각각 가중치 행렬 $w_i^Q, w_i^K, w_i^V$를 곱해 $Q_i, K_i, V_i$ 행렬을 얻을 수 있다.

이제 마스크된 멀티 헤드 어텐션을 살펴보자. 디코더의 입력 문장은 ‘<sos> Je vais bein”이다. 앞에서 셀프 어텐션은 각 단어의 의미를 이해하기 위해 각 단어와 문장 내 전체 단어를 연결했다. 그런데 디코더에서 문장을 생성할 때 이전 단계에서 생성한 단어만 입력문장으로 넣는다는 점이 중요하다. 예를 들어 $t=2$의 경우 디코더의 입력 단어는 [<sos>, Je]만 들어간다.즉, 이런 데이터의 특성을 살려 모델 학습을 진행해야 한다. 따라서 셀프 어텐션은 단어와의 연관성을 “Je”만 고려해야 하며, 모델이 아직 예측하지 않은 오른쪽의 모든 단어를 마스킹해 학습을 진행한다.

그림 1-46 값에 대한 마스킹 처리

이와 같은 단어 마스킹 작업은 셀프 어텐션에서 입력되는 단어에만 집중해 단어를 정확하게 생성하는 긍정적인 효과를 가져온다. 그렇다면 마스킹을 어떻게 수현할 수 있을까? $i$ 헤드의 어텐션 행렬 $Z_i$는 다음 식으로 구할 수 있다.

\[Z_i=softmax({QK^T\over\sqrt{d_k}})V_i\]

어텐션 행렬을 구하는 첫 번째 단계는 쿼리와 키 행렬 사이의 내적을 계산하는 것이다. [그림 1-48]은 쿼리와 키 행렬 사이의 내적값을 구하고, $\sqrt{d_k}$로 나눈 임의의 결과다.

그림 1-48

위 행렬에 소프트맥스 함수를 적용해 정규화 작업을 수행한다. 소프트맥스 함수를 적용하기 전에 행렬값에 대한 마스킹 처리가 필요하다. 예를 들어 위 행렬의 첫 번째 행을 보자. <sos>의 다음 단어를 예측한다고 할 때 모델에서는 <sos> 오른쪽에 있는 모든 단어를 참조하지 말아야한다. <sos> 오른쪽에 있는 모든 단어를 $-\infty$로 마스킹한다. 두 번째, 세 번째 행도 마찬가지로 수행한다.

이제 소프트맥스 함수를 적용한 행렬과 밸류 $(V_i)$ 행렬에 곱해 최종적으로 어텐션 행렬 $Z_i$를 구한다. 멀티 헤드 어텐션의 경우 $h$개의 어텐션 행렬을 구하고 이들을 서로 연결한 후에 새로운 가중치 행렬 $W^0$을 곱해 최종적으로 어텐션 행렬 $M$을 구한다.

\[M=concatenate(Z_1, Z_2, Z_3, \cdots, Z_h)W_0\]

1.3.2 멀티 헤드 어텐션

[그림 1-52]는 인코더와 디코더를 결합한 트랜스포머 모델의 모습이다. 이때 디코더의 멀티 헤드 어텐션은 입력 데이터 2개를 받는다. 하나는 이전 서브레이어의 출력값이고, 다른 하나는 인코더의 표현이다.

그림 1-52 인코더와 디코더 상호 작용

인코더의 표현 값을 $R$, 이전 서브레이어인 마스크된 멀티 헤드 어텐션의 결과로 나온 어텐션 행렬을 $M$이라고 한다. 여기서 인코더의 결과와 디코더의 결과 사이에 상호 작용이 일어난다. 이를 인코더-디코더 어텐션 레이어 (encoder-decoder attention layer)라고 한다.

이제 멀티 헤드 어텐션 레이어가 어떻게 작동하는지 알아보자. 첫 번째 단계에서는 멀티 헤드 어텐션에서 사용하는 쿼리, 키, 밸류 행렬을 생성한다. 앞에서 행렬에 가중치 행렬을 곱해서 쿼리, 키, 밸류 행렬을 만들 수 있다는 것을 배웠다. 하지만 이번에는 입력값이 2개 (인코더 표현 $R$, 이전 서브레이어의 결과인 $M$)다. 이런 경우에는 어떻게 해야 할까?

이전 서브레이어의 출력값인 어텐션 행렬 $M$을 사용해 쿼리 행렬 $Q$를 생성하고, 인코더 표현 값인 $R$을 활용해 $K, V$행렬을 생성한다. 현재 멀티 헤드 어텐션을 사용하고 있으므로 헤드 $i$를 기준으로 다음 절차를 따른다.

  • 어텐션 행렬 $M$에 가중치 행렬 $W_i^Q$를 곱해 쿼리 행렬 $Q_i$를 생성한다.
  • 인코더 표현값 $R$에 가중치 행렬 $W_i^K, W_i^V$를 각각 곱해 키, 밸류 행렬 $K_i, V_i$를 생성한다.

그림 1-53 쿼리, 키, 밸류 행렬 생성

왜 쿼리 행렬은 $M$을 통해 생성하고 키, 밸류 행렬은 $R$을 통해 생성하는 것일까? 일반적으로 쿼리 행렬은 타깃 문장의 표현을 포함하므로 타깃 문장에 대한 값인 $M$의 값을 참조한다. 키와 밸류 행렬은 입력 문장의 표현을 가져서 $R$의 값을 참조한다. 이때 장점은 무엇일까? 셀프 어텐션을 단계적으로 계산하면서 좀 더 자세히 알아보자.

셀프 어텐션의 첫 번째 단계는 쿼리, 키 행렬 간의 내적을 계산하는 것이다. 앞에서 설명했듯이 쿼리 행렬은 $M$의 값을, 키 행렬은 $R$의 값을 참조했다. 쿼리 , 키 행렬값은 다음 그림과 같다.

위 행렬 $Q_i\cdot K_i^T$를 통해 다음 사실을 이해할 수 있다.

  • 행렬의 첫 번째 행에서 쿼리 벡터 $q_1(<sos>)$와 모든 키 벡터 $k_1(I), k_2(am), k_3(good)$ 사이의 내적을 계산한다. 첫 행은 타깃 단어 $<sos>$가 입력 문장의 모든 단어 (I, am, good)와 얼마나 유사한지를 계산하는 것으로 해석할 수 있다.

멀티 헤드 어텐션의 다음 단계는 $Q_i\cdot K_i^T$를 $\sqrt{d_k}$로 나누는 것이다. 이후 소프트맥스 함수를 적용하면 스코어 행렬을 얻을 수 있다.

\[Z_i=softmax({Q_iK_i^T\over \sqrt{d_k}})V_i\]

어텐션 행렬은 다음 그림처럼 표현할 수 있다.

타깃 문장의 어텐션 행렬 $Z_i$의 경우 각 스코어에 대한 가중치를 반영한 벡터값의 합으로 계산된다. 예를 들어 단어 “Je”, $Z_2$의 셀프 벡터값을 계산한다고 가정하자.

\[Z_2=0.98 \cdot V_1(I)+0.02 \cdot V_2(am)+0.0\cdot V_3(good)\]

이와 유사하게 $h$개의 헤드에 대해 어텐션 행렬을 구한 후 이를 연결하고, 가중치 행렬 $W_0$을 곱하면 최종 어텐션 행렬을 구할 수 있다.

\[multi head \ attention = concatenate(Z_1, Z_2, \cdots, Z_h)W_0\]

1.3.3 피드포워드 네트워크

다음 그림 처럼 디코더의 다음 서브레이어는 feedforward network이다.

디코더의 피드포워드 네트워크는 앞에서 배운 인코더의 피드포워드 네트워크와 동일한 구조다. 이제 add와 norm에 대해 알아보자.

1.3.4 add와 norm 요소

인코더에서 배운 것처럼 add와 norm 구성 요소는 [그림 1-60]처럼 서브레이어의 입력과 출력을 서로 연결한다.

그림 1-60 add와 norm 요소가 있는 디코더 블록

1.3.5 선형과 소프트맥스 레이어

디코더가 타깃 문장에 대한 표현을 학습시키면 [그림 1-61] 처럼 최상위 디코더에서 얻은 출력값을 선형 및 소프트맥스 레이어에 전달한다.

그림 1-61 선형 및 소프트맥스 레이어

선형 레이어의 경우 그 크기가 어휘(vocabulary) (이하 vocab) 크기와 같은 logit 형태이다. vocab이 다음과 같이 3개의 요소로 구성되어 있다고 가정하자.

\[vocabulary=[bien, Je, vais]\]

선형 레이어가 반환하는 로짓은 크기가 3인 벡터 형태가 된다. 소프트맥스 함수를 사용해 로짓값을 확률값으로 변환한 다음, 디코더에서 가장 높은 확률값을 갖는 인덱스의 단어로 출력한다. 다음 예제를 통해 더 자세히 알아보자.

디코더의 입력 단어가 $<sos>$와 $Je$ 라고 할 때 디코더는 입력 단어를 보고 다음 단어를 예측한다. 이를 위해 디코더에서는 최상위 출력값을 가져와서 선형 레이어에 입력한다. 이 선형 레이어에서 vocab 크기와 동일한 크기의 로짓 벡터를 생성한다. 이 로짓값이 다음과 같다고 가정하자.

\[logit=[45,40,49]\]

이 로짓값에 소프트맥스 함수를 적용하고 확률값 $prob$을 얻는다.

\[prob=[0.0179, 0.000, 0.981]\]

앞의 행렬에서 인덱스가 2인 경우 확률값은 $0.981$로 가장 높다. 따라서 vocab엣서 인덱스가 2인 $vais$가 타깃 문장의 다음 단어로 예측된다. 이런 방식으로 디코더는 타깃 문장의 다음 단어를 예측한다.

1.3.6 디코더 모든 구성 요소 연결하기

다음 그림은 디코더가 2개 쌓인 형태다. 간결하게 표현하기 위해 디코더의 첫 번째 부분만 확장해서 표현했다.

그림 1-62 디코더 2개가 쌓인 형태

[그림 1-62]에서 다음 사실을 알 수 있다.

  1. 먼저 디코더에 대한 입력 문장을 임베딩 행렬로 변환한 다음 위치 인코딩 정보를 추가하고 디코더(디코더 1)에 입력한다.
  2. 디코더는 입력을 가져와서 마스크된 멀티 헤드 어텐션 레이어에 보내고, 출력으로 어텐션 행렬 $M$을 반환한다.
  3. 어텐션 행렬 $M$, 인코딩 표현 $R$을 입력받아 멀티 헤드 어텐션 레이어 (인코더-디코더 어텐션 레이어)에 값을 입력하고, 출력으로 새로운 어텐션 행렬을 생성한다.
  4. 인코더-디코더 어텐션 레이어에서 출력한 어텐션 행렬을 다음 서브레이어인 피드포워드 네트워크에 입력한다. 피드포워드 네트워크에서는 이 입력값을 받아서 디코더의 표현으로 값을 출력한다.
  5. 디코더 1의 출력값을 디코더 2의 입력값으로 사용한다.
  6. 디코더2는 디코더 1에서 수행한 프로세스와 동일한 형태를 진행하고, 타깃 문장에 대한 디코더 표현을 반환한다.

디코더의 경우 $N$개의 디코더를 쌓을 수 있다. 이때 최종 디코더 (최상위 디코더)에서 얻은 출력 (디코더 표현)은 타깃 문장의 표현이 된다. 다음으로 타깃 문장의 디코더 표현을 선형 및 소프트맥스 레이어에 입력하고 최종으로 예측된 단어를 얻는다.

This post is licensed under CC BY 4.0 by the author.

트랜스포머-인코더

트랜스포머-인코더 디코더 결합