فاز 8 · درس 6

پیاده‌سازی مدل‌های دیفیوژن (DDPM) از صفر

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

مدل‌های زایشی پیش از ظهور دیفیوژن چالش‌های بزرگی داشتند؛ شبکه‌های تقابلی انتساب‌گر (GAN) با بازی‌های مینی‌مکس ناپایدار واگرا می‌شدند و خودرمزگذارهای متغیری (VAE) تصاویری تار تحویل می‌دادند. مدل‌های احتمالاتی نویززدایی دیفیوژن (DDPM) با ساده‌سازی مسئله به تخمین نویز در گام‌های متوالی، پایه‌ی اصلی ابزارهای پیشرفته‌ای مانند Stable Diffusion، Flux و مدل‌های زایشی تصویر و ویدیو شدند.

درک سازوکار داخلی DDPM و نحوه‌ی استخراج فرم بسته‌ی گام‌های رفت و برگشت به شما کمک می‌کند که هنگام استفاده، شخصی‌سازی یا بهینه‌سازی مدل‌های مدرن زایشی، دیدی فراتر از صرفاً اجرای اسکریپت‌های آماده داشته باشید و تفاوت رویکردهای نویززدایی را به شکلی کاربردی بشناسید.

کد آموزشی این درس روی یک توزیع یک‌بعدی پیاده‌سازی شده و محاسبات بسیار سبکی دارد؛ بنابراین بدون نیاز به تهیه‌ی کارت‌های گرافیکی گران‌قیمت یا سرویس‌های ابری خارجی، به‌راحتی روی پردازنده‌ی معمولی سیستم شخصی شما اجرا می‌شود.

مقاله Ho، Jain و Abbeel (2020) فرمولی به این حوزه داد که دیگر نمی‌شد از آن دست کشید: داده‌ها را طی هزار گام کوچک با نویز نابود کنید؛ یک شبکه‌ی عصبی را برای پیش‌بینی نویز آموزش دهید؛ و فرآیند را هنگام استنتاج (inference) معکوس کنید. امروزه تمام مدل‌های جریان اصلی تصویر، ویدیو، سه‌بعدی و موسیقی بر پایه‌ی همین حلقه اجرا می‌شوند؛ گاهی با اضافه شدن ترفندهای تطبیق جریان (flow matching) یا سازگاری (consistency).

صورت مسئله

شما یک نمونه‌گیر (sampler) برای pdata(x)p_{\text{data}}(x) می‌خواهید. شبکه‌های GAN وارد یک بازی مینی‌مکس (minimax) می‌شوند که اغلب واگرا می‌شود. مدل‌های VAE نیز با دکودر گاوسی خود نمونه‌های تار تحویل می‌دهند. چیزی که واقعاً می‌خواهید، یک هدف آموزشی است که: (الف) یک تابع زیان (loss) منفرد و پایدار داشته باشد (نه نقطه‌ی زینی و نه مینی‌مکس)، (ب) کران پایینی برای log⁡p(x)\log p(x) باشد (تا درست‌نمایی داشته باشید)، و (ج) نمونه‌هایی هم‌تراز با بهترین کیفیت روز (SOTA) بسازد.

سول-دیکستین و همکاران (2015) پاسخی تئوریک ارائه دادند: یک زنجیره‌ی مارکوف q(xt∣xt−1)q(x_t \mid x_{t-1}) تعریف کنید که به‌تدریج نویز گاوسی اضافه می‌کند، و سپس زنجیره‌ی معکوس pθ(xt−1∣xt)p_\theta(x_{t-1} \mid x_t) را برای نویززدایی آموزش دهید. هو، جین و اَبیل (2020) نشان دادند که این تابع زیان را می‌توان تا حد یک خط ساده کرد — پیش‌بینی نویز — و ریاضیات آن را پیراسته کردند. در سال 2020 این ایده صرفاً یک کار جالب بود؛ در 2021 نمونه‌هایی در سطح SOTA ساخت؛ در 2022 به Stable Diffusion تبدیل شد؛ و در 2026 بستر اصلی کل این حوزه است.

