귀퉁이 서재

NLP - 14. 어텐션(Attention) 본문

자연어 처리 (NLP)

NLP - 14. 어텐션(Attention)

Baek Kyun Shin 2020. 6. 24. 23:40

이전 장에서는 새로운 문장을 생성하는 seq2seq에 대해 알아봤습니다. 이번 장에서는 seq2seq의 기능을 더 강화시켜 주는 어텐션(Attention)에 대해 알아보겠습니다. 어텐션은 자연어 처리 분야에서 강력하고 중요한 기술 중 하나입니다. seq2seq를 이미 알고 있다는 가정하에 이번 장을 썼습니다. seq2seq를 잘 모르시는 분은 NLP - 13. 시퀀스-투-시퀀스(seq2seq)를 참고해주시기 바랍니다. 이번 장의 모든 글 및 그림은 밑바닥부터 시작하는 딥러닝 2를 참고, 정리하였음을 밝힙니다.

어텐션이라는 메커니즘 덕분에 seq2seq는 인간처럼 필요한 정보에 '주목(Attention)'할 수 있습니다. 어텐션은 기존 seq2seq의 문제점을 해결하여 성능을 향상시킵니다. 우선 기존 seq2seq의 문제점에 대해 알아보겠습니다.

기존 seq2seq의 문제점

이전 장에서 살펴보았듯이 seq2seq에서는 인코더(Encoder)가 입력 데이터를 인코딩합니다. 그리고 인코딩한 정보를 디코더(Decoder)로 전달합니다. 이때 인코더가 디코더로 전달하는 정보는 고정된 길이의 벡터입니다. '고정'된 길이라는 데에 큰 문제가 있습니다. 고정된 길이라는 말은 문장이 아무리 길어도 항상 같은 길이의 벡터로 변환한다는 뜻입니다. 예를 들어 아래의 두 문장은 길이가 서로 다르지만 인코더에서는 모두 같은 길이의 벡터로 변환됩니다.

이것은 사과입니다. --> <Encoder> --> [0, 0, 0, 0]
코스모스는 과거에도 있었고 현재에도 있으며 미래에도 있을 그 모든 것이다. --> <Encoder> --> [0, 0, 0, 0]

일정한 사이즈의 옷장에 단지 몇 벌의 옷만 넣어둔다면 문제없습니다. 그러나 동일한 옷장에 수십 벌의 옷을 욱여넣는다면 문제가 발생합니다. 옷이 옷장에 다 들어가지 않아 옷장 문이 열리거나 옷이 삐져나오게 됩니다.

마찬가지로 인코더가 디코더로 전달하는 벡터가 고정된 길이를 갖는다면 문제가 발생합니다. 필요한 모든 정보를 제한된 길이의 고정된 벡터에 온전히 담지 못하기 때문입니다. 이를 해결할 수 있는 것이 어텐션 메커니즘입니다. 지금부터 어텐션을 활용하여 기존의 seq2seq를 개선해보겠습니다. 우선은 인코더를 개선하고, 이어서 디코더를 개선하겠습니다.

인코더(Encoder) 개선

기존의 seq2seq 모델에서는 인코더 계층 LSTM의 마지막 은닉 상태를 디코더로 전달했습니다. 하지만 디코더로 전달하는 이 마지막 은닉 상태는 고정된 길이를 가진 벡터였습니다. 이 벡터의  길이를 고정시키지 말고 입력 문장의 길이에 맞게 바꿔주는 게 좋겠죠? 이 점이 인코더 개선의 첫번재 포인트입니다. 

방법은 인코더의 마지막 LSTM 계층의 은닉 상태만 이용하는 것이 아니라 아래 그림처럼 모든  LSTM 계층의 은닉 상태를 이용하는 것입니다. 

