Diffusers documentation
تقليل استهلاك الذاكرة
تقليل استهلاك الذاكرة
تحتوي نماذج الانتشار الحديثة مثل Flux وWan على مليارات المعاملات التي تستهلك مساحة كبيرة من الذاكرة على عتادك أثناء الاستدلال. وهذا أمر صعب لأن وحدات معالجة الرسومات الشائعة غالبًا لا تمتلك ذاكرة كافية. للتغلب على قيود الذاكرة، يمكنك استخدام أكثر من وحدة معالجة رسومات (إن توفرت)، ونقل بعض مكونات خط الأنابيب إلى وحدة المعالجة المركزية، وغير ذلك.
ستُظهر لك هذه الدليل كيفية تقليل استهلاك الذاكرة.
تذكر أن هذه التقنيات قد تحتاج إلى تعديل اعتمادًا على النموذج. على سبيل المثال، قد لا يستفيد نموذج الانتشار القائم على المحولات (Transformer) من تحسينات الذاكرة هذه بنفس القدر الذي يستفيد منه نموذج قائم على UNet.
بطاقات رسومات متعددة
إذا كان لديك وصول إلى أكثر من وحدة معالجة رسومية واحدة، فهناك عدة خيارات لتحميل نموذج كبير وتوزيعه بكفاءة عبر أجهزةك. تدعم مكتبة Accelerate هذه الميزات، لذا تأكد من تثبيتها أولاً.
pip install -U accelerate
نقاط التفتيش المجزأة
يُعد تحميل نقاط التفتيش الكبيرة مقسمة إلى عدة أجزاء مفيدًا لأن الأجزاء تُحمَّل واحدًا تلو الآخر. وهذا يحافظ على استهلاك الذاكرة منخفضًا، حيث لا يتطلب سوى ما يكفي من الذاكرة لحجم النموذج وحجم أكبر جزء. ننصح بتقسيم نقاط التفتيش عندما يتجاوز حجم نقطة التفتيش بصيغة fp32 قيمة 5GB. حجم الجزء الافتراضي هو 5GB.
قسّم نقطة التفتيش في save_pretrained() باستخدام معامل max_shard_size.
from diffusers import AutoModel
unet = AutoModel.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0", subfolder="unet"
)
unet.save_pretrained("sdxl-unet-sharded", max_shard_size="5GB")الآن يمكنك استخدام نقطة التفتيش المجزأة، بدلاً من نقطة التفتيش العادية، لتوفير الذاكرة.
import torch
from diffusers import AutoModel, StableDiffusionXLPipeline
unet = AutoModel.from_pretrained(
"username/sdxl-unet-sharded", dtype=torch.float16
)
pipeline = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
unet=unet,
dtype=torch.float16
).to("cuda") # or "mps", "xpu", "cpu"توزيع الأجهزة
توزيع الأجهزة هو ميزة تجريبية وقد تتغير واجهة برمجة التطبيقات. حاليًا، لا يُدعم سوى استراتيجية
balanced. نخطط لدعم استراتيجيات تعيين إضافية في المستقبل.
يتحكم معامل device_map في كيفية توزيع مكونات النموذج داخل خط الأنابيب أو طبقات نموذج فردي عبر الأجهزة.
توزّع استراتيجية وضع الأجهزة balanced خط الأنابيب بالتساوي على جميع الأجهزة المتاحة.
import torch
from diffusers import AutoModel, StableDiffusionXLPipeline
pipeline = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
dtype=torch.float16,
device_map="balanced"
)يمكنك فحص خريطة الأجهزة الخاصة بخط الأنابيب باستخدام hf_device_map.
print(pipeline.hf_device_map)
{'unet': 1, 'vae': 1, 'safety_checker': 0, 'text_encoder': 0}يُفيد device_map في تحميل النماذج الكبيرة، مثل نموذج Flux diffusion transformer الذي يحتوي على 12.5 مليار معامل. اضبطه على "auto" لتوزيع النموذج تلقائيًا على أسرع جهاز أولاً قبل الانتقال إلى الأجهزة الأبطأ. راجع قسم نقاط التفتيش المجزأة لمزيد من التفاصيل.
import torch
from diffusers import AutoModel
transformer = AutoModel.from_pretrained(
"black-forest-labs/FLUX.1-dev",
subfolder="transformer",
device_map="auto",
dtype=torch.bfloat16
)يمكنك فحص خريطة الأجهزة للنموذج باستخدام hf_device_map.
print(transformer.hf_device_map)عند تصميم device_map الخاص بك، يجب أن يكون قاموسًا يربط بين اسم وحدة أو طبقة محددة في النموذج ومعرّف الجهاز (عدد صحيح للوحدات الرسومية، وcpu لوحات المعالجة المركزية، وdisk للتخزين على القرص).
استدعِ hf_device_map على نموذج لرؤية كيفية توزيع طبقات النموذج ثم صمّم توزيعك الخاص.
print(transformer.hf_device_map)
{'pos_embed': 0, 'time_text_embed': 0, 'context_embedder': 0, 'x_embedder': 0, 'transformer_blocks': 0, 'single_transformer_blocks.0': 0, 'single_transformer_blocks.1': 0, 'single_transformer_blocks.2': 0, 'single_transformer_blocks.3': 0, 'single_transformer_blocks.4': 0, 'single_transformer_blocks.5': 0, 'single_transformer_blocks.6': 0, 'single_transformer_blocks.7': 0, 'single_transformer_blocks.8': 0, 'single_transformer_blocks.9': 0, 'single_transformer_blocks.10': 'cpu', 'single_transformer_blocks.11': 'cpu', 'single_transformer_blocks.12': 'cpu', 'single_transformer_blocks.13': 'cpu', 'single_transformer_blocks.14': 'cpu', 'single_transformer_blocks.15': 'cpu', 'single_transformer_blocks.16': 'cpu', 'single_transformer_blocks.17': 'cpu', 'single_transformer_blocks.18': 'cpu', 'single_transformer_blocks.19': 'cpu', 'single_transformer_blocks.20': 'cpu', 'single_transformer_blocks.21': 'cpu', 'single_transformer_blocks.22': 'cpu', 'single_transformer_blocks.23': 'cpu', 'single_transformer_blocks.24': 'cpu', 'single_transformer_blocks.25': 'cpu', 'single_transformer_blocks.26': 'cpu', 'single_transformer_blocks.27': 'cpu', 'single_transformer_blocks.28': 'cpu', 'single_transformer_blocks.29': 'cpu', 'single_transformer_blocks.30': 'cpu', 'single_transformer_blocks.31': 'cpu', 'single_transformer_blocks.32': 'cpu', 'single_transformer_blocks.33': 'cpu', 'single_transformer_blocks.34': 'cpu', 'single_transformer_blocks.35': 'cpu', 'single_transformer_blocks.36': 'cpu', 'single_transformer_blocks.37': 'cpu', 'norm_out': 'cpu', 'proj_out': 'cpu'}على سبيل المثال، يضع device_map أدناه single_transformer_blocks.10 حتى single_transformer_blocks.20 على وحدة معالجة رسومية ثانية (1).
import torch
from diffusers import AutoModel
device_map = {
'pos_embed': 0, 'time_text_embed': 0, 'context_embedder': 0, 'x_embedder': 0, 'transformer_blocks': 0, 'single_transformer_blocks.0': 0, 'single_transformer_blocks.1': 0, 'single_transformer_blocks.2': 0, 'single_transformer_blocks.3': 0, 'single_transformer_blocks.4': 0, 'single_transformer_blocks.5': 0, 'single_transformer_blocks.6': 0, 'single_transformer_blocks.7': 0, 'single_transformer_blocks.8': 0, 'single_transformer_blocks.9': 0, 'single_transformer_blocks.10': 1, 'single_transformer_blocks.11': 1, 'single_transformer_blocks.12': 1, 'single_transformer_blocks.13': 1, 'single_transformer_blocks.14': 1, 'single_transformer_blocks.15': 1, 'single_transformer_blocks.16': 1, 'single_transformer_blocks.17': 1, 'single_transformer_blocks.18': 1, 'single_transformer_blocks.19': 1, 'single_transformer_blocks.20': 1, 'single_transformer_blocks.21': 'cpu', 'single_transformer_blocks.22': 'cpu', 'single_transformer_blocks.23': 'cpu', 'single_transformer_blocks.24': 'cpu', 'single_transformer_blocks.25': 'cpu', 'single_transformer_blocks.26': 'cpu', 'single_transformer_blocks.27': 'cpu', 'single_transformer_blocks.28': 'cpu', 'single_transformer_blocks.29': 'cpu', 'single_transformer_blocks.30': 'cpu', 'single_transformer_blocks.31': 'cpu', 'single_transformer_blocks.32': 'cpu', 'single_transformer_blocks.33': 'cpu', 'single_transformer_blocks.34': 'cpu', 'single_transformer_blocks.35': 'cpu', 'single_transformer_blocks.36': 'cpu', 'single_transformer_blocks.37': 'cpu', 'norm_out': 'cpu', 'proj_out': 'cpu'
}
transformer = AutoModel.from_pretrained(
"black-forest-labs/FLUX.1-dev",
subfolder="transformer",
device_map=device_map,
dtype=torch.bfloat16
)مرر قاموسًا يربط الحد الأقصى لاستخدام الذاكرة بكل جهاز لفرض حد. إذا لم يكن الجهاز مدرجًا في max_memory، يتم تجاهله ولن يتم توزيع مكونات خط المعالجة عليه.
import torch
from diffusers import AutoModel, StableDiffusionXLPipeline
max_memory = {0:"1GB", 1:"1GB"}
pipeline = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
dtype=torch.float16,
device_map="balanced",
max_memory=max_memory
)يستخدم Diffusers الحد الأقصى لذاكرة جميع الأجهزة افتراضيًا، ولكن إذا لم تتسع على وحدات معالجة الرسوميات (GPU)، فستحتاج إلى استخدام وحدة معالجة رسوميات واحدة فقط ونقل الأحمال إلى وحدة المعالجة المركزية (CPU) باستخدام الأساليب أدناه.
- يعمل enable_model_cpu_offload() على وحدة معالجة رسومات واحدة فقط، لكن نموذجًا كبيرًا جدًا قد لا يتسع عليها
- قد يعمل enable_sequential_cpu_offload() لكنه بطيء للغاية كما أنه محدود بوحدة معالجة رسومية واحدة
استخدم طريقة reset_device_map() لإعادة تعيين device_map. هذا ضروري إذا كنت ترغب في استخدام طرق مثل .to() وenable_sequential_cpu_offload() وenable_model_cpu_offload() على خط أنابيب تم تعيينه للأجهزة.
pipeline.reset_device_map()
تقسيم VAE
يحفظ تقطيع VAE الذاكرة من خلال تقسيم دفعات كبيرة من المدخلات إلى دفعة بيانات واحدة ومعالجتها بشكل منفصل. يعمل هذا الأسلوب بشكل أفضل عند توليد أكثر من صورة في كل مرة.
على سبيل المثال، إذا كنت تولّد 4 صور في وقت واحد، فإن فك الترميز سيؤدي إلى زيادة ذروة ذاكرة التفعيل بمقدار 4 أضعاف. يقلّل تقطيع VAE من ذلك من خلال فك ترميز صورة واحدة في كل مرة بدلاً من فك ترميز جميع الصور الأربع في وقت واحد.
استدعِ enable_slicing() لتفعيل VAE المقطّعة. يمكنك توقع زيادة طفيفة في الأداء عند فك ترميز دفعات متعددة الصور، دون أي تأثير على الأداء في حالة الدفعات أحادية الصورة.
import torch
from diffusers import AutoModel, StableDiffusionXLPipeline
pipeline = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
dtype=torch.float16,
).to("cuda") # or "mps", "xpu", "cpu"
pipeline.vae.enable_slicing()
pipeline(["An astronaut riding a horse on Mars"]*32).images[0]
print(f"Max memory reserved: {torch.cuda.max_memory_allocated() / 1024**3:.2f} GB")لا تدعم فئتا AutoencoderKLWan وAsymmetricAutoencoderKL التقطيع.
تقسيم VAE
يحفظ تقطيع VAE الذاكرة من خلال تقسيم الصورة إلى بلاطات أصغر متداخلة بدلاً من معالجة الصورة بأكملها دفعة واحدة. كما يقلل هذا من ذروة استهلاك الذاكرة لأن وحدة معالجة الرسوميات (GPU) تعالج بلاطة واحدة في كل مرة.
استدعِ enable_tiling() لتفعيل تقطيع VAE. قد تظهر بعض التباينات في درجات اللون من قطعة إلى أخرى لأن كل قطعة تُفكّك بشكل مستقل، لكن لا ينبغي أن تظهر أي فواصل واضحة بين القطع. يتم تعطيل التقطيع عند الدقات الأقل من حد مسبق التحديد (ولكن قابل للضبط). على سبيل المثال، هذا الحد هو 512x512 لـ VAE في StableDiffusionPipeline.
import torch
from diffusers import AutoPipelineForImage2Image
from diffusers.utils import load_image
pipeline = AutoPipelineForImage2Image.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16
).to("cuda") # or "mps", "xpu", "cpu"
pipeline.vae.enable_tiling()
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/img2img-sdxl-init.png")
prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k"
pipeline(prompt, image=init_image, strength=0.5).images[0]
print(f"Max memory reserved: {torch.cuda.max_memory_allocated() / 1024**3:.2f} GB")لا يدعم AutoencoderKLWan و AsymmetricAutoencoderKL التقطيع (tiling).
إخلاء
تنقل استراتيجيات الإزاحة الطبقات أو النماذج غير النشطة حاليًا إلى وحدة المعالجة المركزية (CPU) لتجنب زيادة ذاكرة وحدة معالجة الرسوميات (GPU). يمكن دمج هذه الاستراتيجيات مع التكميم وtorch.compile لتحقيق التوازن بين سرعة الاستدلال واستخدام الذاكرة.
راجع دليل تجميع نماذج الكمّية وإزاحتها لمزيد من التفاصيل.
إزاحة المعالجة إلى وحدة المعالجة المركزية
يعمل إزاحة المعالِج المركزي (CPU offloading) على نقل الأوزان من وحدة معالجة الرسوميات (GPU) إلى المعالج المركزي بشكل انتقائي. عندما يُطلب مكوّن ما، يتم نقله إلى وحدة معالجة الرسوميات، وعندما لا يكون مطلوبًا، يتم نقله إلى المعالج المركزي. تعمل هذه الطريقة على الوحدات الفرعية (submodules) بدلاً من النماذج الكاملة. فهي توفر الذاكرة من خلال تجنب تخزين النموذج بأكمله على وحدة معالجة الرسوميات.
يُقلّل تفريغ المعالجة على وحدة المعالجة المركزية (CPU) بشكل كبير من استهلاك الذاكرة، لكنه أيضًا بطيء للغاية لأن الوحدات الفرعية تُمرَّر ذهابًا وإيابًا عدة مرات بين الأجهزة. وغالبًا ما يكون غير عملي بسبب بطئه الشديد.
لا تُنقل خط الأنابيب إلى CUDA قبل استدعاء enable_sequential_cpu_offload()، وإلا فإن كمية الذاكرة الموفَّرة ستكون ضئيلة فقط (راجع هذا المشكلة لمزيد من التفاصيل). هذه عملية ذات حالة تُثبّت الخطافات (hooks) على النموذج.
استدعِ enable_sequential_cpu_offload() لتفعيله على خط أنابيب.
import torch
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"black-forest-labs/FLUX.1-schnell", dtype=torch.bfloat16
)
pipeline.enable_sequential_cpu_offload()
pipeline(
prompt="An astronaut riding a horse on Mars",
guidance_scale=0.,
height=768,
width=1360,
num_inference_steps=4,
max_sequence_length=256,
).images[0]
print(f"Max memory reserved: {torch.cuda.max_memory_allocated() / 1024**3:.2f} GB")تفريغ النموذج
ينقل تحميل النموذج (Model offloading) النماذج كاملةً إلى وحدة معالجة الرسوميات (GPU) بدلاً من نقل بعض الطبقات أو مكونات النموذج بشكل انتقائي. يتم وضع أحد نماذج خطوط الأنابيب الرئيسية، وعادةً ما يكون مُشفّر النص (text encoder) وUNet وVAE، على وحدة معالجة الرسوميات، بينما تُبقي المكونات الأخرى على وحدة المعالجة المركزية (CPU). تبقى المكونات التي تعمل عدة مرات، مثل UNet، على وحدة معالجة الرسوميات حتى تكتمل مهامها بالكامل وتُصبح غير مطلوبة بعد ذلك. هذا يلغي عبء الاتصال المرتبط بـتحميل وحدة المعالجة المركزية (CPU offloading) ويجعل من تحميل النموذج بديلاً أسرع. الثمن هو أن توفير الذاكرة لن يكون بنفس الحجم الكبير.
تذكر أنه إذا تم إعادة استخدام النماذج خارج خط الأنابيب بعد تثبيت الخطافات (انظر إزالة الخطافات لمزيد من التفاصيل)، فستحتاج إلى تشغيل خط الأنابيب بأكمله والنماذج بالترتيب المتوقع لتفريغها بشكل صحيح. هذه عملية ذات حالة تقوم بتثبيت الخطافات على النموذج.
استدعِ enable_model_cpu_offload() لتفعيله على خط أنابيب.
import torch
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"black-forest-labs/FLUX.1-schnell", dtype=torch.bfloat16
)
pipeline.enable_model_cpu_offload()
pipeline(
prompt="An astronaut riding a horse on Mars",
guidance_scale=0.,
height=768,
width=1360,
num_inference_steps=4,
max_sequence_length=256,
).images[0]
print(f"Max memory reserved: {torch.cuda.max_memory_allocated() / 1024**3:.2f} GB")يساعدك enable_model_cpu_offload() أيضًا عند استخدامك طريقة encode_prompt() بمفردها لتوليد حالة المخفية لمشفّرات النص.
تفريغ المجموعات
ينقل تفريغ المجموعات مجموعات من الطبقات الداخلية (torch.nn.ModuleList أو torch.nn.Sequential) إلى وحدة المعالجة المركزية. وهو يستخدم ذاكرة أقل من تفريغ النموذج وهو أسرع من تفريغ وحدة المعالجة المركزية لأنه يقلل من عبء الاتصال.
قد لا يعمل تفريغ المجموعات مع جميع النماذج إذا تضمن تنفيذ الأمامي (forward) تحويل الأجهزة المعتمد على الأوزان للمدخلات، لأن ذلك قد يتعارض مع آلية تحويل الأجهزة الخاصة بتفريغ المجموعات.
فعّل تفريغ المجموعات عن طريق ضبط معامل offload_type على block_level أو leaf_level.
block_levelيُحمّل مجموعات من الطبقات بناءً على معاملnum_blocks_per_group. على سبيل المثال، إذا كانnum_blocks_per_group=2في نموذج يحتوي على 40 طبقة، يتم تحميل 2 طبقة وإخراجهما في كل مرة (20 عملية تحميل/إخراج إجمالاً). وهذا يقلل بشكل كبير من متطلبات الذاكرة.leaf_levelيُحمّل الطبقات الفردية على المستوى الأدنى وهو مكافئ لـ إزاحة المعالجة إلى وحدة المعالجة المركزية (CPU offloading). لكن يمكن تسريعه باستخدام التدفقات (streams) دون التنازل عن سرعة الاستدلال.
يُؤمَّن دعم تفريغ المجموعات (Group offloading) للأنابيب (pipelines) بأكملها أو للنماذج الفردية. يُعد تطبيق تفريغ المجموعات على الأنبوب بأكمله الخيار الأسهل، بينما يمنح تطبيقه الانتقائي على نماذج فردية المستخدمين مرونة أكبر لاستخدام تقنيات تفريغ مختلفة لكل نموذج.
استدعِ enable_group_offload() على خط أنابيب.
import torch
from diffusers import CogVideoXPipeline
from diffusers.hooks import apply_group_offloading
from diffusers.utils import export_to_video
onload_device = torch.device("cuda")
offload_device = torch.device("cpu")
pipeline = CogVideoXPipeline.from_pretrained("THUDM/CogVideoX-5b", dtype=torch.bfloat16)
pipeline.enable_group_offload(
onload_device=onload_device,
offload_device=offload_device,
offload_type="leaf_level",
use_stream=True
)
prompt = (
"A panda, dressed in a small, red jacket and a tiny hat, sits on a wooden stool in a serene bamboo forest. "
"The panda's fluffy paws strum a miniature acoustic guitar, producing soft, melodic tunes. Nearby, a few other "
"pandas gather, watching curiously and some clapping in rhythm. Sunlight filters through the tall bamboo, "
"casting a gentle glow on the scene. The panda's face is expressive, showing concentration and joy as it plays. "
"The background includes a small, flowing stream and vibrant green foliage, enhancing the peaceful and magical "
"atmosphere of this unique musical performance."
)
video = pipeline(prompt=prompt, guidance_scale=6, num_inference_steps=50).frames[0]
print(f"Max memory reserved: {torch.cuda.max_memory_allocated() / 1024**3:.2f} GB")
export_to_video(video, "output.mp4", fps=8)استدعِ enable_group_offload() على مكوّنات نماذج Diffusers القياسية التي ترث من ModelMixin. أما بالنسبة لمكوّنات النماذج الأخرى التي لا ترث من ModelMixin، مثل torch.nn.Module العام، فاستخدمي apply_group_offloading() بدلاً من ذلك.
import torch
from diffusers import CogVideoXPipeline
from diffusers.hooks import apply_group_offloading
from diffusers.utils import export_to_video
onload_device = torch.device("cuda")
offload_device = torch.device("cpu")
pipeline = CogVideoXPipeline.from_pretrained("THUDM/CogVideoX-5b", dtype=torch.bfloat16)
# Use the enable_group_offload method for Diffusers model implementations
pipeline.transformer.enable_group_offload(onload_device=onload_device, offload_device=offload_device, offload_type="leaf_level")
pipeline.vae.enable_group_offload(onload_device=onload_device, offload_type="leaf_level")
# Use the apply_group_offloading method for other model components
apply_group_offloading(pipeline.text_encoder, onload_device=onload_device, offload_type="block_level", num_blocks_per_group=2)
prompt = (
"A panda, dressed in a small, red jacket and a tiny hat, sits on a wooden stool in a serene bamboo forest. "
"The panda's fluffy paws strum a miniature acoustic guitar, producing soft, melodic tunes. Nearby, a few other "
"pandas gather, watching curiously and some clapping in rhythm. Sunlight filters through the tall bamboo, "
"casting a gentle glow on the scene. The panda's face is expressive, showing concentration and joy as it plays. "
"The background includes a small, flowing stream and vibrant green foliage, enhancing the peaceful and magical "
"atmosphere of this unique musical performance."
)
video = pipeline(prompt=prompt, guidance_scale=6, num_inference_steps=50).frames[0]
print(f"Max memory reserved: {torch.cuda.max_memory_allocated() / 1024**3:.2f} GB")
export_to_video(video, "output.mp4", fps=8)بث CUDA
يمكن تفعيل معامل use_stream لأجهزة CUDA التي تدعم تدفقات نقل البيانات غير المتزامنة لتقليل وقت التنفيذ الإجمالي مقارنةً بـ إزاحة المعالجة إلى وحدة المعالجة المركزية. يتداخل نقل البيانات والحساب باستخدام استباق الطبقات. يتم تحميل الطبقة التالية التي سيتم تنفيذها على وحدة معالجة الرسوميات بينما لا تزال الطبقة الحالية قيد التنفيذ. قد يؤدي ذلك إلى زيادة كبيرة في ذاكرة وحدة المعالجة المركزية، لذا تأكد من توفر ضعف حجم ذاكرة حجم النموذج.
اضبط record_stream=True للحصول على تسريع أكبر على حساب زيادة طفيفة في استخدام الذاكرة. راجع وثائق torch.Tensor.record_stream لمعرفة المزيد.
عند استخدام
use_stream=Trueمع نماذج VAE التي تم تفعيل خاصية التقطيع (tiling) فيها، تأكد من إجراء تمريرة أمامية تجريبية (dummy forward pass) (يمكن استخدام مدخلات تجريبية أيضًا) قبل الاستدلال لتجنب أخطاء عدم تطابق الجهاز. قد لا يعمل هذا الحل في جميع التطبيقات، لذا لا تتردد في فتح مشكلة (issue) إذا واجهت أي مشاكل.
إذا كنت تستخدم تفريغ المجموعات على مستوى block_level مع تفعيل use_stream، فيجب تعيين معامل num_blocks_per_group إلى 1، وإلا فسيتم إصدار تحذير.
pipeline.transformer.enable_group_offload(onload_device=onload_device, offload_device=offload_device, offload_type="leaf_level", use_stream=True, record_stream=True)يمكن ضبط معامل low_cpu_mem_usage على True لتقليل استخدام ذاكرة المعالج المركزي عند استخدام التدفقات أثناء تفريغ المجموعات. وهو الخيار الأفضل لتفريغ leaf_level وعند كون ذاكرة المعالج المركزي هي عنق الزجاجة. يتم توفير الذاكرة من خلال إنشاء متجهات مثبتة على الطاير بدلاً من تثبيتها مسبقاً. ومع ذلك، قد يؤدي ذلك إلى زيادة وقت التنفيذ الإجمالي.
إفراغ البيانات على القرص
يمكن أن يستهلك تفريغ المجموعات ذاكرة نظام كبيرة اعتماداً على حجم النموذج. على الأنظمة ذات الذاكرة المحدودة، جرّب تفريغ المجموعات على القرص كذاكرة ثانوية.
اضبط الحجة offload_to_disk_path في enable_group_offload() أو apply_group_offloading() لتحميل النموذج على القرص.
pipeline.transformer.enable_group_offload(onload_device=onload_device, offload_device=offload_device, offload_type="leaf_level", offload_to_disk_path="path/to/disk")
apply_group_offloading(pipeline.text_encoder, onload_device=onload_device, offload_type="block_level", num_blocks_per_group=2, offload_to_disk_path="path/to/disk")راجع هاتين جدولتين للمقارنة بين التنازلات بين السرعة والذاكرة.
الإسناد على مستوى الطبقات
اجمع بين الصب على مستوى الطبقات مع إزاحة المجموعات لتحقيق توفير أكبر في الذاكرة.
يحفظ التحويل على مستوى الطبقات الأوزان في صيغة بيانات أصغر (مثل torch.float8_e4m3fn وtorch.float8_e5m2) لتقليل استهلاك الذاكرة، ثم يرفع دقة تلك الأوزان إلى دقة أعلى مثل torch.float16 أو torch.bfloat16 لأغراض الحساب. يتم تجاوز طبقات معينة (الأوزان المتعلقة بالتطبيع والتعديل) لأن تخزينها بصيغة fp8 قد يُضعف جودة التوليد.
قد لا يعمل التحويل على مستوى الطبقات مع جميع النماذج إذا تضمنت عملية التنفيذ الأمامي تحويلًا داخليًا لأنواع الأوزان. يفترض التنفيذ الحالي للتحويل على مستوى الطبقات أن المرور الأمامي مستقل عن دقة الأوزان وأن أنواع بيانات الإدخال محددة دائمًا في
compute_dtype(انظر هنا لمعرفة مثال على تنفيذ غير متوافق).قد يفشل أيضًا التحويل على مستوى الطبقات في تطبيقات النمذجة المخصصة التي تحتوي على طبقات PEFT. هناك بعض الفحوصات المتاحة، لكنها غير مُختبرة بشكل شامل ولا يُكفَل عملها في جميع الحالات.
استدعِ enable_layerwise_casting() لضبط أنواع بيانات التخزين والحساب.
import torch
from diffusers import CogVideoXPipeline, CogVideoXTransformer3DModel
from diffusers.utils import export_to_video
transformer = CogVideoXTransformer3DModel.from_pretrained(
"THUDM/CogVideoX-5b",
subfolder="transformer",
dtype=torch.bfloat16
)
transformer.enable_layerwise_casting(storage_dtype=torch.float8_e4m3fn, compute_dtype=torch.bfloat16)
pipeline = CogVideoXPipeline.from_pretrained("THUDM/CogVideoX-5b",
transformer=transformer,
dtype=torch.bfloat16
).to("cuda") # or "mps", "xpu", "cpu"
prompt = (
"A panda, dressed in a small, red jacket and a tiny hat, sits on a wooden stool in a serene bamboo forest. "
"The panda's fluffy paws strum a miniature acoustic guitar, producing soft, melodic tunes. Nearby, a few other "
"pandas gather, watching curiously and some clapping in rhythm. Sunlight filters through the tall bamboo, "
"casting a gentle glow on the scene. The panda's face is expressive, showing concentration and joy as it plays. "
"The background includes a small, flowing stream and vibrant green foliage, enhancing the peaceful and magical "
"atmosphere of this unique musical performance."
)
video = pipeline(prompt=prompt, guidance_scale=6, num_inference_steps=50).frames[0]
print(f"Max memory reserved: {torch.cuda.max_memory_allocated() / 1024**3:.2f} GB")
export_to_video(video, "output.mp4", fps=8)يمكن أيضًا استخدام طريقة apply_layerwise_casting() إذا كنت بحاجة إلى مزيد من التحكم والمرونة. يمكن تطبيقها جزئيًا على طبقات النموذج من خلال استدعائها على وحدات داخلية محددة. استخدم المعاملات skip_modules_pattern أو skip_modules_classes لتحديد الوحدات التي يجب تجنّبها، مثل طبقات التطبيع والتعديل.
import torch
from diffusers import CogVideoXTransformer3DModel
from diffusers.hooks import apply_layerwise_casting
transformer = CogVideoXTransformer3DModel.from_pretrained(
"THUDM/CogVideoX-5b",
subfolder="transformer",
dtype=torch.bfloat16
)
# skip the normalization layer
apply_layerwise_casting(
transformer,
storage_dtype=torch.float8_e4m3fn,
compute_dtype=torch.bfloat16,
skip_modules_classes=["norm"],
non_blocking=True,
)torch.channels_last
يعكس torch.channels_last طريقة تخزين المصفوفات من (batch size, channels, height, width) إلى (batch size, height, width, channels). وهذا يُحاذا المصفوفات مع الطريقة التي تصل بها العتاد بشكل تسلسلي إلى المصفوفات المخزنة في الذاكرة، ويتجنب القفزات في الذاكرة للوصول إلى قيم البكسل.
لا تدعم جميع العمليات حاليًا تنسيق القنوات الأخيرة، وقد يؤدي ذلك إلى أداء أسوأ، لكن لا يزال من الجدير بالمحاولة.
print(pipeline.unet.conv_out.state_dict()["weight"].stride()) # (2880, 9, 3, 1)
pipeline.unet.to(memory_format=torch.channels_last) # in-place operation
print(
pipeline.unet.conv_out.state_dict()["weight"].stride()
) # (2880, 1, 960, 320) having a stride of 1 for the 2nd dimension proves that it worksانتباه موفّر للذاكرة
يدعم Diffusers عدة واجهات خلفية لانتباه فعّال من حيث الذاكرة (FlashAttention وxFormers وSageAttention وغيرها) من خلال set_attention_backend(). راجع دليل واجهات خلفية الانتباه لمعرفة كيفية التبديل بينها.
Update on GitHub