مفهوم اصلی

DDPM: نویز در مسیر رفت، نویززدایی در مسیر برگشت

فرآیند رو به جلو qq. نویز گاوسی را طی TT گام کوچک اضافه کنید. دلیل این‌که ریاضیات مسئله حل‌شدنی باقی می‌ماند، وجود فرم بسته (closed-form) است: گام تجمیعی نیز خود گاوسی است:

q(xt∣x0)=N(αˉt⋅x0,(1−αˉt)⋅I)q(x_t \mid x_0) = \mathcal{N}\left( \sqrt{\bar{\alpha}_t} \cdot x_0, (1 - \bar{\alpha}_t) \cdot I \right)

که در آن αˉt=∏s=1t(1−βs)\bar\alpha_t = \prod_{s=1}^{t} (1 - \beta_s) برای برنامه‌ی زمان‌بندیِ (schedule) βt\beta_t تعریف می‌شود. اگر βt\beta_t را به‌صورت خطی از 1e-4 تا 0.02 طی T=1000T=1000 گام انتخاب کنید، مقدار xTx_T تقریباً برابر N(0,I)\mathcal{N}(0, I) خواهد بود.

فرآیند معکوس pθp_\theta. شبکه‌ی عصبیِ ϵθ(xt,t)\epsilon_\theta(x_t, t) را برای پیش‌بینی نویز اضافه‌شده آموزش دهید. با داشتن xtx_t، نویززدایی را به این شکل انجام دهید:

xt−1=1αt⋅(xt−βt1−αˉt⋅ϵθ(xt,t))+σt⋅zx_{t-1} = \frac{1}{\sqrt{\alpha_t}} \cdot \left( x_t - \frac{\beta_t}{\sqrt{1 - \bar{\alpha}_t}} \cdot \epsilon_\theta(x_t, t) \right) + \sigma_t \cdot z

که در آن σt\sigma_t می‌تواند برابر βt\sqrt{\beta_t} یا یک واریانس یادگرفته‌شده باشد. ظاهر عبارت کمی شلوغ است اما در واقع فقط محاسبات جبری است: حل معادله برای xt−1x_{t-1} با در نظر گرفتن توزیع پسین (posterior) یعنی q(xt−1∣xt,x0)q(x_{t-1} \mid x_t, x_0)، و جایگزینی x0x_0 با تخمین حاصل از نویز پیش‌بینی‌شده.

تابع زیان آموزش.

Lsimple=Ex0,t,ϵ[∥ϵ−ϵθ(αˉt⋅x0+1−αˉt⋅ϵ,t)∥2]L_{\text{simple}} = \mathbb{E}_{x_0, t, \epsilon} \left[ \left\lVert \epsilon - \epsilon_\theta\left( \sqrt{\bar{\alpha}_t} \cdot x_0 + \sqrt{1 - \bar{\alpha}_t} \cdot \epsilon, t \right) \right\rVert^2 \right]

داده‌ی x0x_0 را از مجموعه‌داده نمونه‌گیری کنید، یک گام تصادفی tt بردارید، نویز ϵ∼N(0,I)\epsilon \sim \mathcal{N}(0, I) را نمونه بگیرید، داده‌ی نویزی xtx_t را یک‌ضرب از طریق فرم بسته به دست آورید و رگرسیون را روی نویز اجرا کنید. یک تابع زیان منفرد؛ بدون مینی‌مکس، بدون واگرایی KL و بدون ترفند بازپارامتری‌سازی (reparameterization trick).

نمونه‌گیری. با xT∼N(0,I)x_T \sim \mathcal{N}(0, I) شروع کنید. گام معکوس را از t=Tt = T تا 1 تکرار کنید. تمام.

چرا کار می‌کند؟

