فاز 8 · درس 7

دیفیوژن نهان و استیبل دیفیوژن (Stable Diffusion)

نوع
ساختنی
زمان
حدود 75 دقیقه
زبان‌ها
Python
پیش‌نیاز
فاز 8 · درس 02 (VAE)، فاز 8 · درس 06 (DDPM)، فاز 7 · درس 09 (ViT)
یک نقشه مهندسی دولایه که در لایه بالایی تصویری با رزولوشن بالا فشرده شده و وارد یک شبکه با ابعاد کوچکتر می‌شود
فهرست مطالب
  1. صورت مسئله
  2. مفاهیم پایه
  3. گونه‌های معماری
  4. پیاده‌سازی
  5. گام 1: انکودر/دیکودر
  6. گام 2: دیفیوژن در فضای z
  7. گام 3: هدایت بدون طبقه‌بند (Classifier-Free Guidance)
  8. گام 4: شرطی‌سازی متنی (مفهوم، بدون کد)
  9. دام‌های رایج
  10. استفاده‌ی عملی
  11. محصول پایانی
  12. تمرین‌ها
  13. اصطلاحات کلیدی
  14. نکته‌ی پروداکشن: اجرای Flux-12B روی پردازنده‌ی گرافیکی 8 گیگابایتی
  15. برای مطالعه‌ی بیشتر

اجرای مدل‌های دیفیوژن مستقیماً روی پیکسل‌های خام تصاویر با رزولوشن بالا، نیازمند توان پردازشی سرسام‌آوری است که برای اکثر توسعه‌دهندگان توجیه اقتصادی ندارد. مدل‌های دیفیوژن نهان (Latent Diffusion) با انتقال فرایند نویززدایی به یک فضای فشرده‌تر با ابعاد کوچک‌تر، بار محاسباتی را به‌شدت کاهش می‌دهند و کیفیت بصری را کاملاً حفظ می‌کنند. شناخت این معماری کلید درک سازوکار مدل‌های معروفی مثل Stable Diffusion و Flux است.

در عمل، توسعه‌دهندگان هنگام کار با این مدل‌ها با چالش‌هایی مثل مدیریت حافظه‌ی گرافیکی و هزینه اجرای سنگین روبه‌رو می‌شوند. برای کاربرانی که دسترسی به سرویس‌های ابری خارجی یا GPUهای پیشرفته برایشان دشوار و پرهزینه است، پیاده‌سازی تکنیک‌هایی مثل کوانتایز 4 بیتی، تخلیه حافظه روی پردازنده اصلی (CPU Offload) و بارگذاری مرحله‌به‌مرحله اجازه می‌دهد حتی مدل‌های چند ده میلیاردی را روی کارت‌های گرافیک مصرفی خانگی اجرا کنید.

اجرای دیفیوژن در فضای پیکسل (pixel-space) روی تصاویر 512×512 یک جنایت جنگی محاسباتی است! رومباخ و همکاران (2022) متوجه شدند که برای تولید یک تصویر اصلاً نیازی به تمام 786 هزار بُعد ندارید؛ فقط ابعادی را می‌خواهید که ساختار معنایی را نگه دارند، و یک دیکودر جداگانه هم بقیه‌ی جزئیات را بازسازی کند. دیفیوژن را داخل فضای نهان (latent space) یک VAE اجرا کنید. همین یک ایده یعنی همان Stable Diffusion.

صورت مسئله

دیفیوژن در فضای پیکسل با ابعاد 512² یعنی U-Net روی تنسورهایی با ابعاد [B, 3, 512, 512] اجرا می‌شود. هر گام نمونه‌گیری برای یک U-Net با 500 میلیون پارامتر حدود 100 گیگافلاپس (GFLOPS) پردازش می‌برد. 50 گام یعنی 5 ترافلاپس (TFLOPS) به ازای هر تصویر. حالا مدل را روی یک میلیارد تصویر آموزش دهید تا ببینید هزینه‌ی پردازش سر به فلک می‌کشد.

