# نماذج مخصصة

بعض تقنيات الضبط الدقيق، مثل ضبط الموجه (prompt tuning)، مخصصة لنماذج اللغة. وهذا يعني أنه في 🤗 PEFT يُفترض استخدام نموذج 🤗 Transformers. ومع ذلك، لا تقتصر تقنيات الضبط الدقيق الأخرى - مثل [LoRA](../package_reference/lora) - على أنواع محددة من النماذج.

في هذا الدليل، سنرى كيف يمكن تطبيق LoRA على محدد متعدد الطبقات، أو نموذج رؤية حاسوبية من مكتبة [timm](https://huggingface.co/docs/timm/index)، أو بنية جديدة من 🤗 Transformers.

## شبكة عصبية متعددة الطبقات

لنفترض أننا نريد ضبط نموذج محسّن (fine-tune) لشبكة عصبية متعددة الطبقات (multilayer perceptron) باستخدام LoRA. فيما يلي التعريف:

```python
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)
```

هذا مُدرك متعدد الطبقات بسيط يتكوّن من طبقة إدخال وطبقة خفية وطبقة إخراج.

> [!TIP]
> في هذا المثال التوضيحي، اخترنا عددًا كبيرًا بشكل مفرط من الوحدات المخفية لإبراز المكاسب في الكفاءة الناتجة عن PEFT، لكن هذه المكاسب تتوافق مع أمثلة أكثر واقعية.

توجد بعض الطبقات الخطية في هذا النموذج يمكن ضبطها باستخدام LoRA. عند العمل مع نماذج 🤗 Transformers الشائعة، يعرف PEFT الطبقات التي يجب تطبيق LoRA عليها، لكن في هذه الحالة، يتعين علينا كمستخدمين اختيار الطبقات. لتحديد أسماء الطبقات المراد ضبطها:

```python
print([(n, type(m)) for n, m in MLP().named_modules()])
```

يجب أن يطبع هذا:

```
[('', __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 على طبقة الإدخال وطبقة الخفاء، وهما `'seq.0'` و`'seq.2'`. علاوة على ذلك، لنفترض أننا نريد تحديث طبقة الإخراج دون استخدام LoRA، وهي `'seq.4'`. الإعداد المقابل لذلك هو:

```python
from peft import LoraConfig

config = LoraConfig(
    target_modules=["seq.0", "seq.2"],
    modules_to_save=["seq.4"],
)
```

وبذلك، يمكننا إنشاء نموذج PEFT الخاص بنا والتحقق من نسبة المعاملات المُدرَّبة:

```python
from peft import get_peft_model

model = MLP()
peft_model = get_peft_model(model, config)
peft_model.print_healthcheck()
```

أخيرًا، يمكننا استخدام أي إطار عمل للتدريب نرغب فيه، أو كتابة حلقة ملاءمة خاصة بنا، لتدريب `peft_model`.

لمثال كامل، راجع [هذا الدفتر](https://github.com/huggingface/peft/blob/main/examples/multilayer_perceptron/multilayer_perceptron_lora.ipynb).

## نماذج timm

تحتوي مكتبة [timm](https://huggingface.co/docs/timm/index) على عدد كبير من نماذج الرؤية الحاسوبية المُدرَّبة مسبقًا. ويمكن أيضًا ضبط هذه النماذج بدقة باستخدام PEFT. دعونا نطلع على كيفية عمل ذلك عمليًا.

للبداية، تأكد من تثبيت timm في بيئة Python:

```bash
python -m pip install -U timm
```

في الخطوة التالية، نحمّل نموذج timm لمهمة تصنيف الصور:

```python
import timm

num_classes = ...
model_id = "timm/poolformer_m36.sail_in1k"
model = timm.create_model(model_id, pretrained=True, num_classes=num_classes)
```

مرة أخرى، علينا اتخاذ قرار بشأن الطبقات التي سنطبق عليها LoRA. وبما أن LoRA يدعم طبقات الالتفاف ثنائية الأبعاد (2D conv layers)، وبما أن هذه الطبقات تُعدّ عنصراً أساسياً في هذا النموذج، فيجب علينا تطبيق LoRA على طبقات الالتفاف ثنائية الأبعاد. لتحديد أسماء تلك الطبقات، دعونا نطلع على جميع أسماء الطبقات:

```python
print([(n, type(m)) for n, m in model.named_modules()])
```

ستُطبع هذه الجملة قائمة طويلة جداً، وسنعرض أول بضع عناصر فقط:

```
[('', 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)]
 ]