سه شهود کلیدی وجود دارد:

  1. نویززدایی آسان است؛ تولید داده سخت. در t=Tt=T، داده کاملاً نویز خالص است؛ شبکه باید مسئله‌ای پیش‌پاافتاده را حل کند. در t=0t=0، شبکه فقط باید چند پیکسل را تمیز کند. در مقادیر میانی tt، مسئله دشوار است اما گرادیان‌های فراوانی از تمام سطوح نویز از میان وزن‌های مشترک شبکه عبور می‌کنند.

  2. تطبیق امتیاز در لباس مبدل. وینسنت (2011) ثابت کرد پیش‌بینی نویز معادل با تخمین ∇xlog⁡q(xt∣x0)\nabla_x \log q(x_t \mid x_0) یا همان امتیاز (score) است. معادلۀ دیفرانسیل تصادفی معکوس (reverse SDE) از این امتیاز استفاده می‌کند تا در جهت گرادیان چگالی بالا برود؛ نوعی گشت تصادفی (random walk) هدایت‌شده به سمت نواحی با احتمال بالا.

  3. ساده شدن ELBO به یک MSE ساده. کران پایین متغیری کامل برای هر گام زمانی یک عبارت KL دارد. با پارامتری‌سازی DDPM، این عبارات KL به یک تابع MSE روی پیش‌بینی نویز با ضرایبی مشخص تبدیل می‌شوند؛ هو این ضرایب را کنار گذاشت (و آن را زیان «ساده» نامید) و کیفیت در عمل بهتر شد.

حذف نویز در مدل دیفیوژنگام زمانی را بکشید
DIFFUSION DENOISEA diffusion model learns to reverse a noising process. At t = T the signal is pure noise; as t falls toward 0 the model removes noise step by step and the underlying signal (grey dashes) re-emerges. Each x_t is a fixed mix of the clean signal and the same noise, weighted by the schedule.
یک مدل دیفیوژن یاد می‌گیرد که فرآیند افزودن نویز را معکوس کند. در t = T سیگنال کاملاً نویز خالص است؛ با کاهش t به سمت 0، مدل گام‌به‌گام نویز را حذف می‌کند و سیگنال اصلی (خط‌چین‌های خاکستری) دوباره پدیدار می‌شود. هر x_t ترکیبی ثابت از سیگنال تمیز و همان نویز است که بر اساس زمان‌بندی (schedule) وزن‌دهی شده است.

پیاده‌سازی از صفر

فایل code/main.py یک مدل DDPM یک‌بعدی را پیاده‌سازی می‌کند. داده‌ها توزیعی با دو مُد (two-mode mixture) هستند. «شبکه» یک MLP بسیار کوچک است که (xt,t)(x_t, t) را می‌گیرد و نویز پیش‌بینی‌شده را تحویل می‌دهد. آموزش با همان تابع زیان یک‌خطی انجام می‌شود و نمونه‌گیری هم زنجیره‌ی معکوس را تکرار می‌کند.

گام 1: زمان‌بندی فرآیند رو به جلو (فرم بسته)

betas = [1e-4 + (0.02 - 1e-4) * t / (T - 1) for t in range(T)]
alphas = [1 - b for b in betas]
alpha_bars = []
cum = 1.0
for a in alphas:
    cum *= a
    alpha_bars.append(cum)

گام 2: نمونه‌گیری یک‌باره از x_t

def forward_sample(x0, t, alpha_bars, rng):
    a_bar = alpha_bars[t]
    eps = rng.gauss(0, 1)
    x_t = math.sqrt(a_bar) * x0 + math.sqrt(1 - a_bar) * eps
    return x_t, eps

گام 3: یک گام آموزش

def train_step(x0, model, alpha_bars, rng):
    t = rng.randrange(T)
    x_t, eps = forward_sample(x0, t, alpha_bars, rng)
    eps_hat = model_forward(model, x_t, t)
    loss = (eps - eps_hat) ** 2
    return loss, gradient_step(model, ...)

گام 4: نمونه‌گیری معکوس

