معاينة مختبر آمنة
GANPy Torch
هذي معاينة منقّحة للقراءة فقط؛ ما فيه أي شيء يشتغل داخل الصفحة.
قراءة فقط
معاينة الدفتر
GANPy Torch
> **ملاحظة بيئة التشغيل المدمجة:** هالمعاينة تستخدم عيّنة صغيرة وثابتة وآمنة من ناحية الحقوق عشان تكون النتايج قابلة للتكرار. النتايج بالحجم الكامل تحتاج مجموعة البيانات أو النموذج الموثّق بالدرس داخل بيئة خارجية معتمدة.
# شبكات الخصومة التوليدية
الهدف الأساسي من **شبكة الخصومة التوليدية (Generative Adversarial Network أو GAN)** إنها تنشئ صورًا تشبه صور مجموعة البيانات المستخدمة في التدريب، لكن ما تكون مطابقة لها.
تتكوّن GAN من شبكتين عصبيتين تتدرّبان ضد بعض:
* **المولّد (Generator)** يستقبل متجهًا عشوائيًا، والمفروض يولّد منه صورة.
* **المميّز (Discriminator)** شبكة تفرّق بين الصورة الأصلية من مجموعة بيانات التدريب والصورة اللي أنشأها المولّد.
> **وصف الشكل:** حُذف الأصل لأن حقوق إعادة استخدامه غير موثّقة.
import torch
import torchvision
import matplotlib.pyplot as plt
from torchvision import transforms
from torch import nn
from torch import optim
from tqdm import tqdm
import numpy as np
import torch.nn.functional as F
torch.manual_seed(42)
np.random.seed(42)device = 'cuda:0' if torch.cuda.is_available() else 'cpu'
train_size = 1.0
lr = 2e-4
weight_decay = 8e-9
beta1 = 0.5
beta2 = 0.999
batch_size = 256
epochs = 2
plot_every = 2## المولّد
دور المولّد ياخذ متجهًا عشوائيًا بحجم محدد — يشبه المتجه الكامن في المشفّرات التلقائية — ويولّد الصورة المستهدفة. هالعملية قريبة مرة من الجانب التوليدي للمشفّر التلقائي.
في مثالنا بنستخدم شبكات عصبية خطية مع مجموعة البيانات MNIST.
class Generator(nn.Module):
def __init__(self):
super().__init__()
self.linear1 = nn.Linear(100, 256)
self.bn1 = nn.BatchNorm1d(256, momentum=0.2)
self.linear2 = nn.Linear(256, 512)
self.bn2 = nn.BatchNorm1d(512, momentum=0.2)
self.linear3 = nn.Linear(512, 1024)
self.bn3 = nn.BatchNorm1d(1024, momentum=0.2)
self.linear4 = nn.Linear(1024, 784)
self.tanh = nn.Tanh()
self.leaky_relu = nn.LeakyReLU(0.2)
def forward(self, input):
hidden1 = self.leaky_relu(self.bn1(self.linear1(input)))
hidden2 = self.leaky_relu(self.bn2(self.linear2(hidden1)))
hidden3 = self.leaky_relu(self.bn3(self.linear3(hidden2)))
generated = self.tanh(self.linear4(hidden3)).view(input.shape[0], 1, 28, 28)
return generatedنستخدم في المولّد كم حيلة:
* بدل ReLU نستخدم **LeakyReLU**، وهي ReLU ما تصير 0 بالضبط إذا كان $x$ سالبًا، بل تتحول إلى دالة خطية ميلها صغير جدًا.
* نستخدم **BatchNorm1D** عشان نثبّت التدريب.
* دالة التنشيط في الطبقة الأخيرة هي **Tanh**، ولذلك يكون المخرج ضمن [-1,1].
## المميّز
المميّز شبكة تقليدية لمهمة **التصنيف**، وتحديدًا **تصنيف الصور**. في مثالنا الأول بنستخدم بعد مصنّفًا خطيًا.
class Discriminator(nn.Module):
def __init__(self):
super().__init__()
self.linear1 = nn.Linear(784, 512)
self.linear2 = nn.Linear(512, 256)
self.linear3 = nn.Linear(256, 1)
self.leaky_relu = nn.LeakyReLU(0.2)
self.sigmoid = nn.Sigmoid()
def forward(self, input):
input = input.view(input.shape[0], -1)
hidden1 = self.leaky_relu(self.linear1(input))
hidden2 = self.leaky_relu(self.linear2(hidden1))
classififed = self.sigmoid(self.linear3(hidden2))
return classififed## تحميل مجموعة البيانات
بنستخدم مجموعة بيانات MNIST.
# course-edition bundled digits fixture v1
from sklearn.datasets import load_digits as _course_load_digits
def mnist(train_part, transform=None):
_course_digits = _course_load_digits()
_course_images = torch.as_tensor(_course_digits.images, dtype=torch.float32).unsqueeze(1) / 16.0
_course_images = torch.nn.functional.interpolate(
_course_images,
size=(28, 28),
mode="bilinear",
align_corners=False,
)
_course_labels = torch.as_tensor(_course_digits.target, dtype=torch.long)
dataset = torch.utils.data.TensorDataset(_course_images, _course_labels)
train_count = max(1, min(len(dataset) - 1, int(train_part * len(dataset))))
return torch.utils.data.random_split(
dataset,
[train_count, len(dataset) - train_count],
generator=torch.Generator().manual_seed(2026),
)transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=0.5, std=0.5)
])train_dataset, test_dataset = mnist(train_size, transform)train_dataloader = torch.utils.data.DataLoader(train_dataset, drop_last=True, batch_size=batch_size, shuffle=True)
dataloaders = (train_dataloader, )## تدريب الشبكة
في كل خطوة تدريب عندنا **مرحلتان**:
* **تدريب المولّد**. نولّد متجهات عشوائية تمثّل **التشويش**. وبما إن التدريب يصير بدفعات مصغّرة، نستخدم 100 متجه كل مرة. ثم ينشئ المولّد منها صورًا، ونمرّر الصور إلى المميّز **المجمّد**. ننشئ بعد **التسميات الحقيقية**، وهي متجه شكله (bs, 1) وقيمه 1.0، ونحسب خسارة المولّد بمقارنة مخرج المميّز بهالتسميات.
* **تدريب المميّز**. نحسب خسارته من **جزأين**: **الجزء الأول** بين مخرجه عند إدخال الصور اللي ولّدها المولّد و**التسميات المزيّفة**، وهي متجه شكله (bs, 1) وقيمه 0.0. و**الجزء الثاني** بين مخرجه عند إدخال الصور الحقيقية والتسميات الحقيقية، وهي متجه شكله (bs, 1) وقيمه 1.0. تكون **الخسارة النهائية** هي (خسارة_الجزء_الأول + خسارة_الجزء_الثاني) / 2.
def plotn(n, generator, device):
generator.eval()
noise = torch.FloatTensor(np.random.normal(0, 1, (n, 100))).to(device)
imgs = generator(noise).detach().cpu()
fig, ax = plt.subplots(1, n)
for i, im in enumerate(imgs):
ax[i].imshow(im[0])
plt.show()def train_gan(dataloaders, models, optimizers, loss_fn, epochs, plot_every, device):
tqdm_iter = tqdm(range(epochs))
train_dataloader = dataloaders[0]
gen, disc = models[0], models[1]
optim_gen, optim_disc = optimizers[0], optimizers[1]
for epoch in tqdm_iter:
gen.train()
disc.train()
train_gen_loss = 0.0
train_disc_loss = 0.0
test_gen_loss = 0.0
test_disc_loss = 0.0
for batch in train_dataloader:
imgs, _ = batch
imgs = imgs.to(device)
disc.eval()
gen.zero_grad()
noise = torch.FloatTensor(np.random.normal(0.0, 1.0, (imgs.shape[0], 100))).to(device)
real_labels = torch.ones((imgs.shape[0], 1)).to(device)
fake_labels = torch.zeros((imgs.shape[0], 1)).to(device)
generated = gen(noise)
disc_preds = disc(generated)
g_loss = loss_fn(disc_preds, real_labels)
g_loss.backward()
optim_gen.step()
disc.train()
disc.zero_grad()
disc_real = disc(imgs)
disc_real_loss = loss_fn(disc_real, real_labels)
disc_fake = disc(generated.detach())
disc_fake_loss = loss_fn(disc_fake, fake_labels)
d_loss = (disc_real_loss + disc_fake_loss) / 2.0
d_loss.backward()
optim_disc.step()
train_gen_loss += g_loss.item()
train_disc_loss += d_loss.item()
train_gen_loss /= len(train_dataloader)
train_disc_loss /= len(train_dataloader)
if epoch % plot_every == 0 or epoch == epochs - 1:
plotn(5, gen, device)
tqdm_dct = {'generator loss:': train_gen_loss, 'discriminator loss:': train_disc_loss}
tqdm_iter.set_postfix(tqdm_dct, refresh=True)
tqdm_iter.refresh()generator = Generator().to(device)
discriminator = Discriminator().to(device)
optimizer_generator = optim.Adam(generator.parameters(), lr=lr, weight_decay=weight_decay, betas=(beta1, beta2))
optimizer_discriminator = optim.Adam(discriminator.parameters(), lr=lr, weight_decay=weight_decay, betas=(beta1, beta2))
loss_fn = nn.BCELoss()
models = (generator, discriminator)
optimizers = (optimizer_generator, optimizer_discriminator)train_gan(dataloaders, models, optimizers, loss_fn, epochs, plot_every, device)## DCGAN
فكرة **شبكة الخصومة التوليدية الالتفافية العميقة (Deep Convolutional GAN أو DCGAN)** مباشرة: نستخدم طبقات التفاف للمولّد والمميّز. والفرق الأساسي هنا استخدام طبقة **Conv2DTranspose** في المولّد.
> **وصف الشكل:** حُذف الأصل لأن حقوق إعادة استخدامه غير موثّقة.
> الصورة من [هالشرح](https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html)
class DCGenerator(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.ConvTranspose2d(100, 256, kernel_size=(3, 3), stride=(2, 2), bias=False)
self.bn1 = nn.BatchNorm2d(256)
self.conv2 = nn.ConvTranspose2d(256, 128, kernel_size=(3, 3), stride=(2, 2), bias=False)
self.bn2 = nn.BatchNorm2d(128)
self.conv3 = nn.ConvTranspose2d(128, 64, kernel_size=(3, 3), stride=(2, 2), bias=False)
self.bn3 = nn.BatchNorm2d(64)
self.conv4 = nn.ConvTranspose2d(64, 1, kernel_size=(3, 3), stride=(2, 2), padding=(2, 2), output_padding=(1, 1), bias=False)
self.tanh = nn.Tanh()
self.relu = nn.ReLU()
def forward(self, input):
hidden1 = self.relu(self.bn1(self.conv1(input)))
hidden2 = self.relu(self.bn2(self.conv2(hidden1)))
hidden3 = self.relu(self.bn3(self.conv3(hidden2)))
generated = self.tanh(self.conv4(hidden3)).view(input.shape[0], 1, 28, 28)
return generatedclass DCDiscriminator(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
self.conv2 = nn.Conv2d(64, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
self.bn2 = nn.BatchNorm2d(128)
self.conv3 = nn.Conv2d(128, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
self.bn3 = nn.BatchNorm2d(256)
self.conv4 = nn.Conv2d(256, 1, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
self.leaky_relu = nn.LeakyReLU(0.2)
self.sigmoid = nn.Sigmoid()
def forward(self, input):
hidden1 = self.leaky_relu(self.conv1(input))
hidden2 = self.leaky_relu(self.bn2(self.conv2(hidden1)))
hidden3 = self.leaky_relu(self.bn3(self.conv3(hidden2)))
classified = self.sigmoid(self.conv4(hidden3)).view(input.shape[0], -1)
return classifiedتهيئة الأوزان مأخوذة من بحث [DCGAN](https://arxiv.org/pdf/1511.06434.pdf).
def weights_init(model):
classname = model.__class__.__name__
if classname.find('Conv') != -1:
nn.init.normal_(model.weight.data, 0.0, 0.02)
elif classname.find('BatchNorm') != -1:
nn.init.normal_(model.weight.data, 1.0, 0.02)
nn.init.constant_(model.bias.data, 0)transform = transforms.Compose([
transforms.ToTensor(),
])train_dataset, test_dataset = mnist(train_size, transform)
train_dataloader = torch.utils.data.DataLoader(train_dataset, drop_last=True, batch_size=batch_size, shuffle=True)
dataloaders = (train_dataloader, )generator = DCGenerator().to(device)
generator.apply(weights_init)
discriminator = DCDiscriminator().to(device)
discriminator.apply(weights_init)
optimizer_generator = optim.Adam(generator.parameters(), lr=lr, weight_decay=weight_decay, betas=(beta1, beta2))
optimizer_discriminator = optim.Adam(discriminator.parameters(), lr=lr, weight_decay=weight_decay, betas=(beta1, beta2))
loss_fn = nn.BCELoss()
models = (generator, discriminator)
optimizers = (optimizer_generator, optimizer_discriminator)def dcplotn(n, generator, device):
generator.eval()
noise = torch.FloatTensor(np.random.normal(0, 1, (n, 100, 1, 1))).to(device)
imgs = generator(noise).detach().cpu()
fig, ax = plt.subplots(1, n)
for i, im in enumerate(imgs):
ax[i].imshow(im[0])
plt.show()def train_dcgan(dataloaders, models, optimizers, loss_fn, epochs, plot_every, device):
tqdm_iter = tqdm(range(epochs))
train_dataloader = dataloaders[0]
gen, disc = models[0], models[1]
optim_gen, optim_disc = optimizers[0], optimizers[1]
gen.train()
disc.train()
for epoch in tqdm_iter:
train_gen_loss = 0.0
train_disc_loss = 0.0
test_gen_loss = 0.0
test_disc_loss = 0.0
for batch in train_dataloader:
imgs, _ = batch
imgs = imgs.to(device)
imgs = 2.0 * imgs - 1.0
gen.zero_grad()
noise = torch.FloatTensor(np.random.normal(0.0, 1.0, (imgs.shape[0], 100, 1, 1))).to(device)
real_labels = torch.ones((imgs.shape[0], 1)).to(device)
fake_labels = torch.zeros((imgs.shape[0], 1)).to(device)
generated = gen(noise)
disc_preds = disc(generated)
g_loss = loss_fn(disc_preds, real_labels)
g_loss.backward()
optim_gen.step()
disc.zero_grad()
disc_real = disc(imgs)
disc_real_loss = loss_fn(disc_real, real_labels)
disc_fake = disc(generated.detach())
disc_fake_loss = loss_fn(disc_fake, fake_labels)
d_loss = (disc_real_loss + disc_fake_loss) / 2.0
d_loss.backward()
optim_disc.step()
train_gen_loss += g_loss.item()
train_disc_loss += d_loss.item()
train_gen_loss /= len(train_dataloader)
train_disc_loss /= len(train_dataloader)
if epoch % plot_every == 0 or epoch == epochs - 1:
dcplotn(5, gen, device)
tqdm_dct = {'generator loss:': train_gen_loss, 'discriminator loss:': train_disc_loss}
tqdm_iter.set_postfix(tqdm_dct, refresh=True)
tqdm_iter.refresh()train_dcgan(dataloaders, models, optimizers, loss_fn, epochs // 2, plot_every // 2, device)generator.eval()
dcplotn(5, generator, device)> **المهمة**: جرّبوا تولّدون صورًا ملوّنة أعقد باستخدام DCGAN؛ مثلًا خذوا فئة وحدة من مجموعة بيانات [CIFAR-10](https://pytorch.org/vision/stable/generated/torchvision.datasets.CIFAR10.html).
## التدريب على اللوحات الفنية
اللوحات اللي يرسمها فنانون بشر خيار زين لتدريب GAN.
!
(الصورة من مجموعة Art of Artificial)
## المراجع
* [بحث GAN على arXiv](https://arxiv.org/abs/1406.2661)
* [بحث DCGAN على arXiv](https://arxiv.org/abs/1511.06434)
* [شرح DCGAN في PyTorch](https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html)
حذفنا المخرجات وعدّادات التشغيل والودجات والمحتوى النشط وقت الاستيراد. شغّل الدفاتر بس في بيئة خارجية تثق فيها.
سجّل تطبيقك
التسجيل اختياري، يفيدك تتذكر وش طبّقت، ولا يمنع إكمال الدورة.