معاينة مختبر آمنة
Autoencoders TF
هذي معاينة منقّحة للقراءة فقط؛ ما فيه أي شيء يشتغل داخل الصفحة.
قراءة فقط
معاينة الدفتر
Autoencoders TF
> **ملاحظة بيئة التشغيل المدمجة:** هالمعاينة تستخدم عيّنة صغيرة وثابتة وآمنة من ناحية الحقوق عشان تكون النتايج قابلة للتكرار. النتايج بالحجم الكامل تحتاج مجموعة البيانات أو النموذج الموثّق بالدرس داخل بيئة خارجية معتمدة.
# المشفّرات التلقائية (Autoencoders)
إذا جينا ندرّب الشبكات العصبية الالتفافية (CNNs)، بنواجه مشكلة: نحتاج كمية كبيرة من البيانات المعلَّمة. وفي مهمة **تصنيف الصور (Image classification)**، وهي من مسائل **التصنيف (Classification)**، لازم نفرز الصور يدويًا على فئات مختلفة.
لكن نقدر نستخدم بيانات خام ما عليها علامات عشان ندرّب مستخرجات السمات في CNN؛ وهالأسلوب يسمّى **التعلّم ذاتي الإشراف (Self-supervised learning)**. بدال العلامات، نستخدم صور التدريب نفسها مدخلات ومخرجات مطلوبة من الشبكة. فكرة **المشفّر التلقائي (Autoencoder)** إن عندنا **شبكة ترميز (Encoder)** تحوّل الصورة إلى **فضاء كامن (Latent space)**، وغالبًا يكون متجهًا أصغر، وبعدها **شبكة فك الترميز (Decoder)** تحاول تعيد بناء الصورة الأصلية.
ولأننا ندرّب المشفّر التلقائي على الاحتفاظ بأكبر قدر ممكن من معلومات الصورة عشان يعيد بناءها بدقة، فالشبكة تحاول تلقى أفضل **تضمين (Embedding)** يمثّل معنى الصورة.
> **وصف الشكل:** مخطط يوضّح المشفّر التلقائي
*الصورة مأخوذة من [مدونة Keras](https://blog.keras.io/building-autoencoders-in-keras.html)*
أغلب الأمثلة الجاية مستوحاة من [هالمقالة](https://blog.keras.io/building-autoencoders-in-keras.html).
خلّونا نبني أبسط نموذج من **المشفّر التلقائي (Autoencoder)** لـ MNIST:
# Bundled, deterministic handwritten-digits fixture.
from sklearn.datasets import load_digits as _course_load_digits
def _course_digits_load_data():
course_digits = _course_load_digits()
images = np.kron(
course_digits.images.astype(np.float32),
np.ones((3, 3), dtype=np.float32),
)
images = np.pad(images, ((0, 0), (2, 2), (2, 2))) * (255.0 / 16.0)
labels = np.asarray(course_digits.target, dtype=np.int64)
split = int(0.8 * len(images))
return (images[:split], labels[:split]), (images[split:], labels[split:])
import tensorflow as tf
from tensorflow.keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt
(x_train, y_trainclass), (x_test, y_testclass) = _course_digits_load_data()def plotn(n,x):
fig,ax = plt.subplots(1,n)
for i,z in enumerate(x[0:n]):
ax[i].imshow(z.reshape(28,28) if z.size==28*28 else z.reshape(14,14) if z.size==14*14 else z)
plt.show()
plotn(5,x_train)from tensorflow.keras.layers import Input, Dense, Conv2D, MaxPooling2D, UpSampling2D, Lambda
from tensorflow.keras.models import Model
from tensorflow.keras.losses import binary_crossentropy,mse
input_img = Input(shape=(28, 28, 1))
x = Conv2D(16, (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
encoded = MaxPooling2D((2, 2), padding='same')(x)
encoder = Model(input_img,encoded)
input_rep = Input(shape=(4,4,8))
x = Conv2D(8, (3, 3), activation='relu', padding='same')(input_rep)
x = UpSampling2D((2, 2))(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = UpSampling2D((2, 2))(x)
x = Conv2D(16, (3, 3), activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)
decoder = Model(input_rep,decoded)
autoencoder = Model(input_img, decoder(encoder(input_img)))
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
x_test = np.reshape(x_test, (len(x_test), 28, 28, 1))autoencoder.fit(x_train, x_train,
epochs=2,
batch_size=128,
shuffle=True,
validation_data=(x_test, x_test))y_test = autoencoder.predict(x_test[0:5])
plotn(5,x_test)
plotn(5,y_test)encoder = Model(input_img, encoded)
encoded_imgs = encoder.predict(x_test[0:5])plotn(5,encoded_imgs.reshape(5,-1,8))print(encoded_imgs.max(),encoded_imgs.min())
res = decoder.predict(7*np.random.rand(7,4,4,8))
plotn(7,res)> **المهمة 1**: جرّبوا تدرّبون المشفّر التلقائي بمتجه كامن صغير جدًا، مثل 2، وارسموا النقاط اللي تمثّل الأرقام المختلفة. *تلميح: استخدموا طبقة كثيفة متصلة بالكامل بعد الجزء الالتفافي عشان تقلّلون حجم المتجه للقيمة المطلوبة.*
> **المهمة 2**: ابدأوا بأرقام مختلفة، واستخرجوا تمثيلاتها في الفضاء الكامن، ثم شوفوا وش يصير للأرقام الناتجة إذا أضفنا تشويشًا للفضاء الكامن.
## إزالة التشويش
نقدر نستخدم المشفّرات التلقائية بفعالية لإزالة التشويش من الصور. نبدأ بصور صافية ونضيف عليها تشويشًا صناعيًا، ثم ندخل النسخ المشوّشة للشبكة ونخلي الصور الصافية هي المخرجات المطلوبة.
خلّونا نشوف كيف تشتغل الفكرة مع MNIST:
def noisify(data):
return np.clip(data+np.random.normal(loc=0.5,scale=0.5,size=data.shape),0.,1.)
x_train_noise = noisify(x_train)
x_test_noise = noisify(x_test)
plotn(5,x_train_noise)autoencoder.fit(x_train_noise, x_train,
epochs=2,
batch_size=128,
shuffle=True,
validation_data=(x_test_noise, x_test))y_test = autoencoder.predict(x_test_noise[0:5])
plotn(5,x_test_noise)
plotn(5,y_test)> **تمرين:** جرّبوا مزيل التشويش المدرَّب على أرقام MNIST مع صور مختلفة. تقدرون تستخدمون مجموعة [Fashion MNIST](https://keras.io/api/datasets/fashion_mnist/) لأن صورها بالحجم نفسه. لاحظوا إن النموذج يشتغل زين بس على نوع الصور اللي تدرّب عليه؛ يعني على توزيع احتمالي مشابه لبيانات الإدخال.
## رفع الدقة
مثل إزالة التشويش، نقدر ندرّب المشفّرات التلقائية على رفع دقة الصورة. نبدأ بصور عالية الدقة ونخفّض دقتها آليًا عشان نصنع المدخلات، ثم نعطي الشبكة الصور الصغيرة مدخلات والصور عالية الدقة مخرجات مطلوبة.
خلّونا نخفّض دقة MNIST إلى 14x14:
x_train_lr = tf.keras.layers.AveragePooling2D(pool_size=(2, 2), strides=(2, 2))(x_train).numpy()
x_test_lr = tf.keras.layers.AveragePooling2D(pool_size=(2, 2), strides=(2, 2))(x_test).numpy()
plotn(5,x_train_lr)from tensorflow.keras.layers import Input, Dense, Conv2D, MaxPooling2D, UpSampling2D, Lambda
from tensorflow.keras.models import Model
from tensorflow.keras.losses import binary_crossentropy,mse
input_img = Input(shape=(14, 14, 1))
x = Conv2D(16, (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
encoded = MaxPooling2D((2, 2), padding='same')(x)
encoder = Model(input_img,encoded)
input_rep = Input(shape=(4,4,8))
x = Conv2D(8, (3, 3), activation='relu', padding='same')(input_rep)
x = UpSampling2D((2, 2))(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = UpSampling2D((2, 2))(x)
x = Conv2D(16, (3, 3), activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)
decoder = Model(input_rep,decoded)
autoencoder = Model(input_img, decoder(encoder(input_img)))
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')autoencoder.fit(x_train_lr, x_train,
epochs=2,
batch_size=128,
shuffle=True,
validation_data=(x_test_lr, x_test))y_test_lr = autoencoder.predict(x_test_lr[0:5])
plotn(5,x_test_lr)
plotn(5,y_test_lr)> **تمرين**: جرّبوا تدرّبون شبكة رفع الدقة على [CIFAR-10](https://keras.io/api/datasets/cifar10/) للتكبير بمقدار 2x و4x. استخدموا التشويش مدخلًا لنموذج 4x وراقبوا النتيجة.
## المشفّرات التلقائية التغايرية (Variational Autoencoders — VAE)
المشفّرات التلقائية التقليدية تقلّل أبعاد بيانات الإدخال وتكتشف أهم السمات في الصور. لكن المتجهات الكامنة الناتجة غالبًا تكون مب واضحة المعنى. خذوا **مجموعة البيانات (Dataset)** MNIST مثالًا: مو سهل نعرف أي رقم يقابله كل متجه كامن، والمتجهات المتقاربة مو لازم تمثّل الرقم نفسه.
أما إذا كنا بندرّب نماذج *توليدية*، فنحتاج نفهم الفضاء الكامن وترتيبه بشكل أفضل. ومن هنا تجي فكرة **المشفّر التلقائي التغايري (VAE)**.
يتعلّم VAE يتنبأ بـ *توزيع إحصائي* للمعلمات الكامنة، وهذا اللي نسمّيه **التوزيع الكامن**. مثلًا، نفترض أن المتجهات الكامنة موزعة كـ $N(\mathrm{z\_mean},e^{\mathrm{z\_log\_sigma}})$، حيث $\mathrm{z\_mean}, \mathrm{z\_log\_sigma} \in\mathbb{R}^d$. تتنبأ شبكة الترميز في VAE بهالمعلمات، ثم يأخذ فاكّ الترميز متجهًا عشوائيًا من التوزيع ويحاول يعيد بناء العنصر الأصلي.
خلّونا نلخّصها:
* من متجه الإدخال نتنبأ بـ `z_mean` و`z_log_sigma`؛ يعني نتنبأ بلوغاريتم الانحراف المعياري بدال الانحراف نفسه.
* نسحب المتجه `sample` من التوزيع $N(\mathrm{z\_mean},e^{\mathrm{z\_log\_sigma}})$.
* يحاول فاكّ الترميز يعيد بناء الصورة الأصلية باستخدام `sample` متجهَ إدخال.
> **وصف الشكل:** حُذف الأصل لأن حقوق إعادة استخدامه غير موثّقة.
intermediate_dim = 512
latent_dim = 2
batch_size = 128
class Sampling(tf.keras.layers.Layer):
"""Samples a latent vector and registers its KL-divergence loss."""
def call(self, inputs):
z_mean, z_log_var = inputs
epsilon = tf.random.normal(shape=tf.shape(z_mean))
kl = -0.5 * tf.reduce_sum(
1 + z_log_var - tf.square(z_mean) - tf.exp(z_log_var), axis=1
)
self.add_loss(tf.reduce_mean(kl))
return z_mean + tf.exp(0.5 * z_log_var) * epsilon
inputs = Input(shape=(784,), name="encoder_input")
h = Dense(intermediate_dim, activation="relu")(inputs)
z_mean = Dense(latent_dim, name="z_mean")(h)
z_log_var = Dense(latent_dim, name="z_log_var")(h)z = Sampling(name="sampling")([z_mean, z_log_var])encoder = Model(inputs, [z_mean, z_log_var, z], name="encoder")
latent_inputs = Input(shape=(latent_dim,), name="decoder_input")
x = Dense(intermediate_dim, activation="relu")(latent_inputs)
outputs = Dense(784, activation="sigmoid")(x)
decoder = Model(latent_inputs, outputs, name="decoder")
vae_outputs = decoder(encoder(inputs)[2])
vae = Model(inputs, vae_outputs, name="vae")تستخدم المشفّرات التلقائية التغايرية دالة خسارة مركّبة من جزأين:
* **خسارة إعادة البناء (Reconstruction loss)** تقيس قرب الصورة المعاد بناؤها من الهدف، وممكن تكون MSE. وهي نفس دالة الخسارة في المشفّر التلقائي العادي.
* **خسارة KL** تخلي توزيعات المتغيرات الكامنة قريبة من التوزيع الطبيعي. وتعتمد على [تباعد كولباك–ليبلر](https://www.countbayesie.com/blog/2017/5/9/kullback-leibler-divergence-explained)، وهو مقياس يقدّر مدى التشابه بين توزيعين إحصائيين.
vae.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),
loss=tf.keras.losses.BinaryCrossentropy(),
)x_train_flat = x_train.reshape((len(x_train), -1)).astype("float32")
x_test_flat = x_test.reshape((len(x_test), -1)).astype("float32")
vae.fit(
x_train_flat,
x_train_flat,
shuffle=True,
epochs=2,
batch_size=batch_size,
validation_data=(x_test_flat, x_test_flat),
)y_test = vae.predict(x_test_flat[0:5])
plotn(5,x_test_flat)
plotn(5,y_test)x_test_encoded = encoder.predict(x_test_flat)[0]
plt.figure(figsize=(6, 6))
plt.scatter(x_test_encoded[:, 0], x_test_encoded[:, 1], c=y_testclass)
plt.colorbar()
plt.show()def plotsample(n):
dx = np.linspace(-1,1,n)
dy = np.linspace(-1,1,n)
fig,ax = plt.subplots(n,n)
for i,xi in enumerate(dx):
for j,xj in enumerate(dy):
res = decoder.predict(np.array([xi,xj]).reshape(-1,2))[0]
ax[i,j].imshow(res.reshape(28,28))
ax[i,j].axis('off')
plt.show()
plotsample(10)> **المهمة**: في مثالنا درّبنا VAE متصلًا بالكامل. خذوا CNN من المشفّر التلقائي التقليدي اللي فوق، وابنوا VAE قائمًا على CNN.
## مواد إضافية
* [تدوينة NeuroHive](https://neurohive.io/ru/osnovy-data-science/variacionnyj-avtojenkoder-vae/)
* [شرح المشفّر التلقائي التغايري](https://kvfrans.com/variational-autoencoders-explained/)
حذفنا المخرجات وعدّادات التشغيل والودجات والمحتوى النشط وقت الاستيراد. شغّل الدفاتر بس في بيئة خارجية تثق فيها.
سجّل تطبيقك
التسجيل اختياري، يفيدك تتذكر وش طبّقت، ولا يمنع إكمال الدورة.