Diffusers documentation

تسريع الاستدلال

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.42.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

تسريع الاستدلال

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

اجمع بين هذه التقنيات واستخدمها معًا لجعل الاستدلال أسرع مما لو استخدمت أي تقنية على حدة.

ستتناول هذه الدليل كيفية تسريع الاستدلال.

نوع بيانات النموذج

تؤثر دقة أوزان النموذج ونوع البيانات على سرعة الاستدلال، لأن الدقة الأعلى تتطلب ذاكرة أكبر للتحميل ووقتًا أطول لتنفيذ العمليات الحسابية. يقوم PyTorch بتحميل أوزان النموذج بنوع float32 أو الدقة الكاملة افتراضيًا، لذا فإن تغيير نوع البيانات هو طريقة بسيطة للحصول على استدلال أسرع بسرعة.

bfloat16
float16
TensorFloat-32

bfloat16 يشبه float16 لكنه أكثر مقاومة للأخطاء العددية. يختلف دعم الأجهزة لـ bfloat16، لكن معظم وحدات معالجة الرسومات الحديثة قادرة على دعم bfloat16.

import torch
from diffusers import StableDiffusionXLPipeline

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"

prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
pipeline(prompt, num_inference_steps=30).images[0]

انتباه منتج نقطي مُصعّد

تحسّن الانتباه الموفّر للذاكرة سرعة الاستدلال و استهلاك الذاكرة!

انتباه حاصل الضرب النقطي المُقَيَّم (SDPA) ينفّذ عدة واجهات خلفية للانتباه، وهي FlashAttention وxFormers وتنفيذ أصلي بلغة C++. يقوم تلقائيًا باختيار الواجهة الخلفية الأكثر ملاءمة لمعداتك.

يتم تفعيل SDPA افتراضيًا إذا كنت تستخدم PyTorch >= 2.0 ولا يلزم إجراء أي تغييرات إضافية على الكود الخاص بك. ومع ذلك، يمكنك تجربة تجارب مع واجهات برمجة تطبيقات الانتباه الأخرى إذا كنت ترغب في اختيار واجهتك الخاصة. يستخدم المثال أدناه مدير السياق torch.nn.attention.sdpa_kernel لتفعيل الانتباه الفعال.

from torch.nn.attention import SDPBackend, sdpa_kernel
import torch
from diffusers import StableDiffusionXLPipeline

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"

prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"

with sdpa_kernel(SDPBackend.EFFICIENT_ATTENTION):
  image = pipeline(prompt, num_inference_steps=30).images[0]

torch.compile

يسرّع torch.compile الاستدلال من خلال ترميز كود PyTorch وعملياته إلى نوى مُحسّنة. يقوم Diffusers عادةً بترميز النماذج الأكثر كثافة في الحسابات مثل UNet أو transformer أو VAE.

فعّل إعدادات المُجمِّع التالية للحصول على أقصى سرعة (راجع القائمة الكاملة لمعرفة المزيد من الخيارات).

import torch
from diffusers import StableDiffusionXLPipeline

torch._inductor.config.conv_1x1_as_mm = True
torch._inductor.config.coordinate_descent_tuning = True
torch._inductor.config.epilogue_fusion = False
torch._inductor.config.coordinate_descent_check_all_directions = True

حمّل UNet وVAE وجمعهما. هناك عدة أوضاع مختلفة يمكنك الاختيار من بينها، لكن "max-autotune" يُحسّن لأقصى سرعة عن طريق التجميع إلى CUDA graph. تُقلّل رسوم CUDA بشكل فعال من العبء التشغيلي من خلال إطلاق عمليات GPU متعددة عبر عملية CPU واحدة.

With PyTorch 2.3.1, you can control the caching behavior of torch.compile. This is particularly beneficial for compilation modes like "max-autotune" which performs a grid-search over several compilation flags to find the optimal configuration. Learn more in the Compile Time Caching in torch.compile tutorial.

يُحسّن تغيير تخطيط الذاكرة إلى channels_last من كفاءة الذاكرة وسرعة الاستدلال.

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.unet.to(memory_format=torch.channels_last)
pipeline.vae.to(memory_format=torch.channels_last)
pipeline.unet = torch.compile(
    pipeline.unet, mode="max-autotune", fullgraph=True
)
pipeline.vae.decode = torch.compile(
    pipeline.vae.decode,
    mode="max-autotune",
    fullgraph=True
)

prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
pipeline(prompt, num_inference_steps=30).images[0]

