PEFT documentation

Injeksi adapter

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.21.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Injeksi adapter

Dengan PEFT, Anda dapat menyuntikkan adapter yang dapat dilatih ke dalam modul torch mana pun, yang memungkinkan Anda menggunakan metode adapter tanpa bergantung pada kelas pemodelan di PEFT. Ini berlaku untuk semua adapter, kecuali yang berbasis pembelajaran prompt (misalnya prefix tuning atau p-tuning) dan adapter yang mempertahankan state yang dibagikan di antara beberapa lapisan target.

Periksa tabel di bawah untuk melihat kapan Anda harus menyuntikkan adapter.

KelebihanKekurangan
model dimodifikasi secara inplace, mempertahankan semua atribut dan metode aslinyamenulis secara manual fungsi utilitas from_pretrained dan save_pretrained dari Hugging Face untuk menyimpan dan memuat adapter
berfungsi untuk modul torch dan modalitas apa puntidak berfungsi dengan metode utilitas apa pun yang disediakan oleh PeftModel seperti menonaktifkan dan menggabungkan adapter

inject_adapter_in_model tidak mendukung metode PEFT yang mempertahankan status adapter yang dibagikan di antara beberapa lapisan target. Saat ini, ini mencakup TinyLoRA, UniLoRA, VeRA, PVeRA, VBLoRA, dan FRoD. Gunakan get_peft_model() untuk metode-metode ini sebagai gantinya.

Membuat model PEFT baru

Untuk melakukan injeksi adapter, gunakan metode inject_adapter_in_model(). Metode ini menerima 3 argumen, yaitu konfigurasi PEFT, model, dan nama adapter opsional. Anda juga dapat melampirkan beberapa adapter ke model jika memanggil inject_adapter_in_model() beberapa kali dengan nama adapter yang berbeda.

Sebagai contoh, untuk menyuntikkan adapter LoRA ke dalam submodul linear dari modul DummyModel:

import torch
from peft import inject_adapter_in_model, LoraConfig

