فاز 8 · درس 2

آموزش خودکدگذارها و خودکدگذارهای متغیر (VAE)

نوع
ساختنی
زمان
حدود 75 دقیقه
زبان‌ها
Python
پیش‌نیاز
فاز 3 · 02 (انتشار رو به عقب)، فاز 3 · 07 (شبکه‌های کانولوشنی)، فاز 8 · 01 (طبقه‌بندی و دسته‌بندی مدل‌ها)
یک قیف شیشه‌ای شفاف که ساختارهای هندسی پیچیده را فشرده کرده و از انتهای آن ذرات کروی منظم در یک فضای ابری متقارن خارج می‌شوند
فهرست مطالب
  1. صورت مسئله
  2. مفاهیم پایه
  3. پیاده‌سازی
  4. گام 1: گذر رو به جلوی انکودر
  5. گام 2: بازپارامتری‌سازی و دکود کردن
  6. گام 3: ELBO
  7. گام 4: تولید داده
  8. چالش‌ها و دام‌ها
  9. کاربردها
  10. تحویل خروجی
  11. تمرین‌ها
  12. اصطلاحات کلیدی
  13. نکته‌ی پروداکشن: VAE داغ‌ترین مسیر در سرورهای دیفیوژن است
  14. برای مطالعه‌ی بیشتر

اگر تا به حال با خودکدگذارهای سنتی (Autoencoders) کار کرده باشید، احتمالاً متوجه شده‌اید که این شبکه‌ها در فشرده‌سازی و بازسازی ورودی‌ها عملکرد بسیار خوبی دارند، اما نمی‌توان از آن‌ها برای تولید داده‌های جدید استفاده کرد؛ زیرا فضای نهان (latent space) آن‌ها ساختاریافته و پیوسته نیست و انتخاب یک نقطه‌ی تصادفی معمولاً به خروجی‌هایی نامفهوم و نویزی ختم می‌شود.

خودکدگذار متغیر (Variational Autoencoder یا VAE) با اضافه کردن قیدهای آماری و ترفند هوشمندانه‌ی بازپارامتری‌سازی (Reparameterization Trick)، فضای نهان را به یک توزیع مشخص و هموار تبدیل می‌کند. این نوآوری امکان نمونه‌گیری مستقیم و تولید داده‌های معنادار را فراهم می‌سازد؛ عاملی که باعث شده VAE بخش جدایی‌ناپذیر پایپ‌لاین‌های مدرن تولید تصویر، مانند Stable Diffusion و Flux باشد.

برای اجرای کدهای این آموزش نیازی به کارت‌های گرافیک گران‌قیمت یا سرویس‌های ابری خارجی ندارید. پیاده‌سازی گام‌به‌گام ارائه‌شده در این درس به‌صورت کاملاً پایه و با پایتون خالص نوشته شده و بدون وابستگی سنگین روی پردازنده‌ی استاندارد (CPU) هر سیستمی به‌راحتی اجرا می‌شود.

یک خودکدگذار (autoencoder) معمولی ابتدا داده را فشرده و سپس بازسازی می‌کند. داده را حفظ می‌کند، اما چیزی تولید نمی‌کند. کافی است یک ترفند به آن اضافه کنید — کد را وادار کنید توزیع گوسی داشته باشد — تا یک نمونه‌گیر (sampler) به دست آورید. همین یک ترفند، یعنی بازپارامتری‌سازیِ z=μ+σ⋅ϵz = \mu + \sigma \cdot \epsilon، دلیل این است که ورودی همه‌ی مدل‌های تصویر دیفیوژن نهان (latent-diffusion) و تطبیق جریان (flow-matching) در سال 2026 یک VAE است.

صورت مسئله

یک رقم 784 پیکسلی دیتاست MNIST را در کدی متشکل از 16 عدد فشرده و سپس بازسازی کنید. یک خودکدگذار معمولی در خطای میانگین مربعات (MSE) بازسازی نمره‌ی عالی می‌گیرد، اما فضای کد آن ساختاری درهم‌ریخته و پر از برآمدگی است. نقطه‌ای تصادفی از این فضای کد بردارید و آن را دکود کنید؛ خروجی صرفاً نویز خواهد بود. چنین مدلی نمونه‌گیر ندارد و در واقع همان مدل فشرده‌سازی در لباسی دیگر است.