def sample(model, alpha_bars, T, rng):
    x = rng.gauss(0, 1)
    for t in range(T - 1, -1, -1):
        eps_hat = model_forward(model, x, t)
        beta_t = 1 - alphas[t]
        x = (x - beta_t / math.sqrt(1 - alpha_bars[t]) * eps_hat) / math.sqrt(alphas[t])
        if t > 0:
            x += math.sqrt(beta_t) * rng.gauss(0, 1)
    return x

برای یک مسئله‌ی یک‌بعدی با 40 گام زمانی و یک MLP با 24 واحد، این مدل توزیع دو مدی را در حدود 200 ایپاک یاد می‌گیرد.

شرطی‌سازی زمانی

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

  • امبدینگ سینوسی (Sinusoidal embedding). شبیه کدگذاری مکانی (positional encoding) در ترنسفورمر: embed(t)=[sin⁡(t/ω0),cos⁡(t/ω0),sin⁡(t/ω1),…]\text{embed}(t) = [\sin(t/\omega_0), \cos(t/\omega_0), \sin(t/\omega_1), \ldots]. آن را از یک MLP عبور دهید و به ابعاد شبکه برودکست (broadcast) کنید.
  • شرطی‌سازی با FiLM یا Group-Norm. در هر بلوک، امبدینگ را به مقیاس (scale) و بایاس (bias) به‌ازای هر کانال نگاشت کنید (روش FiLM).

کد ساده و آموزشی ما از روش سینوسی و الحاق (concat) استفاده می‌کند؛ مدل‌های U-Net عملیاتی از FiLM بهره می‌برند.

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

  • زمان‌بندی اهمیت بسیار زیادی دارد. زمان‌بندی خطی β\beta پیش‌فرض DDPM است، اما زمان‌بندی کسینوسی (Nichol & Dhariwal, 2021) با همان میزان محاسبات، شاخص FID بهتری می‌دهد. اگر کیفیت مدل در نقطه‌ای متوقف شد، زمان‌بندی را تغییر دهید.
  • امبدینگ گام زمانی شکننده است. ارسال مستقیم tt به‌صورت یک عدد اعشاری (float) برای مسائل ساده‌ی یک‌بعدی جواب می‌دهد ولی برای تصاویر با شکست مواجه می‌شود؛ همیشه از یک امبدینگ اصولی استفاده کنید.
  • پیش‌بینی V در برابر پیش‌بینی ε. در بازه‌های حدی (t بسیار کوچک یا بسیار بزرگ)، ϵ\epsilon نسبت سیگنال به نویز ضعیفی دارد. پیش‌بینی V یا همان v=α⋅ϵ−σ⋅xv = \alpha \cdot \epsilon - \sigma \cdot x پایدارتر است؛ مدل‌های SDXL، SD3 و Flux از این روش استفاده می‌کنند.
  • هدایت بدون طبقه‌بند (Classifier-free guidance). هنگام استنتاج، هم پیش‌بینی شرطی و هم غیرشرطی ϵ\epsilon را محاسبه کنید و سپس فرمول ϵcfg=(1+w)⋅ϵcond−w⋅ϵuncond\epsilon_{\text{cfg}} = (1 + w) \cdot \epsilon_{\text{cond}} - w \cdot \epsilon_{\text{uncond}} را با w≈3-7w \approx 3\text{-}7 اعمال نمایید. این مبحث در درس 08 بررسی می‌شود.
  • 1000 گام بسیار زیاد است. سیستم‌های عملیاتی از الگوریتم‌های DDIM (بین 20 تا 50 گام)، DPM-Solver (بین 10 تا 20 گام) یا تقطیر دانش (distillation) (بین 1 تا 4 گام) استفاده می‌کنند. درس 12 را ببینید.

استفاده از ابزارهای واقعی

