Transformers documentation
アテンションのバックエンド
アテンションのバックエンド
すべてのアテンション実装は同じ計算を実行します。各トークンは他のすべてのトークンと比較されます。違いは、計算がどのように実行されるかという点です。基本的なアテンションは、メモリ上に完全なアテンション行列をマテリアライズするため、スケーリングが不良であり、推論を遅くするボトルネックを生み出します。最適化された実装は、計算を再構成してメモリトラフィックを削減し、より高速で低コストな推論を実現します。
AttentionInterface は、最適化されたアテンションの実装を提供します。アテンションの実装とモデルの実装を分離することで、異なる関数を用いた実験を簡素化します。この一貫したインターフェースにより、新しいバックエンドを簡単に追加できます。
| アテンションバックエンド | 説明 |
|---|---|
"flash_attention_3" | FlashAttention-2を改善し、操作のオーバーラップに加え、フォワードパスとバックワードパスをより密接に融合します |
"flash_attention_2" | 計算をより小さなブロックに分割し、高速なオンチップメモリを使用します |
"flex_attention" | 低レベルのカーネルを手作業で書くことなく、カスタムのアテンションパターン(スパース、ブロックローカル、スライディングウィンドウ)を指定するためのフレームワーク |
"sdpa" | PyTorchの組み込み実装であるスケーリングドットプロダクトアテンション |
"paged|flash_attention_3" | FlashAttention-3のページド版 |
"paged|flash_attention_2" | FlashAttention-2のページド版 |
"paged|sdpa" | SDPAのページング版 |
"paged|eager" | Eagerのページング版 |
アテンションバックエンドを設定する
attn_implementation 引数を from_pretrained() に渡すことで、特定の注意機構関数を用いたモデルをインスタンス化できます。
import torch
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.2-1B", attn_implementation="flash_attention_2"
)モデルを再読み込みせずに、set_attn_implementation() を使用してランタイムでアテンションのバックエンドを切り替えます。
model.set_attn_implementation("sdpa")カーネル
Kernels ライブラリを使用すると、実行時にコンパイル済みの計算カーネルを Hub から直接ダウンロードして読み込むことができます。これにより、PyTorch や CUDA のバージョン不一致によるパッケージングの問題を回避できます。
Kernels は検出時に自動的に AttentionInterface に登録されます。FlashAttention パッケージを明示的にインストールする必要はありません。FlashAttention を名前でリクエストした場合も Hub kernel にフォールバックします。FlashAttention fallback を参照してください。
import torch
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.2-1B", attn_implementation="kernels-community/flash-attn2"
)SDPAコンテキストマネージャー
PyTorchのスケーリングドットプロダクトアテンション(SDPA)は、CUDAバックエンドに対して最速のアテンション関数を自動的に選択します。その他のバックエンドでは、デフォルトでPyTorchのC++実装を使用します。
torch.nn.attention.sdpa_kernel コンテキストマネージャーを使用して、SDPAに特定の実装を使用させることができます。
import torch
from torch.nn.attention import SDPBackend, sdpa_kernel
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.2-1B", attn_implementation="sdpa"
)
with sdpa_kernel(SDPBackend.FLASH_ATTENTION):
outputs = model.generate(**inputs)バックボーン固有の注意
マルチモーダルモデルでは、各モダリティごとに異なるバックボーンを使用します。各バックボーンに特定の注意機構を割り当てることで、パフォーマンスを最適化できます。例えば、一部のビジョンバックボーンはfp32でより良いパフォーマンスを発揮しますが、FlashAttentionはこれをサポートしていません。
dict を使用してビジョンのバックボーンを異なるアテンション関数にマッピングし、テキストのバックボーンは引き続き FlashAttention を使用します。アテンション実装のキーは、サブ設定の名前と一致する必要があります。
from transformers import AutoModelForImageTextToText
attention_implementation_per_backbone = {"vision_config": "sdpa", "text_config": "flash_attention_2"}
for key in attention_implementation_per_backbone:
assert key in model.config.sub_configs, f"Invalid key in `attention_implementation`"
model = AutoModelForImageTextToText.from_pretrained(
"facebook/chameleon-7b", attn_implementation=attention_implementation_per_backbone
)dictから特定のbackboneを省略すると、デフォルトのattention関数(SDPA)が使用されます。
model = AutoModelForImageTextToText.from_pretrained(
"facebook/chameleon-7b", attn_implementation={"text_config": "flash_attention_2"}
)単一の文字列で、すべてのバックボーンに同じアテンション関数を設定します。
model = AutoModelForImageTextToText.from_pretrained(
"facebook/chameleon-7b", attn_implementation="eager"
)空のキーでアテンション関数をグローバルに設定します。
model = AutoModelForImageTextToText.from_pretrained(
"facebook/chameleon-7b", attn_implementation={"": "eager"}
)新しいアテンション関数を作成する
AttentionInterface.register() を使用してアテンションレジストリに追加することで、アテンション関数をカスタマイズしたり、新しいアテンション関数を作成したりできます。モデルは attn_implementation 引数を通じてこれらの関数を使用します。
カスタムアテンション関数を登録する際は、対応するアテンションマスク関数も登録してください。カスタムの
attn_implementation名がAttentionMaskInterfaceに登録されていない場合、Transformersはマスクの作成をスキップし、アテンションレイヤーにattention_mask=Noneを渡します。そのため、アテンション関数内で因果性、パディング、パッキング、またはスライディングウィンドウの制約を自分で処理する必要があります。そうしないと、これらの制約が黙って無視されてしまいます。
この例では、アテンション関数をカスタマイズして、各レイヤーごとにステートメントを出力します。元の実装のマスクを保持するために、masking_utils.sdpa_maskをアテンションマスク関数として登録しています。
import torch
from transformers import AutoModelForCausalLM, AttentionInterface, AttentionMaskInterface
from transformers.integrations.sdpa_attention import sdpa_attention_forward
from transformers.masking_utils import sdpa_mask
def my_new_sdpa(*args, **kwargs):
print("I just entered the attention computation")
return sdpa_attention_forward(*args, **kwargs)
AttentionInterface.register("my_new_sdpa", my_new_sdpa)
AttentionMaskInterface.register("my_new_sdpa", sdpa_mask) # must have the same name as the registered attention function
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.2-1B", attn_implementation="my_new_sdpa")
model(torch.ones(1, 5, dtype=int))アテンション関数に新しい引数を追加することもできます。AttentionInterfaceをサポートするモデルは、kwargsをアテンション層およびアテンション関数に伝播します。モデルのforward関数で引数をkwargsとして渡します。カスタムアテンション関数は、このシグネチャと返却形式に従う必要があります。
import torch
from transformers import AutoModelForCausalLM, AttentionInterface, AttentionMaskInterface
from transformers.integrations.sdpa_attention import sdpa_attention_forward
from transformers.masking_utils import sdpa_mask
def custom_attention(
module: torch.nn.Module, # required arg
query: torch.Tensor, # required arg
key: torch.Tensor, # required arg
value: torch.Tensor, # required arg
attention_mask: Optional[torch.Tensor], # required arg
a_new_kwargs = None, # You can now add as many kwargs as you need
another_new_kwargs = None, # You can now add as many kwargs as you need
**kwargs, # You need to accept **kwargs as models will pass other args
) -> tuple[torch.Tensor, Optional[torch.Tensor]]
... # do your magic!
return attn_output, attn_weights # attn_weights are optional here
AttentionInterface.register("custom", custom_attention)
AttentionMaskInterface.register("custom", sdpa_mask) # to leave the existing mask untouched
model = AutoModelForCausalLM.from_pretrained(model_id, attn_implementation="custom")
model(torch.ones(1, 5, dtype=int), a_new_kwargs=..., another_new_kwargs=...)モデルのmodeling codeを確認し、attention関数に渡される引数とkwargsを特定してください。
AttentionMaskInterface
AttentionMaskInterface は、create_*_mask 関数が参照して、マスクをアクティブなアテンションバックエンドが期待する形式に変換するためのレジストリです。FlexAttention には BlockMask が必要で、SDPA には 4 次元テンソル、FlashAttention にはベースとなる 2 次元パディングマスクが必要です。AttentionMaskInterface.register() を使用して、カスタムバックエンドを登録したり、既存のバックエンドのフォーマッターをオーバーライドしたりできます。
import torch
from transformers import AttentionMaskInterface
from transformers.masking_utils import sdpa_mask
def my_new_sdpa_mask(*args, **kwargs):
print("I just entered the attention mask computation")
return sdpa_mask(*args, **kwargs)
AttentionMaskInterface.register("my_new_sdpa_mask", my_new_sdpa_mask)アクティブなattn_implementationに対応するフォーマッタが登録されていない場合、マスクの作成はスキップされ、attention_mask=Noneがアテンション層に渡されます。
登録された関数は、このシグネチャと一致する必要があります。
def custom_attention_mask(
batch_size: int, # required arg
q_length: int, # required arg
kv_length: int, # required arg
q_offset: int = 0, # required arg
kv_offset: int = 0, # required arg
mask_function: Callable = causal_mask_function, # required arg
attention_mask: Optional[torch.Tensor] = None, # required arg
**kwargs, # a few additional args may be passed as kwargs, especially the model's config is always passed
) -> Optional[torch.Tensor]:mask_function 引数は、PyTorch の mask_mod 関数を模した Callable です。4 つのインデックス (batch_idx, head_idx, q_idx, kv_idx) を受け取り、その位置がアテンション計算に寄与するかどうかを示すブール値を返します。これは、アテンションマスクの構築 で or_mask_function および and_mask_function に使用されているのと同じ基本形です。
mask_functionがマスクの作成に失敗する場合、torch.exportにはこのワークアラウンドを使用してください。
アテンションマスクの構築
transformers.masking_utils の create_*_mask 関数を使用してアテンションマスクを構築します。各関数はモデル設定からアクティブなアテンションバックエンドを読み取り、AttentionMaskInterface でそのバックエンドのマスクフォーマッタを検索し、バックエンドが期待する形式を返します。マスクの反転、拡張、キャストを自分で行う必要はありません。
アテンションパターンに一致する関数を選択します。
| 関数 | ユースケース |
|---|---|
create_causal_mask | 各トークンが自分自身とそれ以前のトークンにアテンションを向けるdecoder-onlyモデル |
create_bidirectional_mask | エンコーダーモデル、またはデコーダーからエンコーダー状態へのクロスアテンション |
create_sliding_window_causal_mask | スライディングウィンドウアテンションパターンを持つデコーダーモデル |
create_chunked_causal_mask | シーケンスを固定サイズのブロックに分割するデコーダーモデル |
create_bidirectional_sliding_window_mask | スライディングウィンドウアテンションパターンを持つエンコーダーモデル |
レガシーの呼び出し可能なマスクヘルパー -
get_extended_attention_mask、create_extended_attention_mask_for_decoder、invert_attention_mask- は非推奨の警告を出力し、将来のリリースで削除されます。代わりにcreate_*_mask関数を使用してください。
デコーダーのフォワードパス内でcreate_causal_maskを呼び出します。設定、入力埋め込み、ユーザーが指定した2次元のattention_mask、およびキャッシュを引数として渡します。この関数は、埋め込みからバッチサイズ、クエリ長、dtype、デバイスを読み取り、キャッシュからキー長を計算します。
from transformers.masking_utils import create_causal_mask
attention_mask = create_causal_mask(
config=self.config,
inputs_embeds=inputs_embeds,
attention_mask=attention_mask,
past_key_values=past_key_values,
)エンコーダーの自己注意に対してcreate_bidirectional_maskを呼び出します。エンコーダーはキャッシュしないため、past_key_valuesは省略します。
from transformers.masking_utils import create_bidirectional_mask
attention_mask = create_bidirectional_mask(
config=self.config,
inputs_embeds=embedding_output,
attention_mask=attention_mask,
)クロスアテンションでは、encoder_hidden_states としてエンコーダーの状態を渡すことで、マスクがデコーダーのクエリ長ではなく、エンコーダーのキーとバリューの長さを使用するようにします。
encoder_attention_mask = create_bidirectional_mask(
config=self.config,
inputs_embeds=embedding_output,
attention_mask=encoder_attention_mask,
encoder_hidden_states=encoder_hidden_states,
)or_mask_function と and_mask_function 引数を使用して、ベースマスクの上に追加の制約を付けます。or_mask_function を使用すると追加の位置がアテンションできるようになり、and_mask_function を使用するとベースパターンをさらに制限できます。どちらも、AttentionMaskInterface で説明されている4インデックスの mask_function シグネチャに従います。(batch_idx, head_idx, q_idx, kv_idx) を受け取り、ブール値を返します。
or_mask_functionとand_mask_functionは任意の注意パターンを表現できますが、組み込みパターンよりも遅く、ExecuTorch と互換性がありません。オーバーヘッドは、マスク生成がフォワードパスの時間に占める割合が大きくなる小型モデル(約 2 億パラメータ)で特に顕著です。標準的なcreate_*_mask関数では必要な表現ができない場合のみ、これらを使用してください。
例えば、どこでもTrueを返す関数をオーバーレイして因果マスクを完全に双方向なものに変換できます。因果パターンとの和集合により、すべてのトークンが他のすべてのトークンに注意を払うようになります。
mask_kwargs = {
"config": self.config,
"inputs_embeds": inputs_embeds,
"attention_mask": attention_mask,
"past_key_values": past_key_values,
"position_ids": position_ids,
"or_mask_function": lambda *args: torch.tensor(True, dtype=torch.bool),
}
attention_mask = create_causal_mask(**mask_kwargs)生成時、generate() は create_masks_for_generate を介してマスクを構築します。この関数は、モデルの構成に基づいて適切な create_*_mask にディスパッチします。モデルクラスでこれをオーバーライドすることで、生成用のカスタムなマスキング戦略を適用できます。
カスタムの4Dアテンションマスクを渡す
Pass your own 4D mask when you need an attention pattern the create_*_mask functions can’t express. A 4D mask has the shape (batch_size, 1, query_length, kv_length), where 1 broadcasts the same mask across every attention head. Transformers detects and uses the mask as-is, skipping create_*_mask.
4Dマスクは、2つの値の慣用法のいずれかを使用します。
| dtype | attend | マスクアウト |
|---|---|---|
| boolean | True | False |
| float | 0.0 | -inf |
float 規約では、softmax の前にマスクをアテンションスコアに加算します。スコアに 0.0 を加えても値は変化しないため、その位置は貢献します。一方、スコアに -inf を加えると softmax 後にゼロになるため、その位置は除外されます。
受け入れられる規約はアテンションバックエンドによって異なります。
sdpaはブール値または浮動小数点マスクを受け取ります。eagerはマスクをスコアに追加するため、浮動小数点マスクのみを受け取ります。flash_attention_2とflex_attentionは独自の形式(2次元パディングマスクとBlockMask)を使用し、生の4次元マスクは受け入れません。
よくある間違いとして、2次元のpaddingマスクで使われる1/0の慣習を、float型の4次元マスクにそのまま適用してしまうことです。マスクはスコアに足し算されるため、0.0は位置を保持し、1.0はわずかなバイアスを加えるだけです。
以下の例では、誤ったマスクと正しいマスクを比較しています。どちらも同じ1/0の因果パターンから始まります。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0", attn_implementation="sdpa")
tokenizer = AutoTokenizer.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0")
input_ids = tokenizer("my favorite condiment on a", return_tensors="pt").input_ids
seq_len = input_ids.shape[1]
# 1 attends, 0 masks
causal = torch.tril(torch.ones(seq_len, seq_len))
# wrong: 1.0/0.0 floats are added to the scores, so 0.0 keeps a token and 1.0 barely changes it
wrong_mask = causal[None, None]
# correct: 0.0 attends, -inf masks
correct_mask = torch.where(causal.bool(), 0.0, float("-inf"))[None, None]0.0がマスクに使用される値であるため、誤ったマスクでは何も除外されず、すべての位置が保持されます。
wrong_mask correct_mask
(1 attends, 0 masks) (0 attends, -inf masks)
k0 k1 k2 k3 k4 k0 k1 k2 k3 k4
q0 1 0 0 0 0 q0 0 -inf -inf -inf -inf
q1 1 1 0 0 0 q1 0 0 -inf -inf -inf
q2 1 1 1 0 0 q2 0 0 0 -inf -inf
q3 1 1 1 1 0 q3 0 0 0 0 -inf
q4 1 1 1 1 1 q4 0 0 0 0 0双方向アテンション
Decoder-onlyモデルはデフォルトでcausal(単方向)アテンションを使用し、各トークンは自分自身とそれ以前のトークンのみにアテンションを向けます。is_causal=Falseを設定すると、すべてのトークンが他のすべてのトークンにアテンションを向ける双方向アテンションに切り替わります。これにより、Decoder-onlyモデルをテキストエンコーダーとして使用できるようになり、例えば埋め込みの生成が可能になります。
これは因果的(デコーダー)モデルでのみ機能します。エンコーダーモデルをデコーダーモデルに変換するものではありません。
モデル設定でis_causal=Falseを設定すると、すべてのフォワードパスで双方向アテンションがデフォルトになります。
from transformers import AutoModel, AutoConfig
config = AutoConfig.from_pretrained("meta-llama/Llama-3.2-1B")
config.is_causal = False
model = AutoModel.from_pretrained("meta-llama/Llama-3.2-1B", config=config)
# all forward passes now use bidirectional attention
outputs = model(**inputs)モデルを2回読み込むことなく、causal attentionとbidirectional attentionの切り替えを行うには、モデルのconfigではなく、forward呼び出しでis_causalを渡します。このkwargはconfigを一時的に上書きし、呼び出し後に元の状態に復元されます。
from transformers import AutoModel
model = AutoModel.from_pretrained("meta-llama/Llama-3.2-1B")
# run with bidirectional attention
outputs = model(**inputs, is_causal=False)
# run with default causal attention
outputs = model(**inputs)