آنچه واقعاً می‌خواهید سه چیز است: (الف) فضای کد، توزیعی تمیز و هموار باشد که بتوان از آن نمونه‌گیری کرد — مثلاً یک گوسی همسانگرد N(0,I)\mathcal{N}(0, I)، (ب) دکود کردن هر نمونه، رقمی معقول بسازد، و (ج) انکودر و دکودر همچنان فشرده‌سازی خوبی داشته باشند. سه هدف، یک معماری، یک تابع زیان (loss).

مدل VAE کینگما در سال 2013 این مسئله را حل می‌کند: انکودر طوری آموزش می‌بیند که یک توزیع خروجی دهد q(z∣x)=N(μ(x),σ(x)2)q(z \mid x) = \mathcal{N}(\mu(x), \sigma(x)^2)، این توزیع با جریمه‌ی واگرایی کولبک-لایبلر (KL penalty) به سمت توزیع پیشین (prior) N(0,I)\mathcal{N}(0, I) کشیده می‌شود، و سپس پیش از دکود کردن، zz از q(z∣x)q(z \mid x) نمونه‌گیری می‌شود. هنگام استنتاج (inference)، انکودر را کنار بگذارید، z∼N(0,I)z \sim \mathcal{N}(0, I) را نمونه‌گیری کنید و دکود نمایید. جریمه‌ی KL همان چیزی است که فضای کد را ساختاریافته می‌کند.

در سال 2026 مدل‌های VAE به‌ندرت به‌صورت مستقل عرضه می‌شوند — دیفیوژن از نظر کیفیت خام تصویر آن‌ها را پشت سر گذاشته است — اما VAE انکودر انتخابی برای تمام مدل‌های دیفیوژن نهان (SD 1/2/XL/3، Flux و AudioCraft) به شمار می‌رود. VAE را یاد بگیرید تا لایه‌ی اول و نادیدنی تمام پایپ‌لاین‌های تصویریِ مورد استفاده‌تان را بشناسید.

مفاهیم پایه

مقایسه‌ی خودکدگذار و VAE: ترفند بازپارامتری‌سازی

خودکدگذار. z=encoder(x)z = \text{encoder}(x)، x^=decoder(z)\hat{x} = \text{decoder}(z)، تابع زیان (loss) = ∥x−x^∥2\lVert x - \hat{x} \rVert^2. فضای کد بدون ساختار است.

انکودر VAE. دو بردار خروجی می‌دهد: μ(x)\mu(x) و log⁡σ2(x)\log \sigma^2(x). این دو بردار q(z∣x)=N(μ,diag(σ2))q(z \mid x) = \mathcal{N}(\mu, \mathop{\mathrm{diag}}(\sigma^2)) را تعریف می‌کنند.

ترفند بازپارامتری‌سازی (Reparameterization Trick). نمونه‌گیری از q(z∣x)q(z \mid x) مشتق‌پذیر نیست. نمونه را به شکل z=μ+σ⋅ϵz = \mu + \sigma \cdot \epsilon بازنویسی کنید که در آن ϵ∼N(0,I)\epsilon \sim \mathcal{N}(0, I). اکنون zz تابعی معین (deterministic) از (μ,σ)(\mu, \sigma) به‌علاوه‌ی نویزی بدون پارامتر است — گرادیان‌ها از میان μ و σ\sigma جاری می‌شوند.

تابع زیان. حد پایین شواهد یا ELBO (مخفف Evidence Lower BOund)، شامل دو عبارت:

loss=reconstruction+β⋅KL[q(z∣x) ∥ N(0,I)]=∥x−x^∥2+β⋅∑iσi2+μi2−log⁡σi2−12\begin{aligned} \text{loss} &= \text{reconstruction} + \beta \cdot \mathrm{KL}[q(z \mid x) \,\Vert\, \mathcal{N}(0, I)] \\ &= \lVert x - \hat{x} \rVert^2 + \beta \cdot \sum_i \frac{\sigma_i^2 + \mu_i^2 - \log \sigma_i^2 - 1}{2} \end{aligned}

