PEFT documentation
PEFT kontrol noktası biçimi
PEFT kontrol noktası biçimi
Bu belge, PEFT’nin kontrol noktası dosyalarının nasıl yapılandırıldığını ve PEFT biçimi ile diğer biçimler arasında nasıl dönüştürme yapılacağını açıklar.
PEFT dosyaları
PEFT (parameter-efficient fine-tuning) yöntemleri, modelin tüm parametreleri yerine yalnızca küçük bir alt kümesini günceller. Bu, checkpoint dosyalarının genellikle orijinal model dosyalarından çok daha küçük olabileceği ve saklanmasının ve paylaşılmasının daha kolay olduğu için avantajlıdır. Ancak bu, bir PEFT modelini yüklemek için orijinal modelin de mevcut olması gerektiği anlamına gelir.
PEFT modelinde save_pretrained() çağrıldığında, PEFT modeli aşağıda açıklanan üç dosyayı kaydeder:
adapter_model.safetensorsveyaadapter_model.bin
Varsayılan olarak model, safetensors biçiminde kaydedilir. Bu biçim, bin biçiminin güvenli bir alternatifi olup, bin biçiminin altta pickle yardımcısını kullanması nedeniyle güvenlik açıklarına karşı savunmasız olduğu bilinmektedir. Her iki biçim de aynı state_dict bilgisini saklar ve birbirinin yerine kullanılabilir.
state_dict yalnızca adaptör modülünün parametrelerini içerir, temel modeli içermez. Boyut farkını göstermek için, normal bir BERT modeli ~420MB disk alanı gerektirirken, bu BERT modeli üzerine eklenen bir IA³ adaptörü yalnızca ~260KB gerektirir.
adapter_config.json
adapter_config.json dosyası, modeli yüklemek için gerekli olan adaptör modülünün yapılandırmasını içerir. Aşağıda, standart ayarlarla bir BERT modeline uygulanan bir IA³ adaptörü için adapter_config.json örneği verilmiştir:
{
"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
}Yapılandırma dosyası şunları içerir:
- kaydedilen adaptör modülü türü,
"peft_type": "IA3" - information about the base model like
"base_model_name_or_path": "bert-base-uncased" - modelin revizyonu (varsa),
"revision": null
Eğer temel model önceden eğitilmiş bir Transformers modeli değilse, son iki giriş null olacaktır. Bunun dışında, ayarların tümü, modelin ince ayarında kullanılan belirli IA³ adaptörüyle ilgilidir.
README.md
Oluşturulan README.md, bir PEFT modelinin model kartıdır ve birkaç önceden doldurulmuş giriş içerir. Bunun amacı, modeli başkalarıyla paylaşmayı kolaylaştırmak ve model hakkında temel bazı bilgiler sağlamaktır. Modeli yüklemek için bu dosyaya gerek yoktur.
PEFT formatına dönüştür
Başka bir formattan PEFT formatına dönüştürürken hem adapter_model.safetensors (veya adapter_model.bin) dosyasına hem de adapter_config.json dosyasına ihtiyaç duyarız.
adapter_model
Model ağırlıkları için, PEFT'nin dosyayı yükleyebilmesi için parametre adından değere doğru eşlemeyi kullanmak önemlidir. PEFT adaptörleri için genel olarak kabul görmüş bir format olmadığından, bu eşlemeyi doğru yapmak, uygulama detaylarını kontrol etme pratiğidir.
Neyse ki, bu eşlemeyi yaygın temel durumlar için belirlemek aşırı karmaşık değildir. Somut bir örneğe, LoraLayer'e bakalım:
# 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 = kwargsPEFT'teki tüm LoraLayer sınıflarının kullandığı __init__ kodunda modeli başlatmak için kullanılan bir dizi parametre vardır, ancak checkpoint dosyası için yalnızca birkaçı ilgilidir: lora_A, lora_B, lora_embedding_A ve lora_embedding_B. Bu parametreler, sınıf özelliği olan adapter_layer_names içinde listelenir ve öğrenilebilir parametreleri içerir, bu nedenle checkpoint dosyasında yer almaları gerekir. Sıralama (rank) r gibi diğer tüm parametreler, adapter_config.json dosyasından türetilir ve orada yer almalıdır (varsayılan değer kullanılmadığı sürece).
BERT üzerine uygulanan bir PEFT LoRA modelinin state_dict değerini inceleyelim. Varsayılan LoRA ayarlarını kullanarak ilk beş anahtarı yazdırdığımızda (kalan anahtarlar aynıdır, yalnızca katman numaraları farklıdır) şu sonucu alırız:
base_model.model.encoder.layer.0.attention.self.query.lora_A.weightbase_model.model.encoder.layer.0.attention.self.query.lora_B.weightbase_model.model.encoder.layer.0.attention.self.value.lora_A.weightbase_model.model.encoder.layer.0.attention.self.value.lora_B.weightbase_model.model.encoder.layer.1.attention.self.query.lora_A.weight- vb.
Bunu parçalara ayıralım:
- Varsayılan olarak, BERT modellerinde LoRA, dikkat modülünün
queryvevaluekatmanlarına uygulanır. Bu nedenle her katman için anahtar adlarındaattention.self.queryveattention.self.valueifadelerini görürsünüz. - LoRA, ağırlıkları iki düşük rütbeli matrise,
lora_Avelora_Bolarak ayrıştırır. Anahtar adlarındakilora_Avelora_Bterimleri buradan gelir. - Bu LoRA matrisleri
nn.Linearkatmanları olarak uygulanır, bu nedenle parametreler.weightözniteliğinde saklanır (lora_A.weight,lora_B.weight). - Varsayılan olarak LoRA, BERT'in gömme katmanına uygulanmaz, bu nedenle
lora_A_embeddingvelora_B_embeddingiçin giriş yoktur. state_dictanahtarları her zaman"base_model.model."ile başlar. Bunun nedeni, PEFT'te temel modelin, bir ayarlayıcıya özgü modelin (LoraModelbu durumda) içine sarılması ve bu modelin de genel bir PEFT modeli (PeftModel) içine sarılmasıdır. Bu nedenle, anahtarlara bu iki önek eklenir. PEFT biçimine dönüştürürken bu öneklerin eklenmesi gerekir.
Bu son nokta, prompt tuning gibi prefix tuning teknikleri için geçerli değildir. Bu tekniklerde, ek gömlemeler (embeddings) anahtarlara herhangi bir önek eklenmeden doğrudan
state_dictiçinde saklanır.
Yüklenen modeldeki parametre adlarını incelediğinizde, biraz farklı göründüklerini fark edip şaşırmış olabilirsiniz, örneğin base_model.model.encoder.layer.0.attention.self.query.lora_A.default.weight. Fark, sondan ikinci segmentteki .default kısmıdır. Bu kısmın var olmasının nedeni, PEFT'nin genellikle birden fazla adaptörün aynı anda eklenmesine izin vermesidir (bunları depolamak için nn.ModuleDict veya nn.ParameterDict kullanılır). Örneğin, “other” adında başka bir adaptör eklerseniz, bu adaptörün anahtarı base_model.model.encoder.layer.0.attention.self.query.lora_A.other.weight olur.
save_pretrained() çağrıldığında, anahtarlardan adaptör adı kaldırılır. Bunun nedeni, adaptör adının model mimarisinin önemli bir parçası olmaması ve yalnızca keyfi bir isim olmasıdır. Adaptör yüklenirken tamamen farklı bir ad seçilebilir ve model yine aynı şekilde çalışır. Bu nedenle adaptör adı, checkpoint dosyasında saklanmaz.
save_pretrained("some/path")çağrısı yapılıp adaptör adı"default"değilse, adaptör, adaptörle aynı ada sahip bir alt dizinde saklanır. Yani ad “other” ise,some/path/otheriçinde saklanır.
Bazı durumlarda, checkpoint dosyasına hangi değerlerin ekleneceğine karar vermek biraz daha karmaşık hale gelebilir. Örneğin PEFT'te DoRA, LoRA'nın özel bir durumu olarak uygulanır. Bir DoRA modelini PEFT'ye dönüştürmek istiyorsanız, DoRA için ek girdilere sahip bir LoRA checkpoint'i oluşturmalısınız. Bunu önceki LoraLayer kodunun __init__ bölümünde görebilirsiniz:
self.lora_magnitude_vector: Optional[torch.nn.ParameterDict] = None # for DoRABu, DoRA için katman başına isteğe bağlı ek bir parametre olduğunu gösterir.
adapter_config
Bir PEFT modelini yüklemek için gereken diğer tüm bilgiler adapter_config.json dosyasında yer alır. BERT'e uygulanan bir LoRA modeli için bu dosyayı inceleyelim:
{
"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
}Bu dosyada çok sayıda kayıt bulunur ve ilk bakışta, içine girecek doğru değerlerin hepsini belirlemek bunaltıcı gelebilir. Ancak, modelin yüklenmesi için kayıtların çoğu gerekli değildir. Bunun nedeni, ya varsayılan değerleri kullanmaları ve eklenmelerine gerek olmaması ya da yalnızca LoRA ağırlıklarının başlatılmasını etkilemeleri ve modelin yüklenmesi açısından bunun önemsiz olmasıdır. Belirli bir parametrenin ne işe yaradığını bilmiyorsanız, örneğin "use_rslora", onu eklemeyin ve sorun yaşamazsınız. Ayrıca, daha fazla seçenek eklendikçe bu dosyanın gelecekte daha fazla kayıt içereceğini, ancak geriye dönük uyumlu kalması gerektiğini de unutmayın.
En azından aşağıdaki girişleri dahil etmelisiniz:
{
"target_modules": ["query", "value"],
"peft_type": "LORA"
}Ancak, mümkün olduğunca çok girdi eklemek, örneğin r derecesi veya base_model_name_or_path (Transformers modeli ise) önerilir. Bu bilgiler, başkalarının modeli daha iyi anlamasına ve daha kolay paylaşmasına yardımcı olabilir. Beklenen anahtarların ve değerlerin neler olduğunu kontrol etmek için PEFT kaynak kodundaki config.py dosyasına (örneğin, bu LoRA için yapılandırma dosyasıdır) göz atın.
Model depolama
Bazı durumlarda, temel ağırlıklar dahil olmak üzere PEFT modelinin tamamını depolamak isteyebilirsiniz. Örneğin, PEFT modelini yüklemeye çalışan kullanıcıların temel modele erişimi yoksa bu gerekebilir. Ağırlıkları birleştirebilir veya modeli bir Transformer modeline dönüştürebilirsiniz.
Ağırlıkları birleştir
PEFT modelinin tamamını depolamanın en doğrudan yolu, adaptör ağırlıklarını temel ağırlıklarla birleştirmektir:
merged_model = model.merge_and_unload() merged_model.save_pretrained(...)
Ancak bu yaklaşımın bazı dezavantajları vardır:
- merge_and_unload() çağrıldıktan sonra, PEFT'ye özgü işlevsellikten yoksun temel bir model elde edersiniz. Bu, PEFT'ye özgü yöntemlerin artık kullanılamayacağı anlamına gelir.
- Ağırlıkları birleştirmeyi geri alamaz, birden fazla adaptörü aynı anda yükleyemez, adaptörü devre dışı bırakamaz vb.
- Tüm PEFT yöntemleri ağırlıkların birleştirilmesini desteklemez.
- Bazı PEFT yöntemleri genel olarak birleştirme işlemine izin verebilir, ancak belirli ayarlarla birlikte çalışmayabilir (ör. belirli kuantizasyon teknikleri kullanıldığında).
- Tüm model, PEFT modelinden çok daha büyük olacaktır, çünkü tüm temel ağırlıkları da içerecektir.
Ancak birleştirilmiş bir modelle çıkarım biraz daha hızlı olmalıdır.
Bir Transformers modeline dönüştür
Taban modelin bir Transformers modeli olduğunu varsayarak tüm modeli kaydetmenin bir başka yolu, PEFT ağırlıklarını doğrudan taban modele yerleştirip onu kaydetmek için bu hileli yaklaşımı kullanmaktır; bu, Transformers'ı PEFT modelinin bir PEFT modeli olmadığına inandırdığınızda işe yarar. Bu, diğer adaptörlerin Transformers'ta uygulanmamış olması nedeniyle yalnızca LoRA ile çalışır.
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>)