بیشتر این فلاپس‌ها صرف عبور دادن جزئیاتی از شبکه می‌شوند که از نظر ادراکی چندان اهمیتی ندارند؛ یعنی همان بافت‌های فرکانس‌بالایی که یک VAE اتلافی می‌تواند آن‌ها را فشرده و حذف کند. ایده‌ی رومباخ ساده بود: یک بار VAE را آموزش دهید (مرحله‌ی اول)، وزنش را فریز کنید، و بعد دیفیوژن را تماماً در فضای نهان 4 کاناله با ابعاد 64×64 اجرا کنید (مرحله‌ی دوم). با همان U-Net، فقط با یک‌شانزدهم تعداد پیکسل‌ها، به کیفیتی مشابه می‌رسید؛ آن هم با حدود 64 برابر فلاپس کمتر.

این همان فرمول ساخت Stable Diffusion است. نسخه‌های SD 1.x / 2.x از یک U-Net با 860 میلیون پارامتر روی فضاهای نهان 64×64×4 استفاده کردند، SDXL از یک U-Net با 2.6 میلیارد پارامتر روی 128×128×4 بهره برد، و SD3 این U-Net را با یک ترنسفورمر دیفیوژن (Diffusion Transformer یا DiT) با تطبیق جریان (flow matching) جایگزین کرد. مدل Flux.1-dev (از Black Forest Labs، سال 2024) با یک DiT-MMDiT دوازده میلیارد پارامتری عرضه شد. همه‌ی این‌ها روی همان بستر دومرحله‌ای کار می‌کنند.

مفاهیم پایه

دیفیوژن نهان: فشرده‌سازی با VAE + دیفیوژن در فضای نهان

دو مرحله که جداگانه آموزش می‌بینند:

  1. مرحله‌ی 1 — VAE. انکودر E(x)→zE(x) \to z، دیکودر D(z)→xD(z) \to x. هدف فشرده‌سازی: کاهش ابعاد (downsample) به میزان 8 برابر در هر بُعد فضایی + تنظیم تعداد کانال‌ها به شکلی که حجم کل فضای نهان تقریباً یک‌شانزدهم تعداد پیکسل‌ها شود. تابع زیان (loss) = بازسازی (L1 + زیان ادراکی LPIPS) + واگرایی KL (با ضریب کم تا zz بیش از حد وادار به توزیع گوسی نشود، چون نیازی به نمونه‌گیری دقیق از zz نداریم). معمولاً همراه با یک تابع زیان ادورساریال (adversarial loss) آموزش داده می‌شود تا تصاویر دیکودشده کاملاً شارپ از آب درآیند.

  2. مرحله‌ی 2 — دیفیوژن روی zz. به z=E(xreal)z = E(x_{\text{real}}) مثل داده‌های ورودی نگاه کنید. یک U-Net (یا DiT) را برای حذف نویز از ztz_t آموزش دهید. در زمان استنتاج: z0z_0 را با دیفیوژن نمونه‌گیری کنید و سپس x=D(z0)x = D(z_0).

شرطی‌سازی با متن (Text conditioning). دو مؤلفه‌ی اضافه نیاز داریم: یک انکودر متن فریز‌شده (CLIP-L برای SD 1.x، ترکیب CLIP-L+OpenCLIP-G برای SD 2/XL، و T5-XXL برای SD3 و Flux)، و تزریق از طریق کراس‌اتنشن (cross-attention): هر بلوک U-Net مقادیر [Q = image features, K = V = text tokens] را می‌گیرد و با هم مخلوط می‌کند. توکن‌ها تنها پلی هستند که متن از طریق آن‌ها روی تصویر اثر می‌گذارد.

تابع زیان دقیقاً همان فرمول درس 06 است. همان خطای میانگین مربعات (MSE) نویز در DDPM یا flow matching؛ تنها چیزی که عوض شده، دامنه‌ی داده‌ها است.

گونه‌های معماری