بخش بازسازی، x^\hat{x} را به سمت xx push می‌کند. جریمه‌ی KL نیز q(z∣x)q(z \mid x) را به سمت توزیع پیشین push می‌کند. بین این دو مصالحه برقرار است. مقادیر کوچک β\beta (<1<1) = نمونه‌های شفاف‌تر، اما فضای کدی با شباهت کمتر به گوسی. مقادیر بزرگ β\beta (>1>1) = فضای کد مرتب‌تر، اما نمونه‌های تارتر. مدل β-VAE (هیگینز، 2017) این پیچ تنظیم را مشهور کرد و جرقه‌ی پژوهش‌های تفکیک متغیرها (disentanglement) را زد.

نمونه‌گیری. هنگام استنتاج: z∼N(0,I)z \sim \mathcal{N}(0, I) را نمونه‌گیری کنید و در یک گذر رو به جلو (forward pass) به دکودر بدهید. فقط یک گذر رو به جلو — بدون نیاز به نمونه‌گیری تکرارشونده مثل مدل‌های دیفیوژن.

شبکهٔ فضای نهان VAEz1 و z2 را بکشید
VAE LATENT GRIDA VAE maps inputs to a smooth latent space and decodes points back to outputs. The square on the left is a slice of that space; the dot is your latent code. Moving it morphs the decoded shape on the right continuously, because the decoder is trained so that nearby codes produce nearby outputs.
یک VAE ورودی‌ها را به یک فضای نهان هموار نگاشت می‌کند و نقطه‌ها را دوباره به خروجی‌ها دیکود می‌نماید. مربع سمت چپ برشی از این فضا است؛ نقطه همان کد نهان شماست. جابه‌جا کردن آن، شکل دیکودشدهٔ سمت راست را به‌طور پیوسته تغییر می‌دهد، زیرا دیکودر طوری آموزش دیده است که کدهای نزدیک به هم، خروجی‌های مشابهی تولید کنند.

پیاده‌سازی

فایل code/main.py یک VAE جمع‌وجور را بدون استفاده از numpy یا torch پیاده می‌کند. ورودی، داده‌ای ساختگی و 8 بُعدی است که از یک مدل آمیخته‌ی گوسی 2 مؤلفه‌ای در فضای 8 بعدی نمونه‌گیری شده است. انکودر و دکودر پرسپترون‌های چندلایه‌ی (MLP) با یک لایه‌ی پنهان هستند. ما تابع فعال‌سازی tanh، گذر رو به جلو، تابع زیان و یک گام رو به عقب (backward pass) دست‌نویس را پیاده می‌کنیم. نه برای پروداکشن — صرفاً با هدف آموزش.

گام 1: گذر رو به جلوی انکودر

def encode(x, enc):
    h = tanh(add(matmul(enc["W1"], x), enc["b1"]))
    mu = add(matmul(enc["W_mu"], h), enc["b_mu"])
    log_sigma2 = add(matmul(enc["W_sig"], h), enc["b_sig"])
    return mu, log_sigma2

به جای σ\sigma از log⁡σ2\log \sigma^2 استفاده می‌کنیم تا خروجی شبکه محدودیتی نداشته باشد (استفاده از softplus برای σ یک تله است — گرادیان‌ها در σ≈0\sigma \approx 0 صفر می‌شوند).

گام 2: بازپارامتری‌سازی و دکود کردن

def reparameterize(mu, log_sigma2, rng):
    eps = [rng.gauss(0, 1) for _ in mu]
    sigma = [math.exp(0.5 * lv) for lv in log_sigma2]
    return [m + s * e for m, s, e in zip(mu, sigma, eps)]

def decode(z, dec):
    h = tanh(add(matmul(dec["W1"], z), dec["b1"]))
    return add(matmul(dec["W_out"], h), dec["b_out"])

