# المساهمة

يدعم Transformers العديد من طرق التكميم مثل QLoRA وGPTQ وLLM.int8 وAWQ. ومع ذلك، لا تزال هناك العديد من أساليب التكميم الأخرى التي لم يتم دمجها بعد. لتسهيل إضافة هذه طرق التكميم واستخدامها مع Transformers، استخدم فئة [HfQuantizer](/docs/transformers/main/ar/main_classes/quantization#transformers.quantizers.HfQuantizer). صُممت فئة [HfQuantizer](/docs/transformers/main/ar/main_classes/quantization#transformers.quantizers.HfQuantizer) لتكون فئة مساعدة داخلية لإضافة طريقة تكميم، بدلاً من أن تكون شيئاً يُطبَّق على كل وحدة من وحدات PyTorch.

ستُظهر لك هذه الدليل كيفية دمج طريقة تكميم جديدة مع [HfQuantizer](/docs/transformers/main/ar/main_classes/quantization#transformers.quantizers.HfQuantizer).

## المتطلبات

قبل دمج طريقة تكميم جديدة في Transformers، تأكد من أن الطريقة تستوفي المتطلبات التالية. يتم دعم طرق التكميم التي يمكن تشغيلها باستخدام وحدات PyTorch فقط.

- يتوفر أسلوب التكميم عبر حزمة Python يمكن تثبيتها باستخدام pip (ولا بأس إذا كان بإمكانك تثبيت الحزمة من المصدر فقط). في المثالي، تتضمن حزمة pip نوى مُجمّعة مسبقًا.
- يمكن تشغيل الطريقة على الأجهزة الشائعة الاستخدام (وحدة المعالجة المركزية، وحدة معالجة الرسوميات، إلخ).
- يُغلَّف الأسلوب داخل [nn.Module](https://pytorch.org/docs/stable/generated/torch.nn.Module.html) (`~bitsandbytes.nn.Linear8bitLt`، `~bitsandbytes.nn.Linear4bit`)، وينبغي أن يكون للخطية المُكمَّمة التعريف التالي.

  ```py
  class Linear4bit(nn.Module):
      def __init__(self, ...):
          ...

      def forward(self, x):
          return my_4bit_kernel(x, self.weight, self.bias)
  ```

  بهذه الطريقة، يمكن تكميم نماذج Transformers بسهولة عن طريق استبدال حالات [nn.Linear](https://pytorch.org/docs/stable/generated/torch.nn.Linear.html) بفئة مستهدفة.

- يجب أن تكون طريقة التكميم قابلة للتسلسل. يمكنك حفظ الأوزان المكمّمة محليًا أو رفعها إلى Hub.
- تأكد من أن الحزمة التي تحتوي على نواة/الأساسيات (primitives) للكمّ (quantization) مستقرة (لا تتضمن تغييرات متكررة تكسر التوافق).

قد تتطلب بعض طرق التكميم "تكميمًا مسبقًا" للنموذج من خلال معايرة البيانات (AWQ). في هذه الحالة، نفضّل دعم الاستدلال فقط في Transformers، وترك مهمة تكميم النموذج نفسه لمكتبة الطرف الثالث التي تُدار من قبل مجتمع تعلم الآلة.

## إنشاء فئة HFQuantizer جديدة

0. أفضل نقطة انطلاق هي الاطلاع على طريقة تكميم أخرى مثل Finegrained Fp8. ستحتاج إلى تحديث أو إنشاء ثلاثة ملفات إجمالاً: [ملف الإعدادات](https://github.com/huggingface/transformers/blob/main/src/transformers/utils/quantization_config.py)، و[ملف التكامل](https://github.com/huggingface/transformers/blob/main/src/transformers/integrations/finegrained_fp8.py)، و[ملف مُكمِّم](https://github.com/huggingface/transformers/blob/main/src/transformers/quantizers/quantizer_finegrained_fp8.py).

1. أنشئ فئة إعدادات تكميم جديدة داخل [src/transformers/utils/quantization_config.py](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/utils/quantization_config.py). أضف فئة إعدادات التكميم الجديدة إلى [\_import_structure](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/__init__.py#L1088) داخل ملف [src/transformers/\_\_init\_\_.py](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/__init__.py) الخاص بـ Transformers.

2. أنشئ ملفًا جديدًا داخل [src/transformers/quantizers/](https://github.com/huggingface/transformers/tree/abbffc4525566a48a9733639797c812301218b83/src/transformers/quantizers) باسم `quantizer_your_method.py`، واجعله يرث من [`~quantizers.HfQuantizer`]. تأكد من إضافة مُكمِّم (quantizer) جديد وإعدادات الكمّ (quantization config) في خرائط الكمّ التلقائية (quantization auto-mapping) الموجودة في [src/transformers/quantizers/auto.py](https://github.com/huggingface/transformers/blob/abbffc4525566a48a9733639797c812301218b83/src/transformers/quantizers/auto.py).

3. حدّد سمات الفئة التالية وطرق الخاصية لطريقة التكميم الخاصة بك:

   - `requires_calibration`: ما إذا كانت طريقة التكميم تتطلب عملية معايرة للبيانات. إذا تم ضبطها على `True`، يمكنك فقط دعم الاستدلال (بأوزان مُكمَّمة) وليس الاستدلال والتكميم معاً.
   - `is_serializable`: طريقة خاصية لتحديد ما إذا كانت الطريقة قابلة للتسلسل أم لا.
   - `is_trainable`: طريقة خاصية لتحديد ما إذا كان بإمكانك ضبط النماذج الدقيقة فوق طريقة التكميم (مع أو بدون أساليب PEFT).

4. اكتب طريقتي `validate_environment` و`update_dtype`. تُستدعى هاتان الطريقتان قبل إنشاء النموذج المُكمَّم للتأكد من استخدام المستخدمين الإعداد الصحيح. راجع مُكمِّمات أخرى كمثال على كيفية تنفيذها.

5. اكتب طريقة `_process_model_before_weight_loading`. في Transformers، يتم تهيئة النماذج المُكمّاة أولاً على جهاز `"meta"` قبل تحميل الأوزان. وهذا يعني أن طريقة `_process_model_before_weight_loading` تتولى تعديل هيكل النموذج لاستبدال بعض الوحدات ([nn.Linear](https://pytorch.org/docs/stable/generated/torch.nn.Linear.html)) بالوحدات المستهدفة (وحدات الكمّ).

يمكنك تحديد منطق استبدال الوحدات أو أي طريقة مساعدة أخرى من خلال إنشاء ملف جديد في [transformers/src/integrations/](https://github.com/huggingface/transformers/tree/abbffc4525566a48a9733639797c812301218b83/src/transformers/integrations) وتعرض الطرق ذات الصلة في ملف `__init__.py` الخاص بذلك المجلد.

6. أضف طريقة `get_quantize_ops` إلى فئة المُكمِّم إذا كان التكميم يدعم التكميم أثناء التشغيل. في transformers، نُجسِّد كل متجه (tensor) ونطبّق سلسلة من العمليات المختلفة عليه. في حالتنا، يحدث عملية التكميم في النهاية. عليك إنشاء `XXXQuantize`، وهي فئة فرعية من `ConversionOps`، وإضافة طريقة `convert`. في طريقة `convert`، عليك تكميم الأوزان وإرجاع قاموس (dictionary) بالمعاملات المُكمَّمة.

7. أضِف طريقة `get_weight_conversions` إلى فئة المُكمِّم إذا كان التكميم يدعم تحميل الأوزان المُكمَّمة مسبقًا. في transformers، يمكننا جمع عدة متجهات وتطبيق عمليات عليها. وهذا مفيد بشكل خاص عندما يكون لدينا متجهات في نقطة الفحص (checkpoint) تتطلب إعادة تجميعها لإعادة إنشاء المتجهات المُكمَّمة.

8. اكتب طريقة `_process_model_after_weight_loading` عند الحاجة. تتيح هذه الطريقة تنفيذ ميزات إضافية تتطلب التلاعب بالنموذج بعد تحميل الأوزان.

9. وثّق كل شيء! تأكد من توثيق طريقة التكميم الخاصة بك عن طريق إضافة ملف جديد تحت `docs/source/en/quantization`.

10. يجب عليك إضافة الاختبارات عن طريق إضافة الحزمة إلى ملف Dockerfile الخاص بنا في `docker/transformers-quantization-latest-gpu` ثم إضافة ملف اختبار جديد في `tests/quantization/xxx`. يمكنك الاطلاع على طرق التكميم الحالية لرؤية كيفية تنفيذها.

## نظرة عامة على الملفات

| ملف                                         | الغرض                                                                                          |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `utils/quantization_config.py`               | حدّد `YourMethodConfig` بحيث يرث من `QuantizationConfigMixin`                              |
| `quantizers/quantizer_your_method.py`        | نفّذ `YourMethodHfQuantizer` بحيث يرث من `HfQuantizer`                                  |
| `integrations/your_method.py`                | تنفيذ فئات فرعية من `ConversionOps` والدوال المساعدة                                        |
| `quantizers/auto.py`                         | سجّل مُكمِّم الإعدادات في `AUTO_QUANTIZER_MAPPING` و`AUTO_QUANTIZATION_CONFIG_MAPPING` |
| `docs/source/en/quantization/your_method.md` | استخدام المستند للمستخدمين                                                                         |
| `tests/quantization/your_method/`            | إضافة اختبارات التكامل                                                                            |

## فهم `get_quantize_ops` مقابل `get_weight_conversions`

تعتمد هاتان الطريقتان على سيناريوهات مختلفة لتحميل الأوزان. ومن الضروري فهم متى يُستخدم كلٌّ منهما.

### `get_quantize_ops` — تكميم أثناء التشغيل

استخدم هذا عند تحميل **نقطة تحقق غير مُكمّاة** (مثل أوزان float16/bfloat16) وإجراء التكميم أثناء التحميل.

```text
Checkpoint: model.safetensors (float16 weights for example)
     ↓
get_quantize_ops → YourQuantize.convert()
     ↓
Result: Quantized weights in memory
```

يستقبل أسلوب `convert` متجهًا واحدًا في كل مرة، ويقوم بتكميمه، ويمكنه إرجاع قاموس للمعاملات المكمّمة، على سبيل المثال:

```py
class YourQuantize(ConversionOps):
    def convert(self, input_dict, model, full_layer_name, missing_keys, **kwargs):
        # input_dict = {"layer.weight": <float16 tensor>}
        value = list(input_dict.values())[0]
        module, tensor_name = get_module_from_name(model, full_layer_name)

        # Quantize and assign
        quantized, scale, zero_point = your_quantize_fn(value)
        return {full_layer_name: quantized, full_layer_name + ".scale": scale, full_layer_name + ".zero_point": zero_point}
```

### `get_weight_conversions` — تحميل نقاط التفتيش المُكمّاة مسبقًا

استخدم هذا عند تحميل **نقطة تحقق مُكمّاة مسبقًا** حيث تُحفظ الأوزان المكمّاة كمكوّنات منفصلة متعددة (مثل البيانات، والمقياس، ونقطة الصفر)، ويتطلب الأمر دمجها في متجه واحد أثناء التحميل. لا تتطلب جميع طرق التكميم هذه الخطوة لإعادة البناء؛ فبعض الطرق مثل FP8 تقوم ببساطة بتحميل الأوزان والمقاييس كما هي دون دمجها. بينما تتطلب طرق أخرى، مثل torchao، إعادة تجميع المتجه المكمّ من مكوّناته المحفوظة المتعددة.

```text
Checkpoint: model.safetensors (quantized components)
  - layer._weight_qdata
  - layer._weight_scale
  - layer._weight_zero_point
     ↓
get_weight_conversions → WeightConverter + YourDeserialize.convert()
     ↓
Result: Reconstructed quantized tensor → layer.weight
```

يجمع `WeightConverter` المتجهات ذات الصلة بناءً على `source_patterns`، ثم يمررها إلى طريقة `convert` الخاصة بك:

```py
def get_weight_conversions(self):
    if self.pre_quantized:
        return [
            WeightConverter(
                source_patterns=["_weight_qdata", "_weight_scale", "_weight_zero_point"],
                target_patterns="weight",
                operations=[YourDeserialize(self)],
            ),
        ]
    return []

class YourDeserialize(ConversionOps):
    def convert(self, input_dict, model, full_layer_name, **kwargs):
        # input_dict contains all collected tensors
        # Reconstruct the quantized tensor from components
        reconstructed_tensor = reconstruct_from_components(input_dict)
        return {full_layer_name: reconstructed_tensor}
```

