# モデルの中間出力のトレース

すべてのモデルの`forward()`メソッドは、`output_attentions`のような`None`フラグを設定のデフォルト値から手動で解決し、レイヤーごとのアテンション重みと隠れ状態をタプルに蓄積し、`return_dict=False`の場合に`ModelOutput`データクラスをプレーンなタプルに変換していました。これらのボイラープレートはすべて、2つのデコレーターに置き換えられます。

- `@capture_outputs` は出力フラグを解決し、中間値を収集して、`return_dict` 変換を処理します。
- `@merge_with_config_defaults` は設定から `use_cache` を解決します。キャッシュを使用しないモデル（[CLIPModel](/docs/transformers/main/ja/model_doc/clip#transformers.CLIPModel) など）では、これを省略してください。

これらのデコレーターは、新しいモデルを統合する際に主に遭遇します。ステップバイステップのガイドについては、[🤗 Transformers にモデルを追加する](./modular_transformers)を参照してください。

## キャプチャするサブモジュールを宣言

ベースモデルの`forward()`メソッドに`@capture_outputs`を適用します。これにより、以下の処理を行うforwardフックがアタッチされます:

- フォワードパス中に指定されたサブモジュールクラスからの出力をインターセプトします。この際、監視されていることをサブモジュール側が認識する必要はありません。
- 各レイヤーのattention weightsとhidden statesをタプルに収集します。
- 収集した値を、返される`ModelOutput`データクラスに注入します。
- `return_dict=False` の場合、dataclass を通常の tuple に変換します。
- `None` の場合、`output_attentions` と `output_hidden_states` を kwargs または `self.config` から解決します。

## 出力フィールドをサブモジュールにマッピングする

`@capture_outputs` は、どのサブモジュールがどの出力を生成するかを把握する必要があります。`PreTrainedModel` のサブクラスに、`_can_record_outputs` というクラスレベルの辞書を宣言してください。各キーは出力フィールド名（`"hidden_states"`、`"attentions"`、`"cross_attentions"`）であり、各値はモジュールクラス、クラス名の文字列、`OutputRecorder` インスタンス、またはそれらのリスト（1 つのキーで複数のモジュールタイプを記録する場合）です。

## OutputRecorderによる詳細な制御

`OutputRecorder`は、収集対象の出力を持つ`nn.Module`のサブクラスである`target_class`と、モジュールの出力タプルからどの要素を取得するかを選択するための任意の`index`を受け取ります。特定の属性名を持つモジュールにのみフックを付与するには、`layer_name`を渡します。自己注意と相互注意のように、2つのレイヤーが同じクラスを共有している場合は、`layer_name`を使用してください。

以下の例では、異なる出力制御レベルで両方のデコレーターを実際に使用する方法を示しています。実際の使用例については、[LlamaModel](https://github.com/huggingface/transformers/blob/main/src/transformers/models/llama/modeling_llama.py)を参照してください。

```python
from ...processing_utils import Unpack
from ...utils import TransformersKwargs
from ...utils.generic import merge_with_config_defaults
from ...utils.output_capturing import capture_outputs, OutputRecorder

class MyPreTrainedModel(PreTrainedModel):
    _can_record_outputs = {
        # Capture hidden_states: hook fires after each MyDecoderBlock forward,
        # grabbing its first output (index 0 by default).
        "hidden_states": MyDecoderBlock,

        # Capture self-attention weights: hook fires after each MyAttention
        # forward, grabbing its second output (index=1 by default).
        "attentions": MyAttention,

        # Capture cross-attention weights: same class, different submodule.
        # layer_name targets the attribute `self.crossattention` inside the block.
        # Captures second output as requested (index=1)
        "cross_attentions": OutputRecorder(
            MyAttention, layer_name="crossattention", index=1
        ),
    }

# Now in base model's forward we need the decorators and `Unpack` `kwargs`
class MyModel(MyPreTrainedModel):

    @merge_with_config_defaults # ← resolves use_cache
    @capture_outputs            # ← handles output collection + return_dict
    def forward(
        self,
        input_ids: torch.LongTensor | None = None,
        past_key_values: Cache = None,
        **kwargs: Unpack[TransformersKwargs],
    ) -> BaseModelOutputWithPast:

        # No manual collection needed. Just run your layers normally.
        hidden_states = self.embed_tokens(input_ids)
        for layer in self.layers:
            hidden_states = layer(hidden_states, **kwargs)

        # Return the primary outputs. The decorator will fill in
        # hidden_states/attentions/cross_attentions automatically.
        return BaseModelOutputWithPast(
            last_hidden_state=hidden_states,
            past_key_values=past_key_values
        )
```

## Patchレイヤーのクラス

Output tracing は、`_can_record_outputs` がモデルのレイヤーがインスタンス化する*正確な*クラスを指していることに依存しています。レイヤーの実装をカスタムなアテンションカーネル、量子化されたエキスパートレイヤー、またはアーキテクチャのバリアントに置き換えた場合、これらのポインタは同期を保たなければなりません。[パッチング API](./monkey_patching) は、`_can_record_outputs` の整合性を保つためのクリーンなグローバルレジストリを提供します。

`register_patch_mapping` は、元のクラス名を置き換え用の `nn.Module` サブクラスにマッピングします。キーには、完全一致するクラス名または正規表現パターンを指定できます。完全一致が優先されます。パターンは `re.search()` でテストされるため、アンカーなしのパターンはクラス名のどこにでも一致します。同じキーを2回登録すると、`overwrite=True` を渡さない限り `ValueError` が発生します。

`unregister_patch_mapping` を使用してエントリを削除します。

```python
from transformers.monkey_patching import register_patch_mapping, unregister_patch_mapping

# Exact name – replaces only Qwen2MoeExperts
register_patch_mapping({"Qwen2MoeExperts": SequentialExperts})

# Regex – replaces every class whose name ends in "Attention"
register_patch_mapping({".*Attention$": FusedAttention})

# Anchored version – only matches Llama2Attention, Llama3Attention, …
register_patch_mapping({"^Llama\\d+Attention$": CustomLlamaAttention})

# Same way, custom keys can be removed from the registry by passing the name that was registered
unregister_patch_mapping(["Qwen2MoeExperts", ".*Attention$"])
```

マッピングが登録されると、`patch_output_recorders`はすべてのサブモジュールを走査し、各`OutputRecorder.target_class`を登録された置換先に更新します。

> [!TIP]
> [from_pretrained()](/docs/transformers/main/ja/main_classes/model#transformers.PreTrainedModel.from_pretrained) メソッドは `patch_output_recorders` を自動的に呼び出します。モデルを直接構築する場合にのみ、自分で呼び出す必要があります。

```python
from transformers.monkey_patching import patch_output_recorders
# Built manually, outside from_pretrained
model = Qwen2MoeModel(config)

# Without this, _can_record_outputs still points at the original Qwen2MoeExperts class
# and hooks will never fire on CustomExperts instances.
patch_output_recorders(model)
```