تكون عملية التجميع بطيئة في المرة الأولى، لكن بمجرد اكتمالها، تصبح أسرع بشكل ملحوظ. حاول استخدام خط الأنابيب المُجمَّع فقط على نفس نوع عمليات الاستدلال. استدعاء خط الأنابيب المُجمَّع على حجم صورة مختلف يعيد تشغيل عملية التجميع، وهي عملية بطيئة وغير فعّالة.

تجميع الأشكال الديناميكية

تأكد دائمًا من استخدام الإصدار اليومي (nightly) من PyTorch للحصول على دعم أفضل.

يحافظ torch.compile على تتبع أشكال المدخلات والشروط، وإذا كانت هذه مختلفة، يعيد تجميع النموذج. على سبيل المثال، إذا تم تجميع نموذج على صورة بدقة 1024x1024 واستُخدم على صورة بدقة مختلفة، فإن ذلك يستدعي إعادة التجميع.

لتجنب إعادة التجميع، أضف dynamic=True لمحاولة توليد نواة (kernel) أكثر ديناميكية لتجنب إعادة التجميع عند تغيّر الشروط.

+ torch.fx.experimental._config.use_duck_shape = False
+ pipeline.unet = torch.compile(
    pipeline.unet, fullgraph=True, dynamic=True
)

يُوجِّه تحديد use_duck_shape=False المُجمِّع (Compiler) بشأن ما إذا كان ينبغي له استخدام المتغير الرمزي نفسه لتمثيل أحجام المدخلات المتطابقة. لمزيد من التفاصيل، راجع هذا تعليق.

قد لا تستفيد جميع النماذج من التجميع الديناميكي بشكل مباشر وقد تتطلب تعديلات. راجع هذا PR الذي حسّن تنفيذ AuraFlowPipeline للاستفادة من التجميع الديناميكي.

لا تتردد في فتح مشكلة إذا لم يعمل التجميع الديناميكي كما هو متوقع لنموذج Diffusers.

التجميع الإقليمي

يقوم التجميع الإقليمي بتقليل زمن بدء التشغيل البارد من خلال تجميع الكتلة أو الكتل الصغيرة والمتكررة بشكل متكرر من النموذج - وهي عادةً طبقة محوّلة - مما يتيح إعادة استخدام المخرجات المُجمَّعة لكل ظهور تالٍ. بالنسبة للعديد من معماريات الانتشار، يحقق هذا نفس تسارعات وقت التشغيل كما في تجميع المخطط الكامل، ويقلل زمن التجميع بمقدار 8 إلى 10 أضعاف.

استخدم طريقة compile_repeated_blocks()، وهي أداة مساعدة تغلف torch.compile، على أي مكوّن مثل نموذج الترانسفورمر كما هو موضح أدناه.

# pip install -U diffusers
import torch
from diffusers import StableDiffusionXLPipeline

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16,
).to("cuda")  # or "mps", "xpu", "cpu"

# compile only the repeated transformer layers inside the UNet
pipeline.unet.compile_repeated_blocks(fullgraph=True)

لتفعيل التجميع الإقليمي لنموذج جديد، أضف سمة _repeated_blocks إلى فئة النموذج تحتوي على أسماء الفئات (كسلاسل نصية) للكتل التي تريد تجميعها.

class MyUNet(ModelMixin):
    _repeated_blocks = ("Transformer2DModel",)  # ← compiled by default

لمزيد من أمثلة التجميع الإقليمية، راجع PR المرجعي.

يوجد أيضاً طريقة compile_regions في Accelerate تختار تلقائياً الكتل المرشحة داخل النموذج لتجميعها. يتم تجميع بقية الرسم البياني بشكل منفصل. وهذا مفيد للتجارب السريعة لأنه لا توجد خيارات كثيرة لتحديد الكتل المراد تجميعها أو تعديل أعلام التجميع.

# pip install -U accelerate
import torch
from diffusers import StableDiffusionXLPipeline
from accelerate.utils import compile_regions

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.unet = compile_regions(pipeline.unet, mode="reduce-overhead", fullgraph=True)

compile_repeated_blocks() صريحةٌ عمدًا. اذكر الكتل التي تريد تكرارها في _repeated_blocks ولن يُجمّع المُساعد سوى تلك الكتل. فهي توفر سلوكًا متوقعًا وسهولةً في الاستدلال حول إعادة استخدام ذاكرة التخزين المؤقت في سطرٍ واحد من الكود.

انقطاعات الرسم البياني

من المهم تحديد fullgraph=True في torch.compile للتأكد من عدم وجود انقطاعات في الرسم البياني للنموذج الأساسي. وهذا يتيح لك الاستفادة من torch.compile دون أي تدهور في الأداء. بالنسبة إلى UNet وVAE، يغيّر هذا الطريقة التي تصل بها إلى متغيرات الإرجاع.

