8장 - 어텐션

8.1 - 어텐션의 구조

이번 장에서는 seq2seq를 한층 더 강력하게 하는 어텐션 메커니즘이라는 아이디어를 살펴본다.

NOTE_ 앞 장에서 살펴본 seq2seq 개선은 '작은 개선'이었다. 이와 달리, 어텐션 기술은 seq2seq가 안고 있던 근본적인 문제를 해결하는 '큰 개선'이다.

8.1.1 - seq2seq의 문제점

seq2seq에서는 Encoder가 시계열 데이터를 인코딩한다. 이때 Encoder의 출력은 '고정 길이의 벡터'였다. 그런데 사실, 이 '고정 길이'라는 데에 큰 문제가 있다.

고정 길이 벡터라 함은 입력 문장이 아무리 길어도 항상 같은 길이의 벡터로 변환한다는 뜻이다.

문장이 아무리 길어져도 길이가 고정되어 있기 때문에, 이런 식으로는 필요한 정보가 다 담기지 못하게 된다. 그러므로 Encoder 출력(은닉 상태 벡터)의 길이는 입력 문장의 길이에 따라 바뀌는 게 좋다.

8.1.2 - Encoder 개선

앞에서 살펴본 것처럼 Encoder 출력의 길이는 입력 문장의 길이에 따라 바꿔주는 게 좋다. 그렇게 하기 위해 이제는 LSTM의 각 시각의 은닉 상태 벡터를 모두 이용할 것이다.

LSTM에서는 단어가 시간 방향으로 펼쳐져 입력되기 때문에, 각 시각의 은닉 상태 벡터를 모두 이용하면 문장 길이에 비례한 벡터를 얻을 수 있다. 이것으로 '고정 길이 벡터'라는 제약에서 해방된다.

그런데 여기서 주목할 것은 은닉 상태 벡터의 '내용'이다. 각 시각의 은닉 상태 벡터에는 직전에 입력된 단어에 대한 정보가 많이 포함되어 있을 것이다.

예를 들어, "고양이"라는 단어를 입력했을 때 그 시각의 은닉 상태 벡터, 즉 LSTM 계층의 출력은 "고양이"라는 단어의 영향을 가장 크게 받는다는 의미이다.

그렇게 생각하면 Encoder가 출력하는 hs\bold {hs} 행렬(각 시각의 은닉 상태 벡터들)은 각 단어에 해당하는 은닉 상태 벡터들의 집합이라고 볼 수 있겠다.

WARNING_ Encoder는 왼쪽에서 오른쪽으로 처리하므로, "고양이" 벡터에는 정확히 3개 단어("나", "는", "고양이")의 정보가 담겨 있다. 그런데 균형을 생각하여 "고양이" 단어의 '주변' 정보를 균형 있게 담아야 할 때도 있을 것이다. 이런 경우에 양방향 RNN 혹은 양방향 LSTM이 효과적이다.

이상이 Encoder의 개선이다.

8.1.3 - Decoder 개선 1

이전과는 다르게, Encoder가 각 단어에 대응하는 은닉 상태 벡터를 hs\bold{hs}로 모아 출력한다. 그리고 이 hs\bold{hs}가 Decoder에 전달되어 시계열 변환이 이뤄진다.

여기서 본래 seq2seq는 Encoder의 마지막 출력만 이용했으므로, Encoder를 개선한 지금 다시 돌아보면 다음과 같은 형태이다.

이제 hs\bold{hs} 전부를 활용할 수 있도록 Decoder를 개선해 보자.

사람이 문장을 번역할 때는 아마 대부분이 '나 = I'나 '고양이 = cat'이라는 지식을 이용할 것이다. 즉, '어떤 단어(혹은 단어의 집합)'에 주목하여 그 단어의 변환을 수시로 하게 될 것이다.

이러한 '입력과 출력의 여러 단어 중 어떤 단어끼리 서로 관련돼 있는가'라는 대응 관계를 seq2seq에게 학습시킬 수는 없을까?