이렇게 모든 LSTM 계층의 은닉 상태를 이용한다면, 입력 단어가 5개이므로 인코더는 5개의 벡터를 출력합니다. 기존에는 입력 단어가 아무리 많아도 디코더로 전달되는 벡터의 길이는 고정이었는데, 이 방법을 이용하면 입력 단어의 길이만큼 디코더로 전달되는 벡터의 길이도 변합니다. 이로부터 고정된 길이의 벡터라는 제약으로부터 벗어날 수 있습니다. 이는 LSTM 하이퍼 파라미터를 return_sequences=True로 설정하면 됩니다. False면 마지막 은닉 상태만, True면 모든 은닉 상태를 반환합니다. 위 그림에서 hs 행렬은 다른 말로하면 각 단어를 나타내는 벡터들의 집합이라고 볼 수 있습니다.

인코더를 이렇게 개선하니 고정된 길이의 벡터가 아닌 입력 문장의 길이에 비례하는 정보로 인코딩을 할 수 있게 되었습니다. 이제 디코더를 개선해보겠습니다.

디코더 개선 ①

seq2seq 모델은 아래와 같습니다. 입력된 문장을 인코더가 받아 정보를 압축해 은닉 상태 벡터들(hs)로 만들어주고, 이를 디코더에 전달합니다. 디코더는 hs를 받아 최종적으로 새로운 문장을 출력합니다.

기존의 단순한 seq2seq 모델은 인코더의 마지막 은닉 상태 벡터만을 디코더로 넘겼습니다. 즉, 인코더의 LSTM 계층의 마지막 은닉 상태 벡터를 디코더의 LSTM 계층의 첫 은닉 상태 벡터로 사용한 것입니다. 아래 그림은 어텐션 적용 전의 학습 시 디코더 구조입니다. (기존 디코더 구조)

(위 그림에 오타가 있습니다. 디코더의 첫 입력 단어는 <eos>가 아니라 <sos>입니다. eos는 End of String의 약자이며, sos는 Start of String의 약자입니다.)

인코더의 마지막 은닉 상태 벡터만을 디코더가 받아 학습하는 것입니다. 즉, 위 그림에서 hs 행렬의 마지막 행만 전달한 겁니다. 인코더 개선 부분에서 마지막 은닉 상태 벡터만 사용하는 게 아니라 전체 은닉 상태 벡터들을 사용하는 것으로 개선했습니다. 따라서 hs의 모든 벡터를 사용하여 디코더를 개선해보겠습니다.

우리가 문장을 번역할 때 머리 속에서 어떤 일이 일어나나요? I=나, cat=고양이와 같이 어떤 단어에 주목(attention)하여 번역합니다. 이와 같이 단어의 대응 관계를 나타내는 정보를 얼라인먼트(alignment)라고 합니다. 지금까지는 얼라인먼트를 수작업으로 만들었습니다. 하지만 어텐션 메커니즘을 활용하면 얼라인먼트를 자동으로 만들 수 있습니다. 

한국어를 영어로 번역한다고 가정했을때, '특정 영어 단어'와 대응 관계가 있는 '특정 한국어 단어' 정보를 골라내는 것이 어텐션의 핵심 포인트입니다. 다시 말해, 필요한 정보에 주목하여 그 정보로부터 문장 변환하는 것입니다.

아래는 최종적으로 구현한 디코더의 구조입니다. 여기에는 "어떤 계산"이라는 계층이 추가되었습니다. "어떤 계산"이 받는 입력은 두 가지인데, 하나는 인코더로부터 전달받은 hs(은닉 상태)이고 다른 하나는 디코더의 각 스텝 별 LSTM의 출력인 은닉 상태입니다. 그리고 여기서 필요한 정보만 골라 Affine 계층으로 전달합니다. 

다시 말하자면, 기존의 seq2seq 모델 디코더의 첫 번째 LSTM은 인코더의 마지막 은닉 상태를 전달받습니다. 하지만 어텐션 구조에서는 위 그림처럼 디코더가 hs(은닉 상태 벡터 전체)도 추가로 전달받습니다. 이미 설명드린 것처럼 hs는 인코더에 있는 모든 LSTM 계층의 은닉 상태 벡터를 나타냅니다. 이를 통해 인코더로 입력된 문장에 대한 모든 정보를 디코더에 전달한 겁니다.

