RoFormer: 회전식 위치 임베딩 기능을 갖춘 향상된 변압기

August 02, 2024에서 업데이트 6 분을 읽습니다

RoFormer: 회전식 위치 임베딩 기능을 갖춘 향상된 변압기

Transformer 기반 모델은 복잡한 텍스트를 구문 분석하고 해석하는 능력으로 유명합니다. 이는 단어의 순서와 맥락을 이해하는 데 의존합니다. 이는 전통적인 위치 인코딩 방법이 한계를 보여준 작업입니다. 이러한 격차를 해결하기 위해 **RoPE(Rotary Position Embedding)**를 기반으로 하는 ROFORMER 모델은 위치 인코딩에 대한 접근 방식을 재정의합니다.

전통적인 위치 인코딩

변환기는 텍스트를 일련의 토큰으로 처리하고 효율성을 높이기 위해 시퀀스의 병렬 처리를 허용합니다. 그러나 이러한 강점은 모델의 토큰 주문에 대한 불가지론이라는 문제를 가져왔습니다. 위치 인코딩이 답이었습니다. 각 토큰에 시퀀스 위치를 나타내는 고유한 서명을 제공했습니다.

절대 위치 임베딩

처음에 BERT와 같은 모델은 절대 위치 임베딩을 사용하여 시퀀스의 각 위치에 고정 벡터를 할당했습니다. 이 방법은 간단하지만 본질적으로 시퀀스 길이 변화에 적응하거나 많은 언어 구조를 이해하는 데 중요한 토큰 간의 상대적 거리를 강조하는 기능이 부족합니다.

상대 위치 임베딩

언어의 동적 특성을 포착하기 위해 절대 위치보다는 토큰 간의 거리에 초점을 맞춘 상대 위치 임베딩이 도입되었습니다. 개념적 이점에도 불구하고 이러한 임베딩은 계산상의 복잡성을 가져왔고 Transformers의 self-attention 메커니즘에 원활하게 통합되지 않아 효율성이 제한되었습니다.

ROFORMER 및 로터리 포지션 임베딩

ROFORMER는 기존 위치 인코딩 전략의 한계를 인식하여 각각의 단점 없이 절대 위치 정보와 상대 위치 정보의 이점을 결합한 접근 방식인 RoPE(Rotary Position Embedding)를 도입했습니다.

로터리 포지션 임베딩

RoPE는 회전 행렬을 사용하여 위치 정보를 인코딩하므로 모델이 토큰의 위치뿐 아니라 토큰이 순서대로 다른 모든 토큰과 어떻게 관련되는지 이해할 수 있습니다.

Reformer.pngCredit: ArXiv

이는 기하학적 렌즈를 통해 작동하며 토큰 위치를 순차적 관계를 표시하기 위해 회전되는 다차원 공간의 점으로 처리합니다. 이러한 회전을 통해 모델은 self-attention 메커니즘 내에서 절대 및 상대 위치 단서를 모두 보존하고 활용할 수 있습니다.

RoPE 구현

RoPE 구현에는 각 토큰의 위치를 ​​회전 행렬로 인코딩하고 Transformer의 self-attention 메커니즘 내에서 이 행렬을 적용하는 작업이 포함됩니다. 이 프로세스를 통해 위치 정보를 유연하고 동적으로 해석할 수 있으며, 상당한 계산 오버헤드 없이 다양한 시퀀스 길이를 수용하고 토큰 상호 관계의 본질을 포착할 수 있습니다.

먼저 회전식 임베딩을 생성하는 함수가 필요한 다음 이러한 임베딩을 모델에 통합합니다. 아래 예에서는 사용자가 Keras에서 사용자 정의 레이어를 만드는 데 익숙하다고 가정합니다.

1단계: 로터리 임베딩 기능 정의

이 함수는 최대 시퀀스 길이와 임베딩의 차원을 고려하여 회전 임베딩을 생성합니다.

from tensorflow.keras.layers import Layer
import numpy as np

def get_rotary_embedding(dim, max_seq_len):
    inv_freq = 1.0 / (10000 ** (tf.range(0, dim, 2, dtype=tf.float32) / dim))
    t = tf.range(max_seq_len, dtype=tf.float32)
    freqs = tf.einsum('i,j->ij', t, inv_freq)
    emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
    return emb
inv_freq = 1.0 / (10000 ** (tf.range(0, dim, 2, dtype=tf.float32) / dim))

