# Pemuatan bobot dinamis

Checkpoint sering kali diserialisasi dalam format yang tidak sesuai dengan yang diharapkan model saat runtime. Skenario umum meliputi:

1. **Bobot terfusi**: Checkpoint menyimpan bobot `gate_proj` dan `up_proj` secara terpisah, tetapi model menggunakan `gate_up_proj` yang terfusi untuk efisiensi.
2. **Konsolidasi pakar MoE**: Bobot pakar individual (`experts.0.weight`, `experts.1.weight`, ...) perlu ditumpuk menjadi satu tensor 3D.
3. **Penamaan lama**: Checkpoint lama menggunakan konvensi penamaan yang berbeda (misalnya, `LayerNorm.gamma` vs `LayerNorm.weight`).
4. **Model komposit**: Model bahasa-visi berisi dua sub-modul `PreTrainedModel`, masing-masing dengan konvensi checkpoint-nya sendiri.
5. **Kuantisasi**: Bobot mungkin disimpan dalam format terkuantisasi yang memerlukan deserialisasi.

Dynamic weight loading mengatasi hal ini dengan menerapkan operasi terjadwal yang dapat dibalik pada tensor checkpoint saat dimuat. Transformers menyediakan fitur ini melalui [WeightConverter](/docs/transformers/main/id/internal/modeling_utils#transformers.WeightConverter) dan `WeightRenaming`, yang menjelaskan bagaimana satu atau beberapa kunci checkpoint dipetakan ke satu atau beberapa parameter model, serta [ConversionOps](/docs/transformers/main/id/internal/modeling_utils#transformers.ConversionOps) yang dapat dikomposisikan mana yang harus dijalankan pada tensor yang cocok. Pendekatan ini beradaptasi dengan tata letak bobot baru, mendukung mixture-of-experts (MoE) yang terkuantisasi, dan terintegrasi dengan tensor parallelism.

Panduan ini menunjukkan cara menggunakan [WeightConverter](/docs/transformers/main/id/internal/modeling_utils#transformers.WeightConverter) untuk mengonversi tensor. Pemetaan konversi terdapat di [`conversion_mapping.py`](https://github.com/huggingface/transformers/blob/main/src/transformers/conversion_mapping.py); pemetaan yang terdaftar menggunakan kunci berupa string `model_type` (misalnya `"mixtral"`) atau nama kelas (misalnya `"LlavaModel"`).

## Pipeline pemuatan penuh

Semua model melewati sistem pemuatan bobot dinamis. Pemetaan konversi adalah **langkah opsional dalam sistem tersebut** yang hanya diaktifkan ketika model memiliki entri yang terdaftar untuk nama kelasnya atau `model_type`.

```
Checkpoint File → from_pretrained() → convert_and_load_state_dict_in_model()
                                              ↓
                         ┌───────────────────────────────────────────────────────────┐
                         │  For each weight in checkpoint:                           │
                         │  1. Match renamed/processed source key to model parameter │
                         │  2. Shard the weight and send to device (async)           │
                         │  3. Collect tensors with the same source_pattern together │
                         │     (e.g. MoE experts, gate_up_proj)                      │
                         │  4. Apply dequantization/deserialization (if pre-quant)   │
                         │  5. Apply conversion (if defined)                         │
                         │  6. Apply quantization (if enabled and step 4 not used)   │
                         │  7. Set parameter on model                                │
                         └───────────────────────────────────────────────────────────┘
```

| Langkah | Saat diaktifkan |
|------|-------------------|
| Pemuatan dinamis | Selalu, untuk semua model |
| Pemetaan konversi | Hanya ketika kelas model atau `model_type` terdaftar di `_MODEL_TO_CONVERSION_PATTERN` |
| Sharding TP | Hanya ketika `DistributedConfig(tp_size=N)` digunakan dan model memiliki `base_model_tp_plan` |
| Dequantisasi/deserialisasi | Hanya saat memuat checkpoint yang sudah terkuantisasi |
| Kuantisasi | Hanya ketika konfigurasi kuantisasi disediakan dan bobot belum terkuantisasi sebelumnya |

### Model padat (misalnya, Llama)

Untuk sebagian besar model dense, format checkpoint secara langsung sesuai dengan format model, sehingga pemetaan konversi tidak diperlukan. Beberapa model mungkin masih memerlukan penamaan ulang (misalnya, konvensi penamaan lama). Sharding TP tetap berlaku saat diaktifkan.

```
Checkpoint:                             Model:
model.layers.0.self_attn.q_proj.weight  →  model.layers.0.self_attn.q_proj.weight
model.layers.0.self_attn.k_proj.weight  →  model.layers.0.self_attn.k_proj.weight
model.layers.0.mlp.gate_proj.weight     →  model.layers.0.mlp.gate_proj.weight
model.layers.0.mlp.up_proj.weight       →  model.layers.0.mlp.up_proj.weight
model.layers.0.mlp.down_proj.weight     →  model.layers.0.mlp.down_proj.weight
```

Checkpoint lama mungkin menggunakan konvensi penamaan yang lebih lama, yang ditangani oleh penamaan ulang bawaan yang diterapkan ke semua model:

```
Checkpoint:                          Model:
LayerNorm.gamma              →       LayerNorm.weight
LayerNorm.beta               →       LayerNorm.bias
```

### Model MoE (misalnya, Mixtral)

Untuk model MoE, format checkpoint berbeda dari format model. Pemetaan konversi mengubah bobot ahli yang terpisah menjadi tensor 3D yang digabungkan, dan sharding TP diterapkan setelah konversi.

```
Checkpoint:                              Model:
experts.0.w1.weight  ─┐
experts.1.w1.weight   │ MergeModulelist
...                   ├───────────────→  experts.gate_up_proj (8, hidden, 2*intermediate)
experts.0.w3.weight   │ + Concatenate
experts.1.w3.weight  ─┘
```

### Model komposit (misalnya, visi-bahasa)

Sebuah `PreTrainedModel` dapat berisi sub-modul `PreTrainedModel` lainnya. Setiap sub-model dapat memiliki pemetaan konversi miliknya sendiri, yang didaftarkan berdasarkan nama kelasnya atau `model_type`-nya. Ketika model induk dimuat, `get_model_conversion_mapping` menelusuri sub-model dalam urutan depth-first, mengumpulkan pemetaan mereka, dan **secara otomatis membatasi ruang lingkup** setiap transformasi ke jalur sub-modulnya melalui `scope_prefix`.

```
Composite model:                              Per-submodel mappings (auto-scoped):
LlavaForConditionalGeneration
├── vision_model: SiglipVisionModel    →     SiglipVisionModel mapping (scope="vision_model")
└── language_model: LlamaForCausalLM   →     LlamaForCausalLM mapping  (scope="language_model")
```

`scope_prefix` adalah jalur titik dari sub-modul (`"vision_model"`, `"language_model.model"`, dll.). Transformasi berscope hanya memicu pada kunci yang diawali dengan `f"{scope_prefix}."`; prefix dihapus sebelum pencocokan pola dan dilampirkan kembali setelah substitusi, sehingga pemetaan untuk setiap sub-model ditulis *relatif terhadap sub-model*, persis seolah-olah sub-model tersebut adalah akar.

## Arsitektur

Sistem ini dibangun di sekitar beberapa komponen utama yang didefinisikan dalam [`core_model_loading.py`](https://github.com/huggingface/transformers/blob/main/src/transformers/core_model_loading.py):

**Fase 1 — Pemrosesan per kunci** (mengiterasi kunci checkpoint):

1. **Lewati daftar transform** sekali. Setiap `WeightRenaming` yang cocok akan dipicu (mis. `block_sparse_moe` → `mlp`), dan **hanya satu** [WeightConverter](/docs/transformers/main/id/internal/modeling_utils#transformers.WeightConverter) yang dapat mengklaim kunci tersebut (mis. `experts.*.w1.weight`).
2. **Shard (TP) dan kirim ke perangkat** secara asinkron melalui `ThreadPoolExecutor`.
3. **Kumpulkan** tensor dengan `source_pattern` yang sama secara bersama-sama (misalnya semua bobot pakar MoE, proyeksi gate + up).

**Fase 2 — Pemrosesan per pemetaan** (mengiterasi pemetaan yang dikumpulkan):

1. **Dequantize/deserialize** (hanya untuk checkpoint yang sudah terkuantisasi sebelumnya).
2. **Terapkan rantai [ConversionOps](/docs/transformers/main/id/internal/modeling_utils#transformers.ConversionOps)**: `Chunk`, `Concatenate`, `MergeModulelist`, `Transpose`, dll.
3. **Kuantisasi** secara langsung (jika belum dikuantisasi sebelumnya).
4. **Set parameter** pada model.

### WeightTransform

Kelas dasar yang menangani pencocokan pola dan pengumpulan tensor:

- **Kompilasi pola**: Pola sumber adalah ekspresi reguler penuh yang dicocokkan dengan `re.search()`. Wildcard `*` mencocokkan komponen yang dapat diindeks apa pun dan mengelompokkan semua kecocokan untuk operasi batch.
- **Grup penangkap & referensi balik**: Grup penangkap dalam pola sumber dapat dirujuk sebagai `\1`, `\2`, ... dalam pola target untuk mempertahankan substring (misalnya indeks layer) selama proses penggantian nama.
- **Penentuan ruang lingkup**: `scope_prefix` (diatur secara otomatis per sub-model oleh `get_model_conversion_mapping`) membatasi transformasi pada kunci di bawah path tersebut. Prefiks dihapus sebelum pencocokan dan dilekatkan kembali setelah substitusi.
- **Penamaan ulang kunci**: `rename_source_key()` menerapkan regex (dengan penanganan scope) dan mengembalikan `(renamed_key, source_pattern)` agar loader mengetahui konverter mana, jika ada, yang mengklaim kunci tersebut.
- **Koleksi tensor**: `add_tensor()` mengakumulasi tensor yang telah di-resolve (atau `Future`) berdasarkan `source_pattern` mereka, sehingga semua tensor yang dibutuhkan oleh satu konversi (misalnya semua bobot pakar MoE) di-batch bersama sebelum rantai operasi dijalankan.
- **Reversibilitas**: `reverse_transform()` menukar pola sumber ↔ target dan membalik setiap operasi, sehingga daftar yang sama jika dibalik akan menggerakkan proses penyimpanan.
- **Tracking**: `was_used()` melaporkan apakah transform benar-benar cocok dengan kunci mana pun selama proses loading; hal ini diperlukan agar penamaan ulang yang tidak bijektif (misalnya `PrefixChange` yang menambahkan prefiks yang mungkin sudah ada) dapat diterapkan secara simetris saat penyimpanan.

### WeightRenaming

`WeightRenaming` adalah `WeightTransform` khusus untuk penggantian nama kunci murni tanpa operasi tensor. Berbeda dengan [WeightConverter](/docs/transformers/main/id/internal/modeling_utils#transformers.WeightConverter), `WeightRenaming` tidak **claim** kuncinya, sehingga beberapa penggantian nama dapat dirangkai secara bebas. Pada jalur load, semua penggantian nama dijalankan sebelum converter; pada jalur save (daftar terbalik), semua penggantian nama terbalik dijalankan setelah converter terbalik — lihat aturan urutan di atas.

```py
# Legacy checkpoint compatibility
WeightRenaming("LayerNorm.gamma", "LayerNorm.weight")

# Module path changes
WeightRenaming(".block_sparse_moe.", ".mlp.")
```

`PrefixChange` adalah wrapper tingkat lebih tinggi di atas `WeightRenaming` untuk kasus umum berupa penghapusan atau penambahan seluruh komponen path. Parameter opsional `model_prefix` membatasi operasi pada key di bawah namespace tersebut:

```py
# "model.layers.bad_prefix.weight" → "model.layers.weight"
PrefixChange(prefix_to_remove="bad_prefix", model_prefix="model.layers")
# "layers.0.weight" → "model.layers.0.weight"
PrefixChange(prefix_to_add="model")
```

### WeightConverter

[WeightConverter](/docs/transformers/main/id/internal/modeling_utils#transformers.WeightConverter) memperluas `WeightTransform` dengan rantai [ConversionOps](/docs/transformers/main/id/internal/modeling_utils#transformers.ConversionOps) yang bekerja pada tensor yang dikumpulkan. Keempat kardinalitas yang didukung adalah:

| Kardinalitas | Pola sumber | Pola target | Operasi tipikal |
|-------------|-----------------|-----------------|-------------------|
| satu-ke-satu | 1 | 1 | `Transpose`, [PermuteForRope](/docs/transformers/main/id/internal/modeling_utils#transformers.PermuteForRope) |
| satu-ke-banyak | 1 | >1 | [Chunk](/docs/transformers/main/id/internal/modeling_utils#transformers.Chunk) (misalnya, unpack `qkv_proj`) |
| banyak-ke-satu | >1 | 1 | [Concatenate](/docs/transformers/main/id/internal/modeling_utils#transformers.Concatenate), [MergeModulelist](/docs/transformers/main/id/internal/modeling_utils#transformers.MergeModulelist) (misalnya, gabungkan para ahli) |
| banyak-ke-banyak | >1 | >1 | hanya dengan operasi yang secara eksplisit mendukungnya (misalnya `ErnieFuseAndSplitTextVisionExperts`) |

```python
WeightConverter(
    source_patterns=[".experts.*.w1.weight", ".experts.*.w3.weight"],
    target_patterns=".experts.gate_up_proj",
    operations=[MergeModulelist(dim=0), Concatenate(dim=1)],
)
```

`WeightConverter` juga dapat dibalik: `reverse_transform()` menukar sumber ↔ target dan mengganti setiap operasi dengan `reverse_op`-nya, sehingga pemetaan konversi yang terdaftar bersifat dua arah secara bawaan (pemuatan menggunakan daftar apa adanya, penyimpanan menggunakannya secara terbalik).

### Urutan penamaan ulang dan konverter

Daftarkan entri `WeightRenaming` sebelum entri `WeightConverter`, dan pastikan daun-daunnya saling terpisah: rename menormalisasi kunci yang dikonsumsi oleh converter, tetapi tidak pernah menargetkan daun yang dihasilkan oleh converter. Jalur penyimpanan bergantung pada hal ini untuk membalikkan pemetaan dalam dua fase (balikkan converter, lalu balikkan rename).

Daftar transformasi diproses secara berurutan, satu kali untuk setiap kunci checkpoint. Untuk setiap kunci:

- Setiap `WeightRenaming` yang cocok akan dipicu; beberapa penamaan ulang dapat berantai.
- The first `WeightConverter` yang **claims** kunci tersebut. Konverter berikutnya dilewati — hal ini menjamin tensor diarahkan ke satu konverter untuk langkah merge/split, dan inilah alasan mengapa dua konverter dengan maksud yang tumpang tindih merupakan konfigurasi yang salah.

```python
weight_mapping = [
    WeightRenaming("^old_prefix", "encoder"),              # rename runs always
    WeightConverter(                                       # converter claims the key
        "attn.qkv_proj.weight",
        ["attn.q_proj.weight", "attn.k_proj.weight", "attn.v_proj.weight"],
        operations=[Chunk(dim=0)],
    ),
]
# Load:  "old_prefix.attn.qkv_proj.weight"
#   → WeightRenaming  → "encoder.attn.qkv_proj.weight"
#   → WeightConverter → "encoder.attn.{q,k,v}_proj.weight"
#
# Save: list reversed, each transform inverted:
#   → rev(WeightConverter) repacks QKV → "encoder.attn.qkv_proj.weight"
#   → rev(WeightRenaming)  fixes prefix → "old_prefix.attn.qkv_proj.weight"
```

## Operasi konversi

Kelas [WeightConverter](/docs/transformers/main/id/internal/modeling_utils#transformers.WeightConverter) memiliki beberapa operasi yang dijalankan saat [from_pretrained()](/docs/transformers/main/id/main_classes/model#transformers.PreTrainedModel.from_pretrained) dipanggil untuk mengubah tensor sumber checkpoint menjadi tensor target model.

Operasi sepenuhnya dapat dibalik. Menyimpan akan membalikkan konversi dan mengembalikan checkpoint asli sehingga Anda dapat dengan mudah bekerja di berbagai framework.

| Operasi | Balik |
|-----------|---------|
| `Chunk(dim)` | `Concatenate(dim)` |
| `Concatenate(dim)` | `Chunk(dim)` |
| `MergeModulelist(dim)` | `SplitModulelist(dim)` |
| `SplitModulelist(dim)` | `MergeModulelist(dim)` |
| `Transpose(d0, d1)` | `Transpose(d1, d0)` |
| `PermuteForRope()` | `PermuteForRope()` |
| `Conv3dToLinear(...)` | `LinearToConv3d(...)` |

### Chunk

Operasi [Chunk](/docs/transformers/main/id/internal/modeling_utils#transformers.Chunk) membagi tensor menjadi bagian-bagian yang sama besar sepanjang satu dimensi. Misalnya, jika sebuah model mengharapkan Q, K, dan V sebagai tiga tensor terpisah, bukan sebagai satu tensor tunggal.

```py
WeightConverter(
    "self_attn.qkv_proj",
    ["self_attn.q_proj", "self_attn.k_proj", "self_attn.v_proj"],
    operations=[Chunk(dim=0)],
)
```

### Gabungkan

Operasi [Concatenate](/docs/transformers/main/id/internal/modeling_utils#transformers.Concatenate) menggabungkan tensor yang terpisah menjadi satu tensor tunggal. Misalnya, jika sebuah model mengharapkan Q, K, dan V sebagai satu tensor tunggal, bukan sebagai tensor yang terpisah.

```py
WeightConverter(
    ["self_attn.q_proj", "self_attn.k_proj", "self_attn.v_proj"],
    "self_attn.qkv_proj",
    operations=[Concatenate(dim=0)],
)
```

### MergeModulelist

[MergeModulelist](/docs/transformers/main/id/internal/modeling_utils#transformers.MergeModulelist) menggabungkan daftar tensor 2D menjadi satu tensor 3D. Sebagai contoh, Anda dapat mengomposisikan [MergeModulelist](/docs/transformers/main/id/internal/modeling_utils#transformers.MergeModulelist) dengan [Concatenate](/docs/transformers/main/id/internal/modeling_utils#transformers.Concatenate) untuk menumpuk para ahli dalam MoE dan mengemasnya ke dalam satu tensor.

```py
WeightConverter(
    ["block_sparse_moe.experts.*.w1.weight", "block_sparse_moe.experts.*.w3.weight"],
    "mlp.experts.gate_up_proj",
    operations=[MergeModulelist(dim=0), Concatenate(dim=1)],
)
```

### SplitModulelist

[SplitModulelist](/docs/transformers/main/id/internal/modeling_utils#transformers.SplitModulelist) memecah tensor 3D kembali menjadi daftar tensor 2D. Misalnya, Anda dapat memecah tumpukan pakar menjadi pakar-pakar individual.

```py
WeightConverter(
    "mlp.experts.down_proj",
    "block_sparse_moe.experts.*.w2.weight",
    operations=[SplitModulelist(dim=0)],
)
```

### PermuteForRope

[PermuteForRope](/docs/transformers/main/id/internal/modeling_utils#transformers.PermuteForRope) mengonversi bobot dari format terinterleaved ke format sin/cos. Sebagai contoh, Anda dapat menggabungkan [Chunk](/docs/transformers/main/id/internal/modeling_utils#transformers.Chunk) dengan [PermuteForRope](/docs/transformers/main/id/internal/modeling_utils#transformers.PermuteForRope) untuk memisahkan tensor QKV yang terfusi dan menerapkan permutasi RoPE sin/cos ke Q dan K.

```py
WeightConverter(
    ["model.layers.*.self_attn.qkv_proj.weight"],
    [
        "model.layers.*.self_attn.q_proj.weight",
        "model.layers.*.self_attn.k_proj.weight",
        "model.layers.*.self_attn.v_proj.weight",
    ],
    operations=[Chunk(dim=0), PermuteForRope()],
)
```

### Transpose

`Transpose` menukar dimensi sebuah tensor. Berguna untuk mengonversi tata letak bobot antar konvensi yang berbeda.

```py
WeightConverter(
    source_patterns="mlp.gate.weight",
    target_patterns="mlp.text_moe.gate.weight",
    operations=[Transpose(dim0=0, dim1=1)],
)
```

## Rantai operasi

Operasi dapat dirangkai untuk melakukan transformasi kompleks. Operasi dieksekusi secara berurutan, dengan output dari setiap operasi menjadi input untuk operasi berikutnya.

### Contoh: Konversi Mixtral MoE

```python
WeightConverter(
    source_patterns=[
        ".experts.*.w1.weight",  # gate_proj per expert
        ".experts.*.w3.weight",  # up_proj per expert
    ],
    target_patterns=".experts.gate_up_proj",
    operations=[
        MergeModulelist(dim=0),  # Stack all experts: (n_experts, in, out)
        Concatenate(dim=1),      # Fuse gate+up: (n_experts, in, 2*out)
    ],
)
```

**Alur data:**
```
Input:
  ".experts.*.w1.weight": [tensor_0, tensor_1, ..., tensor_7]  # 8 experts
  ".experts.*.w3.weight": [tensor_0, tensor_1, ..., tensor_7]  # 8 experts

After MergeModulelist(dim=0):
  ".experts.*.w1.weight": (8, 4096, 14336)  # stacked gate
  ".experts.*.w3.weight": (8, 4096, 14336)  # stacked up

After Concatenate(dim=1):
  ".experts.gate_up_proj": (8, 4096, 28672)  # fused gate_up
```

### Aturan pencocokan pola

- Pola sumber adalah regex penuh yang dievaluasi dengan `re.search()`. `^` mengikat ke awal kunci **scoped** (setelah penghapusan `scope_prefix`).
- `*` adalah wildcard per-indeks yang mengumpulkan semua tensor yang cocok di bawah pola sumber yang sama (menjaga urutan checkpoint untuk penggabungan yang benar).
- Grup yang ditangkap dalam pola sumber dapat dirujuk sebagai `\1`, `\2`, ... dalam pola target untuk mempertahankan bagian dari kunci asli (misalnya indeks layer).
- Transform berscope (`scope_prefix` diset) hanya mencocokkan kunci yang diawali dengan `f"{scope_prefix}."`.

## Mendaftarkan pemetaan konversi

Daftar konversi didaftarkan terhadap kunci string — baik berupa `model_type` (misalnya `"mixtral"`) maupun nama kelas (misalnya `"LlavaModel"`):

```python
from transformers.conversion_mapping import register_checkpoint_conversion_mapping

register_checkpoint_conversion_mapping(
    "my_model_type",
    [
        WeightRenaming(".old.", ".new."),
        WeightConverter(
            ".experts.*.w1.weight",
            ".experts.gate_proj",
            operations=[MergeModulelist(dim=0)],
        ),
    ],
)
```

### Aturan pencarian

Ketika `get_model_conversion_mapping` memproses `PreTrainedModel`, setiap sub-`PreTrainedModel` dikunjungi dalam urutan DFS (`nn.Module.named_modules()` yang difilter menjadi instance `PreTrainedModel`). Setiap sub-model diselesaikan dalam dua tahap, filter kode kustom dijalankan terlebih dahulu, kemudian pemetaan yang terdaftar dicari.

Model dengan kode kustom dilewati sebelum pencarian apa pun dilakukan. Sub-model dianggap sebagai kode kustom ketika [is_custom_code()](/docs/transformers/main/id/main_classes/model#transformers.PreTrainedModel.is_custom_code) mengembalikan `True`, yang mencakup model yang dimuat dari Hub dengan `trust_remote_code=True` dan kelas model apa pun yang tidak ada di Transformers. Model kode kustom tidak mewarisi pemetaan konversi karena nama kelas atau `model_type` mungkin bertabrakan dengan model native. Sebagai contoh, arsitektur kustom yang mengatur `model_type="mixtral"` tidak akan mengambil transformasi fusi pakar bawaan Mixtral.

> [!IMPORTANT]
> Daftarkan model kode kustom secara eksplisit dengan `register_checkpoint_conversion_mapping` untuk menerapkan pemetaan konversi. Pendaftaran menambahkan nama kelas atau `model_type` ke dalam himpunan pemetaan yang didaftarkan pengguna, yang membebaskan model dari lewati kode kustom dan mengarahkannya melalui pencarian normal di bawah.

Untuk setiap sub-model yang lolos filter kode kustom:

1. **Pencarian berdasarkan nama kelas dicoba terlebih dahulu**, kemudian `model_type`. Jika keduanya terdaftar untuk modul yang sama, **pemetaan nama kelas yang menang** dan `model_type` diabaikan untuk modul tersebut — hal ini memungkinkan task head (misalnya `LlavaForConditionalGeneration`) untuk menimpa baseline `model_type` bersama (`"llava"`).
2. Pemetaan yang dipilih memiliki `scope_prefix` yang diatur ke jalur titik sub-modul (`""` untuk root).
3. **Deduplikasi berbasis nenek moyang** memutuskan apakah pemetaan akan dipertahankan:
   - Jika jalur **ancestor** sudah mengklaim identifier yang sama (nama kelas atau `model_type`), sub-modul akan **dilewati** — pemetaan tanpa scope atau dengan scope yang lebih tinggi dari ancestor sudah mencakup subtree ini.
   - Jika hanya ada satu **sibling** yang mengklaimnya, sub-modul akan **dipertahankan** dengan `scope_prefix` miliknya sendiri. Setiap sibling mendapatkan pemetaan ber-scope miliknya sendiri.

Daftar yang telah dilihat untuk nama kelas dan `model_type` dilacak secara terpisah, dengan satu hal yang perlu diperhatikan: ketika sebuah modul dicocokkan **melalui nama kelasnya**, `model_type`-nya *tidak* ditambahkan ke daftar yang telah dilihat. Hal ini dilakukan agar modul lain yang berbagi `model_type` yang sama tetapi tidak memiliki pemetaan khusus kelas (misalnya `DetrModel` di bawah `DetrForSegmentation`) tetap dapat dijangkau melalui pencarian `model_type`.

### Pemetaan berbasis kelas vs alias `model_type`

Kedua gaya tersebut coexist dalam `_MODEL_TO_CONVERSION_PATTERN`:

```python
_MODEL_TO_CONVERSION_PATTERN = {
    # model_type aliases (lookup by config.model_type)
    "minimax": "mixtral",
    "qwen3_moe": "qwen2_moe",
    "mistral3": "llava",
    # class-name aliases (lookup by type(submodule).__name__)
    "PaliGemmaModel": "LlavaModel",
    "MaskFormerDetrDecoder": "DetrModel",
    ...
}
```

Kunci nama kelas lebih disarankan ketika `model_type` dibagikan tetapi kelas tertentu memerlukan perilaku yang berbeda.

## Integrasi paralelisme tensor

Sistem pemuatan dinamis terintegrasi dengan paralelisme tensor (TP) melalui hierarki `TensorParallelLayer` yang didefinisikan di `src/transformers/integrations/tensor_parallel.py`.

Ketika TP diaktifkan, tensor dipecah **selama** materialisasi, bukan setelahnya. Ini berarti setiap rank hanya memuat bagian tensor yang dibutuhkannya.

```python
def spawn_tp_materialize(thread_pool, tensor, sharding_method, tensor_idx, device, dtype):
    def _job():
        return sharding_method.shard_tensor(tensor, tensor_idx=tensor_idx, device=device, dtype=dtype)
    return thread_pool.submit(_job)
```

### Gaya paralel yang tersedia

| Gaya | Dimensi Shard Bobot | Deskripsi |
|-------|------------------|-------------|
| `colwise` | -2 | Secara kolom: fitur output di-shard |
| `rowwise` | -1 | Per baris: fitur input di-shard |
| `packed_colwise` | -2 | Untuk bobot yang digabungkan (gate_up_proj) |
| `packed_rowwise` | -1 | Untuk bobot yang digabungkan |
| `embedding_rowwise` | 0 | Paralelisme kosakata |
| `grouped_gemm` | 0 | Paralelisme pakar untuk MoE |
| `sequence_parallel` | None | Tidak ada pembagian bobot |

### Penanganan bobot terpadat

Untuk bobot yang digabungkan seperti `gate_up_proj`, perhatian khusus diperlukan untuk melakukan sharding dengan benar:

```python
def get_packed_weights(param, empty_param, device_mesh, rank, dim):
    """
    Interleaves gate and up shards correctly.

    Packed tensor: [G0 G1 G2 G3 | U0 U1 U2 U3]

    With TP=2:
    - Rank 0 gets: [G0 G1 | U0 U1]
    - Rank 1 gets: [G2 G3 | U2 U3]
    """
```

Operasi TP disimpan dalam `WeightTransform` dan diterapkan setelah operasi konversi:

```python
if matched_tp_pattern := tp_plan_alt.search(renamed_key):
    tp_layer = ALL_PARALLEL_STYLES[model.tp_plan[matched_tp_pattern]]
    mapping.distributed_operation = tp_layer(
        device_mesh=device_mesh,
        rank=device_mesh.get_local_rank(),
        empty_param=empty_param.clone()
    )
```

## Integrasi kuantisasi

Quantisasi terintegrasi ke dalam pipeline pemuatan dengan dua cara, tergantung pada apakah checkpoint sudah dikuantisasi:

- **Checkpoint terkuantisasi**: Kuantizer menyediakan instance [WeightConverter](/docs/transformers/main/id/internal/modeling_utils#transformers.WeightConverter) (melalui `get_weight_conversions()`) untuk mendeserialisasi tensor terkuantisasi. Dtype checkpoint dipertahankan untuk menghindari cast yang tidak diinginkan.
- **Kuantisasi on-the-fly**: Kuantizer menyediakan operasi kuantisasi yang diterapkan setelah operasi konversi, sehingga bobot dikuantisasi saat dimuat.

Quantizer juga dapat menulis ulang seluruh daftar konversi di akhir `get_model_conversion_mapping` melalui `update_weight_conversions(...)` — misalnya, dequantizer FP8 menambahkan op `Fp8Dequantize` di depan setiap konverter yang sudah ada sehingga skala per-blok diterapkan *sebelum* op expert-merge / concat meratakan struktur per-expert.

## Memuat model yang cepat dan efisien

Memuat model menjadi lebih cepat dan menggunakan lebih sedikit memori karena loader mengetahui tensor mana yang diperlukan untuk operasi dan menjadwalkan materialisasinya secara *lazy*.

Loader memindai checkpoint *sekali* untuk menemukan kecocokan pola dan mengumpulkan tensor. Tensor tersebut disimpan sebagai objek `Future` dan dikirim ke thread pool untuk dimuat secara asinkron tanpa memblokir GIL. Sebuah parameter mulai dimuat begitu ada thread yang tersedia untuknya.

Jika sistem Anda menjalankan proses berat lainnya, beberapa thread mungkin justru memperlambat proses pemuatan alih-alih mempercepatnya. Dalam kasus ini, atur variabel lingkungan `HF_DEACTIVATE_ASYNC_LOAD=1` untuk memuat bobot secara berurutan.

> [!NOTE]
> Nilai defaultnya adalah 4 thread untuk pemuatan parameter asinkron. Ini memberikan keseimbangan terbaik di antara berbagai skenario pemuatan dan perangkat keras. Pekerjaan ini sebagian besar terikat pada I/O, tetapi tergantung pada perangkat keras akselerator dan `dtype` yang diperlukan saat pemuatan, proses ini dapat menjadi terikat pada CPU/GPU jika `dtype` berbeda dari yang terserialisasi (hal ini memerlukan operasi salinan tambahan).

### Pemuatan asinkron vs sinkron

```python
def spawn_materialize(thread_pool, tensor, device, dtype) -> Future | Callable:
    def _job():
        return _materialize_copy(tensor, device, dtype)

    if thread_pool is not None:
        return thread_pool.submit(_job)  # Async: returns Future
    else:
        return _job  # Sync: returns Callable (deferred execution)
```

Sync loading digunakan ketika:
- variabel lingkungan `HF_DEACTIVATE_ASYNC_LOAD=1` telah disetel.
- Disk offloading diaktifkan (keterbatasan memori memerlukan pemuatan berurutan).
- Kuantisasi on-the-fly diaktifkan (menghindari thread pekerja yang melampaui langkah kuantisasi).

### Alur materialisasi

```
1. Checkpoint iteration (Phase 1):
   - For each key, walk the transform list once
   - Submit materialization job to ThreadPoolExecutor
   - Job returns Future (async) or Callable (sync)
   - Collect into the matching WeightConverter / WeightRenaming

2. Per-mapping processing (Phase 2, one mapping at a time):
   - materialize_tensors() waits for this mapping's Futures only
   - Apply conversion operations chain (self.operations)
   - Apply quantization operation (if on-the-fly)
   - Set parameters on model
   - Delete realized tensors immediately

3. Cleanup:
   - Thread pool shutdown (with cancel_futures=True for interrupts)
```

### Efisiensi memori

Saat mengonversi bobot, konverter menunggu hingga semua tensor yang diperlukan terealisasi jika belum dimuat. Sebagai contoh, operasi [MergeModulelist](/docs/transformers/main/id/internal/modeling_utils#transformers.MergeModulelist) membutuhkan semua bobot dalam `ModuleList` untuk dimuat sebelum penggabungan dilakukan.

Mengonkatenasi tensor memerlukan salinan sementara, sehingga operasi seperti [MergeModulelist](/docs/transformers/main/id/internal/modeling_utils#transformers.MergeModulelist) dan [Concatenate](/docs/transformers/main/id/internal/modeling_utils#transformers.Concatenate) membutuhkan 2x memori dari tensor yang mendasarinya selama konversi. Setelah digabungkan, hanya tensor hasil yang tetap berada di memori. Puncak memori terburuk secara teoretis adalah ukuran model ditambah tensor yang diperlukan untuk operasi [MergeModulelist](/docs/transformers/main/id/internal/modeling_utils#transformers.MergeModulelist) atau [Concatenate](/docs/transformers/main/id/internal/modeling_utils#transformers.Concatenate) terbesar.

Kasus terburuk ini hanya terjadi ketika semua parameter lain telah dimuat sebelum konversi yang membutuhkan sumber daya besar dijalankan. Dua skenario memicu hal ini.

1. Semua parameter dimuat secara asinkron sebelum masuk ke proses konversi yang berat (thread pool lebih cepat daripada antrean konversi).
2. Konversi yang paling menantang adalah yang terakhir.

Sebagai contoh, model MoE yang menggunakan [MergeModulelist](/docs/transformers/main/id/internal/modeling_utils#transformers.MergeModulelist) untuk para ahli pada setiap lapisan, puncak memori terburuk secara teoretis adalah ukuran model ditambah para ahli pada satu lapisan.

Skenario terburuk ini jarang terjadi. Puncak memori aktual cenderung tetap mendekati ukuran model.

## Reversibilitas

Sistem ini mendukung penyimpanan model dengan transformasi invers, sehingga memungkinkan proses simpan/muat bolak-balik. Penyimpanan berjalan dalam dua fase: konverter terbalik terlebih dahulu (setiap tensor cocok dengan paling banyak satu), kemudian penamaan ulang terbalik, sesuai aturan urutan di atas.

```python
def revert_weight_conversion(model, state_dict):
    """Applies reverse conversions for saving."""
    weight_conversions = getattr(model, "_weight_conversions", None)

    # Reverse all transforms
    reverse_weight_conversion = [
        conversion.reverse_transform() for conversion in weight_conversions
    ]

    # Apply in reverse
    for first_param_name, reversed_converter in conversion_mapping.items():
        realized_value = reversed_converter.convert(first_param_name, model=model)
```

Daftar transformasi yang digunakan saat pemuatan di-cache pada model sebagai `_weight_conversions` (hanya entri yang benar-benar dipicu yang disimpan, sehingga penamaan ulang non-bijektif seperti `PrefixChange` diterapkan secara simetris dengan benar). Ketika model diinstansiasi tanpa `from_pretrained` (dan karenanya tidak memiliki `_weight_conversions`), `revert_weight_conversion` kembali ke perhitungan ulang pemetaan melalui `get_model_conversion_mapping` dan membuang semua `PrefixChange` darinya (kita tidak dapat mengetahui apakah checkpoint asli memiliki prefiks tersebut).

Pola target dapat berisi elemen regex yang perlu diproses untuk arah sebaliknya:

```python
def process_target_pattern(pattern: str) -> tuple[str, str | None]:
    """
    - Removes `^` and `$` anchors
    - Removes negative lookahead/lookbehind
    - Detects capturing groups, replaces with \1
    """
```

## Contoh nyata

### MoE bergaya Mixtral

**Format checkpoint:**
```
model.layers.0.block_sparse_moe.experts.0.w1.weight  # gate per expert
model.layers.0.block_sparse_moe.experts.0.w2.weight  # down per expert
model.layers.0.block_sparse_moe.experts.0.w3.weight  # up per expert
...
model.layers.0.block_sparse_moe.experts.7.w1.weight
```

**Format model:**
```
model.layers.0.mlp.experts.gate_up_proj  # (8, 4096, 28672)
model.layers.0.mlp.experts.down_proj     # (8, 14336, 4096)
```

**Pemetaan konversi** (dari `conversion_mapping.py`):
```python
"mixtral": [
    WeightRenaming(".block_sparse_moe.", ".mlp."),
    WeightConverter(
        source_patterns=[".experts.*.w1.weight", ".experts.*.w3.weight"],
        target_patterns=".experts.gate_up_proj",
        operations=[MergeModulelist(dim=0), Concatenate(dim=1)],
    ),
    WeightConverter(
        source_patterns=[".experts.*.w2.weight"],
        target_patterns=".experts.down_proj",
        operations=[MergeModulelist(dim=0)],
    ),
],
```

### Model bahasa-visi komposit (Gemma3)

Gemma3 memiliki pengaturan kanonik "logika prefiks yang berbeda untuk model head daripada untuk model base". Dua entri didaftarkan (di sini melalui alias yang menunjuk ke daftar `"llava"` / `"LlavaModel"` yang dibagikan):

```python
# model_type: applies to Gemma3ForConditionalGeneration / Gemma3ForSequenceClassification
"gemma3": "llava"           # → adds "model." in front of language_model / vision_tower / ...

# class:    applies to the inner Gemma3Model only
"Gemma3Model": "LlavaModel"  # → minimal rename inside the already-prefixed namespace
```

DFS walk untuk `Gemma3ForConditionalGeneration`:

1. Root — pencarian class tidak ditemukan (`Gemma3ForConditionalGeneration` tidak terdaftar), pencarian model_type ditemukan (`"gemma3"`) → transformasi penulisan ulang prefiks `"llava"` ditambahkan **tanpa scope**, sehingga `language_model`, `vision_tower`, `multi_modal_projector` berada di bawah `model.*`.
2. Inner `model: Gemma3Model` — **class lookup hits** (`Gemma3Model` → `LlavaModel`) → hanya pemetaan `LlavaModel` yang diterapkan, dengan cakupan `"model"`. Penamaan ulang prefix `"gemma3"` (=`"llava"`) yang jauh lebih luas **tidak** diterapkan ulang di sini, yang memang persis seperti yang Anda inginkan: model inner berada dalam namespace di mana prefix sudah benar.

Bentuk pasangan yang sama digunakan oleh setiap VLM keluarga Llava (`PaliGemma`, `InternVL`, `Mistral3`, ...). Entri model_type menangani operasi prefix pada model head; entri class menjaga pemetaan model dasar internal tetap minimal.

### Penyaringan lebih dalam dengan penimpaan spesifik untuk head (DETR)

`DetrForSegmentation` menunjukkan override yang spesifik untuk head, yang ditumpangkan di atas dua tingkat bersarang:

```
DetrForSegmentation                  (class registered: segmentation-only renames)
├── detr: DetrForObjectDetection     (no mapping; just walked through)
│   └── model: DetrModel             (class registered: shared base transforms)
│       └── backbone, encoder, ...
└── mask_head, bbox_attention        (head-specific weights)
```

Dua pemetaan terlibat (satu untuk setiap kunci yang terdaftar):

```python
"DetrModel":           [WeightRenaming("backbone.conv_encoder", "backbone"), ...]      # shared base
"DetrForSegmentation": [WeightRenaming("mask_head.lay1", "mask_head.conv1.conv"), ...] # head-specific
```

Jalan DFS:

1. Root `DetrForSegmentation` cocok berdasarkan kelas → penamaan ulang segmentasi ditambahkan **unscoped**.
2. `detr: DetrForObjectDetection` tidak terdaftar → tidak ada transformasi yang ditambahkan; DFS terus masuk ke dalamnya.
3. `detr.model: DetrModel` cocok berdasarkan kelas → transformasi dasar ditambahkan dengan `scope_prefix="detr.model"`.

Inilah mengapa `DetrForObjectDetection` tidak memerlukan pemetaan tersendiri: satu-satunya pemetaan yang terdaftar di subpohonnya (`DetrModel`) secara otomatis di-cakupan ke jalur yang tepat.

Alias berbasis kelas (class-keyed aliases) menggunakan pemetaan dasar tanpa perlu registrasi tambahan: `"MaskFormerDetrDecoder": "DetrModel"` membuat decoder `MaskFormer` mengambil transformasi yang sama di bawah nama kelasnya sendiri.

### Operasi kustom (ERNIE 4.5 VL MoE)

Ketika operasi bawaan tidak memadai, Anda dapat membuat subclass [ConversionOps](/docs/transformers/main/id/internal/modeling_utils#transformers.ConversionOps) kustom. Sebagai contoh, ERNIE 4.5 VL MoE perlu memisahkan daftar expert bersama antara modalitas teks dan visual — sesuatu yang tidak ditangani oleh satu pun operasi bawaan. Operasi kustom `ErnieFuseAndSplitTextVisionExperts` memisahkan dan menumpuk ulang expert di antara dua kunci target:

```python
"ernie4_5_vl_moe": [
    WeightRenaming("vision_model", "vision_tower"),
    WeightConverter(
        source_patterns=["experts.*.down_proj.weight"],
        target_patterns=[
            "text_moe.experts.down_proj",
            "vision_moe.experts.down_proj",
        ],
        operations=[ErnieFuseAndSplitTextVisionExperts(stack_dim=0, concat_dim=1)],
    ),
],
```

Custom ops harus mengimplementasikan `convert()` dan properti `reverse_op` untuk mendukung round-trip save/load.

### Alias tipe model

Banyak model memiliki pola konversi yang sama:

```python
_MODEL_TO_CONVERSION_PATTERN = {
    "mixtral": "mixtral",
    "minimax": "mixtral",
    "qwen2_moe": "qwen2_moe",
    "deepseek_v2": "qwen2_moe",
    "deepseek_v3": "qwen2_moe",
    "qwen3_moe": "qwen2_moe",
    "olmoe": "qwen2_moe",
    ...
}
```

## Memanfaatkan kembali komponen dasar pemuatan dinamis

Pemuatan bobot dinamis tidak terbatas pada checkpoint model penuh. Blok bangunan yang sama memungkinkan Anda memuat *setiap* kumpulan bobot selama Anda dapat mendeskripsikan bagaimana kunci checkpoint dipetakan ke parameter dan memastikan modul target ada.

Secara garis besar, kontraknya terlihat seperti ini:

1. **Siapkan namespace model.** Pastikan modul/parameter yang ingin dimuat ada dan diberi nama sesuai dengan target pemetaan Anda. Untuk adapter, hal ini berarti memanggil `inject_adapter_in_model(...)` agar modul adapter ada sebelum pemuatan. Untuk kepala kustom atau modul tambahan, instansiasikan terlebih dahulu pada model.
2. **Uraikan cara memetakan bobot.** Buat daftar konversi/penamaan ulang (misalnya, di dalam helper seperti `_build_peft_weight_mapping(...)`) menggunakan [WeightConverter](/docs/transformers/main/id/internal/modeling_utils#transformers.WeightConverter) atau `WeightRenaming`. Di sinilah Anda menyatakan bagaimana kunci checkpoint harus dikonversi, dipecah, digabungkan, atau dinamai ulang agar sesuai dengan namespace model Anda. Anda dapat melakukan 3 hal utama:
    - tambahkan operasi ke daftar konverter: operasi ini akan diterapkan pada semua bobot, kecuali bobot yang dikumpulkan di salah satu `WeightConverter`. Secara umum, operasi ini seharusnya berupa operasi `WeightRenaming`
    - tambahkan operasi ke daftar operasi dari setiap converter: inilah yang terjadi untuk `Quantization`, di mana kami hanya menambahkan operasi kuantisasi setelah daftar operasi dari `WeightConverter` mana pun.
    - ganti operasi / map ke operasi kustom Anda: inilah yang terjadi dengan `peft`. Kami mengganti operasi `Concatenate` dari, misalnya, `mixtral`, menjadi `PeftConcatenate` (yang didefinisikan di PEFT). Dengan cara ini, saat checkpoint adapter dibaca, bobot yang akan digabungkan akan dikumpulkan, dan diformat dengan benar untuk `peft`
3. **Memuat + finalisasi + laporan.** Gunakan pemuat inti untuk melakukan konversi dan mengisi tensor, kemudian finalisasi dan catat hasilnya. Secara konkret, alur ini adalah:
   - `LoadStateDictConfig(...)` + `_load_pretrained_model(...)` untuk memuat dan mengonversi.
   - `_finalize_load_state_dict(...)` untuk memindahkan tensor yang hilang/tidak cocok dari `meta`, menginisialisasinya, dan mengikat bobot.
   - `log_state_dict_report(...)` untuk melaporkan kunci yang hilang/tidak diharapkan/tidak cocok (dan kesalahan konversi).

API-API ini disediakan agar Anda dapat menangani kode kustom dan format bobot kustom, sekaligus memastikan Anda mendapatkan manfaat dari pemuatan bobot, sharding, dan kenyamanan penggunaan terbaik serta paling efisien dari API `transformers`!

## Referensi file utama

| File | Tujuan |
|------|---------|
| `src/transformers/core_model_loading.py` | Logika pemuatan inti, `WeightConverter`, `WeightRenaming`, `ConversionOps` |
| `src/transformers/conversion_mapping.py` | Pemetaan bawaan dan komposisi per-submodel (`get_model_conversion_mapping`) |
| `src/transformers/integrations/tensor_parallel.py` | Kelas dan utilitas sharding TP |
| `src/transformers/quantizers/base.py` | Hook kuantisasi dan kelas dasar |

