# توسيع مُصدِّرات

يتتبع `torch.export` النموذج في رسم بياني، ثم تحوّل المراحل اللاحقة ذلك الرسم البياني، وتُصدر المرحلة النهائية أو تُخفضه للتشغيل على بيئة التشغيل المستهدفة. تمر معظم النماذج دون أي تعديل. عندما يكون هناك نمط في PyTorch لا تستطيع الخلفية التعامل معه، يطبّق المُصدِّر حلاً بديلاً بسيطاً في تلك المرحلة بدلاً من تعديل النموذج.

أضف حلًا بديلًا من خلال كتابة دالة واحدة وتسجيلها باستخدام مُزيِّن. يجب أن ينتمي كل حل بديل إلى أدنى مرحلة يمكن التعبير عنه فيها بوضوح.

## التصحيحات والإصلاحات

الحل البديل هو إما رقع أو إصلاح. يختلف الاثنان في إمكانية التراجع عنهما.

|              | إصدار                                                                          | إصلاح                                                                                   |
| ------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| ما يفعله | يستبدل خاصية (عملية `torch`، أو عنصر داخلي في ExecuTorch، أو طريقة في النموذج) طوال مدة التصدير | يعيد كتابة الرسم البياني أو البرنامج المُتتبَّع                                                  |
| تم التراجع     | نعم، يتم استعادة الأصل بعد ذلك                                        | لا، بل يُصلح العنصر قبل تشغيل المرحلة التالية                                |
| سجّل مع | `@register_patch(backend, *paths)`                                            | `@register_fx_node_fix(backend)` أو `@register_fx_program_fix(backend)`               |

