کیفیت DCGAN و حساب در فضای Latent
پس از حدود ۵۰ Epoch، DCGAN روی Fashion MNIST میتواند نمونههای نسبتاً قانعکننده بسازد.
شکل 17-16. کیفیت DCGAN و حساب در فضای Latent
Latent Space در GAN میتواند Semantic Structure یاد بگیرد. در مثال مشهور DCGAN روی Face، Average Coding «مرد عینکی» منهای «مرد بدون عینک» بهاضافهٔ «زن بدون عینک» Codingی میسازد که Decoder آن را به «زن عینکی» تبدیل میکند. این Arithmetic در Pixel Space انجام نمیشود؛ عملیات روی Representation نهفته صورت میگیرد.
شکل 17-17. کیفیت DCGAN و حساب در فضای Latent
با اضافهکردن Label Class به Input Generator و Discriminator میتوان Conditional GAN ساخت و Class نمونهٔ تولیدی را کنترل کرد.
Progressive Growing of GANs
برای تولید Imageهای بزرگ، DCGANهای ساده ممکن است Local Featureهای خوب ولی Global Structure نامنسجم بسازند. Progressive Growing ابتدا GAN را روی Resolution بسیار کوچک، مثلاً 4×4، Train میکند و سپس Layerهای جدید بهتدریج اضافه میشوند تا Resolution به 8×8، 16×16 و در نهایت 1024×1024 برسد. Layerهای قبلی همچنان Trainable میمانند.
شکل 17-18. Progressive Growing of GANs
هنگام اضافهشدن Resolution جدید، خروجی قدیمی و مسیر جدید مدتی با ضریب Fade-in ترکیب میشوند تا Transition نرم باشد و Training ناگهان ناپایدار نشود.
تکنیکهای کمکی
- Mini-batch Standard Deviation: Statistic تنوع Batch به Discriminator داده میشود تا Fakeهای بیش از حد شبیه را رد کند و Mode Collapse کاهش یابد.
- Equalized Learning Rate: Scale Weightها در Runtime تنظیم میشود تا Layerها سرعت یادگیری متعادلتری داشته باشند.
- Pixelwise Normalization: Activationهای هر Pixel در Generator روی Channelها Normalize میشوند.
این ترکیب امکان تولید Faceهای Resolution بالا را فراهم کرد. ارزیابی GAN همچنان دشوار است؛ Visual Quality، Diversity و Metricهای آماری باید همزمان در نظر گرفته شوند.
StyleGAN
StyleGAN Generator را بازطراحی کرد تا کنترل بهتر روی Featureهای سطح بالا و جزئیات تصادفی داشته باشد. Coding خام ابتدا وارد یک Mapping Network میشود و Vector میانی w را تولید میکند. سپس در Synthesis Network، این Style در چند Resolution به Layerها Inject میشود.
شکل 17-19. StyleGAN
Mapping Network
هدف Mapping این است که Latent Space ورودی را به Spaceای تبدیل کند که Factorهای Variation بهتر از هم جدا شوند. در نتیجه تغییر یک Style میتواند اثر قابلفهمتری مانند Pose، Shape کلی یا Texture داشته باشد.
Synthesis Network و Noise
Generator از یک Constant Learned Tensor شروع میکند و Styleها را در Layerهای متعدد اعمال میکند. Noise مستقل نیز در هر Resolution اضافه میشود. این تفکیک بسیار مهم است: Style برای ویژگیهای کنترلپذیر و ساختاری، و Noise برای جزئیات Stochastic مانند جای تار مو، ککومک یا بافتهای کوچک استفاده میشود.
Style Mixing نیز در Training استفاده میشود: دو Coding مختلف وارد Mapping Network میشوند و در Resolutionهای مختلف Style از یکی به دیگری Switch میشود. این کار Generator را وادار میکند Layerهای مختلف را بیش از حد به هم وابسته نکند.
Diffusion Modelها
Diffusion Model بهجای بازی Adversarial، یک Process تدریجی Noise و Denoise را یاد میگیرد. در Forward Process به Image در T مرحله Gaussian Noise اضافه میشود تا در انتها Distribution تقریباً Gaussian شود. سپس Model Reverse Process را یاد میگیرد تا از Noise خالص به Data برگردد.
برای هر Step Forward:
q(x_t | x_(t-1)) = N( sqrt(1 - β_t) x_(t-1), β_t I )
مزیت Gaussian این است که میتوان مستقیماً x_t را از x_0 Sample کرد، بدون اینکه همهٔ Stepهای قبلی را محاسبه کنیم:
q(x_t | x_0) = N( sqrt(ᾱ_t) x_0, (1 - ᾱ_t) I )
شکل 17-20. Diffusion Modelها
Variance Schedule
اگر α_t = 1 - β_t و ᾱ_t = ∏(i=1..t) α_i باشد، ᾱ_t سهم Signal اصلی را بعد از t Step نشان میدهد. در Improved DDPM از Cosine Schedule استفاده میشود:
f(t) = cos²( ((t/T + s) / (1+s)) * π/2 )
ᾱ_t = f(t) / f(0)
β_t = 1 - ᾱ_t / ᾱ_(t-1)
در متن مقدار s=0.008 و Clip حداکثر β_t=0.999 پیشنهاد شده است.
شکل 17-21. Variance Schedule
def variance_schedule(T, s=0.008, max_beta=0.999):
t = np.arange(T + 1)
f = np.cos((t / T + s) / (1 + s) * np.pi / 2) ** 2
alpha = np.clip(f[1:] / f[:-1], 1 - max_beta, 1)
alpha = np.append(1, alpha).astype(np.float32)
beta = 1 - alpha
alpha_cumprod = np.cumprod(alpha)
return alpha, alpha_cumprod, beta
T = 4000
alpha, alpha_cumprod, beta = variance_schedule(T)
ساخت Batch برای Training DDPM
برای هر Image یک Time Step تصادفی انتخاب میشود، Noise Gaussian ساخته میشود و با Shortcut Forward Diffusion نسخهٔ Noisy تولید میگردد. Target خود Noise است؛ Model باید Noise اضافهشده را پیشبینی کند.
def prepare_batch(X):
X = tf.cast(X[..., tf.newaxis], tf.float32) * 2 - 1
X_shape = tf.shape(X)
t = tf.random.uniform([X_shape[0]], minval=1, maxval=T + 1,
dtype=tf.int32)
alpha_cm = tf.gather(alpha_cumprod, t)
alpha_cm = tf.reshape(alpha_cm,
[X_shape[0]] + [1] * (len(X_shape) - 1))
noise = tf.random.normal(X_shape)
return {
"X_noisy": alpha_cm ** 0.5 * X +
(1 - alpha_cm) ** 0.5 * noise,
"time": t,
}, noise
پیشبینی Noise در عمل بهتر از Predict مستقیم Image تمیز جواب داده است.
Architecture مدل Denoising
DDPM اصلی از U-Net اصلاحشده استفاده میکند: مسیر Downsampling، سپس Upsampling، با Skip Connection میان Resolutionهای متناظر. Time Step نیز مانند Positional Encoding Transformer Encode و در Levelهای مختلف Inject میشود. Multi-Head Attention نیز در بعضی Resolutionها استفاده میشود.
def build_diffusion_model():
X_noisy = tf.keras.layers.Input([28, 28, 1], name="X_noisy")
time_input = tf.keras.layers.Input([], dtype=tf.int32, name="time")
# U-Net-like body + time encoding + attention
outputs = ... # same shape as X_noisy: predicted noise
return tf.keras.Model([X_noisy, time_input], outputs)
model = build_diffusion_model()
model.compile(loss=tf.keras.losses.Huber(), optimizer="nadam")
model.fit(train_set, validation_data=valid_set, epochs=100)
Reverse Diffusion و Generation
برای Generation از x_T تصادفی Gaussian شروع میشود. Model Noise را Predict میکند و با Formula Reverse یک Step به x_(t-1) میرویم. این کار تا رسیدن به x_0 تکرار میشود:
x_(t-1) = 1/sqrt(α_t) * (
x_t - β_t/sqrt(1-ᾱ_t) * ε_θ(x_t,t)
) + sqrt(β_t) * z
z Gaussian Noise است و در آخرین Step صفر میشود. Process Stochastic است، بنابراین Runهای مختلف Imageهای متفاوت میسازند.
def generate(model, batch_size=32):
X = tf.random.normal([batch_size, 28, 28, 1])
for t in range(T, 0, -1):
noise = (tf.random.normal if t > 1 else tf.zeros)(tf.shape(X))
X_noise = model({"X_noisy": X,
"time": tf.constant([t] * batch_size)})
X = (
1 / alpha[t] ** 0.5
* (X - beta[t] / (1 - alpha_cumprod[t]) ** 0.5 * X_noise)
+ (1 - alpha[t]) ** 0.5 * noise
)
return X
Drawback اصلی سرعت است: اگر T=4000 باشد Model هزاران بار برای یک Sample اجرا میشود. در عوض Training پایدارتر از GAN است و Diversity/Quality بسیار خوب است.
شکل 17-22. Reverse Diffusion و Generation
Latent Diffusion و Stable Diffusion
Latent Diffusion Process را بهجای Pixel Space در Latent Space یک Autoencoder قدرتمند اجرا میکند. Image ابتدا Compress میشود، Diffusion روی Representation کوچکتر انجام میگیرد و Decoder در پایان Latent نهایی را به Image تبدیل میکند. این کار Cost و زمان را شدیداً کاهش میدهد.
Conditioning نیز میتواند با Text Prompt، Image یا Signal دیگر انجام شود؛ به همین دلیل کاربردهایی مانند Text-to-Image، Inpainting و Outpainting ممکن میشوند. متن کتاب همچنین Stable Diffusion را بهعنوان Latent Diffusion Model قدرتمند و Open-source معرفی میکند که در KerasCV نیز در دسترس قرار گرفت.
تمرینهای فصل ۱۷
- Taskهای اصلی Autoencoder را نام ببرید.
- اگر دادهٔ Unlabeled فراوان و فقط چند هزار Label دارید، چگونه از Autoencoder برای Classifier استفاده میکنید؟
- آیا Reconstruction کامل الزاماً به معنی Autoencoder خوب است؟ Performance را چگونه ارزیابی میکنید؟
- Undercomplete و Overcomplete Autoencoder چیست و Risk هرکدام چیست؟
- Tied Weights در Stacked Autoencoder چگونه پیادهسازی میشود و چه فایدهای دارد؟
- Generative Model چیست و چه نوع Autoencoder مولدی میشناسید؟
- GAN چیست و در چه Taskهایی میتواند مفید باشد؟
- مشکلات اصلی Training GAN چیست؟
- Diffusion Modelها در چه کاری قویاند و محدودیت مهم آنها چیست؟
- یک Denoising Autoencoder برای Pretraining Classifier بسازید و کیفیت Reconstruction و اثر Pretraining را مقایسه کنید.
- یک VAE روی Dataset تصویری دلخواه Train کنید و Sample جدید بسازید.
- یک DCGAN بسازید، Experience Replay را امتحان کنید و سپس آن را به Conditional GAN تبدیل کنید.
- Tutorial مربوط به Stable Diffusion در KerasCV را اجرا و Image متنی دلخواه تولید کنید.
فصل بعدی کتاب وارد شاخهٔ متفاوتی از Deep Learning یعنی Deep Reinforcement Learning میشود؛ این مجموعه در فایل بعدی ادامه خواهد یافت.