فصل ۱۷: Autoencoder، GAN و Diffusion Model
Autoencoderها شبکههایی هستند که بدون Label یک Representation فشرده یا Latent Representation از ورودی یاد میگیرند. این Representation میتواند برای کاهش ابعاد، Visualization، Feature Extraction، Unsupervised Pretraining و در برخی معماریها برای تولید نمونههای جدید استفاده شود.
GANها نیز Modelهای مولد هستند، اما از رقابت دو شبکه استفاده میکنند: Generator نمونهٔ Fake میسازد و Discriminator تلاش میکند Real را از Fake تشخیص دهد. Diffusion Modelها مسیر متفاوتی دارند: در Training یاد میگیرند Noise را مرحلهبهمرحله از Data حذف کنند و در Generation از Gaussian Noise شروع میکنند و با چندین Step آن را به نمونهای شبیه Data تبدیل میکنند.
- Autoencoder: Identity Function را تحت Constraint یاد میگیرد؛ Coding فشرده محصول جانبی این Constraint است.
- GAN: Generator و Discriminator با Objectiveهای متضاد بهصورت Adversarial Train میشوند.
- DDPM: در هر Step مقدار کمی Noise را پیشبینی و حذف میکند تا Reverse Diffusion شکل بگیرد.
Representation کارآمد داده
Constraint باعث میشود Model Pattern پیدا کند. همانطور که حفظ Sequence طولانیِ تصادفی دشوار است ولی Sequence قانونمند بهراحتی با Rule فشرده میشود، Autoencoder نیز وقتی ظرفیت Bottleneck محدود باشد مجبور است Structure مهم Data را کشف کند.
Autoencoder دو بخش دارد: Encoder که Input را به Coding میبرد و Decoder که Coding را به Reconstruction تبدیل میکند. Loss اختلاف Reconstruction و Input را جریمه میکند.
شکل 17-1. Representation کارآمد داده
اگر Coding Dimension از Input کمتر باشد، Autoencoder Undercomplete است. چنین Modelی نمیتواند Input را مستقیم Copy کند و باید مهمترین Featureها را نگه دارد.
PCA با Linear Undercomplete Autoencoder
اگر همهٔ Activationها Linear و Loss برابر MSE باشد، یک Undercomplete Autoencoder میتواند همان Subspace اصلی PCA را یاد بگیرد:
encoder = tf.keras.Sequential([tf.keras.layers.Dense(2)])
decoder = tf.keras.Sequential([tf.keras.layers.Dense(3)])
autoencoder = tf.keras.Sequential([encoder, decoder])
optimizer = tf.keras.optimizers.SGD(learning_rate=0.5)
autoencoder.compile(loss="mse", optimizer=optimizer)
autoencoder.fit(X_train, X_train, epochs=500, verbose=False)
codings = encoder.predict(X_train)
نکتهٔ کلیدی این است که X_train هم Input و هم Target است. Model از Reconstruction Objective Representation دوبعدی مناسب را میآموزد.
شکل 17-2. PCA با Linear Undercomplete Autoencoder
Stacked Autoencoder
برای یادگیری Representation غیرخطی و پیچیدهتر میتوان چند Layer در Encoder و Decoder Stack کرد. معماری معمولاً حول Coding Layer تقریباً Symmetric است: Dimension در Encoder کاهش و در Decoder دوباره افزایش مییابد.
شکل 17-3. Stacked Autoencoder
stacked_encoder = tf.keras.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(100, activation="relu"),
tf.keras.layers.Dense(30, activation="relu")
])
stacked_decoder = tf.keras.Sequential([
tf.keras.layers.Dense(100, activation="relu"),
tf.keras.layers.Dense(28 * 28),
tf.keras.layers.Reshape([28, 28])
])
stacked_ae = tf.keras.Sequential([stacked_encoder, stacked_decoder])
stacked_ae.compile(loss="mse", optimizer="nadam")
stacked_ae.fit(X_train, X_train, epochs=20,
validation_data=(X_valid, X_valid))
Output Layer عمداً Activation محدودکننده ندارد، مگر اینکه Scale Data آن را ایجاب کند. Coding بسیار کوچک Information Loss زیاد ایجاد میکند؛ Coding بسیار بزرگ نیز ممکن است Constraint کافی برای یادگیری Featureهای مفید ایجاد نکند.
بررسی Reconstruction
یکی از سادهترین روشهای ارزیابی Autoencoder مشاهدهٔ Input و Reconstruction کنار هم است. اگر Reconstructionها ساختار اصلی را حفظ کنند، Coding احتمالاً Information مهم را نگه داشته است.
شکل 17-4. بررسی Reconstruction
در مثال Fashion MNIST، Reconstructionها قابل تشخیصاند ولی جزئیات ظریف از دست میروند؛ با افزایش Training، عمق Model یا Coding Size میتوان Quality را بهتر کرد، البته با Risk کاهش Constraint.
Visualization با Autoencoder و t-SNE
برای Visualization Dataset با Dimension بالا، ابتدا Encoder میتواند Dimension را مثلاً از 784 به 30 کاهش دهد، سپس Algorithmی مانند t-SNE Representation 30بعدی را به 2D تبدیل کند. این Pipeline سریعتر و گاهی Structureمندتر از اجرای مستقیم t-SNE روی Pixelهاست.
X_valid_compressed = stacked_encoder.predict(X_valid)
tsne = TSNE(init="pca", learning_rate="auto", random_state=42)
X_valid_2D = tsne.fit_transform(X_valid_compressed)
plt.scatter(X_valid_2D[:, 0], X_valid_2D[:, 1], c=y_valid, s=10, cmap="tab10")
شکل 17-5. Visualization با Autoencoder و t-SNE
Unsupervised Pretraining با Autoencoder
اگر Dataset بزرگ ولی Label کم باشد، میتوان ابتدا Autoencoder را با همهٔ دادهٔ Unlabeled Train کرد. سپس Decoder کنار گذاشته میشود و Layerهای Encoder بهعنوان پایهٔ Classifier یا Regressor استفاده میشوند. این روش نوعی Representation Learning بدون Label است.
شکل 17-6. Unsupervised Pretraining با Autoencoder
پس از Pretraining، ابتدا Weightهای Reused Freeze میشوند و Head جدید روی دادهٔ Labelدار Train میشود؛ سپس در صورت نیاز بخشی از Encoder Unfreeze و با Learning Rate کوچک Fine-tune میگردد.
Tying Weights
اگر Decoder دقیقاً Symmetric Encoder باشد، میتوان Weight Matrix Decoder را Transpose Weight متناظر Encoder در نظر گرفت. این کار تعداد Parameterها را تقریباً نصف میکند، Training را سریعتر میکند و Overfitting را کاهش میدهد. در Keras میتوان یک Custom Layer ساخت که به Kernel Layer قبلی Reference بدهد و هنگام Forward از Transpose آن استفاده کند.
Biasهای Decoder همچنان مستقل باقی میمانند. Tied Weights زمانی مناسب است که Shape لایههای متناظر اجازهٔ Transpose مستقیم را بدهد.
Training یک Autoencoder در هر مرحله
روش تاریخی دیگر Greedy Layer-wise Training است. ابتدا Autoencoder اول Train میشود؛ سپس Encoder آن Freeze و Output Coding بهعنوان Data ورودی Autoencoder دوم استفاده میشود. در پایان Encoderها و Decoderها به Stack کامل تبدیل میشوند.
شکل 17-7. Training یک Autoencoder در هر مرحله
این روش در روزهای ابتدایی Deep Learning برای Train کردن شبکههای عمیق مهم بود، زیرا Optimization شبکههای Deep دشوارتر بود. امروزه Initialization، Activation و Optimizerهای بهتر معمولاً Training End-to-End را ممکن میکنند، ولی ایدهٔ Pretraining مرحلهای هنوز در برخی کاربردها ارزشمند است.
Convolutional Autoencoder
برای Image، Dense Autoencoder معمولاً ساختار Spatial را نادیده میگیرد. Encoder Convolutional از Conv2D و Pooling برای کاهش Height/Width و افزایش Depth استفاده میکند. Decoder عملیات معکوس را با Conv2DTranspose یا Upsampling+Conv انجام میدهد:
conv_encoder = tf.keras.Sequential([
tf.keras.layers.Reshape([28, 28, 1]),
tf.keras.layers.Conv2D(16, 3, padding="same", activation="relu"),
tf.keras.layers.MaxPool2D(2),
tf.keras.layers.Conv2D(32, 3, padding="same", activation="relu"),
tf.keras.layers.MaxPool2D(2),
tf.keras.layers.Conv2D(64, 3, padding="same", activation="relu"),
tf.keras.layers.MaxPool2D(2),
tf.keras.layers.Conv2D(30, 3, padding="same", activation="relu"),
tf.keras.layers.GlobalAvgPool2D()
])
conv_decoder = tf.keras.Sequential([
tf.keras.layers.Dense(3 * 3 * 16),
tf.keras.layers.Reshape((3, 3, 16)),
tf.keras.layers.Conv2DTranspose(32, 3, strides=2, activation="relu"),
tf.keras.layers.Conv2DTranspose(16, 3, strides=2,
padding="same", activation="relu"),
tf.keras.layers.Conv2DTranspose(1, 3, strides=2, padding="same"),
tf.keras.layers.Reshape([28, 28])
])
Denoising Autoencoder
بهجای کوچک کردن Bottleneck، میتوان Input را خراب کرد و Model را مجبور کرد نسخهٔ Clean را بازسازی کند. Noise میتواند Gaussian باشد یا بخشی از Inputها مانند Dropout صفر شوند. چون Model باید Information گمشده را حدس بزند، Featureهای Robustتری یاد میگیرد.
dropout_encoder = tf.keras.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(100, activation="relu"),
tf.keras.layers.Dense(30, activation="relu")
])
dropout_decoder = tf.keras.Sequential([
tf.keras.layers.Dense(100, activation="relu"),
tf.keras.layers.Dense(28 * 28),
tf.keras.layers.Reshape([28, 28])
])
dropout_ae = tf.keras.Sequential([dropout_encoder, dropout_decoder])
شکل 17-8. Denoising Autoencoder
Denoising Autoencoder علاوه بر Visualization و Pretraining، مستقیماً برای Noise Removal نیز کاربرد دارد. Model ممکن است جزئیاتی را که در Input خرابشده وجود ندارد، بر اساس Patternهای آموختهشده بازسازی کند.