NOTE_ 단어(혹은 문구)의 대응 관계를 나타내는 정보를 얼라인먼트alignment^{alignment}라 하는데, 지금까지는 얼라인먼트를 주로 사람이 수작업으로 만들었다. 그러나 어텐션 기술은 얼라인먼트라는 아이디어를 seq2seq에 자동으로 도입하는 데 성공했다.

우리의 목표는 도착어 단어와 대응 관계에 있는 출발어 단어의 정보를 골라내고 그 정보를 이용하여 번역을 수행하는 것이다.

다시 말해, 필요한 정보에만 주목하여 그 정보로부터 시계열 변환을 수행하는 것이 목표이다. 이 구조를 어텐션이라 부른다.

앞으로 구현하고자 하는 신경망의 계층 구성은 다음과 같다.

이 '어떤 계산'은 Encoder로부터의 hs\bold{hs}와 시각별 LSTM 계층의 은닉 상태 벡터를 입력으로 받는다. 그리고 여기에서 필요한 정보만 골라 위쪽의 Affine 계층으로 출력한다.

참고로, 지금까지와 똑같이 Encoder의 마지막 은닉 상태 벡터는 Decoder의 첫 번째 LSTM 계층에 전달한다.

여기서, 위의 신경망으로 하고 싶은 일은 단어들의 얼라인먼트 추출이다. 예를 들면 Decoder가 "I"를 출력할 때 hs\bold{hs}에서 "나"에 대응하는 벡터를 선택하면 된다. 그런데 이 '선택'하는 작업은 미분할 수 없다는 점이 문제가 된다.

  • 학습이 오차역전파법으로 이루어지기 때문에 미분이 불가능한 연산을 사용하면 기본적으로는 오차역전파법을 사용할 수 없다.

그래서 이 '선택한다'라는 작업을 대체할 아주 단순한 아이디어를 소개한다.

바로 각 단어의 중요도를 나타내는 가중치를 이용하는 것이다. a\bold a는 확률분포처럼 각 원소가 0.0~1.0 사이의 스칼라이고, 모든 원소의 총합이 1이다.

이 가중치 a\bold a와 각 단어의 벡터 hs\bold{hs}로부터 가중합weighted sum^{weighted \space sum}을 구한다. 이 일련의 계산을 그려보면 다음과 같다.

여기에서는 가중합의 결과를 맥락 벡터라고 부르고, 기호는 c\bold c로 표기한다. 위 그림에서는 "나"에 대응하는 가중치가 0.8이다. 즉, 맥락 벡터 c\bold c에는 "나" 벡터의 성분이 많이 포함되어 있다는 것이다.

이로써 "나" 벡터를 선택하는 작업을 이 가중합으로 대체하고 있다고 할 수 있다. 만약 "나"에 대응하는 가중치가 1이면 "나" 벡터를 선택한다고 해석할 수 있다.

8.1.4 - Decoder 개선 2

위에서 본 것처럼, 각 단어의 중요도를 나타내는 가중치 a가 있다면, 가중합을 이용해 '맥락 벡터'를 얻을 수 있다. 그런데 정작 이 a는 어떻게 구해야 할까?

각 단어의 가중치 a를 구하는 방법을 살펴보기 전에, 우선 Decoder의 첫 시각 LSTM 계층의 처리부터 알아봐야 한다.

위 그림에서는 Decoder의 LSTM 계층의 은닉 상태 벡터를 h라고 했다.

각 단어의 가중치 a는 은닉 상태 벡터 h가 Encoder의 출력 hs의 각 단어 벡터와 얼마나 '비슷한지'를 수치로 나타낸 벡터이다. 이 벡터의 수치를 구할 방법은 여러 가지가 있지만, 여기에서는 가장 단순한 방법인 벡터의 내적을 이용하고자 한다.

  • 내적의 직관적인 의미는 '두 벡터가 얼마나 같은 방향을 향하고 있는가'이기 때문에 두 벡터의 유사도를 표현하는 척도로 내적을 이용하는 것은 자연스러운 선택이다.

그럼, 내적을 이용해 벡터 사이의 유사도를 산출할 때까지의 처리를 그림으로 살펴보자.

