RoPer はジョルジュ・ハリク (@gharik) の作品で、この実装は彼のオリジナルコードに基づいています。

相対距離付き回転式位置埋め込み(RoPer)

ロータリー・ポジション・エンベディング(RoPE)では、アテンション・スコアの計算に相対的な位置が含まれます。ただし、埋め込み自体は、因果関係から暗黙的に取得できるものを除いて、位置情報を取得しません

RoPer は値の埋め込みに相対位置情報を明示的に追加します。具体的には、注目したトークンの相対的な位置を追加します。同じ回転位置埋め込みを使用して注目している値を回転させ、加重合計を取った後、最後の埋め込みを反対方向に回転させます。これは、現在の位置を基準に(注意の前に)各値を回転させるのと同じです。

以下は、算術加算で RoPer を使用してトランスフォーマーモデルをトレーニングするためのトレーニングコードです。RoPe よりも大幅に改善されていることがわかります。

埋め込みの相対距離

どんな頭でも、ポジションからポジションへと注目しポジションに価値を埋め込むようにしましょう個々の機能を次のように表してみましょう

通常は、値の埋め込みの加重和を取ります。

これにより、位置に関する距離情報が最終結果に明示的に追加されることはありません。

RoPer は RoPe などの機能を組み合わせて変換します。ペアの場合、次の方法で変換されます。変換された機能を寄付しましょう。次に、加重和をと逆方向に回転させます。注意してください.

次の点に注意してください。

変換後の最終出力は、

その点に注意してください

第1学期を広げてみよう

同様に、2 番目の項が次の式と等しいことがわかります。

これにより、

つまり、現在の位置を基準にローテーションされた値の加重平均です。

これは、算術加算タスクにRoPerを使用する実験です

118from typing import Optional
119
120import torch
121
122from labml_nn.transformers.rope import RotaryPositionalEmbeddings, RotaryPEMultiHeadAttention

反対方向に回転するRoPEモジュール

これはRoPE回転の実装を継承し、方向を変えます。

125class ReverseRotaryPositionalEmbeddings(RotaryPositionalEmbeddings):
  • x キーまたは形状のあるクエリの先頭にあるテンソルです [seq_len, batch_size, n_heads, d]
132    def forward(self, x: torch.Tensor):

キャッシュと値

137        self._build_cache(x)

機能を分割して、一部の機能セットにのみロータリー埋め込みを適用することもできます。

140        x_rope, x_pass = x[..., :self.d], x[..., self.d:]

計算

144        neg_half_x = self._neg_half(x_rope)

計算

にとって

160        x_rope = (x_rope * self.cos_cached[:x.shape[0]]) - (neg_half_x * self.sin_cached[:x.shape[0]])

163        return torch.cat((x_rope, x_pass), dim=-1)

回転式位置埋め込みによるマルチヘッドアテンション

オリジナルのトランスフォーマーのマルチヘッドアテンションを無効にします

166class RotaryValuePEMultiHeadAttention(RotaryPEMultiHeadAttention):
173    def __init__(self, heads: int, d_model: int,
174                 rope_percentage: float = 0.5, rope_value_percentage: float = 0.5,
175                 dropout_prob: float = 0.0):
176        super().__init__(heads, d_model, rope_percentage, dropout_prob)

ロータリーポジショナル埋め込みレイヤー

179        d_rope_value = int(self.d_k * rope_value_percentage)
180
181        self.value_rotary_pe = RotaryPositionalEmbeddings(d_rope_value)
182        self.value_reverse_rotary_pe = ReverseRotaryPositionalEmbeddings(d_rope_value)

querykey value およびは、クエリキーおよび値のベクトルのコレクションを格納するテンソルです。形があります[seq_len, batch_size, d_model]

mask [seq_len, seq_len, batch_size] 形状があり、バッチの場合bmask[i, j, b] i その位置のクエリがその位置のキー値にアクセスできるかどうかを示します。j

184    def forward(self, *,
185                query: torch.Tensor,
186                key: torch.Tensor,
187                value: torch.Tensor,
188                mask: Optional[torch.Tensor] = None):

querykey value そして形がある [seq_len, batch_size, d_model]

200        seq_len, batch_size, _ = query.shape
201
202        if mask is not None:
203            mask = self.prepare_mask(mask, query.shape, key.shape)

query key value 注意力計算の準備をして[seq_len, batch_size, heads, d_k] これで形ができあがります。

207        query = self.query(query)
208        key = self.key(key)
209        value = self.value(value)

アテンションスコアを計算します。[seq_len, seq_len, batch_size, heads] これにより形状のテンソルが得られます

213        scores = self.get_scores(query, key)

スケールスコア

216        scores *= self.scale

マスクを適用

219        if mask is not None:
220            scores = scores.masked_fill(mask == 0, float('-inf'))

キーシーケンス次元に沿って注目

224        attn = self.softmax(scores)

ドロップアウトを適用

227        attn = self.dropout(attn)

位置情報を含むように、加重合計を取る前に値の埋め込みをローテーションしてください

230        value = self.value_rotary_pe(value)

値による乗算

234        x = torch.einsum("ijbh,jbhd->ibhd", attn, self.value_rotary_pe(value))

各埋め込み部品が相対的な位置を保持するように、反対方向に回転させます

237        x = self.value_reverse_rotary_pe(x)

他の計算に注意を向けておく

240        self.attn = attn.detach()

複数のヘッドを連結

243        x = x.reshape(seq_len, batch_size, -1)

出力レイヤー

246        return self.output(x)