ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • #The Positional Encoding 를 어떻게 하는 것인가? #Transformer_모델
    Data miner/Information Retrieval 2020. 3. 14. 21:44
    728x90

      Self-attention이 있는 Transformer의 후속 모델들은 positional encoding도 transformer의 방식을 따른다. 본 포스팅은 Positional Encoding부분을 자세하게 다루고자 한다. "Attention is all you need"라는 논문에서 cos, sin함수를 활용하여 토큰의 위치정보를 보완한다고 하는데, 이게 어떻게 이뤄지는 건지 궁금했었다. 

       논문에서는 cos, sin함수를 활용했다고만 언급되어 있다. 이 부분을 읽으면서 들었던 생각은, 단순히 문장에 속한 단어 토큰에 1,2,3, ..., 등의 정수를 붙여주면 안되나? 2π라는 주기와 최대 최소값을 가지고 있는 cos, sin함수를 굳이 활용하는 이유가 있을까라는 의문점이 들었다. 먼저 위치정보를 정수로 단순히 표현하면 다음과 같은 문제점을 가진다고 한다. 1) 1,2,3, ... 정수값으로 위치정보를 표현하는 것은 이 값들이 모델에 끼치는 영향이 지나치게 커질 수 있다. 2) 모델의 강건성의 관점에서 별로 좋지 못한 접근법이기도 하다. 학습시킬 때 일정 길이의 토큰 정보만 활용할 경우, 일정 길이를 넘는 테스트셋에서는 취약할 결과가 나오게 된다. 

    코사인, 사인 함수는 위치정보를 표현하는데 있어서 다음의 조건을 충족시킨다.

    1) 각각의 고유한 토큰 위치값은 유일한 값을 가져야 한다.

    2) 서로 다른 두 토큰이 떨어져 있는 거리가 일정해야 한다. (첫번째 토큰과 두번째 토큰의 사이의 차는, 두번째 토큰과 세번째 토큰의 차이와 같아야 한다)

    3) 일반적으로 긴 길이의 문장들도 무난하게 표현해야 한다. 다만, 모델이 표현하고자 하는 문장의 최대값보다 작은 길이의 문장들이다. 

    4) 함수에 따른 토큰 위치의 값을 예측할 수 있어야 한다. 

     

    이러한 특징 때문에, Transformer 모형을 제시한 저자는 sin과, cos 함수를 사용하며. 특히, 주기가 10000의 2i/d(모델의 차원)승 곱하기 2π인 함수를 사용한다. 전체 차원의 수가 d라면, 각각의 0부터 d차원까지의 값은 짝수일 때는 sin함수의 값을, 홀수 일때는 cos함수값을 따른다. pos값은 각각의 토큰의 위치정보값이며 정수값을 의미한다. 

     

        하나의 sin, cos함수를 사용하는 것이 아니라 두 개의 함수를 차원의 홀수, 짝수에 따라 사용하는 이유는 다음과 같다. 위치가 커질 때마다 그 값이 다시 작아져(주기함수의 특징), 어떤 특정 두 토큰의 위치값이 동일해질 수 있는 것을 방지하기 위함이다. 하지만, cos, sin을 함께 사용한다면 값들을 일정하게 증가/감소하게 표현할 수 있다. 즉, sin(x)와 cos(x)을 동시에 사용해야만, sin(x+k)와 cos(x+k)를 선형 변환으로 표현가능하다.

    아래의 그림을 보면, 중간을 기준으로 색상의 값 분포가 단절되게 표현되는 것을 알 수 있다. 이는 positional embedding시에 값이 sin와 cos함수로 값이 표현되어 합쳐지기(concatenate) 때문이다. 다만 attention is all you need에서 위의 수식의 i가 embedding의 차원의 수라고 밝힌 만큼, sin함수와 cos함수를 사용할지 여부는 차원이 홀수이거나 짝수일때 달라지는지는데, 이를 아래 그림에서 가로축 표현을 잘못하지 않았나 싶다. 

    세로축은 embedding 토큰의 개수, 가로축은 embedding 하는 차원의 개수를 나타낸다. 색깔(값)

     

     

    이와 관련한 코드 구현 부분은 다음과 같다.

    def pos_encoding(dim, maxlen = 1000): # positional encoding
        pe = Tensor(maxlen, dim)
        pos = torch.arange(0, maxlen, 1.).unsqueeze(1)
        k = torch.exp(-np.log(10000) * torch.arange(0, dim, 2.) / dim)
        pe[:, 0::2] = torch.sin(pos * k)
        pe[:, 1::2] = torch.cos(pos * k)
        return pe

       한편, 이러한 과정을 통해서 positional encoding을 통해서 각각의 토큰정보들은 encoding embedding vector와 동일한 차원수를 가지며, 각각의 차원 하나의 들어가는 값은 -1에서 1사이의 값을 가지게 된다. 

     

    * 참고 블로그

    https://kazemnejad.com/blog/transformer_architecture_positional_encoding/

Designed by Tistory.