여기에서 shhs의 각 단어 벡터의 내적의 결과물이다. 참고로 s는 정규화하기 전의 값이며, 점수(score)라고도 한다. s를 정규화하기 위해서는 일반적으로 소프트맥스 함수를 적용한다.

s를 정규화하여 a를 구할 수 있다. 소프트맥스 함수를 통한 정규화는 각 원소가 0.0~1.0 사이의 값이 되고, 총합이 1이 된다.

8.1.5 - Decoder 개선 3

이상의 개선에서 각 단어의 가중치 a를 구하고, a를 이용해 가중합을 구해 단어를 '선택'하는 작업을 진행했다. 단어를 '선택'하는 이유는 도착어 단어와 대응 관계에 있는 출발어 단어의 정보를 골라내기 위함이었다.

여기서 a를 구하는 작업을 수행하는 계층을 Attention Weight 계층이라고 하겠다. 그리고 가중합을 진행하는 계층은 Weighted Sum 계층이라고 하자.

이 일련의 계산을 수행하는 계층을 Attention 계층이라고 부른다. 이상이 어텐션 기술의 핵심이다. Encoder가 건네주는 hs를 바탕으로 맥락 벡터(c, 위 그림에서는 out)를 구해 위쪽 계층으로 전파한다.

이제 이 Attention 계층을 Decoder의 LSTM 계층과 Affine 계층 사이에 삽입하면 된다.

  • 뒤에 나오지만, Attention 계층의 위치가 반드시 LSTM 계층과 Affine 계층 사이일 필요는 없다.

위 그림에서 보듯, Attention 계층에는 hs와 LSTM의 은닉 상태 벡터가 입력된다. 또한 특이한 점으로, LSTM의 은닉 상태 벡터가 Affine 계층에도 입력된다.

그림을 보면, 기존의 Decoder에 Attention 계층이 구한 맥락 벡터 정보를 추가한 것으로 생각할 수 있다. Affine 계층에 원래 입력되던 LSTM의 은닉 상태 벡터에 Attention 계층의 맥락 벡터까지 추가로 입력되는 것이다.

WARNING_ 위 그림에서는 Affine 계층에 맥락 벡터(어텐션)와 은닉 상태 벡터(LSTM)라는 2개의 벡터가 입력되었다. 이는 두 벡터를 연결한 벡터를 Affine 계층에 입력한다는 뜻이다.

마지막으로 Decoder에서 시계열 방향으로 펼쳐진 다수의 Attention 계층을 Time Attention 계층으로 모아 구현하자.

길게 펼쳐져 있을 때는 Decoder의 LSTM의 은닉 상태 벡터가 h로 표기되었는데, 하나로 표현하기 위해 hs_dec가 되었다.

8.1.6 - 어텐션을 갖춘 Decoder

최종적인 구현은 다음과 같다.

Time Affine 계층에 Time Attention 계층의 출력과 Time LSTM 계층의 출력이 연결된 벡터가 입력된다는 것만 주의하자.


8.4 - 어텐션에 관한 남은 이야기

8.4.1 - 양방향 RNN

이번 절은 seq2seq의 Encoder에 초점을 맞춘다. 복습해보자면, Encoder는 다음과 같이 그릴 수 있다.

Encoder가 출력하는 hs의 각 행에는 그 행에 대응하는 단어의 성분이 많이 포함되어 있다.

그런데 여기서 주목할 것은, 우리는 글을 왼쪽에서 오른쪽으로 읽는다는 점이다. 따라서 위의 그림에서 "고양이"에 대응하는 벡터에 "나", "는", "고양이"까지 총 세 단어의 정보가 인코딩되어 들어간다.

하지만 여기에서 전체적인 균형을 생각한다면, "고양이" 단어의 주변 정보를 균형 있게 담고 싶을 것이다. 그래서 LSTM을 양방향으로 처리하는 방법을 생각할 수 있다. 이것이 양방향 LSTM(RNN) 기술이다.