```

عند الفحص الدقيق، نلاحظ أن طبقات الالتفاف ثنائية الأبعاد تحمل أسماء مثل `"stages.0.blocks.0.mlp.fc1"` و`"stages.0.blocks.0.mlp.fc2"`. كيف يمكننا مطابقة أسماء تلك الطبقات تحديداً؟ يمكنك كتابة [تعبير نمطي](https://docs.python.org/3/library/re.html) لمطابقة أسماء الطبقات. في حالتنا، يجب أن يؤدي التعبير النمطي `r".*\.mlp\.fc\d"` المطلوب.

علاوة على ذلك، وكما في المثال الأول، يجب أن نتأكد من تحديث طبقة الإخراج، أي رأس التصنيف في هذه الحالة. بالنظر إلى نهاية القائمة المطبوعة أعلاه، نلاحظ أن اسمها `'head.fc'`. مع وضع ذلك في الاعتبار، إليك إعداد LoRA الخاص بنا:

```python
config = LoraConfig(target_modules=r".*\.mlp\.fc\d", modules_to_save=["head.fc"])
```

ثم نحتاج فقط إلى إنشاء نموذج PEFT عن طريق تمرير النموذج الأساسي والإعداد إلى `get_peft_model`:

```python
peft_model = get_peft_model(model, config)
peft_model.print_healthcheck()
```

يُظهر لنا هذا أنه لا نحتاج إلى تدريب أقل من 2% من جميع المعاملات، وهو ما يمثل مكسبًا هائلًا في الكفاءة.

لمثال كامل، راجع [هذا الدفتر](https://github.com/huggingface/peft/blob/main/examples/image_classification/image_classification_timm_peft_lora.ipynb).

## معماريات Transformers الجديدة

عند إصدار معماريات transformers جديدة وشائعة، نبذل قصارى جهدنا لإضافتها إلى PEFT بسرعة. إذا واجهت نموذج transformers غير مدعوم بشكل افتراضي، فلا تقلق، فمن المرجح أنه سيعمل ما دامت الإعدادات مضبوطة بشكل صحيح. على وجه التحديد، عليك تحديد الطبقات التي يجب تكييفها وإعدادها بشكل صحيح عند تهيئة فئة الإعدادات المقابلة، مثل `LoraConfig`. فيما يلي بعض النصائح لمساعدتك في ذلك.

كخطوة أولى، يُعدّ من الجيد الاطلاع على النماذج الموجودة لاستلهام الأفكار. يمكنك العثور عليها داخل [constants.py](https://github.com/huggingface/peft/blob/main/src/peft/utils/constants.py) في مستودع PEFT. وغالباً ما ستجد بنية معمارية مشابهة تستخدم نفس الأسماء. على سبيل المثال، إذا كانت البنية المعمارية للنموذج الجديد عبارة عن نسخة معدّلة من نموذج "mistral" وتريد تطبيق LoRA، يمكنك ملاحظة أن الإدخال الخاص بـ "mistral" في `TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING` يحتوي على `["q_proj", "v_proj"]`. وهذا يخبرك أنه بالنسبة لنماذج "mistral"، يجب أن تكون `target_modules` الخاصة بـ LoRA هي `["q_proj", "v_proj"]`:

```python
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)
```

إذا لم يُجدِ ذلك نفعًا، فتأكد من الوحدات الموجودة في بنية النموذج الخاص بك باستخدام طريقة `named_modules` وحاول تحديد طبقات الانتباه، لا سيما طبقات المفتاح والاستعلام والقيمة. غالبًا ما تحمل هذه الطبقات أسماء مثل `c_attn`، `query`، `q_proj`، وما إلى ذلك. لا يتم دائمًا تكييف طبقة المفتاح، ومن المثالي أن تتحقق مما إذا كان تضمينها يؤدي إلى أداء أفضل.

علاوة على ذلك، تُعدّ الطبقات الخطية أهدافًا شائعة للتكييف (على سبيل المثال، في [ورقة QLoRA](https://huggingface.co/papers/2305.14314)، يقترح المؤلفون تكييفها أيضًا). وغالبًا ما تحتوي أسماء هذه الطبقات على السلاسل `fc` أو `dense`.

إذا كنت ترغب في إضافة نموذج جديد إلى PEFT، فيرجى إنشاء إدخال في [constants.py](https://github.com/huggingface/peft/blob/main/src/peft/utils/constants.py) وفتح طلب سحب على [المستودع](https://github.com/huggingface/peft/pulls). لا تنسَ تحديث [README](https://github.com/huggingface/peft#models-support-matrix) أيضاً.

## التحقق من المعاملات والطبقات

يمكنك التحقق من أنك طبّقت طريقة PEFT بشكل صحيح على نموذجك بعدة طرق.

* شغّل [print_healthcheck()](/docs/peft/main/ar/package_reference/peft_model#peft.PeftModel.print_healthcheck) لفحص نسبة المعاملات القابلة للتدريب وحالة المُكيّف. إذا كانت نسبة المعاملات أقل أو أعلى من المتوقع، فافحص تمثيل النموذج `repr` عن طريق طباعة النموذج. يُظهر هذا أسماء جميع أنواع الطبقات في النموذج. تأكد من أن الطبقات المستهدفة المقصودة فقط هي التي تم استبدالها بطبقات المُكيّف. على سبيل المثال، إذا تم تطبيق LoRA على طبقات `nn.Linear`، فيجب أن ترى فقط طبقات `lora.Linear` قيد الاستخدام.

```py
peft_model.print_healthcheck()
```

* طريقة أخرى يمكنك من خلالها عرض الطبقات المُكيَّفة هي استخدام خاصية `targeted_module_names` لإدراج اسم كل وحدة تم تكييفها.

```python
print(peft_model.targeted_module_names)
```

## أنواع الوحدات غير المدعومة

لا تعمل طرق مثل LoRA إلا إذا كانت الوحدات المستهدفة مدعومة من قبل PEFT. على سبيل المثال، يمكن تطبيق LoRA على طبقات `nn.Linear` و`nn.Conv2d`، لكن لا يمكن تطبيقها، على سبيل المثال، على `nn.LSTM`. إذا وجدت فئة طبقة تريد تطبيق PEFT عليها غير مدعومة، يمكنك:

 - تعريف خرائط مخصصة لتوجيه وحدات مخصصة ديناميكيًا في LoRA
 -  افتح [قضية](https://github.com/huggingface/peft/issues) واطلب الميزة، حيث سيقوم المسؤولون بتنفيذها أو إرشادك حول كيفية تنفيذها بنفسك إذا كان الطلب على هذا النوع من الوحدات كافياً

### دعم تجريبي للتوجيه الديناميكي للوحدات المخصصة في LoRA

> [!WARNING]
> هذه الميزة تجريبية وقابلة للتغيير، وذلك تبعًا لاستقبال المجتمع لها. سنقدم واجهة برمجة تطبيقات عامة ومستقرة إذا وُجد طلب كبير عليها.

يدعم PEFT واجهة برمجة تجريبية لأنواع وحدات مخصصة لـ LoRA. لنفترض أن لديك تطبيقًا لـ LoRA مخصصًا لشبكات LSTM. في العادة، لن تتمكن من إخبار PEFT باستخدامه، حتى لو كان من الناحية النظرية متوافقًا مع PEFT. ومع ذلك، يصبح هذا ممكنًا عبر التوجيه الديناميكي للطبقات المخصصة.

يبدو الواجهة البرمجية التجريبية حاليًا على النحو التالي:

```python
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 training
```

> [!TIP]
> عند استدعاء [get_peft_model()](/docs/peft/main/ar/package_reference/peft_model#peft.get_peft_model)، ستظهر لك تحذير لأن PEFT لا يتعرف على نوع الوحدة المستهدفة. في هذه الحالة، يمكنك تجاهل هذا التحذير.

عند توفير تعيين مخصص، يتحقق PEFT أولاً من طبقات النموذج الأساسي مقابل التعيين المخصص، ثم يوجّه إلى نوع طبقة LoRA المخصصة إذا وُجد تطابق. وإذا لم يوجد تطابق، يتحقق PEFT من أنواع طبقات LoRA المدمجة بحثاً عن تطابق.

لذلك، يمكن استخدام هذه الميزة أيضًا لتجاوز منطق التوجيه (dispatch) الحالي، على سبيل المثال إذا كنت ترغب في استخدام طبقة LoRA الخاصة بك لـ `nn.Linear` بدلاً من استخدام تلك التي يوفرها PEFT.

عند إنشاء وحدة LoRA المخصصة، يرجى اتباع نفس القواعد المطبقة على [وحدات LoRA الموجودة](https://github.com/huggingface/peft/blob/main/src/peft/tuners/lora/layer.py). بعض القيود المهمة التي يجب مراعاتها:

- يجب أن يرث الوحدة المخصصة من `nn.Module` و`peft.tuners.lora.layer.LoraLayer`.
- يجب أن تحتوي طريقة `__init__` في الوحدة المخصصة على الحجج الموضعية `base_layer` و`adapter_name`. بعد ذلك، توجد حجة `**kwargs` إضافية يمكنك استخدامها أو تجاهلها بحرية.
- يجب تخزين المعاملات القابلة للتعلم في `nn.ModuleDict` أو `nn.ParameterDict`، حيث يتوافق المفتاح مع اسم المحوّل (adapter) المحدد (تذكر أن النموذج يمكن أن يحتوي على أكثر من محوّل في وقت واحد).
- يجب أن تبدأ أسماء هذه الخصائص المعاملات القابلة للتعلم بـ `"lora_"`، على سبيل المثال `self.lora_new_param = ...`.
- بعض الطرق اختيارية، على سبيل المثال، لا تحتاج إلى تنفيذ `merge` و`unmerge` إلا إذا كنت ترغب في دعم دمج الأوزان.

حاليًا، لا يتم الحفاظ على المعلومات المتعلقة بالوحدة المخصصة عند حفظ النموذج. عند تحميل النموذج، يجب عليك تسجيل الوحدات المخصصة مرة أخرى.

```python
# 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)
```

إذا كنت تستخدم هذه الميزة وتجد أنها مفيدة، أو إذا واجهت مشاكل، فأخبرنا عن ذلك من خلال إنشاء مشكلة أو نقاش على GitHub. يتيح لنا ذلك تقدير الطلب على هذه الميزة وإضافة واجهة برمجة تطبيقات عامة إذا كان الطلب مرتفعًا بما يكفي.

