معاينة مختبر آمنة
GANTF
هذي معاينة منقّحة للقراءة فقط؛ ما فيه أي شيء يشتغل داخل الصفحة.
قراءة فقط
معاينة الدفتر
GANTF
> **ملاحظة بيئة التشغيل المدمجة:** هالمعاينة تستخدم عيّنة صغيرة وثابتة وآمنة من ناحية الحقوق عشان تكون النتايج قابلة للتكرار. النتايج بالحجم الكامل تحتاج مجموعة البيانات أو النموذج الموثّق بالدرس داخل بيئة خارجية معتمدة.
# شبكات الخصومة التوليدية
الهدف الأساسي من **شبكة الخصومة التوليدية (Generative Adversarial Network أو GAN)** إنها تنشئ صورًا تشبه صور مجموعة البيانات المستخدمة في التدريب، لكن ما تكون مطابقة لها.
تتكوّن GAN من شبكتين عصبيتين تتدرّبان ضد بعض:
* **المولّد (Generator)** يستقبل متجهًا عشوائيًا، والمفروض يولّد منه صورة.
* **المميّز (Discriminator)** شبكة تفرّق بين الصورة الأصلية من مجموعة بيانات التدريب والصورة اللي أنشأها المولّد.
> **وصف الشكل:** حُذف الأصل لأن حقوق إعادة استخدامه غير موثّقة.
import tensorflow as tf
import tensorflow.keras as keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import *
import matplotlib.pyplot as plt
import numpy as np## المولّد
دور المولّد ياخذ متجهًا عشوائيًا بحجم محدد — يشبه المتجه الكامن في المشفّرات التلقائية — ويولّد الصورة المستهدفة. هالعملية قريبة مرة من الجانب التوليدي للمشفّر التلقائي.
في مثالنا بنستخدم شبكات عصبية كثيفة مع مجموعة البيانات MNIST.
generator = Sequential()
generator.add(Dense(256, input_shape=(100,)))
generator.add(LeakyReLU(negative_slope=0.2))
generator.add(BatchNormalization(momentum=0.8))
generator.add(Dense(512))
generator.add(LeakyReLU(negative_slope=0.2))
generator.add(BatchNormalization(momentum=0.8))
generator.add(Dense(1024))
generator.add(LeakyReLU(negative_slope=0.2))
generator.add(BatchNormalization(momentum=0.8))
generator.add(Dense(784, activation='tanh'))
generator.add(Reshape((28,28)))
generator_optimizer = keras.optimizers.AdamW(learning_rate=0.0002, weight_decay=8e-9)
generator.compile(loss='binary_crossentropy',optimizer=generator_optimizer,metrics=['accuracy'])نستخدم في المولّد كم حيلة:
* بدل ReLU نستخدم **Leaky ReLU**، وهي ReLU ما تصير 0 بالضبط إذا كان $x$ سالبًا، بل تتحول إلى دالة خطية ميلها صغير جدًا. هالشي مهم لأنه يساعد الانحدار المتدرج يمرّر القيم حتى على الجهة السالبة من ReLU، اللي تكون قيم ReLU العادية فيها 0.
* نستخدم التطبيع على مستوى الدفعة (Batch Normalization) عشان نثبّت التدريب.
* دالة التنشيط في الطبقة الأخيرة هي `tanh`، ولذلك يكون المخرج ضمن [-1,1].
## المميّز
المميّز شبكة تقليدية لمهمة **التصنيف**، وتحديدًا **تصنيف الصور**. في مثالنا الأول بنستخدم بعد مصنّفًا كثيفًا.
discriminator = Sequential()
discriminator.add(Flatten(input_shape=(28,28)))
discriminator.add(Dense(784))
discriminator.add(LeakyReLU(negative_slope=0.2))
discriminator.add(Dense(784//2))
discriminator.add(LeakyReLU(negative_slope=0.2))
discriminator.add(Dense(1, activation='sigmoid'))
discriminator_optimizer = keras.optimizers.AdamW(learning_rate=0.0002, weight_decay=8e-9)
discriminator.compile(loss='binary_crossentropy', optimizer=discriminator_optimizer, metrics=['accuracy'])وبنعرف بعد شبكة خصومة تتكوّن من مولّد وبعده مميّز. تبدأ بمتجه تشويش وترجع نتيجة ثنائية.
discriminator.trainable = False
adversarial = Sequential()
adversarial.add(generator)
adversarial.add(discriminator)
adversarial_optimizer = keras.optimizers.AdamW(learning_rate=0.0002, weight_decay=8e-9)
adversarial.compile(loss='binary_crossentropy', optimizer=adversarial_optimizer)## تحميل مجموعة البيانات
بنستخدم مجموعة بيانات MNIST المدمجة في Keras:
# 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:])
(X_train, _), (_, _) = _course_digits_load_data()
X_train = (X_train.astype(np.float32) - 127.5) / 127.5## تدريب الشبكة
في كل خطوة تدريب عندنا مرحلتان:
* تدريب المميّز:
- نولّد متجهات عشوائية `noise`. التدريب يصير بدفعات مصغّرة، لذلك نستخدم `batch//2` من المتجهات عشان ننتج `batch//2` من الصور المولّدة.
- نأخذ عشوائيًا `batch//2` من الصور من مجموعة البيانات.
- ندرّب المميّز على 50% صور حقيقية و 50% صور مولّدة، مع التسميات المناسبة 0 أو 1.
* ندرّب المولّد باستخدام نموذج الخصومة المدمج: نمرّر المتجهات العشوائية مدخلات، ونتوقع القيمة 1 في المخرج، وهي التسمية المقابلة للصور الحقيقية.
def plotn(n):
noise = np.random.normal(0, 1, (n,100))
imgs = generator.predict(noise)
fig,ax = plt.subplots(1,n)
for i,im in enumerate(imgs):
ax[i].imshow(im.reshape(28,28))
plt.show()batch=32
for cnt in range(20):
## train discriminator
random_index = np.random.randint(0, len(X_train) - batch//2)
legit_images = X_train[random_index : random_index + batch//2].reshape(batch//2, 28, 28)
gen_noise = np.random.normal(0, 1, (batch//2,100))
syntetic_images = generator.predict(gen_noise)
x_combined_batch = np.concatenate((legit_images, syntetic_images))
y_combined_batch = np.concatenate((np.ones((batch//2, 1)), np.zeros((batch//2, 1))))
d_loss = discriminator.train_on_batch(x_combined_batch, y_combined_batch)
# train generator
noise = np.random.normal(0, 1, (batch,100))
y_mislabled = np.ones((batch, 1))
g_loss = adversarial.train_on_batch(noise, y_mislabled)
if cnt % 10==0:
print ('epoch: %d, [Discriminator :: d_loss: %f], [ Generator :: loss: %f]' % (cnt, d_loss[0], g_loss))
plotn(5)> **المهمة**: درّبوا GAN على مجموعة البيانات MNIST كاملة، وشوفوا وش مستوى النتيجة.
## DCGAN
في المثال السابق استخدمنا شبكات كثيفة للمولّد والمميّز، لكننا نعرف إن الشبكات العصبية الالتفافية (CNNs) تعطي أداء أفضل مع الصور. **شبكة الخصومة التوليدية الالتفافية العميقة (Deep Convolutional GAN أو DCGAN)** تشبه البنية اللي فوق، لكنها تستخدم طبقات التفاف في المولّد والمميّز.
الصعوبة الأساسية هنا بناء بنية المولّد؛ لأنه يسوي مهمة عكس CNN التقليدية: يولّد صورة من متجه سمات. هالشي يشبه جزء فاكّ الترميز في المشفّرات التلقائية، وعشان كذا بنستخدم طبقات `Conv2DTranspose` في المولّد.
# 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:])
(X_train, _), (_, _) = _course_digits_load_data()
X_train = (X_train.astype(np.float32)-127.5) / 127.5
print(X_train.min(),X_train.max())generator = Sequential()
generator.add(Dense(128 * 7 * 7, activation="relu", input_dim=100))
generator.add(Reshape((7, 7, 128)))
generator.add(UpSampling2D())
generator.add(Conv2DTranspose(128, kernel_size=3, padding="same"))
generator.add(BatchNormalization(momentum=0.8))
generator.add(Activation("relu"))
generator.add(UpSampling2D())
generator.add(Conv2DTranspose(64, kernel_size=3, padding="same"))
generator.add(BatchNormalization(momentum=0.8))
generator.add(Activation("relu"))
generator.add(Conv2DTranspose(1, kernel_size=3, padding="same"))
generator.add(Activation("tanh"))
generator_optimizer = keras.optimizers.Adam(0.0001) #, 0.5)
generator.compile(loss='binary_crossentropy',optimizer=generator_optimizer,metrics=['accuracy'])
generator.summary()discriminator = Sequential()
discriminator.add(Conv2D(32, kernel_size=3, strides=2, input_shape=(28,28,1), padding="same"))
discriminator.add(LeakyReLU(negative_slope=0.2))
discriminator.add(Dropout(0.25))
discriminator.add(Conv2D(64, kernel_size=3, strides=2, padding="same"))
discriminator.add(ZeroPadding2D(padding=((0,1),(0,1))))
discriminator.add(BatchNormalization(momentum=0.8))
discriminator.add(LeakyReLU(negative_slope=0.2))
discriminator.add(Dropout(0.25))
discriminator.add(Conv2D(128, kernel_size=3, strides=2, padding="same"))
discriminator.add(BatchNormalization(momentum=0.8))
discriminator.add(LeakyReLU(negative_slope=0.2))
discriminator.add(Dropout(0.25))
discriminator.add(Conv2D(256, kernel_size=3, strides=1, padding="same"))
discriminator.add(BatchNormalization(momentum=0.8))
discriminator.add(LeakyReLU(negative_slope=0.2))
discriminator.add(Dropout(0.25))
discriminator.add(Flatten())
discriminator.add(Dense(1, activation='sigmoid'))
discriminator_optimizer = keras.optimizers.Adam(0.0001)
discriminator.compile(loss='binary_crossentropy', optimizer=discriminator_optimizer)discriminator.trainable = False
adversarial = Sequential()
adversarial.add(generator)
adversarial.add(discriminator)
adversarial_optimizer = keras.optimizers.Adam(0.0001)
adversarial.compile(loss='binary_crossentropy', optimizer=adversarial_optimizer)batch=32
y_labeled = np.ones((batch, 1))
y_mislabeled = np.zeros((batch, 1))
for cnt in range(20):
## train discriminator
random_index = np.random.randint(0, len(X_train) - batch)
legit_images = X_train[random_index : random_index + batch].reshape(batch, 28, 28, 1)
gen_noise = np.random.normal(0, 1, (batch,100))
syntetic_images = generator.predict(gen_noise)
d_loss_1 = discriminator.train_on_batch(legit_images, y_labeled)
d_loss_2 = discriminator.train_on_batch(syntetic_images, y_mislabeled)
d_loss = 0.5*np.add(d_loss_1,d_loss_2)
# train generator
g_loss = adversarial.train_on_batch(gen_noise, y_labeled)
if cnt % 10==0:
print ('epoch: %d, [Discriminator :: d_loss: %f], [ Generator :: loss: %f]' % (cnt, d_loss, g_loss))
plotn(5)> **المهمة**: جرّبوا تولّدون صورًا ملوّنة أعقد باستخدام DCGAN؛ مثلًا خذوا فئة وحدة من مجموعة بيانات [CIFAR-10](https://keras.io/api/datasets/cifar10/).
## التدريب على اللوحات الفنية
اللوحات اللي يرسمها فنانون بشر خيار زين لتدريب GAN. تحت تشوفون صورة أنتجتها DCGAN تدرّبت على مجموعة البيانات من [WikiArt](https://www.wikiart.org/). استُخدمت مكتبة [KeraGAN](https://github.com/shwars/keragan) لإنتاج الصورة باستخدام Azure Machine Learning في مجال تعلم الآلة.
!
(الصورة من مجموعة Art of Artificial)
## المراجع
* [تنفيذ Keras لعدة بنى GAN تجريبية](https://github.com/eriklindernoren/Keras-GAN)
* [مكتبة KeraGAN](https://github.com/shwars/keragan)
* تدوينة عن إنشاء GANs على Azure ML
حذفنا المخرجات وعدّادات التشغيل والودجات والمحتوى النشط وقت الاستيراد. شغّل الدفاتر بس في بيئة خارجية تثق فيها.
سجّل تطبيقك
التسجيل اختياري، يفيدك تتذكر وش طبّقت، ولا يمنع إكمال الدورة.