위 그림에서 보듯, 양방향 LSTM에서는 지금까지의 LSTM 계층에 더해 역방향으로 처리하는 LSTM 계층도 추가한다. 그리고 각 시각에서는 이 두 LSTM 계층의 은닉 상태를 연결시킨 벡터를 최종 은닉 상태로 처리한다.

  • 연결 외에도 합하거나, 평균을 내는 방법 등도 생각할 수 있다.

이처럼 양방향으로 처리함으로써, 각 단어에 대응하는 은닉 상태 벡터에는 균형 잡힌 정보가 인코딩되게 된다.

양방향 LSTM은 구현하기도 쉽다. 한 가지 방법은, 2개의 LSTM 계층(우리의 경우 Time LSTM 계층)을 사용하여 각각의 계층에 주는 단어의 순서를 조정하면 된다.

LSTM 계층 하나는 지금까지와 똑같고, 다른 하나의 LSTM 계층에는 입력 문장의 단어들을 반대 순서로, 즉 오른쪽에서 왼쪽으로 입력하는 것이다.

마지막으로 이 두 LSTM 계층의 출력을 연결하기만 하면 된다.

8.4.2 - Attention 계층 사용 방법

이어서 Attention 계층의 사용법을 알아보자. 우선은 복습부터.

위 그림에서 보듯, 우리는 Attention 계층을 LSTM 계층과 Affine 계층 사이에 삽입했다. 그러나 Attention 계층을 이용하는 장소가 반드시 이 그림과 같을 필요는 없다. 예를 들면 다음과 같은 사례도 있다.

위 그림에서는 Attention 계층의 출력(맥락 벡터)이 다음 시각의 LSTM 계층에 입력되도록 연결했다. 이렇게 구성하면 LSTM 계층이 맥락 벡터의 정보를 이용할 수 있다. 한편, 우리가 구현한 모델은 Affine 계층이 맥락 벡터를 이용했다.

그렇다면 Attention 계층의 위치를 바꾸는 게 최종 정확도에는 어떤 영향을 줄까? 그 답은 실제 데이터를 사용해 검증할 수밖에 없다. 다만, 앞의 두 모델은 모두 맥락 벡터를 잘 활용하는 구성이라서 큰 차이가 없을지도 모른다.

8.4.3 - seq2seq 심층화와 skip 연결

번역 등 현실에서 풀어야 할 문제들은 훨씬 복잡하다. 그렇다면 어텐션을 갖춘 seq2seq에도 더 높은 표현력이 요구된다. 이때 우선 생각해볼 만한 것은 RNN(LSTM) 계층을 깊게 쌓는 방법이다.

층을 깊게 쌓으면 표현력 높은 모델을 만들 수 있고, 그건 어텐션을 갖춘 seq2seq도 다르지 않다. 그러면 어텐션을 갖춘 seq2seq를 깊게 하면 어떻게 될지 한번 보자.

위 그림은 하나의 예시이다. Encoder와 Decoder로 3층 LSTM 계층을 사용하고 있는 걸 볼 수 있다. 이 예시처럼 Encoder와 Decoder에서는 같은 층수의 LSTM 계층을 이용하는 것이 일반적이다.

한편 Attention 계층의 사용법은 여러 변형이 있을 수 있다. 여기에서는 원래처럼 hs를 받는 것 외에도, Decoder에서 은닉 상태 벡터를 입력받고, 맥락 벡터를 Decoder의 여러 계층(LSTM 계층과 Affine 계층)으로 전파한다.

  • 이렇게 표현력을 높이기 위해 계층을 깊게 할 경우에는 일반화 성능을 떨어뜨리지 않는 게 중요하다. 이런 목적으로는 드롭아웃과 가중치 공유 등이 효과적이다.

층을 깊게 할 때 사용되는 중요한 기법 중 skip 연결skip connection^{skip \space connection}이라는 게 있다.
잔차 연결residual connection^{residual \space connection}, 숏컷shortcut^{short-cut}이라고도 하는데, 개요는 다음과 같다.

위 그림처럼 계층을 건너뛰어 연결하는 기법이다. 이때 skip 연결의 접속부에서는 2개의 출력이 더해진다.