여기서 우리가 하고 싶은 것은 단어들의 얼라인먼트를 추출하는 것입니다. 디코더의 각 스텝에서 출력하고자 하는 단어와 대응 관계인 단어의 벡터를 hs에서 골라내겠다는 뜻입니다. 예를 들어 디코더가 "I"를 출력할 때, hs에서 "나"에 대응하는 벡터를 선택하면 됩니다. 이런 선택을 "어떤 계산"이 수행하겠다는 뜻입니다. 

하지만 단순히 선택하는 것은 아닙니다. 아래와 같이 가중치를 별도로 구하는 방식으로 수행합니다. 

왜 하나의 행을 선택하지 않고 모든 행에 가중치를 곱해줄까요? 이는 '미분 가능한 연산'을 하기 위함입니다. 신경망 학습은 일반적으로 오차역전파법(Backpropagation)으로 이루어집니다. 오차역전파법을 사용하기 위해서는 미분 가능한 연산이어야 합니다. 하지만 '선택한다'는 연산은 미분 가능하지 않습니다. 따라서 미분 가능하게 하면서 선택하는 행위를 대신해주기 위해 가중치 합을 구하는 것입니다.

이때, 각 단어의 중요도를 나타내는 가중치(기호 a)를 이용합니다. a는 0 ~ 1 사이의 값으로 구성되어 있으며 모든 값의 총합은 1입니다. 아래와 같이 각 단어의 벡터 hs와 가중치 a의 가중합 (weighted sum)을 구합니다. 

가중합이란 대응하는 원소끼리 곱한 뒤 결과를 모두 합하는 계산입니다. 아래는 hsa의 가중합을 구하는 절차를 도식화한 그림입니다.

이렇게 구한 가중합 벡터를 Context Vector(맥락 벡터)라고 하며 기호로는 c로 표현합니다. 위 그림에서 처럼 '나'에 대한 가중치가 0.8입니다. 이 말은 맥락 벡터 c에는 '나'에 대한 정보를 나타내는 벡터 성분이 많이 포함되어 있다는 뜻입니다. 우리는 미분 가능한 연산을 위해 '나' 벡터를 선택하는 대신, '나' 벡터 성분이 많이 포함된 맥락 벡터를 구했습니다. 미분 가능한 연산인 가중합을 이용해서 말이죠. 

디코더 개선 ②

각 단어의 중요도를 나타내는 가중치 a가 있으면 가중합을 이용해 Context Vector를 구할 수 있습니다. 그런데 이 가중치 a는 어떻게 구할까요? 이제부터 가중치 a를 구하는 방법에 대해 알아보겠습니다. 먼저 아래 그림을 보겠습니다.

위 그림에서 디코더의 LSTM 계층의 은닉 상태 벡터를 h로 표기했습니다. 여기서 우리가 알고자 하는 것은 hhs의 각 단어 벡터와 얼마나 비슷한지를 파악하는 것입니다. 이 말은 디코더의 입력 단어(ex. <sos>, I, am, a, cat 중 하나)와 대응되는 인코더의 입력 단어(ex. 나, 는, 고양이, 로소, 이다)가 무엇인지 파악하는 것입니다.

이를 파악하기 위한 여러가지 방법이 있지만 가장 간단한 방법인 '내적'을 이용해보겠습니다. 벡터의 내적을 통해 두 벡터가 얼마나 유사한지 판단할 수 있습니다. 직관적으로 설명하면 내적을 통해 두 벡터가 얼마나 같은 방향을 향하고 있는지를 판단할 수 있습니다.

아래는 hsh를 내적하여 유사도를 산출한 그림입니다.

