PEFT documentation
Özel modeller
Özel modeller
Prompt tuning gibi bazı ince ayar teknikleri, dil modellerine özgüdür. Bu da 🤗 PEFT'te bir 🤗 Transformers modelinin kullanıldığı varsayıldığını gösterir. Ancak, LoRA gibi diğer ince ayar teknikleri belirli model türleriyle sınırlı değildir.
Bu kılavuzda, LoRA'nın çok katmanlı algılayıcıya, timm kütüphanesinden bir bilgisayar görme modeline veya yeni bir 🤗 Transformers mimarisine nasıl uygulanabileceğini göreceğiz.
Çok katmanlı algılayıcı
Bir çok katmanlı algılayıcıyı LoRA ile ince ayar yapmak istediğimizi varsayalım. Tanım şu şekildedir:
from torch import nn
class MLP(nn.Module):
def __init__(self, num_units_hidden=2000):
super().__init__()
self.seq = nn.Sequential(
nn.Linear(20, num_units_hidden),
nn.ReLU(),
nn.Linear(num_units_hidden, num_units_hidden),
nn.ReLU(),
nn.Linear(num_units_hidden, 2),
nn.LogSoftmax(dim=-1),
)
def forward(self, X):
return self.seq(X)Bu, bir giriş katmanı, bir gizli katman ve bir çıkış katmanına sahip basit bir çok katmanlı algılayıcıdır.
Bu basit örnek için, PEFT'nin sağladığı verimlilik kazanımlarını vurgulamak amacıyla aşırı büyük bir gizli birim sayısı seçtik, ancak bu kazanımlar daha gerçekçi örneklerle de uyumludur.
Bu modelde LoRA ile ayarlanabilecek birkaç doğrusal katman bulunmaktadır. Yaygın 🤗 Transformers modelleriyle çalışırken PEFT, LoRA'nın hangi katmanlara uygulanacağını bilir, ancak bu durumda katmanları seçmek kullanıcı olarak bize kalmaktadır. Ayarlanacak katmanların adlarını belirlemek için:
print([(n, type(m)) for n, m in MLP().named_modules()])Bunun şu şekilde yazdırması gerekir:
[('', __main__.MLP),
('seq', torch.nn.modules.container.Sequential),
('seq.0', torch.nn.modules.linear.Linear),
('seq.1', torch.nn.modules.activation.ReLU),
('seq.2', torch.nn.modules.linear.Linear),
('seq.3', torch.nn.modules.activation.ReLU),
('seq.4', torch.nn.modules.linear.Linear),
('seq.5', torch.nn.modules.activation.LogSoftmax)]LoRA'yı giriş katmanına ve gizli katmana uygulamak istediğimizi varsayalım, bunlar 'seq.0' ve 'seq.2'. Ayrıca, çıktı katmanını LoRA olmadan güncellemek istediğimizi varsayalım, bu da 'seq.4' olur. İlgili yapılandırma şudur:
from peft import LoraConfig
config = LoraConfig(
target_modules=["seq.0", "seq.2"],
modules_to_save=["seq.4"],
)Böylece PEFT modelimizi oluşturabilir ve eğitilen parametrelerin oranını kontrol edebiliriz:
from peft import get_peft_model
model = MLP()
peft_model = get_peft_model(model, config)
peft_model.print_healthcheck()Son olarak, peft_model'i eğitmek için istediğimiz herhangi bir eğitim çerçevesini kullanabilir veya kendi fit döngümüzü yazabiliriz.
Tam bir örnek için bu defterine göz atın.
timm modelleri
timm kütüphanesi, büyük miktarda önceden eğitilmiş bilgisayarla görme modeli içerir. Bu modeller PEFT ile ince ayar da yapılabilir. Bunun pratikte nasıl çalıştığa bakalım.
Başlamak için, Python ortamında timm'in yüklü olduğundan emin olun:
python -m pip install -U timm
Sıradaki adımımız, bir görüntü sınıflandırma görevi için bir timm modeli yüklemektir:
import timm
num_classes = ...
model_id = "timm/poolformer_m36.sail_in1k"
model = timm.create_model(model_id, pretrained=True, num_classes=num_classes)Yine, LoRA'nın hangi katmanlara uygulanacağına karar vermemiz gerekiyor. LoRA, 2B konvolüsyon katmanlarını desteklediği ve bu katmanların bu modelin temel yapı taşlarından biri olduğu için, LoRA'yı 2B konvolüsyon katmanlarına uygulamalıyız. Bu katmanların adlarını belirlemek için tüm katman adlarına bakalım:
print([(n, type(m)) for n, m in model.named_modules()])Bu, çok uzun bir liste basacaktır; yalnızca ilk birkaçını göstereceğiz:
[('', timm.models.metaformer.MetaFormer),
('stem', timm.models.metaformer.Stem),
('stem.conv', torch.nn.modules.conv.Conv2d),
('stem.norm', torch.nn.modules.linear.Identity),
('stages', torch.nn.modules.container.Sequential),
('stages.0', timm.models.metaformer.MetaFormerStage),
('stages.0.downsample', torch.nn.modules.linear.Identity),
('stages.0.blocks', torch.nn.modules.container.Sequential),
('stages.0.blocks.0', timm.models.metaformer.MetaFormerBlock),
('stages.0.blocks.0.norm1', timm.layers.norm.GroupNorm1),
('stages.0.blocks.0.token_mixer', timm.models.metaformer.Pooling),
('stages.0.blocks.0.token_mixer.pool', torch.nn.modules.pooling.AvgPool2d),
('stages.0.blocks.0.drop_path1', torch.nn.modules.linear.Identity),
('stages.0.blocks.0.layer_scale1', timm.models.metaformer.Scale),
('stages.0.blocks.0.res_scale1', torch.nn.modules.linear.Identity),
('stages.0.blocks.0.norm2', timm.layers.norm.GroupNorm1),
('stages.0.blocks.0.mlp', timm.layers.mlp.Mlp),
('stages.0.blocks.0.mlp.fc1', torch.nn.modules.conv.Conv2d),
('stages.0.blocks.0.mlp.act', torch.nn.modules.activation.GELU),
('stages.0.blocks.0.mlp.drop1', torch.nn.modules.dropout.Dropout),
('stages.0.blocks.0.mlp.norm', torch.nn.modules.linear.Identity),
('stages.0.blocks.0.mlp.fc2', torch.nn.modules.conv.Conv2d),
('stages.0.blocks.0.mlp.drop2', torch.nn.modules.dropout.Dropout),
('stages.0.blocks.0.drop_path2', torch.nn.modules.linear.Identity),
('stages.0.blocks.0.layer_scale2', timm.models.metaformer.Scale),
('stages.0.blocks.0.res_scale2', torch.nn.modules.linear.Identity),
('stages.0.blocks.1', timm.models.metaformer.MetaFormerBlock),
('stages.0.blocks.1.norm1', timm.layers.norm.GroupNorm1),
('stages.0.blocks.1.token_mixer', timm.models.metaformer.Pooling),
('stages.0.blocks.1.token_mixer.pool', torch.nn.modules.pooling.AvgPool2d),
...
('head.global_pool.flatten', torch.nn.modules.linear.Identity),
('head.norm', timm.layers.norm.LayerNorm2d),
('head.flatten', torch.nn.modules.flatten.Flatten),
('head.drop', torch.nn.modules.linear.Identity),
('head.fc', torch.nn.modules.linear.Linear)]
]Daha yakından incelediğimizde, 2D conv katmanlarının "stages.0.blocks.0.mlp.fc1" ve "stages.0.blocks.0.mlp.fc2" gibi isimlere sahip olduğunu görüyoruz. Bu katman adlarını özellikle nasıl eşleştirebiliriz? Katman adlarını eşleştirmek için bir düzenli ifade yazabilirsiniz. Bizim durumumuzda r".*\.mlp\.fc\d" düzenli ifadesi işi görecektir.
Ayrıca, ilk örnekte olduğu gibi, çıktı katmanının, bu durumda sınıflandırma başlığının da güncellendiğinden emin olmalıyız. Yukarıda yazdırılan listenin sonuna baktığımızda, adının 'head.fc' olduğunu görebiliriz. Bu düşünceyle, LoRA yapılandırma dosyamız şu şekildedir:
config = LoraConfig(target_modules=r".*\.mlp\.fc\d", modules_to_save=["head.fc"])Sonra, temel modelimizi ve yapılandırmayı get_peft_model işlevine iletmemiz yeterlidir:
peft_model = get_peft_model(model, config) peft_model.print_healthcheck()
Bu, tüm parametrelerin yalnızca %2'sinden azını eğitmemiz gerektiğini göstermektedir; bu da büyük bir verimlilik artışıdır.
Tam bir örnek için bu defterine göz atın.
Yeni transformers mimarileri
Yeni popüler transformers mimarileri yayınlandığında, bunları PEFT'ye hızla eklemek için elimizden geleni yapıyoruz. Kutudan çıktığı gibi desteklenmeyen bir transformers modeliyle karşılaşırsanız endişelenmeyin, yapılandırma doğru ayarlandığı sürece büyük olasılıkla yine de çalışacaktır. Özellikle, uyarlanması gereken katmanları belirlemeniz ve ilgili yapılandırma sınıfını, örneğin LoraConfig, başlatırken bunları doğru şekilde ayarlamanız gerekir. Bu konuda size yardımcı olacak bazı ipuçları aşağıdadır.
İlk adım olarak, ilham almak için mevcut modellere göz atmak iyi bir fikirdir. Bunları PEFT deposundaki constants.py dosyasının içinde bulabilirsiniz. Sık sık, aynı adları kullanan benzer bir mimariyle karşılaşacaksınız. Örneğin, yeni model mimarisi “mistral” modelinin bir varyasyonu ise ve LoRA uygulamak istiyorsanız, TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING içindeki “mistral” girdisinin ["q_proj", "v_proj"] içerdiğini görebilirsiniz. Bu, “mistral” modelleri için LoRA’nın target_modules değerinin ["q_proj", "v_proj"] olması gerektiğini gösterir:
from peft import LoraConfig, get_peft_model
my_mistral_model = ...
config = LoraConfig(
target_modules=["q_proj", "v_proj"],
..., # other LoRA arguments
)
peft_model = get_peft_model(my_mistral_model, config)Eğer bu yardımcı olmazsa, model mimarinizdeki mevcut modülleri named_modules yöntemiyle kontrol edin ve özellikle anahtar, sorgu ve değer katmanlarını belirlemeye çalışın. Bu katmanların adları genellikle c_attn, query, q_proj gibi isimler taşır. Anahtar katmanı her zaman uyarlanmaz ve ideal olarak, bu katmanı dahil etmenin daha iyi performans sağladığını kontrol etmelisiniz.
Ayrıca, doğrusal katmanlar uyarlamanın yaygın hedefleri arasındadır (örneğin QLoRA makalesinde, yazarlar bunların da uyarlanmasını önermektedir). Adları genellikle fc veya dense dizelerini içerir.
PEFT'ye yeni bir model eklemek istiyorsanız, constants.py dosyasında bir giriş oluşturun ve depozituar üzerinde bir çekme isteği (pull request) açın. README dosyasını da güncellemeyi unutmayın.
Parametreleri ve katmanları doğrulayın
PEFT yöntemini modelinize doğru şekilde uygulayıp uygulamadığınızı birkaç farklı yolla doğrulayabilirsiniz.
- Eğitilebilir parametre oranını ve adaptör durumunu incelemek için print_healthcheck() çalıştırın. Parametre oranı beklenenden düşük veya yüksekse, modeli yazdırarak modelin
repr'ini kontrol edin. Bu, modeldeki tüm katman türlerinin adlarını gösterir. Sadece hedeflenen katmanların adaptör katmanlarıyla değiştirildiğinden emin olun. Örneğin, LoRAnn.Linearkatmanlarına uygulanıyorsa, yalnızcalora.Linearkatmanlarının kullanıldığını görmelisiniz.
peft_model.print_healthcheck()
- Uyarlanan katmanları görüntülemenin bir başka yolu, uyarlanan her modülün adını listelemek için
targeted_module_namesözniteliğini kullanmaktır.
print(peft_model.targeted_module_names)Desteklenmeyen modül türleri
LoRA gibi yöntemler, hedef modüller PEFT tarafından destekleniyorsa çalışır. Örneğin, LoRA'yı nn.Linear ve nn.Conv2d katmanlarına uygulamak mümkündür, ancak örneğin nn.LSTM'ye uygulanamaz. PEFT uygulamak istediğiniz ve desteklenmeyen bir katman sınıfı bulursanız, şunları yapabilirsiniz:
- LoRA'da özel modülleri dinamik olarak yönlendirmek için özel bir eşleme tanımlayın
- talep yeterince yüksekse geliştiricilerin bu özelliği uygulayacağı veya kendiniz nasıl uygulayacağınız konusunda size rehberlik edeceği bir issue açın ve özelliği talep edin
LoRA'da özel modüllerin dinamik dağıtımı için deneysel destek
Bu özellik deneyseldir ve topluluk tarafından nasıl karşılandığına göre değişiklik gösterebilir. Bu konuda önemli bir talep olması durumunda herkese açık ve kararlı bir API sunacağız.
PEFT, LoRA için özel modül türlerine yönelik deneysel bir API destekler. LSTMs için bir LoRA uygulamanız olduğunu varsayalım. Normalde, teorik olarak PEFT ile çalışabilecek olsa bile, PEFT'ye bunu kullanmasını söyleyemezdiniz. Ancak bu, özel katmanların dinamik dağıtımı ile mümkündür.
Deneysel API şu anda şu görünüme sahip:
class MyLoraLSTMLayer:
...
base_model = ... # load the base model that uses LSTMs
# add the LSTM layer names to target_modules
config = LoraConfig(..., target_modules=["lstm"])
# define a mapping from base layer type to LoRA layer type
custom_module_mapping = {nn.LSTM: MyLoraLSTMLayer}
# register the new mapping
config._register_custom_module(custom_module_mapping)
# after registration, create the PEFT model
peft_model = get_peft_model(base_model, config)
# do trainingget_peft_model() çağrıldığında, PEFT hedeflenen modül türünü tanımadığı için bir uyarı görürsünüz. Bu durumda bu uyarıyı yoksayabilirsiniz.
Özel bir eşleme sağlandığında, PEFT önce temel modelin katmanlarını özel eşlemeyle karşılaştırır ve bir eşleşme varsa özel LoRA katman türüne yönlendirir. Eşleşme yoksa PEFT, yerleşik LoRA katman türlerinde bir eşleşme arar.
Dolayısıyla bu özellik, mevcut dağıtım mantığını geçersiz kılmak için de kullanılabilir; örneğin PEFT tarafından sağlanan yerine nn.Linear için kendi LoRA katmanınızı kullanmak istiyorsanız.
Özel LoRA modülünüzü oluştururken lütfen mevcut LoRA modüllerindeki kuralları takip edin. Dikkate almanız gereken bazı önemli kısıtlamalar:
- Özel modül
nn.Modulevepeft.tuners.lora.layer.LoraLayersınıflarından devralmalıdır. - Özel modülün
__init__metodu,base_layerveadapter_namekonumsal argümanlarına sahip olmalıdır. Bunun ardından, kullanmak ya da yok saymak serbest olduğunuz ek**kwargsgelir. - Öğrenilebilir parametreler, anahtarın belirli bir adaptörün adıyla eşleştiği bir
nn.ModuleDictveyann.ParameterDictiçinde saklanmalıdır (bir modelin aynı anda birden fazla adaptöre sahip olabileceğini unutmayın). - Bu öğrenilebilir parametre özniteliklerinin adları
"lora_"ile başlamalıdır, örneğinself.lora_new_param = .... - Bazı yöntemler isteğe bağlıdır, örneğin ağırlık birleştirmeyi desteklemek istiyorsanız yalnızca
mergeveunmergeyöntemlerini uygulamanız gerekir.
Şu anda, modeli kaydettiğinizde özel modüle ilişkin bilgiler kalıcı olarak saklanmaz. Modeli yüklerken özel modülleri yeniden kaydetmeniz gerekir.
# saving works as always and includes the parameters of the custom modules
peft_model.save_pretrained(<model-path>)
# loading the model later:
base_model = ...
# load the LoRA config that you saved earlier
config = LoraConfig.from_pretrained(<model-path>)
# register the custom module again, the same way as the first time
custom_module_mapping = {nn.LSTM: MyLoraLSTMLayer}
config._register_custom_module(custom_module_mapping)
# pass the config instance to from_pretrained:
peft_model = PeftModel.from_pretrained(model, tmp_path / "lora-custom-module", config=config)Bu özelliği kullanıyorsanız ve işinize yaradığını düşünüyorsanız ya da sorunlarla karşılaşıyorsanız, GitHub'da bir issue veya tartışma oluşturarak bize bildirin. Bu sayede bu özelliğe olan talebi değerlendirebilir ve talep yeterince yüksekse herkese açık bir API ekleyebiliriz.
Update on GitHub