- latents = unet(
-   latents, timestep=timestep, encoder_hidden_states=prompt_embeds
-).sample

+ latents = unet(
+   latents, timestep=timestep, encoder_hidden_states=prompt_embeds, return_dict=False
+)[0]

مزامنة وحدة معالجة الرسوميات

تُستدعى الدالة step() على المجدول بعد كل تنبؤ يصدره مُزيل الضوضاء، ويُستخدم فهرسة متغير sigmas. عند وضعه على وحدة معالجة الرسوميات، يُدخل تأخيراً بسبب مزامنة الاتصال بين وحدة المعالجة المركزية ووحدة معالجة الرسوميات. ويصبح هذا التأخير أكثر وضوحاً عندما يكون مُزيل الضوضاء مُجمَّعاً بالفعل.

بشكل عام، يجب أن تبقى على وحدة المعالجة المركزية (CPU) sigmas لتجنب مزامنة الاتصال وزمن الاستجابة.

راجع منشور المدونة torch.compile و Diffusers: دليل عملي لتحقيق الأداء الأقصى لتعظيم الأداء باستخدام torch.compile لنماذج الانتشار.

المعايير

راجع مجموعة بيانات diffusers/benchmarks للاطلاع على بيانات زمن الاستدلال واستهلاك الذاكرة للخطوط الأنابيب المُجمّعة.

تحتوي مستودعة diffusers-torchao أيضًا على نتائج قياس الأداء للإصدارات المُجمَّعة من Flux وCogVideoX.

النوى

Kernels هو مكتبة لبناء وتوزيع وتحميل نوى الحساب المُحسَّنة على Hub. يدعم مكتبة attention النوى المخصصة لنوى CUDA للعمليات مثل RMSNorm وGEGLU وRoPE وAdaLN.

يُبيّن دليل تكامل خط أنابيب Diffusers كيفية دمج نواة مع مهارة إضافة cuda-kernels. تُمكّن هذه المهارة وكيلًا، مثل Claude أو Codex، من كتابة نوى مخصصة موجهة نحو نموذج محدد وأجهزتك.

Install the add cuda-kernels skill to teach an agent how to write a kernel. The Custom kernels for all from Codex and Claude blog post covers this in more detail.

على سبيل المثال، نواة RMSNorm مخصصة (مولّدة بواسطة مهارة add cuda-kernels) مع torch.compile تسرّع توليد LTX-Video بمقدار 1.43x على H100.

التكميم الديناميكي

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

يطبّق المثال أدناه التكميم الديناميكي int8 على UNet وVAE باستخدام مكتبة torchao.

راجع مستندات torchao لدينا لمعرفة المزيد حول كيفية استخدام تكامل Diffusers مع torchao.

اضبط وسوم المُجمِّع للحصول على أقصى سرعة.

import torch
from torchao import apply_dynamic_quant
from diffusers import StableDiffusionXLPipeline

torch._inductor.config.conv_1x1_as_mm = True
torch._inductor.config.coordinate_descent_tuning = True
torch._inductor.config.epilogue_fusion = False
torch._inductor.config.coordinate_descent_check_all_directions = True
torch._inductor.config.force_fuse_int_mm_with_mul = True
torch._inductor.config.use_mixed_mm = True

استبعاد بعض طبقات الخطية في UNet و VAE التي لا تستفيد من التكميم الديناميكي باستخدام dynamic_quant_filter_fn.

pipeline = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"

apply_dynamic_quant(pipeline.unet, dynamic_quant_filter_fn)
apply_dynamic_quant(pipeline.vae, dynamic_quant_filter_fn)

prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
pipeline(prompt, num_inference_steps=30).images[0]

مصفوفات الإسقاط المدمجة

طريقة fuse_qkv_projections تجريبية، والدعم فيها محدود في الغالب لأنابيب Stable Diffusion. اطّلع على هذا PR لمعرفة المزيد حول كيفية تفعيلها لأنابيب أخرى

يُستَخرَج مدخل إلى ثلاثة فضاءات فرعية، ممثلة بمصفوفات الإسقاط Q وK وV، داخل كتلة الانتباه. عادةً ما تُحسب هذه الإسقاطات بشكل منفصل، لكن يمكنك دمجها أفقيًا في مصفوفة واحدة وتنفيذ الإسقاط في خطوة واحدة. يزيد هذا من حجم عمليات ضرب المصفوفات الخاصة بإسقاطات المدخلات، كما يحسّن أثر التكميم.

pipeline.fuse_qkv_projections()

الموارد

Update on GitHub