مدل سال بک‌بون شکل فضای نهان انکودر متن پارامترها
SD 1.5 2022 U-Net 64×64×4 CLIP-L (77 توکن) 860M
SD 2.1 2022 U-Net 64×64×4 OpenCLIP-H 865M
SDXL 2023 U-Net + ریفاینر 128×128×4 CLIP-L + OpenCLIP-G 2.6B + 6.6B
SDXL-Turbo 2023 تقطیرشده (Distilled) 128×128×4 مشابه بالا نمونه‌گیری در 1 تا 4 گام
SD3 2024 MMDiT (ترنسفورمر چندوجهی) 128×128×16 T5-XXL + CLIP-L + CLIP-G 2B / 8B
Flux.1-dev 2024 MMDiT 128×128×16 T5-XXL + CLIP-L 12B
Flux.1-schnell 2024 MMDiT تقطیرشده 128×128×16 T5-XXL + CLIP-L 12B، در 1 تا 4 گام

روند مشخص است: جایگزینی U-Net با DiT (ترنسفورمر روی پچ‌های فضای نهان)، بزرگ‌تر کردن انکودر متن (T5 در پیروی از پرامپت CLIP را پشت سر می‌گذارد)، و افزایش کانال‌های فضای نهان (حرکت از 4 به 16، دست مدل را برای جزئیات خیلی بازتر می‌کند).

زمان‌بندی نویزانتخاب یک زمان‌بندی
NOISE SCHEDULEalpha_bar is the fraction of signal surviving at step t, and its ratio to the remaining noise is the signal-to-noise ratio. A linear schedule wipes out the signal fast in the early steps; the cosine schedule decays more gently in the middle, leaving useful signal for longer and giving the model more informative intermediate targets.
پارامتر alpha_bar کسر سیگنال باقی‌مانده در گام t است و نسبت آن به نویز باقی‌مانده، نسبت سیگنال به نویز (SNR) را مشخص می‌کند. زمان‌بندی خطی سیگنال را در گام‌های اولیه به سرعت محو می‌کند؛ در حالی که زمان‌بندی کسینوسی در میانه آرام‌تر کاهش می‌یابد، سیگنال مفید را برای مدت طولانی‌تری حفظ می‌کند و اهداف میانی آموزنده‌تری در اختیار مدل می‌گذارد.

پیاده‌سازی

کد code/main.py یک «VAE» یک‌بعدی آموزشی (یک انکودر با مقیاس ثابت 0.5 و معکوس دقیقش به عنوان دیکودر؛ صرفاً برای نمایش ایده، چرا که VAE واقعی یک شبکه‌ی کانوولوشنی است) را روی DDPM درس 06 می‌گذارد و شرطی‌سازی مبتنی بر کلاس را با classifier-free guidance به آن اضافه می‌کند. این کد نشان می‌دهد فرقی نمی‌کند روی مقادیر خام یک‌بعدی کار کنید یا مقادیر انکودشده؛ تابع زیان دیفیوژن در هر دو حالت جواب می‌دهد. ایده‌ی اصلی دقیقاً همین است.

گام 1: انکودر/دیکودر

def encode(x):    return x * 0.5          # toy "compression" to smaller scale
def decode(z):    return z * 2.0

یک VAE واقعی وزن‌های آموزش‌دیده دارد. برای آموزش، همین نگاشت خطی کافی است تا نشان دهیم دیفیوژن چطور روی zz کار می‌کند بدون این‌که کوچک‌ترین اهمیتی به فضای اولیه‌ی داده‌ها بدهد.

گام 2: دیفیوژن در فضای z

همان DDPM درس 06. داده‌ای که شبکه دریافت می‌کند z=E(x)z = E(x) است. بعد از نمونه‌گیری z0z_0، آن را با D(z0)D(z_0) دیکود کنید.

گام 3: هدایت بدون طبقه‌بند (Classifier-Free Guidance)

در طول آموزش، برچسب کلاس را در 10٪ مواقع حذف کنید (و با یک توکن پوچ یا null جایگزینش کنید). در زمان استنتاج، هر دو مقدار ϵcond\epsilon_{\text{cond}} و ϵuncond\epsilon_{\text{uncond}} را به دست آورید و سپس:

eps_cfg = (1 + w) * eps_cond - w * eps_uncond

مقدار w=0w = 0 یعنی بدون هدایت (حداکثر تنوع)، w=3w = 3 حالت پیش‌فرض است، و w=7+w = 7+ خروجی را بیش از حد اشباع و زیادی شارپ می‌کند.

گام 4: شرطی‌سازی متنی (مفهوم، بدون کد)

برچسب کلاس را با خروجی یک انکودر متنِ فریز‌شده جایگزین کنید. امبدینگ متن را هم از طریق کراس‌اتنشن به U-Net بفرستید:

h = h + CrossAttention(Q=h, K=text_embed, V=text_embed)

تنها تفاوت بنیادین میان یک مدل دیفیوژن شرطی‌شده روی کلاس و Stable Diffusion همین است.

دام‌های رایج

  • ناهماهنگی در ضریب مقیاس VAE. مدل‌های SD 1.x یک ضریب مقیاس ثابت (scaling_factor ≈ 0.18215) دارند که بعد از انکود اعمال می‌شود. اگر این ضریب را از قلم بیندازید، U-Net روی داده‌های نهانی آموزش می‌بیند که واریانس‌شان از زمین تا آسمان با واقعیت فرق دارد. هر چک‌پوینت چنین ضریبی همراه خود دارد.
  • خطای خاموش در انکودر متن. مدل SD3 به T5-XXL با حداقل 128 توکن نیاز دارد، و اگر مدل را فقط به CLIP محدود کنید کیفیت به‌شدت افت می‌کند. همیشه مطمئن شوید use_t5=True باشد، وگرنه میزان وفاداری به پرامپت سقوط می‌کند.
  • ترکیب فضاهای نهان ناسازگار. مدل‌های SDXL، SD3 و Flux هر کدام VAE متفاوتی دارند. یک LoRA که روی فضاهای نهان SDXL آموزش دیده روی SD3 اصلاً کار نخواهد کرد. کتابخانه‌ی diffusers در Hugging Face از نسخه‌ی 0.30 به بعد اجازه‌ی بارگذاری چک‌پوینت‌های ناسازگار را نمی‌دهد.
  • مقدار بیش از حد CFG. اگر w > 10 باشد، تصاویر بیش از حد اشباع و روغنی می‌شوند و مدل به قیمت قربانی کردن تنوع، روی پرامپت اورفیت می‌کند. بازه‌ی ایده‌آل w = 3-7 است.
  • نشتی پرامپت منفی. اگر پرامپت منفی خالی بماند، به توکن پوچ تبدیل می‌شود؛ اما اگر چیزی بنویسید تبدیل به ϵuncond\epsilon_{\text{uncond}} می‌شود. این دو با هم یکی نیستند؛ در حالی که برخی پایپ‌لاین‌ها بی‌سروصدا پیش‌فرض را روی توکن پوچ می‌گذارند.

استفاده‌ی عملی

پشته‌های نرم‌افزاری پروداکشن در سال 2026:

هدف بک‌بون پیشنهادی
دامنه‌ی محدود، داده‌های جفت‌شده، آموزش مدل از صفر فاین‌تیون SDXL (به صورت LoRA یا کامل) — سریع‌ترین مسیر تحویل
تولید تصویر از متن در دامنه‌ی باز، وزن‌های متن‌باز Flux.1-dev (12B، با مجوز آپاچی / غیرتجاری) یا SD3.5-Large
سریع‌ترین زمان استنتاج، وزن‌های متن‌باز Flux.1-schnell (1 تا 4 گام، آپاچی) یا SDXL-Lightning
بهترین وفاداری به پرامپت، سرویس‌های ابری GPT-Image / DALL-E 3 (همچنان)، Midjourney v7، Imagen 4
فرایندهای ویرایش تصویر Flux.1-Kontext (دسامبر 2024) — ورودی متن و تصویر را مستقیماً دریافت می‌کند
پژوهش، بیس‌لاین SD 1.5 — قدیمی اما کاملاً بررسی‌شده و قابل اتکا