이 덧셈(정확히는 원소별 덧셈)이 핵심이다. 왜냐하면 덧셈은 역전파 시 기울기를 그대로 흘려보내기 때문에 skip 연결의 기울기가 아무런 영향을 받지 않고 모든 계층으로 흐르기 때문이다.

따라서 층이 깊어져도 기울기가 소실(혹은 폭발)되지 않고 전파되어, 결과적으로 좋은 학습을 기대할 수 있다.

NOTE_ RNN 계층의 역전파는 시간 방향에서 기울기 소실 혹은 폭발이 일어날 수 있다. 기울기 소실은 LSTM과 GRU 등의 '게이트가 달린 RNN'으로 대응하고, 기울기 폭발에는 '기울기 클리핑'으로 대응할 수 있다고 앞에서 설명했다. 한편, 깊이 방향 기울기 소실에는 skip 연결이 효과적이다.


8.5 - 어텐션 응용

어텐션을 이용한 최첨단 연구를 살펴보자.

8.5.2 - 트랜스포머

RNN의 단점 중 하나로 병렬 처리를 들 수 있다.

RNN은 이전 시각에 계산한 결과를 이용하여 순서대로 계산한다. 따라서 RNN의 계산을 시간 방향으로 병렬 계산하기란 (기본적으로는) 불가능하다. 이 점은 딥러닝 학습이 GPU를 사용한 병렬 계산 환경에서 이뤄진다는 점을 생각하면 큰 병목이 아닐 수 없다.

이러한 배경에서, 현재는 RNN을 없애거나 병렬 계산이 가능한 RNN을 사용하는 연구가 활발히 이뤄지고 있다. 그중에서 유명한 것이 바로 'Attention is all you need'라는 논문에서 제안한 기법인
트랜스포머Transformer^{Transformer} 모델이다.

논문 제목이 말해주듯 이 모델은 RNN이 아닌 어텐션을 사용해 처리한다. 그럼 이 트랜스포머를 한번 살펴보자.

트랜스포머는 어텐션으로 구성되는데, 그중 셀프어텐션SelfAttention^{Self-Attention}이라는 기술을 이용하는 게 핵심이다. 직역하자면 '자신에 대한 주목'인데, 하나의 시계열 데이터를 대상으로 한 어텐션이다. '하나의 시계열 데이터 내에서' 각 원소가 다른 원소들과 어떻게 관련되는지를 살펴보자는 취지이다.

  • 기존에 살펴본 어텐션은 출발어와 도착어의 대응 관계를 구하는 것이었다. 즉, 2개의 시계열 데이터를 사용했다.

위 그림에서 기존의 어텐션은 서로 다른 두 시계열 데이터를 받는 반면, 셀프어텐션은 두 입력선이 모두 하나의 시계열 데이터로부터 나오는 걸 알 수 있다. 이렇게 하면 하나의 시계열 데이터 내에서의 원소 간 대응 관계가 구해진다.

이어서 트랜스포머의 계층 구성을 살펴보자. 트랜스포머에서는 RNN 대신 어텐션을 사용한다. 위 그림에서 볼 수 있듯, Encoder와 Decoder 모두에서 셀프어텐션을 사용할 수 있다.

또한, 위 그림의 Feed Forward 계층은 피드포워드 신경망(시간 방향으로 독립적으로 처리하는 신경망)을 나타낸다. 정확하게는 은닉층이 1개이고 활성화 함수로 ReLU를 이용한 완전연결계층 신경망을 이용한다.

또한, 그림에서 Nx는 회색 배경으로 둘러싸인 계층들을 N겹 쌓았다는 뜻이다.

NOTE_ 위 그림은 트랜스포머를 단순화해 보여준다. 실제로는 skip 연결과 계층 정규화 등도 이용한다. 다수의 어텐션을 병렬로 이용하거나 시계열 데이터의 위치 정보를 인코딩하는
위치 인코딩Positional Encoding^{Positional \space Encoding} 같은 기법도 볼 수 있다.

트랜스포머를 이용하면 계산량을 줄이고 GPU를 이용한 병렬 계산의 혜택도 더 많이 누릴 수 있다.

profile
초보입니다.

0개의 댓글

Powered by GraphCDN, the GraphQL CDN