گام 3: ELBO

def elbo(x, x_hat, mu, log_sigma2, beta=1.0):
    recon = sum((a - b) ** 2 for a, b in zip(x, x_hat))
    kl = 0.5 * sum(math.exp(lv) + m * m - lv - 1 for m, lv in zip(mu, log_sigma2))
    return recon + beta * kl, recon, kl

چون هر دو توزیع گوسی هستند، فرم بسته‌ی (closed-form) دقیق برای KL داریم. آن را به‌صورت عددی انتگرال‌گیری نکنید. هنوز در سال 2026 کدهایی با تخمین مونت‌کارلو برای KL منتشر می‌شوند — کاری که بی‌دلیل سرعت را 3 برابر کمتر می‌کند.

گام 4: تولید داده

def sample(dec, z_dim, rng):
    z = [rng.gauss(0, 1) for _ in range(z_dim)]
    return decode(z, dec)

این کل مدل زایشی (generative model) است. فقط پنج خط.

چالش‌ها و دام‌ها

  • فروپاشی توزیع پسین (Posterior collapse). عبارت KL چنان با شدت q(z∣x)→N(0,I)q(z \mid x) \to \mathcal{N}(0, I) را پایین می‌آورد که zz هیچ اطلاعاتی درباره‌ی xx نگه نمی‌دارد. راهکار: بازپخت ضریب بتا (β-annealing) (شروع از β=0\beta=0 و افزایش تا 1)، بیت‌های آزاد (free bits)، یا صرف‌نظر از جریمه‌ی KL در ابعاد غیرفعال.
  • تار شدن نمونه‌ها. درست‌نمایی (likelihood) گوسی دکودر مستلزم بازسازی با MSE است؛ این معیار برای L2 بهینه‌ی بیزی است (میانگین) — و میانگینِ مجموعه‌ای از ارقام باورپذیر، رقمی محو و مات خواهد بود. راهکار: دکودر گسسته (VQ-VAE ،NVAE)، یا استفاده از VAE صرفاً به‌عنوان انکودر و سوار کردن دیفیوژن روی متغیرهای نهان (کاری که Stable Diffusion انجام می‌دهد).
  • بزرگ بودن بیش از حد β در ابتدای کار. به بخش فروپاشی پسین مراجعه کنید. از β≈0.01\beta \approx 0.01 شروع کنید و به‌مرور مقدار را افزایش دهید.
  • ابعاد نهان بیش از حد کوچک. فضای 16 بعدی برای MNIST، 256 بعدی برای ImageNet 256² و 2048 بعدی برای ImageNet 1024² جواب می‌دهد. مدل VAE در Stable Diffusion تصویر 512×512×3 را به 64×64×4 فشرده می‌کند (کاهش 8 برابری ابعاد در هر ضلع، کاهش 64 برابری مساحت؛ تعداد کانال‌ها از 3 به 4 می‌رسد، یعنی در کل داده‌ها حدود 48 برابر کمتر می‌شوند).

کاربردها

پشته‌ی ابزارهای VAE در سال 2026:

موقعیت انتخاب
انکودر تصویر نهان برای دیفیوژن VAE مربوط به Stable Diffusion (مدل sd-vae-ft-ema) یا VAE مدل Flux
انکودر صوت نهان Encodec (از Meta)، SoundStream یا DAC (از Descript)
متغیرهای نهان ویدیو پچ‌های فضا-زمانی Sora، مدل Latte VAE، یا WAN VAE
یادگیری بازنمایی تفکیک‌شده (Disentangled representation learning) β-VAE، FactorVAE، TCVAE
متغیرهای نهان گسسته (برای مدل‌سازی با ترنسفورمر) VQ-VAE، RVQ (کوآنتایزر باقی‌مانده)
متغیرهای نهان پیوسته برای تولید VAE معمولی، سپس شرطی‌سازی مدل جریان/دیفیوژن در همان فضای نهان

