# Format checkpoint PEFT

Dokumen ini menjelaskan bagaimana file checkpoint PEPEFT disusun dan bagaimana cara mengonversi antara format PEFT dan format lainnya.

## File PEFT

PEFT Metode (parameter-efficient fine-tuning) hanya memperbarui sebagian kecil parameter model, bukan semuanya. Ini menguntungkan karena file checkpoint umumnya jauh lebih kecil daripada file model asli, sehingga lebih mudah disimpan dan dibagikan. Namun, hal ini juga berarti bahwa untuk memuat model PEFT, Anda juga harus memiliki model aslinya.

Saat Anda memanggil [save_pretrained()](/docs/peft/main/id/package_reference/peft_model#peft.PeftModel.save_pretrained) pada model PEFT, model PEFT menyimpan tiga file, yang dijelaskan di bawah ini:

1. `adapter_model.safetensors` atau `adapter_model.bin`

Secara default, model disimpan dalam format `safetensors`, alternatif yang aman dari format `bin`, yang diketahui rentan terhadap [kerentanan keamanan](https://huggingface.co/docs/hub/security-pickle) karena menggunakan utilitas pickle di baliknya. Namun, kedua format menyimpan `state_dict` yang sama dan dapat saling ditukar.

`state_dict` hanya berisi parameter dari modul adapter, bukan model dasar. Untuk mengilustrasikan perbedaan ukurannya, model BERT normal membutuhkan ruang disk sekitar 420MB, sedangkan adapter IA³ di atas model BERT ini hanya membutuhkan sekitar 260KB.

2. `adapter_config.json`

File `adapter_config.json` berisi konfigurasi modul adapter, yang diperlukan untuk memuat model. Di bawah ini adalah contoh `adapter_config.json` untuk adapter IA³ dengan pengaturan standar yang diterapkan pada model BERT:

```json
{
  "auto_mapping": {
    "base_model_class": "BertModel",
    "parent_library": "transformers.models.bert.modeling_bert"
  },
  "base_model_name_or_path": "bert-base-uncased",
  "fan_in_fan_out": false,
  "feedforward_modules": [
    "output.dense"
  ],
  "inference_mode": true,
  "init_ia3_weights": true,
  "modules_to_save": null,
  "peft_type": "IA3",
  "revision": null,
  "target_modules": [
    "key",
    "value",
    "output.dense"
  ],
  "task_type": null
}
```

File konfigurasi berisi:

- tipe modul adapter yang disimpan, `"peft_type": "IA3"`
- informasi tentang model dasar seperti `"base_model_name_or_path": "bert-base-uncased"`
- revisi model (jika ada), `"revision": null`

Jika model dasar bukan model Transformers yang telah dilatih sebelumnya, dua entri terakhir akan menjadi `null`. Selain itu, semua pengaturan terkait dengan adapter IA³ spesifik yang digunakan untuk fine-tune model.

3. `README.md`

`README.md` yang dihasilkan adalah kartu model dari model PEFT dan berisi beberapa entri yang sudah terisi. Tujuannya adalah untuk memudahkan berbagi model dengan orang lain dan menyediakan informasi dasar tentang model. File ini tidak diperlukan untuk memuat model.

## Konversi ke format PEFT

Saat mengonversi dari format lain ke format PEFT, kami membutuhkan file `adapter_model.safetensors` (atau `adapter_model.bin`) dan file `adapter_config.json`.

### adapter_model

Untuk bobot model, penting untuk menggunakan pemetaan yang benar dari nama parameter ke nilai agar PEFT dapat memuat file. Mendapatkan pemetaan yang benar adalah latihan dalam memeriksa detail implementasi, karena tidak ada format yang disepakati secara umum untuk adapter PEFT.

Untungnya, menentukan pemetaan ini tidak terlalu rumit untuk kasus dasar yang umum. Mari kita lihat contoh konkretnya, yaitu [`LoraLayer`](https://github.com/huggingface/peft/blob/main/src/peft/tuners/lora/layer.py):

```python
# showing only part of the code

class LoraLayer(BaseTunerLayer):
    # All names of layers that may contain (trainable) adapter weights
    adapter_layer_names = ("lora_A", "lora_B", "lora_embedding_A", "lora_embedding_B")
    # All names of other parameters that may contain adapter-related parameters
    other_param_names = ("r", "lora_alpha", "scaling", "lora_dropout")

    def __init__(self, base_layer: nn.Module, **kwargs) -> None:
        self.base_layer = base_layer
        self.r = {}
        self.lora_alpha = {}
        self.scaling = {}
        self.lora_dropout = nn.ModuleDict({})
        self.lora_A = nn.ModuleDict({})
        self.lora_B = nn.ModuleDict({})
        # For Embedding layer
        self.lora_embedding_A = nn.ParameterDict({})
        self.lora_embedding_B = nn.ParameterDict({})
        # Mark the weight as unmerged
        self._disable_adapters = False
        self.merged_adapters = []
        self.use_dora: dict[str, bool] = {}
        self.lora_magnitude_vector: Optional[torch.nn.ParameterDict] = None  # for DoRA
        self._caches: dict[str, Any] = {}
        self.kwargs = kwargs
```

Dalam kode `__init__` yang digunakan oleh semua kelas `LoraLayer` di PEFT, terdapat sejumlah parameter yang digunakan untuk menginisialisasi model, tetapi hanya beberapa yang relevan untuk file checkpoint: `lora_A`, `lora_B`, `lora_embedding_A`, dan `lora_embedding_B`. Parameter-parameter ini tercantum dalam atribut kelas `adapter_layer_names` dan berisi parameter yang dapat dipelajari, sehingga harus disertakan dalam file checkpoint. Semua parameter lainnya, seperti rank `r`, diturunkan dari `adapter_config.json` dan harus disertakan di sana (kecuali nilai default yang digunakan).

Mari kita periksa `state_dict` dari model PEFT LoRA yang diterapkan pada BERT. Saat mencetak lima kunci pertama menggunakan pengaturan LoRA default (kunci sisanya sama, hanya dengan nomor lapisan yang berbeda), kita mendapatkan:

- `base_model.model.encoder.layer.0.attention.self.query.lora_A.weight` 
- `base_model.model.encoder.layer.0.attention.self.query.lora_B.weight` 
- `base_model.model.encoder.layer.0.attention.self.value.lora_A.weight` 
- `base_model.model.encoder.layer.0.attention.self.value.lora_B.weight` 
- `base_model.model.encoder.layer.1.attention.self.query.lora_A.weight`
- dst.

Mari kita uraikan ini:

- Secara default, untuk model BERT, LoRA diterapkan pada lapisan `query` dan `value` dari modul perhatian. Inilah sebabnya Anda melihat `attention.self.query` dan `attention.self.value` dalam nama kunci untuk setiap lapisan.
- LoRA menguraikan bobot menjadi dua matriks ber-rank rendah, `lora_A` dan `lora_B`. Inilah asal `lora_A` dan `lora_B` dalam nama kunci.
- Matriks LoRA ini diimplementasikan sebagai lapisan `nn.Linear`, sehingga parameter disimpan dalam atribut `.weight` (`lora_A.weight`, `lora_B.weight`).
- Secara default, LoRA tidak diterapkan pada lapisan embedding BERT, sehingga tidak ada _entri_ untuk `lora_A_embedding` dan `lora_B_embedding`.
- Kunci dari `state_dict` selalu diawali dengan `"base_model.model."`. Alasannya adalah, dalam PEFT, model dasar dibungkus di dalam model yang spesifik untuk tuner (`LoraModel` dalam kasus ini), yang pada gilirannya dibungkus di dalam model PEFT umum (`PeftModel`). Karena alasan ini, dua prefiks tersebut ditambahkan ke kunci. Saat mengonversi ke format PEFT, penambahan prefiks ini menjadi wajib.

> [!TIP]
> Poin terakhir ini tidak berlaku untuk teknik penyesuaian awalan seperti penyesuaian prompt. Di sana, embedding tambahan disimpan langsung di dalam `state_dict` tanpa ada awalan yang ditambahkan ke kunci.

Saat memeriksa nama parameter pada model yang dimuat, Anda mungkin terkejut melihat bahwa nama-nama tersebut tampak sedikit berbeda, misalnya `base_model.model.encoder.layer.0.attention.self.query.lora_A.default.weight`. Perbedaannya terletak pada bagian *`.default`* di segmen kedua dari akhir. Bagian ini ada karena PEFT umumnya memungkinkan penambahan beberapa adapter sekaligus (menggunakan `nn.ModuleDict` atau `nn.ParameterDict` untuk menyimpannya). Sebagai contoh, jika Anda menambahkan adapter lain bernama "other", kunci untuk adapter tersebut akan menjadi `base_model.model.encoder.layer.0.attention.self.query.lora_A.other.weight`.

Saat Anda memanggil [save_pretrained()](/docs/peft/main/id/package_reference/peft_model#peft.PeftModel.save_pretrained), nama adapter dihapus dari kunci. Alasannya adalah nama adapter bukan bagian penting dari arsitektur model; itu hanya nama sewenang-wenang. Saat memuat adapter, Anda bisa memilih nama yang sama sekali berbeda, dan model akan tetap berfungsi dengan cara yang sama. Inilah mengapa nama adapter tidak disimpan dalam file checkpoint.

> [!TIP]
> Jika Anda memanggil `save_pretrained("some/path")` dan nama adapter bukan `"default"`, adapter disimpan dalam sub-direktori dengan nama yang sama dengan adapter. Jadi, jika namanya adalah "other", adapter akan disimpan di dalam `some/path/other`.

Dalam beberapa situasi, menentukan nilai mana yang harus ditambahkan ke file checkpoint bisa menjadi sedikit lebih rumit. Misalnya, dalam PEFT, DoRA diimplementasikan sebagai kasus khusus dari LoRA. Jika Anda ingin mengonversi model DoRA ke PEFT, Anda harus membuat checkpoint LoRA dengan entri tambahan untuk DoRA. Anda dapat melihat ini di `__init__` dari kode `LoraLayer` sebelumnya:

```python
self.lora_magnitude_vector: Optional[torch.nn.ParameterDict] = None  # for DoRA
```

Ini menunjukkan bahwa ada parameter tambahan opsional per lapisan untuk DoRA.

### adapter_config

Semua informasi lain yang diperlukan untuk memuat model PEFT terkandung dalam file `adapter_config.json`. Mari kita periksa file ini untuk model LoRA yang diterapkan pada BERT:

```json
{
  "alpha_pattern": {},
  "auto_mapping": {
    "base_model_class": "BertModel",
    "parent_library": "transformers.models.bert.modeling_bert"
  },
  "base_model_name_or_path": "bert-base-uncased",
  "bias": "none",
  "fan_in_fan_out": false,
  "inference_mode": true,
  "init_lora_weights": true,
  "layer_replication": null,
  "layers_pattern": null,
  "layers_to_transform": null,
  "loftq_config": {},
  "lora_alpha": 8,
  "lora_dropout": 0.0,
  "megatron_config": null,
  "megatron_core": "megatron.core",
  "modules_to_save": null,
  "peft_type": "LORA",
  "r": 8,
  "rank_pattern": {},
  "revision": null,
  "target_modules": [
    "query",
    "value"
  ],
  "task_type": null,
  "use_dora": false,
  "use_rslora": false
}
```

Ini berisi banyak entri, dan pada pandangan pertama, mungkin terasa membingungkan untuk menentukan semua nilai yang tepat untuk dimasukkan. Namun, sebagian besar entri tidak diperlukan untuk memuat model. Hal ini karena mereka menggunakan nilai default dan tidak perlu ditambahkan, atau karena mereka hanya memengaruhi inisialisasi bobot LoRA, yang tidak relevan saat memuat model. Jika Anda tidak tahu apa yang dilakukan oleh parameter tertentu, misalnya `"use_rslora",` jangan tambahkan, dan Anda seharusnya tidak akan mengalami masalah. Perhatikan juga bahwa seiring bertambahnya opsi, file ini akan memiliki lebih banyak entri di masa depan, tetapi seharusnya tetap kompatibel mundur.

Sebagai minimal, Anda harus menyertakan entri berikut:

```json
{
  "target_modules": ["query", "value"],
  "peft_type": "LORA"
}
```

Namun, disarankan untuk menambahkan sebanyak mungkin entri, seperti rank `r` atau `base_model_name_or_path` (jika itu adalah model Transformers). Informasi ini dapat membantu orang lain memahami model dengan lebih baik dan membagikannya dengan lebih mudah. Untuk memeriksa kunci dan nilai apa yang diharapkan, lihat file [config.py](https://github.com/huggingface/peft/blob/main/src/peft/tuners/lora/config.py) (sebagai contoh, ini adalah file config untuk LoRA) di kode sumber PEFT.

## Penyimpanan model

Dalam beberapa situasi, Anda mungkin ingin menyimpan seluruh model PEFT, termasuk bobot dasar. Hal ini dapat diperlukan jika, misalnya, model dasar tidak tersedia bagi pengguna yang mencoba memuat model PEFT. Anda dapat menggabungkan bobot terlebih dahulu atau mengonversinya menjadi model Transformer.

### Gabungkan bobot

Cara paling sederhana untuk menyimpan seluruh model PEFT adalah dengan menggabungkan bobot adapter ke dalam bobot dasar:

```python
merged_model = model.merge_and_unload()
merged_model.save_pretrained(...)
```

Namun, pendekatan ini memiliki beberapa kerugian:

- Setelah [merge_and_unload()](/docs/peft/main/id/package_reference/tuners#peft.tuners.tuners_utils.BaseTuner.merge_and_unload) dipanggil, Anda akan mendapatkan model dasar tanpa fungsionalitas khusus PEFT. Ini berarti Anda tidak dapat lagi menggunakan metode-metode khusus PEFT.
- Anda tidak dapat menggabungkan kembali bobot, memuat beberapa adapter sekaligus, menonaktifkan adapter, dan sebagainya.
- Tidak semua metode PEFT mendukung penggabungan bobot.
- Beberapa metode PEFT umumnya memungkinkan penggabungan, tetapi tidak dengan pengaturan tertentu (misalnya, saat menggunakan teknik kuantisasi tertentu).
- Seluruh model akan jauh lebih besar daripada model PEFT, karena akan berisi semua bobot dasar juga.

Namun, inferensi dengan model yang sudah digabungkan seharusnya sedikit lebih cepat.

### Konversi ke model Transformers

Cara lain untuk menyimpan seluruh model, dengan asumsi model dasarnya adalah model Transformers, adalah menggunakan pendekatan yang agak janggal ini untuk langsung menyisipkan bobot PEFT ke dalam model dasar dan menyimpannya, yang hanya berhasil jika Anda "menipu" Transformers agar meyakini bahwa model PEFT tersebut bukan model PEFT. Ini hanya berfungsi dengan LoRA karena adapter lain tidak diimplementasikan di dalam Transformers.

```python
model = ...  # the PEFT model
...
# after you finish training the model, save it in a temporary location
model.save_pretrained(<temp_location>)
# now load this model directly into a transformers model, without the PEFT wrapper
# the PEFT weights are directly injected into the base model
model_loaded = AutoModel.from_pretrained(<temp_location>)
# now make the loaded model believe that it is _not_ a PEFT model
model_loaded._hf_peft_config_loaded = False
# now when we save it, it will save the whole model
model_loaded.save_pretrained(<final_location>)
# or upload to Hugging Face Hub
model_loaded.push_to_hub(<final_location>)
```