이 라인은 위치 인덱스를 기반으로 지수적으로 스케일링된 주파수의 역수를 계산합니다. 이러한 주파수는 회전식 임베딩을 위한 정현파 패턴을 생성하는 데 사용되며, 이는 시퀀스의 상대적 위치 정보를 인코딩하는 데 도움이 됩니다. 이 메커니즘은 자연어 처리나 시계열 분석과 같이 요소의 순서와 상대적 위치를 이해하는 것이 중요한 작업에 특히 유용합니다.

세부사항:

  • tf.range(0, 희미한, 2, dtype=tf.float32)는 0에서 시작하여 dim(제외)까지 2씩 증가하는 값 범위를 생성합니다. dtype=tf.float32 인수는 다음을 지정합니다. 이 텐서의 요소는 32비트 부동 소수점 숫자입니다. 예를 들어 dim이 8이면 [0, 2, 4, 6]이 생성됩니다.

  • tf.range에 의해 생성된 텐서는 임베딩의 차원(dim)으로 나뉩니다. 이 작업은 이러한 인덱스를 0과 1 사이의 범위로 축소합니다(‘dim’이 짝수이면 제외되고, ‘dim’이 홀수이면 약간 포함됩니다. 범위 단계가 다른 모든 값을 건너뛰기 때문입니다). dim = 8인 예를 계속해서 8로 나누면 [0.0, 0.25, 0.5, 0.75]가 생성됩니다.

  • ‘10000 ** (…)’ 연산은 이전에 스케일링된 텐서의 각 요소에 대해 10,000을 거듭제곱합니다. 10,000이라는 기준은 다소 임의적이지만 주파수가 넓은 범위에 걸쳐 변하도록 선택되어 모델이 다양한 위치를 보다 효과적으로 구별하는 데 도움이 됩니다. [0.0, 0.25, 0.5, 0.75]의 경우 각각에 거듭제곱 연산을 적용하여 더 높은 요소에 대한 값이 훨씬 더 커집니다.

  • 마지막으로 이전 단계의 값의 역수(1/x)를 취하여 역주파수를 구합니다. 역주파수는 인덱스가 높을수록 더 작습니다. 즉, 시퀀스에 있는 요소의 주파수가 더 작아지고 해당 위치가 모델에 인코딩되는 방식에 영향을 미칩니다. 이를 통해 모델의 주의 메커니즘을 통해 상대 위치를 추론할 수 있는 방식으로 임베딩을 확장할 수 있습니다.

라인:

freqs = tf.einsum('i,j->ij', t, inv_freq)

Einstein 합계 표기법을 사용하여 텐서 연산을 간결하고 효율적으로 표현할 수 있는 도구인 TensorFlow의 tf.einsum 함수를 사용합니다.