یک مدل دیفیوژن نهان در حقیقت همان VAE است که یک مدل دیفیوژن بین انکودر و دکودرش جا خوش کرده است. VAE فشرده‌سازی درشت‌دانه را انجام می‌دهد و مدل دیفیوژن بار سنگین کار را به دوش می‌کشد. همین الگو برای ویدیو (ترکیب VAE + ترنسفورمر DiT دیفیوژن ویدیو) و صدا (ترکیب Encodec + ترنسفورمر MusicGen) نیز حاکم است.

تحویل خروجی

فایل outputs/skill-vae-trainer.md را ذخیره کنید.

این مهارت مشخصات زیر را ورودی می‌گیرد: مشخصات دیتاست + بعد نهان هدف + کاربرد پایین‌دستی (بازسازی، نمونه‌گیری، یا ورودی مدل دیفیوژن نهان) و خروجی می‌دهد: انتخاب معماری (معمولی/β/VQ/RVQ)، برنامه‌ی زمانی تغییرات β، بعد نهان، درست‌نمایی دکودر (گوسی در برابر دسته‌ای)، و طرح ارزیابی (MSE بازسازی، KL به ازای هر بعد، و فاصله‌ی فرشه بین q(z∣x)q(z \mid x) و N(0,I))\mathcal{N}(0, I))).

تمرین‌ها

  1. ساده. مقدار β\beta را در code/main.py به 0.01، 0.1، 1.0 و 5.0 تغییر دهید. مقادیر نهایی MSE بازسازی و KL را ثبت کنید. کدام مقدار β\beta برای داده‌های ساختگی شما بهترین تعادل پارتو (Pareto-best) را به دست می‌دهد؟
  2. متوسط. درست‌نمایی گوسی دکودر را با درست‌نمایی برنولی (زیان آنتروپی متقاطع یا Cross-Entropy) جایگزین کنید. کیفیت نمونه‌ها را روی نسخه‌ی باینری‌شده‌ی همان داده‌های ساختگی مقایسه کنید.
  3. سخت. کد code/main.py را به یک VQ-VAE کوچک ارتقا دهید: متغیر پیوسته‌ی zz را با جست‌وجوی نزدیک‌ترین همسایه در یک کتابچه‌ی کد (codebook) با 32=K مدخل جایگزین کنید. MSE بازسازی را مقایسه کنید و گزارش دهید چه تعداد از اعضای کتابچه‌ی کد واقعاً استفاده می‌شوند (مسئله‌ی فروپاشی کتابچه‌ی کد کاملاً واقعی است).

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

اصطلاح عامه چه می‌گویند واقعاً چه معنایی دارد
خودکدگذار (Autoencoder) شبکه‌ی انکود-دکود x→z→x^x \to z \to \hat{x}، یادگیری با MSE. زایشی نیست.
VAE خودکدگذار با نمونه‌گیر انکودر یک توزیع خروجی می‌دهد، جریمه‌ی KL فضای کد را شکل می‌دهد.
حد پایین شواهد (ELBO) کران پایین شواهد log⁡p(x)≥recon−KL[q(z∣x)∥p(z)]\log p(x) \ge \text{recon} - \mathrm{KL}[q(z \mid x) \Vert p(z)]؛ وقتی مساوی و دقیق است که q=p(z∣x)q = p(z \mid x).
ترفند بازپارامتری‌سازی z=μ+σ⋅ϵz = \mu + \sigma \cdot \epsilon بازنویسی گره تصادفی به‌صورت تابعی معین به‌علاوه‌ی نویز خالص؛ انتشار رو به عقب را از میان نمونه‌گیری ممکن می‌سازد.
پیشین (Prior) p(z)p(z) توزیع هدف برای متغیر نهان، که معمولاً N(0,I)\mathcal{N}(0, I) است.
فروپاشی توزیع پسین (Posterior collapse) «جریمه‌ی KL برنده شد» انکودر xx را نادیده می‌گیرد و توزیع پیشین را تحویل می‌دهد؛ دکودر مجبور است توهم بزند.
مدل β-VAE وزن قابل‌تنظیم برای KL loss=recon+β⋅KL\text{loss} = \text{recon} + \beta \cdot \mathrm{KL}. مقادیر بالاتر β\beta = ویژگی‌های تفکیک‌شده‌تر اما نمونه‌های تارتر.
مدل VQ-VAE متغیر نهان گسسته جایگزینی zz پیوسته با نزدیک‌ترین بردار کتابچه‌ی کد؛ امکان مدل‌سازی با ترنسفورمر را فراهم می‌کند.