벡터의 내적을 통해 유사도를 구했습니다. s를 보면 첫 번째 원소의 유사도가 0.9로 가장 높습니다. 이는 '나' 벡터와 h의 내적 값(유사도)이 0.9로 가장 높다는 뜻입니다. 따라서 h는 '나' 벡터와 가장 유사합니다. 하지만 s는 정규화하기 전의 값입니다. 소프트맥스(Softmax)를 적용하여 s를 정규화해보겠습니다. (참고로 이 s를 점수(score)라고 합니다.)

소프트맥스 함수를 적용하면 sa와 같이 정규화가 됩니다. 소프트맥스를 적용하여 정규화를 했기 때문에 a의 모든 값의 합은 1입니다. 이로써 가중치 a를 구했습니다.

디코더 개선 ③

디코더 개선 ①과 디코더 개선 ②에서 했던 것을 합쳐서 도식화해보면 아래와 같습니다. 

Weight Sum은 디코더 개선 ①에서, Attention Weight는 디코더 개선 ②에서 구했습니다. hs는 인코더가 출력하는 각 단어에 대한 벡터이고, h는 디코더의 LSTM 계층이 출력한 은닉 상태입니다. a는 가중치 벡터이며, c는 맥락 벡터(Context Vector)입니다. 간단히 다시 설명하자면 Attention Weight 계층은 hsh를 내적 하여 가중치 a를 구합니다. Weight Sum 계층은 ahs의 가중합을 구하고, 그 결과를 맥락 벡터 c로 출력합니다. 이러한 일련의 계산을 수행하는 계층을 어텐션(Attention) 계층이라 부릅니다.

이상으로 어텐션 메커니즘에 대해 알아봤습니다. 인코더가 출력한 hs에서 중요한 원소에 주목하여, 그것을 기반으로 맥락 벡터를 구해 위쪽 계층으로 전달합니다. 

정리

처음에 봤던 어텐션의 전체 구조를 다시 살펴보겠습니다.

여기서 "어떤 계산" 부분 바로 Attention 계층입니다. "어떤 계산"을 "Attention"으로만 바꾸어 주면 됩니다. 기존의 seq2seq 디코더 구조에서 어텐션만 추가해준 것입니다.

아래는 디코더의 첫 스텝만 따로 떼어 어텐션 전, 후의 구조를 비교한 그림입니다.

기존 seq2seq 모델에서의 Affine 계층은 LSTM의 은닉 상태 벡터만 입력으로 받았는데 어텐션이 추가되니 LSTM의 은닉 상태 뿐만 아니라 어텐션 계층이 출력한 맥락 벡터도 같이 입력받습니다. 이 맥락 벡터에는 디코더의 LSTM으로 입력된 단어(본 예에서는, 현재 번역하려는 영어 단어)와 가장 유사한 (인코더로 입력된) 단어(본 예에서는, 한국어 단어)에 대한 정보가 담겨 있습니다. 따라서 사람이 문장을 번역할 때 "I=나, cat=고양이"라고 특정 단어에 주목하듯이 어텐션 메커니즘도 특정 단어에 주목하여 번역할 수 있습니다. "I=나, cat=고양이"라는 정보가 없을 때보다 있을 때 번역 품질이 더 좋아지기 때문에 어텐션 메커니즘을 활용하면 seq2seq의 성능이 더 향상됩니다.

지금까지 어텐션 메커니즘에 대해 알아봤습니다. 어텐션은 위에서 예로 든 기계 번역 뿐만 아니라 seq2seq가 쓰이는 모든 분야에서 큰 효과를 발휘합니다. 이번 장에서는 설명을 쉽게 하기 위해 '번역'을 예로 들었지만 실제 어텐션은 seq2seq 모델의 모든 분야에 쓰입니다.

References

Reference1: 밑바닥부터 시작하는 딥러닝 2

Reference2: 딥러닝을 이용한 자연어 처리 (어텐션 메커니즘)

Comments