توجد كلاهما في سجل داخل [exporters/utils.py](https://github.com/huggingface/transformers/blob/main/src/transformers/exporters/utils.py)، ويقوم المُصدِّر بتثبيت كل ما تم تسجيله لخلفية تشغيله في المرحلة المناسبة.

لجأ إلى تطبيق رقع (patch) عندما يكون الخلل ناتجًا عن خطأ في عملية التحويل (lowering) في واجهة خلفية واحدة: مثل غياب ترجمة إلى ONNX، أو غرابة في التحقق من صحة ORT، أو تفكيك FX يُنتج عملية ميتة. يبقى الحل البديل داخل المُصدِّر (exporter)، وتبقى كود نمذجة (modeling) نظيفًا.

عندما تُصدَّر كتل الأنماط عبر عدة واجهات خلفية، مثل الحلقات المعتمدة على البيانات، أو ذاكرات التخزين المؤقت ذات الحالة خارج `Cache`، أو انتباه حلقة مقسومة مكتوبة يدوياً، فصحّح النموذج بدلاً من ذلك. إن إجراء التصحيح مرة واحدة في النموذج يفيد كل مُصدِّر.

## إضافة رقعة

افترض أن طريقة في النموذج تقوم بشيء لا يمكن لـ`torch.export` تتبّعه. يقوم `NllbMoeTop2Router._cast_classifier` في NLLB-MoE بتحويل أوزان المصنّف إلى نوع بيانات آخر، وهو أمر غير قابل للتتبّع. استبدله بعملية لا شيء (no-op) طوال مدة التصدير.

اكتب مصنعًا يستقبل الطريقة الأصلية ويعيد بديلها، ثم سجّل المصنع مقابل المسار المنقّط للطريقة:

```python
from transformers.exporters.utils import register_patch

@register_patch("dynamo", "transformers.models.nllb_moe.modeling_nllb_moe.NllbMoeTop2Router._cast_classifier")
def _patch_classifier_cast(_original):
    # Replace the untraceable dtype cast with a no-op during export.
    return lambda self, *args, **kwargs: None
```

يستبدل المُصدِّر الطريقة قبل التتبع ثم يعيدها إلى حالتها الأصلية بعد ذلك، لذا فإن التصحيح يؤثر فقط على التصدير. بعض التغيرات:

- مرّر مسارات إضافية لمشاركة مصنع واحد عبر مواقع الاستدعاء، على سبيل المثال `@register_patch("dynamo", path_a, path_b)`.
- قم بتعديل عملية `torch` عن طريق توجيه المسار إليها، على سبيل المثال `@register_patch("onnx", "torch.where")`. تستقبل الدالة المصنّعة العملية الفعلية كحجة لها، مما يتيح للبدائل استدعاء العملية الأصلية.
- اكتب إصلاحًا (fix) بدلاً من رقع (patch) عندما تحتاج إلى إعادة كتابة الرسم البياني بعد التتبع. الآلية هي نفسها، وهي دالة مُزيَّنة (decorated) في السجل المطابق.

## مرجع المرحلة

تُوسِّم مصادر كل مُصدِّر مراحله بتعليقات على شكل `# ── Stage N: … ──`، مما يجعل الملف يتوافق مع هذا المرجع. راجع تلك المصادر لمعرفة العمليات والفئات الدقيقة التي يعالجها كل مرحلة.

### DynamoExporter

تُنفّذ أداة التصدير الأساسية مرحلة تصحيح واحدة وأربعة مساعدين، بالترتيب، داخل `DynamoExporter.export` (انظر [exporter_dynamo.py](https://github.com/huggingface/transformers/blob/main/src/transformers/exporters/exporter_dynamo.py)).

1. إصلاح التوقيع الأمامي: يمنح `model.forward` توقيعا مسطحا للحجج حتى لا يجمع `torch.export` المدخلات في مجموعة `**kwargs` واحدة. هذا داخلي وليس نقطة توسيع.
2. رقعات النموذج: استبدل طرق النموذج غير القابلة للتتبع أثناء التتبع بنظائر آمنة للتصدير. وسّع باستخدام `@register_patch("dynamo", ...)`.
3. تسجيل Pytree: سجّل كل من `Cache` و`ModelOutput` بحيث يستطيع `torch.export` تسطيحها وإعادة بنائها (يحدث ذلك عادةً تلقائيًا). أضف فرعًا إلى `_flatten_to_context` / `_unflatten_from_context` لنوع لا يمكن الوصول إليه عبر فحص السمات.
4. الأشكال الديناميكية: اسند `Dim.AUTO` إلى كل متجه وخزّن الورقة عند `dynamic=True`. يمكن تجاوز ذلك عبر `DynamoConfig.dynamic_shapes`.
5. تنظيف الحالة: إعادة تعيين خصائص الموترات التي يضبطها النموذج داخل `forward` والتي يتركها `torch.export` كموترات وهمية. قم بالتمديد عن طريق إضافة اسم الخاصية إلى `_STATEFUL_CACHE_ATTRS`.

### OnnxExporter

يضيف `OnnxExporter` خمس مراحل حول `torch.onnx.export` (انظر [exporter_onnx.py](https://github.com/huggingface/transformers/blob/main/src/transformers/exporters/exporter_onnx.py)). ابحث في الملف عن القائمة الكاملة للترقيعات:

```bash
grep -nE "^def (_patch_|_fix_|_aten_)" src/transformers/exporters/exporter_onnx.py
```

1. رقعات Torch: استبدال عمليات `torch` التي لا يستطيع مُصدِّر ONNX ترجمتها كما هي. قم بالتوسّع باستخدام `@register_patch("onnx", ...)`.
2. إصلاحات ONNX: أعد تشغيل إصلاحات العقد بعد `run_decompositions` حتى تُصلح عقد حراسة الأشكال الجديدة قبل التحويل. يستخدم نفس سجل `@register_patch("onnx", ...)`.
3. إصلاحات عقد FX: إعادة كتابة عقد الرسم البياني التي لا يستطيع مُصدِّر ONNX تحويلها، مثل عمليات الاستنساخ (alias ops)، وعروض الموقع (in-place views)، والتحققات غير الفعّالة (dead asserts). وسّع ذلك باستخدام `@register_fx_node_fix("onnx")`.
4. ترجمات ONNX: قدّم تحويلًا مخصصًا لعملية aten في حال كان التحويل الافتراضي مفقودًا أو يحتوي على أخطاء (مثل `aten.index_put` أو `aten._grouped_mm`). أضف دالة `_aten_*` إلى `_ONNX_TRANSLATION_TABLE`.
5. إصلاحات ONNX IR: إعادة كتابة برنامج ONNX المكتمل لتجاوز أخطاء ONNX Runtime (على سبيل المثال، فرض `TopK(sorted=True)`). أضف دالة `_fix_ir_*` إلى `_IR_FIXES`.

### ExecutorchExporter

`ExecutorchExporter` يضيف خمس مراحل حول `to_edge_transform_and_lower` و`to_executorch`، بدءًا من تجهيز الخلفية (انظر [exporter_executorch.py](https://github.com/huggingface/transformers/blob/main/src/transformers/exporters/exporter_executorch.py)).

1. تجهيز الخلفية: انقل النموذج إلى الجهاز ونوع البيانات المستهدفين واختر مُقسِّمه (`prepare_for_xnnpack`، `prepare_for_cuda`). أضف خلفية جديدة عن طريق تسجيل `prepare_for_<name>` في `_BACKEND_PREPARE`.
2. رقعات Torch: استبدال عمليات `torch` التي لا تقبلها واجهات ExecuTorch الخلفية، مثل `split_copy` و`chunk` و`topk(k>dim)`. قم بالتوسعة باستخدام `@register_patch("executorch", ...)`.
3. رقعات ExecuTorch: استبدال مكوّنات ExecuTorch الداخلية التي تتسبب في انهيار البرامج ذات الأشكال الديناميكية الصالحة. تستخدم نفس سجل `@register_patch("executorch", ...)`.
4. إصلاحات برنامج FX: إصلاح البرنامج المُصدَّر عندما يتطلب الإصلاح سياق البرنامج الكامل، مثل توسيع قيود المدى أو ملء بيانات الوصف المفقودة للمواضع الوهمية. قم بالتمديد باستخدام `@register_fx_program_fix("executorch")`.
5. إصلاحات عقد FX: إعادة كتابة العقد الفردية، مثل تعيين عمليات Python الرمزية إلى `executorch_prim.*` أو إعادة كتابة `pow` كسلسلة من `mul`. قم بالتوسعة باستخدام `@register_fx_node_fix("executorch")`.

## حلول مؤقتة معروفة في المنبع

تتعرّض بعض فئات النماذج إلى أخطاء مؤكدة في مُحسِّن الرسوم البيانية في `onnxscript` (مثل انهيار طيّ الثوابت عند `SplitToSequence`، أو حذف مُهيِّئات FPN). يُعطِّل [ONNX_DISABLE_OPTIMIZE](https://github.com/huggingface/transformers/blob/main/tests/exporters/test_export.py) تحسين `onnxscript` لتلك النماذج. يسجّل كل بند المشكلة في المصدر المجاور لاسم النموذج. ومن المتوقع أن يتقلّص هذا القائمة مع معالجة الأخطاء في المصدر، لذا يجب أن يُحيل أي بند جديد إلى خطأ محدد في المصدر بدلاً من تعطيل التحسين بشكل تعسفي.

[EXPORT_SKIPS](https://github.com/huggingface/transformers/blob/main/tests/exporters/test_export.py)، يستثني عددًا من فئات النماذج من عملية التصدير بالكامل عندما يكون النموذج غير قابل للتصدير بشكل أساسي كما هو (تدفق تحكم يعتمد على البيانات ولا يمكن تحويله إلى متجهات، أو وحدات تُعامل كحجوزات للأمام). يحمل كل عنصر سببًا يحدد التغيير المطلوب على جانب النموذج. ومن المتوقع أن تتقلص هذه القائمة، لا أن تتوسع.

