PEFT documentation
Injeksi adapter
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.
| Kelebihan | Kekurangan |
|---|---|
| model dimodifikasi secara inplace, mempertahankan semua atribut dan metode aslinya | menulis secara manual fungsi utilitas from_pretrained dan save_pretrained dari Hugging Face untuk menyimpan dan memuat adapter |
berfungsi untuk modul torch dan modalitas apa pun | tidak berfungsi dengan metode utilitas apa pun yang disediakan oleh PeftModel seperti menonaktifkan dan menggabungkan adapter |
inject_adapter_in_modeltidak 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_parametersalih-alihtarget_modules, injeksi daristate_dicttidak 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 TrueUntuk 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.