نقش استک رایج در سال 2026
دیفیوژن در فضای پیکسل تصویر (کوچک و آزمایشی) DDPM + U-Net
دیفیوژن نهان تصویر انکودر VAE به همراه U-Net یا DiT (درس 07)
دیفیوژن نهان ویدیو DiT مکانی-زمانی (Sora، Veo، WAN)
دیفیوژن نهان صدا Encodec به همراه ترنسفورمر دیفیوژن
علوم پایه (مولکول‌ها، پروتئین‌ها، فیزیک) دیفیوژن هم‌وردا (EDM، RFdiffusion، AlphaFold3)

دیفیوژن بک‌بون (backbone) زایشی جهان‌شمول است. تطبیق جریان (درس 13) رقیب سال‌های 2024 تا 2026 آن به شمار می‌رود که معمولاً با کیفیت یکسان، در سرعت استنتاج برنده می‌شود.

خروجی این درس

فایل outputs/skill-diffusion-trainer.md را ذخیره کنید. این مهارت، یک مجموعه‌داده و بودجه‌ی محاسباتی را تحویل می‌گیرد و این موارد را خروجی می‌دهد: نوع زمان‌بندی (خطی/کسینوسی/سیگموئید)، هدف پیش‌بینی (ε/v/x)، تعداد گام‌ها، مقیاس هدایت (guidance scale)، خانواده‌ی نمونه‌گیر، و پروتکل ارزیابی.

تمرین‌ها

  1. ساده. در فایل code/main.py مقدار T را از 40 به 10 تغییر دهید. کیفیت نمونه‌ها (هیستوگرام بصری خروجی‌ها) چگونه افت می‌کند؟ در چه مقداری از T ساختار دو مدی فرومی‌پاشد؟
  2. متوسط. پیش‌بینی را از ε به v تغییر دهید. گام معکوس را از نو استخراج کنید. کیفیت نمونه‌های نهایی را مقایسه کنید.
  3. سخت. هدایت بدون طبقه‌بند را اضافه کنید. مدل را روی برچسب کلاس c∈{0,1}c \in \lbrace 0, 1 \rbrace شرطی کنید، در 10% مراحل آموزش برچسب را نادیده بگیرید، و هنگام نمونه‌گیری از ϵ=(1+w)⋅ϵcond−w⋅ϵuncond\epsilon = (1+w) \cdot \epsilon_{\text{cond}} - w \cdot \epsilon_{\text{uncond}} بهره ببرید. نرخ تولید مُد شرطی را در w=0,1,3,7w = 0, 1, 3, 7 بسنجید.

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

اصطلاح در گفت‌وگوها معنای دقیق فنی
فرآیند رو به جلو (Forward process) «اضافه کردن نویز» زنجیره‌ی مارکوف ثابت q(xt∣xt−1)q(x_t \mid x_{t-1}) که داده را نابود می‌کند.
فرآیند معکوس (Reverse process) «نویززدایی» زنجیره‌ی آموزش‌دیده‌ی pθ(xt−1∣xt)p_\theta(x_{t-1} \mid x_t) که داده را بازسازی می‌کند.
زمان‌بندی β «نردبان نویز» واریانس در هر گام؛ خطی، کسینوسی یا سیگموئید.
α̅ «آلفا بار» حاصل‌ضرب تجمیعی ∏(1−β)\prod (1 - \beta)؛ ارائه‌ی فرم بسته‌ی xtx_t از روی x0x_0.
زیان ساده (Simple loss) «خطای MSE روی نویز» ∥ϵ−ϵθ(xt,t)∥2\lVert \epsilon - \epsilon_\theta(x_t, t) \rVert^2؛ تمام بسط‌های متغیری به همین فرمول ختم می‌شوند.
پیش‌بینی ε «پیش‌بینی نویز» خروجی مدل، نویز اضافه‌شده است؛ حالت استاندارد DDPM.
پیش‌بینی V «پیش‌بینی سرعت» خروجی مدل برابر α⋅ϵ−σ⋅x\alpha \cdot \epsilon - \sigma \cdot x است؛ شرطی‌سازی پایدارتر در تمام طول t.
DDPM «مقاله‌ی اصلی» کار Ho et al. 2020؛ زمان‌بندی β خطی، 1000 گام، U-Net.
DDIM «نمونه‌گیر معین (deterministic)» نمونه‌گیر غیرمارکوفی، 20 تا 50 گام، با همان هدف آموزشی یکسان.
هدایت بدون طبقه‌بند (CFG) «ضریب CFG» ترکیب پیش‌بینی‌های نویز شرطی و غیرشرطی برای تقویت شرط.