class DummyModel(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.embedding = torch.nn.Embedding(10, 10)
        self.linear = torch.nn.Linear(10, 10)
        self.lm_head = torch.nn.Linear(10, 10)

    def forward(self, input_ids):
        x = self.embedding(input_ids)
        x = self.linear(x)
        x = self.lm_head(x)
        return x


lora_config = LoraConfig(
    lora_alpha=16,
    lora_dropout=0.1,
    r=64,
    bias="none",
    target_modules=["linear"],
)

model = DummyModel()
model = inject_adapter_in_model(lora_config, model)

dummy_inputs = torch.LongTensor([[0, 1, 2, 3, 4, 5, 6, 7]])
dummy_outputs = model(dummy_inputs)

Cetak model untuk melihat bahwa adapter telah disuntikkan dengan benar.

DummyModel(
  (embedding): Embedding(10, 10)
  (linear): Linear(
    in_features=10, out_features=10, bias=True
    (lora_dropout): ModuleDict(
      (default): Dropout(p=0.1, inplace=False)
    )
    (lora_A): ModuleDict(
      (default): Linear(in_features=10, out_features=64, bias=False)
    )
    (lora_B): ModuleDict(
      (default): Linear(in_features=64, out_features=10, bias=False)
    )
    (lora_embedding_A): ParameterDict()
    (lora_embedding_B): ParameterDict()
  )
  (lm_head): Linear(in_features=10, out_features=10, bias=True)
)

Injeksi berdasarkan state_dict

Kadang-kadang, mungkin saja terdapat checkpoint adapter PEFT tetapi konfigurasi PEFT yang sesuai tidak diketahui karena alasan tertentu. Untuk menyuntikkan lapisan PEFT untuk checkpoint ini, biasanya Anda harus melakukan reverse-engineering pada konfigurasi PEFT yang sesuai, terutama argumen target_modules, berdasarkan state_dict dari checkpoint. Hal ini bisa merepotkan dan rawan kesalahan. Untuk menghindarinya, Anda juga dapat memanggil inject_adapter_in_model() dan meneruskan state_dict yang dimuat sebagai argumen:

from safetensors.torch import load_file

model = ...
state_dict = load_file(<path-to-safetensors-file>)
lora_config = LoraConfig(...)
model = inject_adapter_in_model(lora_config, model, state_dict=state_dict)

Dalam kasus ini, PEFT akan menggunakan state_dict sebagai referensi untuk lapisan mana yang akan ditargetkan, alih-alih menggunakan konfigurasi PEFT. Sebagai pengguna, Anda tidak perlu mengatur target_modules yang tepat pada konfigurasi PEFT agar ini berfungsi. Namun, Anda tetap harus meneruskan konfigurasi PEFT dengan tipe yang benar, dalam contoh ini LoraConfig, dan Anda dapat membiarkan target_modules sebagai None.

Perlu diperhatikan bahwa ini masih hanya membuat lapisan PEFT yang belum diinisialisasi; nilai dari state_dict tidak digunakan untuk mengisi bobot model. Untuk mengisi bobot, lanjutkan dengan memanggil set_peft_model_state_dict() seperti yang dijelaskan di bawah.

⚠️ Perhatikan bahwa jika terdapat ketidakcocokan antara konfigurasi yang diatur dalam PEFT config dan yang ditemukan di state_dict, PEFT akan memberikan peringatan. Anda dapat mengabaikan peringatan tersebut jika Anda yakin bahwa PEFT config tidak ditentukan dengan benar.

Jika adapter PEFT asli menggunakan target_parameters alih-alih target_modules, injeksi dari state_dict tidak akan berfungsi dengan benar. Dalam kasus ini, Anda wajib menggunakan konfigurasi PEFT yang benar untuk injeksi.

Menyimpan model

Untuk hanya menyimpan adapter, gunakan fungsi get_peft_model_state_dict():

from peft import get_peft_model_state_dict

peft_state_dict = get_peft_model_state_dict(model)
print(peft_state_dict)

Jika tidak, model.state_dict() mengembalikan state dict lengkap dari model.

Memuat model

Setelah memuat state_dict yang disimpan, ia dapat diterapkan menggunakan fungsi set_peft_model_state_dict():

from peft import set_peft_model_state_dict

model = DummyModel()
model = inject_adapter_in_model(lora_config, model)
outcome = set_peft_model_state_dict(model, peft_state_dict)
# check that there were no wrong keys
print(outcome.unexpected_keys)

Jika injeksi adapter lambat atau Anda perlu memuat sejumlah besar adapter, Anda dapat menggunakan optimasi yang memungkinkan pembuatan adapter “kosong” pada meta device dan hanya mengisi bobot dengan bobot asli ketika set_peft_model_state_dict() dipanggil. Untuk melakukan ini, teruskan low_cpu_mem_usage=True ke inject_adapter_in_model() dan set_peft_model_state_dict().

model = DummyModel()
model = inject_adapter_in_model(lora_config, model, low_cpu_mem_usage=True)

print(model.linear.lora_A["default"].weight.device.type == "meta")  # should be True
set_peft_model_state_dict(model, peft_state_dict, low_cpu_mem_usage=True)
print(model.linear.lora_A["default"].weight.device.type == "cpu")  # should be True

Untuk memuat bobot dari hub, ada fungsi tingkat rendah load_peft_weights():

state_dict = load_peft_weights("my-account/my-adapter-repo")

Mengatur dan memuat bobot dasar

Fungsi-fungsi di atas menangani state dict dari adapter. Ada juga situasi di mana bobot base model perlu dibaca atau ditulis melalui wrapper PEFT. Hal ini tidak sepenuhnya sederhana karena PEFT mengganti nama parameter dari modul yang ditargetkan (misalnya q_proj.weight menjadi q_proj.base_layer.weight) dan menambahkan parameter adapter yang tidak ada di base model. Gunakan get_base_model_state_dict() dan set_base_model_state_dict() untuk menerjemahkan antara kedua penamaan tersebut:

from peft import LoraConfig, get_peft_model, get_base_model_state_dict, set_base_model_state_dict
from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("facebook/opt-125m")
model = get_peft_model(model, LoraConfig(target_modules="all-linear"))

# state dict with the original (pre-PEFT) key names, adapter parameters excluded
base_state_dict = get_base_model_state_dict(model)

# load a state dict with original key names into the PEFT-wrapped model
outcome = set_base_model_state_dict(model, base_state_dict, strict=False)
# check that there were no wrong keys
print(outcome.missing_keys, outcome.unexpected_keys)

Kasus penggunaan utama untuk ini adalah memuat bobot dasar setelah model sudah dibungkus oleh PEFT. Sebagai contoh, setup pelatihan FSDP seperti TorchTitan menginisialisasi model pada perangkat meta, menerapkan PEFT, dan memecah hasilnya menjadi shard. Memori riil baru ada setelah pemecahan shard, sehingga checkpoint, yang kuncinya menggunakan nama asli, harus dimuat ke dalam model yang sudah dibungkus. Perhatikan bahwa get_base_model_state_dict() mengembalikan tensor langsung dari model, bukan salinannya.

Update on GitHub