# تصدير الكمّ

يمكن لكل إعداد [تصدير](./exporters) أن يستقبل `quantizer`، يقوم بتكميم النموذج أثناء التصدير. وتوجد أدوات التكميم في `transformers.exporters.quantizers`، وينفّذ كلٌّ منها في نقطة ثابتة من عملية التصدير: إما على رسم `torch.export` FX، قبل أي تحويل، أو على النموذج الذي يحوّله أحد الخلفيات.

| مُكمِّم | يكمّم | Dynamo | ONNX | ExecuTorch | OpenVINO |
| --- | --- | --- | --- | --- | --- |
| [PT2EQuantizer](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.PT2EQuantizer) | مخطط FX، مع تكميم تصدير PyTorch 2 (PT2E) | 🟢 | 🟢 | 🟢 | 🟢 |
| [NNCFTorchFXQuantizer](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.NNCFTorchFXQuantizer) | رسم بياني FX، مع [NNCF](https://github.com/openvinotoolkit/nncf) | 🟢 | 🟢 | 🟢 (الأوزان فقط) | 🟢 |
| [OnnxRuntimeQuantizer](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.OnnxRuntimeQuantizer) | نموذج ONNX، باستخدام `onnxruntime.quantization` | 🔴 | 🟢 | 🔴 | 🔴 |
| [NNCFOnnxQuantizer](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.NNCFOnnxQuantizer) | نموذج ONNX، مع [NNCF](https://github.com/openvinotoolkit/nncf) | 🔴 | 🟢 | 🔴 | 🔴 |
| [NNCFOpenVINOQuantizer](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.NNCFOpenVINOQuantizer) | نموذج OpenVINO، مع [NNCF](https://github.com/openvinotoolkit/nncf) | 🔴 | 🔴 | 🔴 | 🟢 |

يتحقق التصدير من مُكمِّم (quantizer) مقابل الخلفية (backend) والبرامج الحزمة المثبتة قبل التتبع (tracing)، لذا فإن أي عدم تطابق يؤدي إلى فشل مبكر. وكما هو الحال مع مُصدِّرات (exporters)، يُدرج كل مُكمِّم إصدارات الحزم التي تم اختبارها عليها، ويصدر تحذيراً إذا كانت إصداراتك مختلفة.

يُكمّم المثال أدناه مصنّف المشاعر إلى int8 باستخدام ONNX Runtime، ثم يشغّله.

```python
from transformers import AutoModelForSequenceClassification, AutoTokenizer
from transformers.exporters import OnnxConfig, OnnxExporter, OnnxRuntimeQuantizer

model_id = "distilbert/distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)

texts = [
    "A wonderful, heartfelt film.",
    "The plot was dull and the acting worse.",
    "I would watch it again tomorrow.",
    "Two hours I will never get back.",
]
calibration_dataset = [tokenizer(text, return_tensors="pt") for text in texts]
inputs = tokenizer("An absolute delight from start to finish.", return_tensors="pt")

config = OnnxConfig(dynamic=True, quantizer=OnnxRuntimeQuantizer(calibration_dataset=calibration_dataset))
onnx_program = OnnxExporter().export(model, inputs, config)

logits = onnx_program(**inputs)[0]
print(model.config.id2label[int(logits.argmax())])  # POSITIVE
```

## PT2E

يغلّف [PT2EQuantizer](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.PT2EQuantizer) مُكمِّم PT2E، سواءً من torchao أو من وقت التشغيل المستهدف، وينفذه على مخطط FX (`prepare_pt2e` → معايرة → `convert_pt2e`)، مما يجعله يعمل مع كل وحدة خلفية. يُدخِل كل مُكمِّم PT2E عمليات التكميم/إلغاء التكميم الخاصة به، لذا اختر ذلك الذي يدعمه وقت التشغيل لديك:

| المكان الذي ستشغّل فيه | مكمّم PT2E | استيراد من |
| --- | --- | --- |
| PyTorch inductor، ONNX Runtime (QDQ)، OpenVINO | `X86InductorQuantizer` | `torchao.quantization.pt2e.quantizer.x86_inductor_quantizer` |
| خلفية ExecuTorch XNNPACK | `XNNPACKQuantizer` | `executorch.backends.xnnpack.quantizer.xnnpack_quantizer` |

في ExecuTorch، قم بتغليف `XNNPACKQuantizer` الخاص بـ ExecuTorch نفسه:

```python
from executorch.backends.xnnpack.quantizer.xnnpack_quantizer import (
    XNNPACKQuantizer,
    get_symmetric_quantization_config,
)
from transformers.exporters import ExecutorchConfig, PT2EQuantizer

xnnpack = XNNPACKQuantizer().set_global(get_symmetric_quantization_config())
config = ExecutorchConfig(backend="xnnpack", quantizer=PT2EQuantizer(xnnpack, calibration_dataset=calibration_dataset))
```

قم بتكميم النماذج التوليدية عبر `~HfExporter.export_for_generation`. هنا، يحتوي `calibration_dataset` على معاملات التوليد، مثل `inputs`، ويقوم كل مكوّن بالتعاير على المدخلات التي تم التقاطها له. يحتوي النموذج النصي فقط على مكوّن `prefill` ومكوّن `decode`؛ بينما يقسم النموذج متعدد الوسائط مكوّن المعايرة الأولية (prefill) بشكل إضافي إلى مشفّرات الوسائط (مثل `image_encoder`)، ومشغّله (projector)، ونموذج اللغة الخاص به `language_model`، ورأسه اللغوي `lm_head` (انظر [كيف يعمل `export_for_generation`](./exporters#how-exportforgeneration-works)).

```python
from torchao.quantization.pt2e.quantizer.x86_inductor_quantizer import (
    X86InductorQuantizer,
    get_default_x86_inductor_quantization_config,
)
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.exporters import DynamoConfig, DynamoExporter, PT2EQuantizer

model_id = "meta-llama/Llama-3.2-1B"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id)

prompts = ["The capital of France is", "Photosynthesis turns sunlight into", "The first moon landing was in"]
calibration_dataset = [tokenizer(prompt, return_tensors="pt") for prompt in prompts]
inputs = tokenizer("Once upon a time", return_tensors="pt")

x86 = X86InductorQuantizer().set_global(get_default_x86_inductor_quantization_config())
config = DynamoConfig(dynamic=True, quantizer=PT2EQuantizer(x86, calibration_dataset=calibration_dataset))
components = DynamoExporter().export_for_generation(model, inputs, config)  # {"prefill": ..., "decode": ...}
```

في OpenVINO، تبقى أوزان PT2E بدقة كاملة خلف أزواج الكميّة/إلغاء الكميّة، حيث يقوم OpenVINO بتحويلها إلى `FakeQuantize` وضغطها إلى int8 بنفسه.

## ONNX Runtime

يُنفّذ [OnnxRuntimeQuantizer](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.OnnxRuntimeQuantizer) بشكل افتراضي `quantize_static` (تفعيلات وأوزان من نوع int8 QDQ، مُعايرة على `calibration_dataset`)، كما في المثال أعلاه، مع استخدام معاملات [OnnxRuntimeStaticConfig](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.OnnxRuntimeStaticConfig). أما عند استخدام [OnnxRuntimeDynamicConfig](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.OnnxRuntimeDynamicConfig) فيُنفّذ `quantize_dynamic` (أوزان من نوع int8، وتفعيلات تُكمّمة وقت التشغيل، دون معايرة).

```python
from transformers.exporters import OnnxRuntimeDynamicConfig, OnnxRuntimeStaticConfig

config = OnnxConfig(
    quantizer=OnnxRuntimeQuantizer(OnnxRuntimeStaticConfig(per_channel=True), calibration_dataset=calibration_dataset)
)
config = OnnxConfig(quantizer=OnnxRuntimeQuantizer(OnnxRuntimeDynamicConfig()))
```

## NNCF

تعمل مُكمّسات NNCF الثلاثة المذكورة في الجدول أعلاه على `nncf.quantize` افتراضياً (تفعيلات وأوزان من نوع int8، مع معايرة على `calibration_dataset`، مع `model_type=nncf.ModelType.TRANSFORMER` ما لم تُمرّر نوعاً آخر)، باستخدام معاملات [NNCFQuantizationConfig](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.NNCFQuantizationConfig). عند استخدام [NNCFWeightCompressionConfig](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.NNCFWeightCompressionConfig)، تعمل على `nncf.compress_weights` (أوزان فقط من نوع int8 أو int4، مع AWQ وتقدير المقياس لـ int4، بالإضافة إلى GPTQ وتصحيح LoRA على OpenVINO). يخفض ExecuTorch فقط نتيجة الأوزان فقط من [NNCFTorchFXQuantizer](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.NNCFTorchFXQuantizer).

```python
import nncf
from transformers.exporters import (
    DynamoConfig,
    NNCFOnnxQuantizer,
    NNCFOpenVINOQuantizer,
    NNCFTorchFXQuantizer,
    NNCFWeightCompressionConfig,
    OnnxConfig,
    OpenVINOConfig,
    OpenVINOExporter,
)

int4 = NNCFWeightCompressionConfig(mode=nncf.CompressWeightsMode.INT4_SYM, group_size=128)

# OpenVINO: int8 activations and weights, or weight-only int4
openvino_int8 = OpenVINOConfig(quantizer=NNCFOpenVINOQuantizer(calibration_dataset=calibration_dataset))
openvino_int4 = OpenVINOConfig(quantizer=NNCFOpenVINOQuantizer(int4))
# ONNX: weight-only int4, as ONNX Runtime's `MatMulNBits`
onnx_int4 = OnnxConfig(quantizer=NNCFOnnxQuantizer(int4))
# Dynamo: int8 on the FX graph
dynamo_int8 = DynamoConfig(quantizer=NNCFTorchFXQuantizer(calibration_dataset=calibration_dataset))

ov_model = OpenVINOExporter().export(model, inputs, openvino_int4)
```

تعمل التفعيلات من نوع Int8 على عمليات ضرب المصفوفات (matmuls) من نوع int8، والتي تصل إلى حد التشبع (saturate) على وحدات المعالجة المركزية التي لا تدعم VNNI (مثل معالجات Intel التي تدعم AVX2 فقط، ومعالجات AMD Zen 2 والأقدم)، لذا يُتوقع انخفاض الدقة هناك. أما ضغط الأوزان فقط فيُبقي عمليات ضرب المصفوفات في نظام الفاصلة العائمة.

## المعايرة

تُعدّ خاصية `calibration_dataset` في مُكمِّم (quantizer) قابلةً للتكرار (iterable) من مدخلات النموذج، مثل قائمة من مخرجات المُرمِّز (tokenizer) أو `DataLoader` تُجمَّع دفعاته (batches) لتُشكِّل مدخلات النموذج. وهي خاصية عادية، لذا يمكنك تعيينها بعد إنشاء المُكمِّم (`quantizer.calibration_dataset = samples`). إذا حذفتها، فسيُجري المُكمِّم الذي يحتاج إلى بيانات عملية المعايرة (calibration) على مدخلات العيّنات الخاصة بالتصدير نفسه، مع إصدار تحذير، لأن عيّنة واحدة قد تُشوِّه النطاقات المرصودة. أما المُكمِّمات التي لا تحتاج إلى بيانات (كمعايرة ONNX Runtime الديناميكية، وضغط الأوزان بصيغة int8) فلا تقرأ هذه الخاصية إطلاقًا.

## وصفة مختلفة لكل مكوّن

مرّر قاموس `{component: config}` إلى `~HfExporter.export_for_generation` لتكميم كل مكوّن على حدة. بالنسبة للنموذج أعلاه، استخدم int8 ثابتًا على `prefill` و int8 ديناميكيًا أخف على `decode`:

```python
dynamic_x86 = X86InductorQuantizer().set_global(get_default_x86_inductor_quantization_config(is_dynamic=True))

config = {
    "prefill": DynamoConfig(dynamic=True, quantizer=PT2EQuantizer(x86)),
    "decode": DynamoConfig(dynamic=True, quantizer=PT2EQuantizer(dynamic_x86)),
}
components = DynamoExporter().export_for_generation(model, inputs, config)
```

يجب أن يسمّي القاموس كل مكوّن ينتجه [decompose_for_generation()](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.utils.decompose_for_generation) (يضيف نموذج متعدد الوسائط مشفّراته، وقد يفصل `language_model` و`lm_head`). يبقى المكوّن الذي لا يحدد مُكمِّم في ملفه الإعدادي بدقة كاملة. في هذا القاموس، يحمل `calibration_dataset` الخاص بالمُكمِّم معاملات استدعاء الأمامي للمكوّن، وليس معاملات generate.

## إضافة مُكمِّم

توجد مُكمِّمات في `transformers.exporters.quantizers`، فئة واحدة لكل سلسلة أدوات وتمثيل. ورِّث [ExportQuantizer](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.ExportQuantizer) وأعلن:

- `quantizes_fx_graph`: `True` لرسم `torch.export` FX، قبل أي تحويل (لذا يتلقى كل طرف خلفي النتيجة)، أو `False` (الافتراضي) للنموذج الذي يحوله الطرف الخلفي (`onnx_ir.Model`، `openvino.Model`).
- `supported_formats`: صيغ التصدير التي يقوم بتكميمها. يرفض التصدير أي صيغة أخرى قبل التتبع.
- `required_packages`، وإختياريًا `min_versions` و`tested_versions`: ما يستورده. يتحقق التصدير من تثبيتها قبل التتبع، ويرفع خطأً إذا كانت الإصدار أدنى من الحد الأدنى، ويصدر تحذيرًا عندما يختلف الإصدار عن الإصدار المُختبَر.

ثم نفّذ `quantize(model, sample_inputs, export_format)`. حيث يمثل `model` رسم FX أو نموذج الخلفية، بينما تمثل `sample_inputs` مدخلات التصدير الخاصة به. قم بالمعايرة على [CalibrationSet](/docs/transformers/main/ar/main_classes/exporters#transformers.exporters.quantizers.CalibrationSet): إذ يقرأ مجموعة بيانات المعايرة `calibration_dataset` (أو `sample_inputs` في حال عدم وجودها) ويحوّل كل عيّنة إلى مدخلات النموذج الخاصة به عبر تغذية من `transformers.exporters.quantizers.utils` (`traced_inputs` لرسم FX، و`onnx_feed` لـ ONNX، و`openvino_feed` لـ OpenVINO).

```python
from transformers.exporters import ExportFormat
from transformers.exporters.quantizers import CalibrationSet, ExportQuantizer
from transformers.exporters.quantizers.utils import onnx_feed

class MyOnnxQuantizer(ExportQuantizer):
    supported_formats = (ExportFormat.ONNX,)
    required_packages = ("my_toolkit",)
    tested_versions = {"my_toolkit": "1.0.0"}

    def quantize(self, model, sample_inputs, export_format):
        import my_toolkit

        names = [graph_input.name for graph_input in model.graph.inputs]
        calibration = CalibrationSet(self.calibration_dataset, sample_inputs, lambda sample: onnx_feed(names, sample))
        return my_toolkit.quantize(model, calibration_feeds=list(calibration))
```

