PEFT documentation
ShadowPEFT
ShadowPEFT
يعزز ShadowPEFT نموذج فك الترميز الأساسي المجمد (decoder-only) بشبكة ظل خفيفة الوزن وقابلة للتدريب المسبق تعمل بالتوازي مع الهيكل العظمي. ينتج هيكل ظل صغير حالة ظل أولية s^(0)، ثم تنضم هذه الحالة إلى حلقة فك الترميز في النموذج الأساسي: في كل كتلة مستهدفة، يُحقن الفرق بين الحالات المخفية الأساسية وحالة الظل مرة أخرى في مدخلات الكتلة (تصحيح منخفض الرتبة)، وتُقدَّم حالة الظل عبر تحديث متبقي مُتحكَّم به (gated residual update) يُحسب من مخرجات الكتلة. يتم تدريب مكونات الظل فقط؛ بينما يبقى النموذج الأساسي مجمداً.
Input
├──► Shadow backbone (small, trainable) ──► s^(0)
└──► Base model (frozen, large)
block_0 ◄── inject(h, s) ─► h_0 ──► update ─► s_1
block_1 ◄── inject(h, s) ─► h_1 ──► update ─► s_2
... (the (hidden, shadow) pair rides the loop together)بما أن التكيف هو مسار يعتمد على المدخلات في فضاء الطبقات (حيث يتطور الحالة الظل مع البيانات) وليس فرقًا ثابتًا في فضاء الأوزان، فإن ShadowPEFT لا يمكن دمجها مع الأوزان الأساسية. يؤدي استدعاء merge أو merge_adapter أو merge_and_unload إلى رفع خطأ صريح. بالنسبة لنماذج اللغة في Transformers، يمكنك الحصول على الشبكة الظل الخفيفة الوزن بشكل مستقل باستخدام model.base_model.unload_shadow()، والتي تُرجع DetachedShadowModel مستقلًا. لا يُدعم التفريغ المستقل لنماذج Diffusers لأن إعادة بناء مُزيل الضوضاء الكامل تعتمد على البنية المعمارية.
تعمل إضافة عدة محوِّلات، والتبديل بينها باستخدام set_adapter، وحذفها، وتفعيلها/تعطيلها جميعها بنفس طريقة عمل طرق PEFT الأخرى. لا يمكن تفعيل محوِّل واحد في كل مرة، لأن حالة الظل هي مسار واحد عبر الشبكة.
يمكن بناء الهيكل الظلي بطريقتين، يتحكم فيهما ShadowConfig.shadow_model:
"mirror"(الافتراضي): يتم إنشاء عمود فقري ظلّي أصغر حجمًا تلقائيًا. تستخدم نماذج اللغة نسخة مخفّضة من البنية الأساسية. تستخدم بنيات Diffusers التي لديها واجهة خلفية مسجّلة نموذجًا مشابهًا للبنية المخفّضة ومهيّأً من أوزان أساسية مختارة؛ بينما تعتمد البنيات المتوافقة التي لا تملك واجهة خلفية على شبكة MLP متبقية على مستوى الرمز. عندما يختلف حجم الإخفاء الظلّي عن الأساسي، تُجسر شبكة إسقاط مدرّبة هذا الفارق.- معرف نموذج أو مسار محلي: يتم تحميل العمود الفقري كنموذج مُدرَّب مسبقًا أصغر حجمًا. تستخدم نماذج Transformers
AutoModel؛ بينما تُعرّف واجهات Diffusers المسجلة تحميل نقاط التفتيش المتوافقة الخاصة بها.
يتم تحديد دعم Diffusers الواعي بالبنية تلقائيًا من فئة النموذج. حاليًا، يوجد مُنفِّذ مسجَّل لـ Flux2؛ بينما تستخدم نماذج Diffusers الأخرى المتوافقة القائمة على محولات عامة آلية الاحتياط العامة MLP. تبقى الاستدعاء المستقل لـ unload_shadow() غير مدعوم لجميع نماذج Diffusers لأن إعادة بناء مُزيل الضوضاء الكامل تعتمد على البنية.
مقارنةً بطرق LoRA، يضيف ShadowPEFT مزيدًا من المعاملات والحسابات (حيث يشغّل شبكةً موازيةً ويلفّ كتل فك الترميز كاملةً)، لكن المُكيّف هو شبكةٌ مكتفيةٌ ذاتيًا يمكن تدريبها مركزياً، وإعادة استخدامها عبر المهام، وتبديؤها من نموذجٍ صغيرٍ مُدرَّب مسبقاً. الخسارة الإضافية الاختيارية (auxiliary_loss_weight) تطبّق رأس المهمة على الحالة الظلية الأولية s^(0) وتضيفها إلى خسارة المهمة، مما يشجّع المسار الظلي القابل للفصل على حلّ المهمة بمفرده. بالنسبة لنماذج اللغة السببية، يُعاد استخدام رأس الإخراج الأساسي؛ شمل "lm_head" في modules_to_save لتدريبه وحفظه عبر آلية PEFT القياسية.
ذاكرة التخزين المؤقت KV
يدعم ShadowPEFT فك الترميز التدرّجي مع ذاكرة KV مزدوجة: واحدة للنموذج الأساسي المتجمّد والأخرى لهيكل الظل. عمليات الحقن/التحديث محلية على مستوى الرمز، لذا لا يحتاج الرمز الجديد سوى حالة ظله الخاصة s؛ وتُبقي السببية مفاتيح/قيم البادئة الأساسية (المُحسوبة تحت الحقن) صالحة. الكائن المرافق هو ShadowCache، ويُعاد بوصفه past_key_values عند use_cache=True. يمكنك تمرير use_cache=True إلى generate() كما هو معتاد.
out = model.generate(input_ids, max_new_tokens=32) # dual KV cache enabled by default
# or explicitly:
out = model.generate(input_ids, use_cache=True, max_new_tokens=32)لا يزال use_cache=False يعمل ويعيد معالجة التسلسل الكامل في كل خطوة (مفيد للتصحيح).
الاستخدام
from transformers import AutoModelForCausalLM
from peft import ShadowConfig, get_peft_model
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-0.6B")
config = ShadowConfig(r=8, shadow_num_hidden_layers=1, task_type="CAUSAL_LM")
model = get_peft_model(model, config)
model.print_healthcheck()
out = model.generate(input_ids, max_new_tokens=32)لتشغيل العمود الفقري الظلّي من نموذج مُدرَّب مسبقًا أصغر حجمًا، مرّر معرّفه أو مساره كـshadow_model:
config = ShadowConfig(shadow_model="Qwen/Qwen3-0.6B", task_type="CAUSAL_LM")
model = get_peft_model(base_model, config)تقييم مسار الظل
بشكل افتراضي، يكون مخرج النموذج (logits) هو النموذج الأساسي المُكيَّف بالظل: حيث تُحقن تصحيحات الظل في الحالات المخفية للنموذج الأساسي في كل طبقة، لذا فإن logits يعكس بالفعل ShadowPEFT (استخدم model.disable_adapter() للحصول على النموذج الأساسي العادي للمقارنة). بالإضافة إلى ذلك، يدرب الخسارة المساعدة مسار الظل على حل المهمة بمفرده.
لتقييم الشبكة الظل المستقلة لنموذج لغة Transformers (النموذج القابل للفصل والخفيف —
المقابل لـ merge_and_unload في ShadowPEFT)، استخدم unload_shadow(). يعيد هذا الدالة head(projection(backbone(x))) كنموذج مهمة عادي يمكنك تقييمه مثل أي نموذج آخر: بالنسبة لمهمة LM السببية، يكون
نموذج LM سببيًا قادرًا على التوليد (يدعم generate() وتخزين KV)، وبالنسبة لمهمة تصنيف التسلسل، فإنه يجمع
الرمز الأخير ويعيد قيم logits للفئات. استدعاء هذه الدالة لنموذج Diffusers يثير NotImplementedError.
shadow = model.base_model.unload_shadow() # a DetachedShadowModel (a PreTrainedModel)
shadow.eval()
# causal LM:
out = shadow.generate(input_ids, max_new_tokens=32)
# sequence classification:
logits = shadow(input_ids=input_ids, attention_mask=attention_mask).logits # (batch, num_labels)بشكل افتراضي (copy=False)، يتشارك النموذج المُعاد وحداته مع نموذج PEFT، ويصل إلى هذه الوحدات عبر مرجع ليس فرعيًا من الوحدات الفرعية، عمود فقري ظلّي يتشارك ترميزات الإدخال الأساسية المجمّدة. هذا مقبول للتقييم، لكنه يعني أن save_pretrained سيكتب نقطة تحقق دون جدول الترميزات. مرّر copy=True عندما تريد حفظ النموذج المستقل أو رفعه:
shadow = model.base_model.unload_shadow(copy=True)
shadow.save_pretrained("standalone-shadow")واجهة برمجة التطبيقات
ShadowConfig
فئة peft.ShadowConfig
< source >( task_type: Optional[Union[str, TaskType]] = Nonepeft_type: Optional[Union[str, PeftType]] = Noneauto_mapping: Optional[dict] = Nonepeft_version: Optional[str] = Nonebase_model_name_or_path: Optional[str] = Nonerevision: Optional[str] = Noneinference_mode: bool = Falsetarget_modules: list[str] | str | None = Noneexclude_modules: list[str] | str | None = Noner: int = 8shadow_alpha: float = 0.1shadow_dropout: float = 0.2init_weights: bool = Trueshadow_model: str = 'mirror'shadow_num_hidden_layers: int | None = Noneshadow_hidden_size: int | None = Noneshadow_num_attention_heads: int | None = Noneshadow_intermediate_size: int | None = Noneshare_embeddings: bool = Trueupdate_hidden_size: int | None = Noneauxiliary_loss_weight: float = 0.05layers_to_transform: list[int] | int | None = Nonelayers_pattern: list[str] | str | None = Nonemodules_to_save: list[str] | None = None )
المعاملات
- target_modules (
Optional[Union[list[str], str]]) — كتل التحويل (transformer blocks) التي سيتم تغليفها بآلية الظل (whole decoder blocks, not linear layers). يمكن أن تكون قائمة بأسماء الوحدات، أو تعبيرًا نمطيًا (regex)، مثلr'.*\.layers\.\d+$'لاستهداف كل كتل فك الترميز. القيمة الافتراضية هيNone، وفي هذه الحالة يتم تغليف كل كتل فك الترميز في النموذج الأساسي. لاحظ أن الكتل المغلفة يجب أن تكون متتالية، لأن حالة الظل (shadow state) تسير مع حلقة فك الترميز من أول كتلة مغلفة إلى آخرها. - exclude_modules (
Optional[Union[list[str], str]]) — أسماء الوحدات التي لا تُغلَّف بآلية الظل، معطاةً كقائمة أو كنمط تعبير نمطي. القيمة الافتراضية هيNone. - r (
int) — رتبة عنق الزجاجة الحقن منخفض الرتبةW_down/W_up. القيمة الافتراضية هي8. - shadow_alpha (
float) — قوة التصحيح المُحقن المضاف إلى مدخل الكتلة (المعادلة 4). القيمة الافتراضية هي0.1. - shadow_dropout (
float) — احتمالية الإسقاط (dropout) المطبقة على إشارة الاختلاف قبل عنق الزجاجة (المعادلة 3). القيمة الافتراضية هي0.2. - init_weights (
bool) — ما إذا كان سيتم تهيئةW_upبالقيمة الصفرية بحيث يكون الحقن بلا تأثير في بداية التدريب (على غرار اتفاقيةB=0في LoRA). لا تغيّر هذا الإعداد ما لم تكن على دراية تامة بما تفعله. القيمة الافتراضية هيTrue. - shadow_model (
str) — طريقة بناء الهيكل الخلفي الظلي."mirror"(الافتراضي) يبني هيكلًا خلفيًا جديدًا بنفس بنية النموذج الأساسي ولكن بطبقات أقل/أصغر (انظر تجاوزاتshadow_*أدناه). تستخدم بنيات Diffusers التي لديها واجهة خلفية ظلية مسجّلة نموذجًا مشابهًا للبنية المختزلة؛ بينما تعتمد محولات الانتشار المتوافقة الأخرى على محول متعدد الطبقات العام (MLP) على مستوى الرمز. يُعامل أي نص آخر كمعرّف نموذج أو مسار محلي. يُحمّل نقطة التفتيش الظلية "المُسقطة" من Transformers (model_type == "causal_lm_with_hidden_projection"، مثلshadow-llm/Qwen3-0.6B-H8B) هيكلها الخلفي المُدرَّب مسبقًا معًا مع إسقاطها المُدرَّب من المخفي الظلي إلى المخفي الأساسي. تتطلب نقاط التفتيش الصريحة من Diffusers واجهة خلفية مسجّلة؛ حاليًا يتم دعم Flux2. - shadow_num_hidden_layers (
Optional[int]) — عمق العمود الفقري الظلّي المُنشأ تلقائيًا ("mirror"). بالنسبة إلى Flux2، يُنشئ هذا العدد المطلوب من كتل التدفق المزدوج وعددًا مضاعفًا من كتل التدفق الأحادي؛ أما في حالة بديل Diffusers العام، فهو عدد كتل MLP المتبقية. القيمة الافتراضية هيNone(1طبقة). - shadow_hidden_size (
Optional[int]) — حجم الطبقة المخفية للهيكل الخلفي الظلي المُنشأ تلقائيًا؛ قد يختلف عن حجم الطبقة المخفية الأساسي (حيث يُدرج إسقاط تلقائيًا). قد تفرض واجهات البنية الخلفية قيودًا إضافية؛ ويجب أن تحافظ اختزالات Flux2 على بُعد رأس الانتباه الأساسي الخاص بها. القيمة الافتراضية هيNone(نفس نموذج الأساس). - shadow_num_attention_heads (
Optional[int]) — عدد رؤوس الانتباه في العمود الفقري الظللي المُنشأ تلقائيًا. القيمة الافتراضية هيNone(نفس نموذج الأساس). تُستخدم هذه الخيارات بواسطة الخوادم الواعية بالبنية، لكن لا تُستخدم بواسطة بديل Diffusers العام لشبكات MLP. - shadow_intermediate_size (
Optional[int]) — عرض الشبكة الأمامية (feed-forward) للهيكل الخلفي الظلي (shadow backbone) المُنشأ تلقائيًا. القيمة الافتراضية هيNone(وهي نفسها حجم الإخفاء (hidden size) في حالة استخدام بديل MLP العام في Diffusers). - share_embeddings (
bool) — ما إذا كان يجب إعادة استخدام ترميزات الإدخال الأساسية المجمّدة لتغذية العمود الفقري الظلّي (عبرinputs_embeds) بدلاً من جدول الترميزات الخاص بالعمود الفقري الظلّي نفسه. القيمة الافتراضية هيTrue. - update_hidden_size (
Optional[int]) — العرض الخفي لشبكاتT(المرشح) وG(البوابة) المحدثة. القيمة الافتراضية هيNone(تستخدمr). - auxiliary_loss_weight (
float) — الوزنlambdaلخسارة الظل المساعدة (المعادلتان 8-9) التي تُضاف إلى خسارة المهمة عند تمريرlabels. يمكن لحلقات تدريب الانتشار استخدام القيمة نفسها لخسارة إزالة الضوضاء المنفصلة. اضبطها على0لتعطيلها. القيمة الافتراضية هي0.05. - layers_to_transform (
Optional[Union[list[int], int]]) — فهارس الكتل التي سيتم تحويلها. إذا تم تمرير قائمة، يُطبَّق آلية الظل على الكتل عند تلك الفهارس. إذا تم تمرير عدد صحيح واحد، يُطبَّق عند ذلك الفهرس فقط. القيمة الافتراضية هيNone(يتم تحويل كل كتلة مطابقة). - layers_pattern (
Optional[Union[list[str], str]]) — اسم نمط الطبقة، يُستخدم فقط إذا كانlayers_to_transformمختلفاً عنNone. هذا هو اسمnn.ModuleListالذي يحتوي على كتل فك الترميز (غالباً"layers"أو"h"). القيمة الافتراضية هيNone. - modules_to_save (
Optional[list[str]]) — وحدات إضافية تُضبط لتكون قابلة للتدريب وتُحفظ في نقطة الفحص النهائية (مثل"lm_head"أو رأس مصنّف). القيمة الافتراضية هيNone.
فئة التهيئة لـ ShadowModel (ShadowPEFT).
يعزز ShadowPEFT نموذجًا أساسيًا مجمّدًا يعتمد على المُفكِّك فقط، بشبكة ظلّية صغيرة قابلة للتدريب تعمل بالتوازي. ينتج العمود الفقري الظليّ حالة ظلّية أولية s^(0) ثمّ تتحرك ضمن حلقة المُفكِّك في النموذج الأساسي: في كل كتلة مستهدفة، يُحقن الفرق بين الحالات المخفية الأساسية والحالة الظلية إلى مدخل الكتلة (المعادلات 2-4)، وتُقدَّم الحالة الظلية عبر تحديث متبقّي مُتحكَّم به من مخرج الكتلة (المعادلات 5-7). لا يُدرَّب سوى المكوّنات الظلية؛ ويبقى النموذج الأساسي مجمّدًا. وبما أنّ التكيّف هو مسار يعتمد على المدخلات في فضاء الطبقات وليس فرقًا ثابتًا في الأوزان، فلا يمكن دمج ShadowPEFT في أوزان النموذج الأساسي.
ShadowModel
فئة peft.ShadowModel
< source >( modelpeft_config: Union[PeftConfig, dict[str, PeftConfig]]adapter_name: strlow_cpu_mem_usage: bool = Falsestate_dict: Optional[dict[str, torch.Tensor]] = None )
ينشئ نموذج ShadowPEFT من نموذج transformers مُدرَّب مسبقًا.
تُوسّع ShadowPEFT نموذجًا قاعديًا مجمّدًا يعتمد على المُفكِّك فقط، عبر إضافة شبكة ظلّية صغيرة قابلة للتدريب. ينتج العمود الفقري الظلّي حالة ظلّية أولية تسير مع حلقة المُفكِّك القاعدي؛ ففي كل كتلة مستهدفة، يُحقن الفرق بين الحالات المخفية القاعدية والحالة الظلّية داخل الكتلة، وتُقدَّم الحالة الظلّية عبر تحديث متبقّي مُتحكَّم به. لا يُدرَّب سوى المكوّنات الظلّية. راجع ShadowConfig للاطلاع على الإعدادات.
لا يمكن دمج الطريقة في الأوزان الأساسية (إذ إن التكيّف هو مسار يعتمد على المدخلات وليس فرقاً ثابتاً في الأوزان)؛ استخدم ShadowModel.unload_shadow() للحصول على شبكة الظل المستقلة بدلاً من ذلك.
خسارة المهمة الخاصة بمسار الظل، CE(shadow_head(s^(0)), labels) (غير موزونة؛ يطبّق forward الوزن).
يُحسب الخسارة على الحالة الظلية الأولية s^(0) (مخرج العمود الفقري الظلي، المُسقط) — وهو بالضبط ما يحسبه نموذج unload_shadow() المستقل على أنه head(projection(backbone(x))). وهذا ما يجعل الشبكة الظلية المنفصلة قابلة للاستخدام بشكل مستقل؛ فلو تم تدريبها على s^(L) (الحالة النهائية، التي تعتمد على مخرجات النموذج الأساسي لكل طبقة ولا توجد بشكل مستقل)، لظل النموذج المنفصل غير مدرَّب.
unload_shadow
< source >( adapter_name: str | None = Nonecopy: bool = False )
المعاملات
- adapter_name (
str، اختياري) — المُكيّف الذي سيتم تفريغ الشبكة الظلية الخاصة به. الافتراضي هو المُكيّف النشط. - copy (
bool، اختياري، القيمة الافتراضيةFalse) — إذا كانتTrue، يتم إنشاء نسخة عميقة من النموذج المُعاد ليكون مستقلاً عن هذا النموذج (يستهلك ذاكرة أكثر). إذا كانتFalse(الافتراضي)، تتم مشاركة الوحدات — بشكل مشابه لـmerge_and_unload، الذي يعيد استخدام الوحدات بدلاً من استنساخها. أي تعديل على أحد النموذجين سيؤثر على الآخر.
أعد الهيكل الخلفي الظلي (+ الرأس) كنموذج مستقل، بدون النموذج الأساسي.
المقابل الخاص بـ ShadowPEFT للدالة merge_and_unload: حيث تُرجع تلك الدالة النموذج الأساسي مع التكيف مدمجًا فيه، تُرجع هذه الدالة شبكة الظل الخفيفة الوزن فقط للاستدلال عالي الكفاءة / على الحافة. تعمل الدالة عبر head(projection(backbone(x))) — فالتحديثات لكل كتلة تتطلب المخرجات الأساسية، وبالتالي لا يمكنها الوجود بشكل مستقل. بالنسبة لنماذج اللغة، يتصرف الناتج كنموذج سببي عادي (يدعم generate() وتخزين KV). لا تُدعم نماذج Diffusers لأن إعادة بناء مُزيل الضوضاء الكامل المستقل تعتمد على كل معمارية انتشار على حدة.
اسند النتيجة إلى متغير واستخدمها؛ مع copy=False تبقى الوحدات مشتركة مع هذا النموذج.
مرّر copy=True عندما تنوي save_pretrained النموذج المستقل. الهيكل الخلفي الظلّي الذي يتشارك تضمينات الإدخال المجمّدة للقاعدة (إعداد "mirror" الافتراضي) يصل إليها عبر مرجع ليس فرعيًا من النموذج المُعاد، لذا فإن نقطة التفتيش بـ copy=False تفتقر إلى جدول التضمينات؛ يعيد copy=True إرفاق نسخة خاصة ويحفظ نقطة تفتيش كاملة.
ShadowCache
ذاكرة KV المزدوجة لفك ترميز ShadowPEFT التزايدي.
يتطلب التوليد التتابعي ذاكرة تخزين مؤقتة للنموذج الأساسي (مفاتيح/قيم محسوبة تحت حقن الظل) و ذاكرة تخزين مؤقتة منفصلة لنموذج الظل (لتقديم رمز s^(0) رمزًا تلو الآخر). هذا هو فرع من فئات Cache في Transformers: تُطبَّق العمليات التي تؤثر على تخطيط الدفعة أو التسلسل (إعادة الضبط، إعادة الترتيب، القص، التكرار، والاختيار) على كلا الذاكرتين المؤقتتين، بينما تُفوّض بيانات التتبع الوصفية للانتباه والتحديثات إلى الذاكرة المؤقتة الأساسية. كما تُفوّض سمات الذاكرة المؤقتة غير المعروفة إلى الذاكرة المؤقتة الأساسية لضمان التوافق مع تنسيقات الذاكرة المؤقتة الخاصة بكل معمارية.
تم تصميم الغلاف عمداً بحيث لا يمكن تجميعه (compile) لأن المخبأين (caches) يمكن أن يكون لديهما تخطيطات طبقات وأحجام مخفية مختلفة. لا يُدعم التحويل إلى tuples قديمة (legacy) لأن tuple قديماً واحداً لا يمكنه تمثيل المسارين معاً. يتم فك حزمة النصف الظلّي (shadow half) قبل التقدّم الأساسي (base forward) ثم يُعاد تغليفه عند الخروج — راجع خطافات ShadowModel.