이 연산은 tinv_freq 벡터의 외부 곱을 효과적으로 계산하여 각 요소 (i, j)ti 번째 요소와 의 곱인 행렬을 생성합니다. inv_freq의 j번째 요소입니다. 이 행렬(freqs`)은 회전식 임베딩에 대한 정현파 패턴을 생성하는 데 사용되는 주파수를 나타냅니다.

2단계: 회전식 임베딩을 위한 사용자 정의 Keras 레이어

이제 입력 텐서에 회전식 임베딩을 적용하는 사용자 정의 Keras 레이어를 만들어 보겠습니다. 이 계층에서는 입력 텐서의 모양이 ‘(batch_size, 시퀀스_길이, embedding_dim)‘이라고 가정합니다.

class RotaryEmbeddingLayer(Layer):
    def __init__(self, dim, max_seq_len, **kwargs):
        super().__init__(**kwargs)
        self.dim = dim
        self.max_seq_len = max_seq_len
        self.rotary_embeddings = get_rotary_embedding(dim, max_seq_len)

    def call(self, inputs):
        seq_len = tf.shape(inputs)[1]
        embeddings = self.rotary_embeddings[:seq_len]
        
        cos_emb = embeddings[:, None, :self.dim // 2]
        sin_emb = embeddings[:, None, self.dim // 2:]
        
        # Decompose inputs into sine and cosine components
        inputs_cos = inputs[..., :self.dim // 2]
        inputs_sin = inputs[..., self.dim // 2:]
        
        # Apply rotary embeddings
        rotated_cos = inputs_cos * cos_emb - inputs_sin * sin_emb
        rotated_sin = inputs_sin * cos_emb + inputs_cos * sin_emb
        
        return tf.concat([rotated_cos, rotated_sin], axis=-1)

    def get_config(self):
        config = super().get_config()
        config.update({
            "dim": self.dim,
            "max_seq_len": self.max_seq_len
        })
        return config

embeddings = self.rotary_embeddings[:seq_len] 줄은 현재 입력 시퀀스 길이를 기반으로 사전 계산된 회전식 임베딩의 적절한 하위 집합을 선택합니다. 시퀀스의 길이는 배치마다 다를 수 있으므로 이 슬라이싱 작업을 통해 실제 시퀀스 길이에 해당하는 임베딩만 사용됩니다.

이제 ‘embeddings’ 변수는 ‘(seq_len, embedding_dim)’ 모양의 텐서를 보유합니다. 여기서 ‘seq_len’은 현재 배치의 시퀀스 길이이고 ‘embedding_dim’은 임베딩의 차원입니다. 이 텐서는 seq_len까지 시퀀스의 각 위치에 대한 회전식 위치 임베딩을 포함합니다.

emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)은 위치 주파수의 사인 및 코사인 변환을 단일 텐서로 결합합니다.

-tf.cos(freqs)tf.sin(freqs)는 각각 코사인 및 사인 변환을 freqs 텐서에 적용합니다. ‘freqs’ 텐서는 시퀀스 위치와 임베딩 차원의 역주파수를 기반으로 계산된 입력 시퀀스의 각 위치와 임베딩 공간의 각 차원에 대한 빈도 값을 포함합니다. 사인 및 코사인 함수는 요소별로 적용되어 ‘freqs’와 동일한 모양의 텐서 두 개가 생성됩니다. 이러한 변환은 위치 관계의 순환적 특성을 포착하는 방식으로 위치를 인코딩하는 데 도움이 되며, 상대적 위치를 이해하는 모델의 능력을 촉진합니다.

-tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)은 마지막 축(axis=-1로 표시)을 따라 코사인 및 사인 변환된 텐서를 연결합니다. 이러한 텐서를 나란히 연결하면 ‘freqs’ 텐서의 차원이 효과적으로 두 배가 됩니다. 전반부는 코사인 변환된 값을 나타내고 후반부는 각 위치에 대한 사인 변환된 값을 나타냅니다. 연결을 통해 각 위치 인코딩에 사인 및 코사인 정보가 모두 포함되어 위치 신호의 진폭과 위상 모두에 대한 정보를 보존할 수 있습니다.

  • 연결된 텐서 emb는 이제 입력 위치에 대한 완전한 회전 임베딩을 보유합니다. ‘emb’의 모양은 처음 두 차원(시퀀스 위치 및 임베딩 차원에 해당)에서 ‘freqs’와 동일하지만 마지막 차원은 사인 및 코사인 값을 모두 고려하여 두 배 더 커집니다. 이러한 임베딩은 회전 등변적 방식으로 위치 정보를 추가하여 입력 임베딩을 변조하는 데 사용됩니다.

-cos_emb = 임베딩[:, None, :self.dim // 2]:

  1. 첫 번째 콜론 :은 “이 차원의 모든 요소 선택”을 의미하며, 이 경우 시퀀스의 모든 위치를 나타냅니다.

  2. ‘None’은 차원을 추가하여 텐서를 3차원으로 만드는 데 사용됩니다. 이는 특정 차원 수의 입력이 필요한 특정 작업과의 호환성을 보장하기 위해 수행되는 경우가 많습니다. 예를 들어, 다른 3차원 텐서와 요소별 곱셈을 수행할 때 모양은 브로드캐스팅 규칙에 따라 정렬되어야 합니다.

  3. :self.dim // 2, 마지막 축에 있는 차원의 전반부를 선택합니다. ‘embedding_dimension’은 사인 값과 코사인 값을 모두 포함하도록 두 배가 되므로 2로 나누면 임베딩의 코사인 구성 요소만 효과적으로 선택됩니다.

3단계: Keras 모델과 통합

RotaryEmbeddingLayer를 정의한 후 이를 Keras 모델에 통합할 수 있습니다. 이 레이어는 어텐션 레이어나 후속 모델 레이어에 적용하기 전에 임베딩에 적용되어야 합니다.

다음은 회전식 임베딩을 모델에 통합하는 방법에 대한 간단한 예입니다.

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Embedding, Dense

max_seq_len = 512
embedding_dim = 64

inp = Input(shape=(max_seq_len,))
x = Embedding(input_dim=10000, output_dim=embedding_dim)(inp)
x = RotaryEmbeddingLayer(dim=embedding_dim, max_seq_len=max_seq_len)(x)
# Add your model's layers here, e.g., Transformer blocks
x = Dense(1, activation='sigmoid')(x)

model = Model(inputs=inp, outputs=x)
model.summary()