نکته‌ی پروداکشن: VAE داغ‌ترین مسیر در سرورهای دیفیوژن است

در پایپ‌لاین Stable Diffusion / Flux / SD3، مدل VAE در هر درخواست دو بار فراخوانی می‌شود — یک بار برای انکود (در صورت انجام کارهای تصویر-به-تصویر یا اینپینتینگ) و یک بار برای دکود. در وضوح 1024²، گذر دکودر معمولاً بالاترین اوج مصرف حافظه‌ی اکتیویشن (activation memory) را در کل پایپ‌لاین رقم می‌زند؛ زیرا باید متغیرهای نهان با ابعاد 128×128×16 را مجدداً به اندازه‌ی 1024×1024×3 بزرگ‌نمایی کند. این موضوع دو پیامد عملی دارد:

  • دکود کردن برشی (Slicing) یا کاشی‌کاری‌شده (Tiling). پکیج diffusers دو قابلیت pipe.vae.enable_slicing() و pipe.vae.enable_tiling() را در اختیارتان می‌گذارد. کاشی‌کاری با پذیرش یک خطای مرزی و درز جزئی، مصرف حافظه را از O(H⋅W)O(H \cdot W) به O(tile2)O(\text{tile}^2) کاهش می‌دهد. این ترفند برای اجرای رزولوشن‌های 1024²+ روی پردازنده‌های گرافیکی تجاری حیاتی است.
  • دکودر با دقت bf16، محاسبات عددی fp32 برای تغییر اندازه‌ی نهایی. مدل VAE در SD 1.x با دقت fp32 منتشر شد و در صورت تبدیل به fp16 در رزولوشن‌های 1024²+ بی‌صدا مقادیر نامعتبر عددی (NaN) تولید می‌کند. در SDXL مدل madebyollin/sdxl-vae-fp16-fix عرضه شده است — همیشه از این نسخه‌ی اصلاح‌شده استفاده کنید یا سراغ دقت bf16 بروید.

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

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

چرا خودکدگذار سنتی یک مدل زایشی (Generative) محسوب نمی‌شود؟

خودکدگذار سنتی صرفاً ورودی را فشرده و با کمینه‌سازی خطای بازسازی حفظ می‌کند؛ فضای نهان آن پیوسته و منظم نیست و در نتیجه نمونه‌گیری تصادفی از آن خروجی‌های معنادار تولید نمی‌کند.

ترفند بازپارامتری‌سازی (Reparameterization Trick) در VAE چه کاری انجام می‌دهد؟

نمونه‌گیری تصادفی عملیاتی مشتق‌ناپذیر است. این ترفند گره تصادفی را به تابعی معین از میانگین و واریانس به‌همراه یک نویز تصادفی مستقل تبدیل می‌کند تا انتشار رو به عقب گرادیان‌ها ممکن شود.

نقش VAE در مدل‌های دیفیوژن مثل Stable Diffusion چیست؟

مدل VAE وظیفه‌ی فشرده‌سازی ابعاد تصویر خام ورودی به یک فضای نهان کوچک‌تر و بازسازی نهایی آن را بر عهده دارد تا مدل دیفیوژن محاسبات سنگین خود را روی این فضای فشرده انجام دهد.

پدیده‌ی فروپاشی توزیع پسین (Posterior Collapse) در آموزش VAE چیست و چگونه حل می‌شود؟

زمانی رخ می‌دهد که جریمه‌ی واگرایی KL بیش از حد قوی شود و انکودر ورودی را نادیده بگیرد. با شروع ضریب بتا (β) از صفر و افزایش تدریجی آن (β-annealing) می‌توان از این مشکل جلوگیری کرد.

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

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