RoPer はジョルジュ・ハリク (@gharik) の作品で、この実装は彼のオリジナルコードに基づいています。
ロータリー・ポジション・エンベディング(RoPE)では、アテンション・スコアの計算に相対的な位置が含まれます。ただし、埋め込み自体は、因果関係から暗黙的に取得できるものを除いて、位置情報を取得しません
。RoPer は値の埋め込みに相対位置情報を明示的に追加します。具体的には、注目したトークンの相対的な位置を追加します。同じ回転位置埋め込みを使用して注目している値を回転させ、加重合計を取った後、最後の埋め込みを反対方向に回転させます。これは、現在の位置を基準に(注意の前に)各値を回転させるのと同じです。
以下は、算術加算で RoPer を使用してトランスフォーマーモデルをトレーニングするためのトレーニングコードです。RoPe よりも大幅に改善されていることがわかります。
どんな頭でも、ポジションからポジションへと注目し、ポジションに価値を埋め込むようにしましょう。個々の機能を次のように表してみましょう
。通常は、値の埋め込みの加重和を取ります。
これにより、位置に関する距離情報が最終結果に明示的に追加されることはありません。
RoPer は RoPe などの機能を組み合わせて変換します。ペアの場合、次の方法で変換されます。変換された機能を寄付しましょう。次に、加重和をと逆方向に回転させます。注意してください.
次の点に注意してください。
変換後の最終出力は、
その点に注意してください。
第1学期を広げてみよう
同様に、2 番目の項が次の式と等しいことがわかります。
これにより、
つまり、現在の位置を基準にローテーションされた値の加重平均です。
118from typing import Optional
119
120import torch
121
122from labml_nn.transformers.rope import RotaryPositionalEmbeddings, RotaryPEMultiHeadAttention125class 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)query
、key
value
およびは、クエリ、キー、および値のベクトルのコレクションを格納するテンソルです。形があります[seq_len, batch_size, d_model]
。
mask
[seq_len, seq_len, batch_size]
形状があり、バッチの場合b
、mask[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):query
、key
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)