محصول پایانی

فایل outputs/skill-sd-prompter.md را ذخیره کنید. این مهارت پرامپت متنی و سبک هدف را می‌گیرد و این موارد را تحویل می‌دهد: مدل و چک‌پوینت، مقدار ضریب CFG، نمونه‌گیر، پرامپت منفی، رزولوشن، ترکیب اختیاری ControlNet/IP-Adapter، و یک چک‌لیست تضمین کیفیت برای تک‌تک گام‌ها.

تمرین‌ها

  1. ساده. کد code/main.py را با هدایت w∈{0,1,3,7,15}w \in \lbrace 0, 1, 3, 7, 15 \rbrace اجرا کنید. میانگین نمونه‌ها را به تفکیک هر کلاس ثبت کنید. در چه مقداری از ww، میانگین کلاس‌ها از میانگین داده‌های واقعی فاصله می‌گیرد؟
  2. متوسط. انکودر خطی ساده را با یک زوج انکودر/دیکودر MLP با فعال‌ساز tanh و تابع زیان بازسازی جایگزین کنید. دیفیوژن را روی فضاهای نهان جدید بازآموزی کنید. آیا کیفیت نمونه‌ها تغییر می‌کند؟
  3. سخت. یک پایپ‌لاین استنتاج واقعی Stable Diffusion را با diffusers بسازید: چک‌پوینت sdxl-base را لود کنید، 30 گام اویلر (Euler) را با CFG=7 اجرا کرده و زمان بگیرید. حالا به sdxl-turbo با 4 گام و CFG=0 جابه‌جا شوید. با یک سوژه‌ی مشترک به کیفیت‌های متفاوتی می‌رسید — تفاوت‌ها و دلیلشان را تحلیل کنید.

اصطلاحات کلیدی

اصطلاح در مکالمات روزمره معنای دقیق فنی
مرحله‌ی اول (First stage) «همان VAE» زوج انکودر/دیکودر آموزش‌دیده که تصاویر 512² را به 64² فشرده می‌کند.
مرحله‌ی دوم (Second stage) «همان U-Net» مدل دیفیوژن که روی فضای نهان اجرا می‌شود.
CFG «ضریب هدایت» (Guidance scale) ضریب (1+w)⋅ϵcond−w⋅ϵuncond(1+w) \cdot \epsilon_{\text{cond}} - w \cdot \epsilon_{\text{uncond}} که شدت شرطی‌سازی را تنظیم می‌کند.
توکن پوچ (Null token) «امبدینگ پرامپت خالی» امبدینگ غیرشرطی که برای ϵuncond\epsilon_{\text{uncond}} به کار می‌رود.
کراس‌اتنشن (Cross-attention) «روش تزریق متن» هر بلوک U-Net با توکن‌های متنی مثل K و V رفتار می‌کند و به آن‌ها اتنشن می‌زند.
DiT «ترنسفورمر دیفیوژن» جایگزینی U-Net با ترنسفورمر روی پچ‌های فضای نهان؛ مقیاس‌پذیری بسیار بهتری دارد.
MMDiT «DiT چندوجهی» معماری مدل SD3: مسیرهای پردازش متن و تصویر مجزا ولی با اتنشن مشترک.
ضریب مقیاس VAE «عدد جادویی» مقادیر نهان را بر حدود 5.4 تقسیم می‌کند تا دیفیوژن در فضایی با واریانس یک کار کند.

نکته‌ی پروداکشن: اجرای Flux-12B روی پردازنده‌ی گرافیکی 8 گیگابایتی