نکته‌ی فنی محیط عملیاتی: استنتاج دیفیوژن در اصل مسئله‌ی تعداد گام‌هاست

مقاله‌ی DDPM از T=1000 گام معکوس استفاده می‌کند. هیچ‌کس در محیط عملیاتی چنین کاری نمی‌کند. هر استک استنتاج واقعی یکی از این سه استراتژی را در پیش می‌گیرد — که هر کدام دقیقاً مشخص می‌کند منشأ تاخیر (latency) کجاست:

  1. نمونه‌گیر سریع‌تر، مدل یکسان. ابزارهایی مثل DDIM (بین 20 تا 50 گام)، ++DPM-Solver (بین 10 تا 20 گام) و UniPC (بین 8 تا 16 گام). این‌ها جایگزین مستقیم حلقه‌ی معکوس هستند و به وزن‌های آموزش‌دیده‌ی ϵθ\epsilon_\theta دست نمی‌زنند. تاخیر را 20 تا 50 برابر کاهش می‌دهند.
  2. تقطیر دانش (Distillation). آموزش یک مدل شاگرد تا رفتار استاد را در گام‌های کمتری بازسازی کند: تقطیر تدریجی (2 ← 1)، مدل‌های سازگاری (هر تعداد گام ← 1 تا 4)، LCM، SDXL-Turbo و SD3-Turbo. تاخیر را 5 تا 10 برابر دیگر کم می‌کند ولی نیازمند بازآموزی است.
  3. کش کردن و کامپایل. استفاده از torch.compile(unet, mode="reduce-overhead")، بک‌اندهای دیفیوژن در TensorRT-LLM، اتنشن‌های xformers/SDPA و وزن‌های bf16. تاخیر هر گام را حدود 2 برابر کم می‌کند و با موارد (1) و (2) نیز ترکیب می‌شود.

برای یک سرور دیفیوژن عملیاتی، فرمول‌بندی بودجه و هزینه‌ها دقیقاً مشابه متون فنی مدل‌های زبانی است: تاخیر برابر است با num_steps × step_cost + VAE_decode، و نرخ پردازش (throughput) از رابطه‌ی batch_size × (num_steps × step_cost)^-1 به دست می‌آید. معیار TTFT کم است (معادل زمان یک گام)؛ اما معادل TPOT برابر با کل زمان پاسخ‌دهی است، چون تولید تصویر از دیدگاه کاربر «یک‌جا و آنی» انجام می‌شود.

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

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

مدل DDPM چطور تصویر جدید تولید می‌کند؟

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

تفاوت فرآیند رو به جلو (Forward) و معکوس (Reverse) در دیفیوژن چیست؟

فرآیند رو به جلو یک زنجیره‌ی ثابت ریاضی است که در هر گام به داده نویز گاوسی می‌افزاید، در حالی که فرآیند معکوس توسط شبکه‌ی عصبی یاد گرفته می‌شود تا نویز افزوده را قدم‌به‌قدم حذف کند.

چرا مدل‌های تجاری از 1000 گام معکوس مقاله اصلی DDPM استفاده نمی‌کنند؟

اجرای 1000 گام استنتاج را بسیار کند می‌کند؛ بنابراین در محیط‌های عملیاتی از روش‌های جایگزین مثل نمونه‌گیرهای سریع‌تر (DDIM یا DPM-Solver) یا تکنیک‌های تقطیر دانش (مانند LCM و Turbo) برای کاهش گام‌ها به زیر 50 استفاده می‌شود.

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

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