پیاده‌سازی مرجع Flux بهترین پاسخ به این سوال است که «من یک GPU معمولی دارم؛ چطور این مدل را پروداکشن کنم؟». ترفند کار، همان سه‌گانه‌ی معروفی است که در منابع حوزه‌ی استنتاج برای یک DiT دیفیوژن توصیه می‌شود:

  1. بارگذاری مرحله‌به‌مرحله (Staggered loading). مدل Flux چهار شبکه دارد که اصلاً نیازی نیست هم‌زمان در حافظه‌ی گرافیکی (VRAM) جا خوش کنند: انکودر متنی T5-XXL (حدود 10 گیگابایت در حالت fp32)، انکودر کم‌حجم CLIP-L، شبکه‌ی 12 میلیاردی MMDiT، و خود VAE. ابتدا پرامپت را انکود کنید، انکودرها را از حافظه حذف کنید، DiT را لود کنید، نویززدایی را پیش ببرید، DiT را حذف کنید، VAE را لود کنید و در نهایت دیکود را انجام دهید. کارت‌های گرافیک 8 گیگابایتی مصرفی در هر لحظه فقط گنجایش یکی از این بخش‌ها را دارند.
  2. کوانتایز 4 بیتی با bitsandbytes. تنظیم BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16) روی هر دوی انکودر T5 و DiT. این کار حافظه را 8 برابر کاهش می‌دهد، در حالی که طبق بنچمارک‌های Aritra (لینک‌شده در نوت‌بوک)، افت کیفیت خروجی در تولید تصویر از متن تقریباً به چشم نمی‌آید.
  3. تخلیه روی پردازنده‌ی اصلی (CPU offload). با فعال‌سازی pipe.enable_model_cpu_offload()، ماژول‌ها حین اجرای هر گذر رو به جلو (forward pass) به‌طور خودکار بین CPU و GPU دست‌به‌دست می‌شوند. این کار 10 تا 20 درصد به تاخیر (latency) اضافه می‌کند، اما در عوض اجرای کل پایپ‌لاین را روی چنین سخت‌افزاری ممکن می‌سازد.

حساب‌وکتاب مصرف حافظه به این صورت است: انکودر T5 به میزان 10 گیگابایت تقسیم بر 8 = 1.25 گیگابایت کوانتایزشده، مدل DiT به صورت 12 میلیارد پارامتر ضربدر 0.5 بایت = حدود 6 گیگابایت کوانتایزشده، به اضافه‌ی اکتیویشن‌ها. به تعبیر stas00، این حالت نهایتِ استنتاج روی TP=1 است — یعنی بدون هیچ‌گونه توازی مدل (model parallelism) و با حداکثر فشرده‌سازی و کوانتایز. در محیط پروداکشن واقعی معمولاً روی کارت‌های H100 از TP=2 یا TP=4 استفاده می‌کنید؛ اما برای یک لپ‌تاپ برنامه‌نویسی، راهکار عملی دقیقاً همین است.

برای مطالعه‌ی بیشتر

سؤالات متداول

چرا Stable Diffusion فرایند دیفیوژن را در فضای نهان (Latent) اجرا می‌کند؟

برای صرفه‌جویی شدید در محاسبات؛ به جای کار با تمام پیکسل‌های تصویر اصلی، دیفیوژن روی یک فضای نهان فشرده‌تر با ابعاد 64×64 اجرا می‌شود که بار محاسباتی را تا 64 برابر کاهش می‌دهد و در عین حال ساختار معنایی را حفظ می‌کند.

ضریب هدایت بدون طبقه‌بند (CFG) چیست و چه کاربردی دارد؟

مکانیزمی است که میزان وفاداری به پرامپت را کنترل می‌کند؛ مقدار کم CFG تنوع را بالا می‌برد و مقدار بیش از حد آن خروجی را اشباع و غیرطبیعی می‌کند (بازه مطلوب معمولاً 3 تا 7 است).

چگونه می‌توان مدل‌های بسیار بزرگی مانند Flux-12B را روی یک GPU معمولی 8 گیگابایتی اجرا کرد؟

با ترکیب سه تکنیک: کوانتایز 4 بیتی، بارگذاری مرحله‌به‌مرحله زیرشبکه‌ها در VRAM، و تخلیه بخش‌های غیرفعال روی پردازنده اصلی (CPU Offload).

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.