Teammasik

GAN_Artem

May 14th, 2026
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.77 KB | None | 0 0
  1. import numpy as np
  2. import os
  3. from PIL import Image
  4. import matplotlib.pyplot as plt
  5. from IPython.display import clear_output
  6.  
  7. import torch
  8. import torch.nn as nn
  9. import torch.nn.functional as F
  10. from torch.utils.data import Dataset, DataLoader
  11.  
  12. from torchvision import transforms
  13.  
  14. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  15. device
  16.  
  17. ! wget http://vis-www.cs.umass.edu/lfw/lfw-deepfunneled.tgz -O tmp.tgz
  18. os.system("tar xvzf tmp.tgz && rm tmp.tgz")
  19.  
  20. images = []
  21.  
  22. for dirpath, _, filenames in os.walk('lfw-deepfunneled'):
  23.     for fname in filenames:
  24.         if fname.endswith(".jpg"):
  25.             fpath = os.path.join(dirpath,fname)
  26.             image = Image.open(fpath)
  27.             images.append(image)
  28. len(images)
  29.  
  30.  
  31. def plot_gallery(images, n_row=3, n_col=6, from_torch=False):
  32.     """Helper function to plot a gallery of portraits"""
  33.  
  34.     if from_torch:
  35.         images = [x.data.numpy().transpose(1, 2, 0) for x in images]
  36.  
  37.     plt.figure(figsize=(1.5 * n_col, 1.7 * n_row))
  38.     plt.subplots_adjust(bottom=0, left=.01, right=.99, top=.90, hspace=.35)
  39.  
  40.     for i in range(n_row * n_col):
  41.         plt.subplot(n_row, n_col, i + 1)
  42.         plt.imshow(images[i])
  43.  
  44.         plt.xticks(())
  45.         plt.yticks(())
  46.  
  47.     plt.show()
  48. plot_gallery(images)
  49.  
  50.  
  51. class Faces(Dataset):
  52.     def __init__(self, faces):
  53.         self.data = faces
  54.         self.transform = transforms.Compose([
  55.                                     transforms.CenterCrop((90, 90)),
  56.                                     transforms.Resize((64, 64)),
  57.                                     transforms.ToTensor(),
  58.                                     # transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))
  59.                                     ])
  60.  
  61.     def __getitem__(self, index):
  62.  
  63.         x = self.data[index]
  64.         return self.transform(x).float()
  65.  
  66.     def __len__(self):
  67.         return len(self.data)
  68.  
  69. dataset = Faces(images)
  70.  
  71. # dataset[0] — это вызов метода __getitem__(0)
  72. img = dataset[0]
  73.  
  74. print(img.shape)
  75. plot_gallery(dataset, from_torch=True)
  76.  
  77.  
  78. train_size = int(len(dataset) * 0.8)
  79. val_size = len(dataset) - train_size
  80.  
  81. g_cpu = torch.Generator().manual_seed(8888)
  82. train_data, val_data = torch.utils.data.random_split(dataset, [train_size, val_size], generator=g_cpu)
  83. train_loader = torch.utils.data.DataLoader(train_data, batch_size=16, shuffle=True)
  84. val_loader = torch.utils.data.DataLoader(val_data, batch_size=16, shuffle=False)
  85.  
  86. # Реализация GAN
  87. # Дискриминатор и генератор - свёрточные сети.
  88. class Discriminator(nn.Module):
  89.     def __init__(self):
  90.         super().__init__()
  91.         # in: 3 x 64 x 64
  92.  
  93.         self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1, bias=False)
  94.         self.bn1 = nn.BatchNorm2d(64)
  95.         # out: 64 x 32 x 32
  96.  
  97.         self.conv2 = nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1, bias=False)
  98.         self.bn2 = nn.BatchNorm2d(128)
  99.         # out: 128 x 16 x 16
  100.  
  101.         self.conv3 = nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1, bias=False)
  102.         self.bn3 = nn.BatchNorm2d(256)
  103.         # out: 256 x 8 x 8
  104.  
  105.         self.conv4 = nn.Conv2d(256, 512, kernel_size=4, stride=2, padding=1, bias=False)
  106.         self.bn4 = nn.BatchNorm2d(512)
  107.         # out: 512 x 4 x 4
  108.  
  109.         self.conv5 = nn.Conv2d(512, 1, kernel_size=4, stride=1, padding=0, bias=False)
  110.         # out: 1 x 1 x 1
  111.  
  112.         self.flatten = nn.Flatten()
  113.         self.sigmoid = nn.Sigmoid()
  114.  
  115.     def forward(self, x):
  116.  
  117.         x = F.relu(self.bn1(self.conv1(x)))
  118.         x = F.relu(self.bn2(self.conv2(x)))
  119.         x = F.relu(self.bn3(self.conv3(x)))
  120.         x = F.relu(self.bn4(self.conv4(x)))
  121.  
  122.         x = self.conv5(x)
  123.         x = self.flatten(x)
  124.         x = self.sigmoid(x)
  125.  
  126.         return x
  127.  
  128. class Generator(nn.Module):
  129.     def __init__(self, latent_size):
  130.         super().__init__()
  131.         # in: latent_size x 1 x 1
  132.  
  133.         self.convT1 = nn.ConvTranspose2d(latent_size, 512, kernel_size=4, stride=1, padding=0, bias=False)
  134.         self.bn1 = nn.BatchNorm2d(512)
  135.         # out: 512 x 4 x 4
  136.  
  137.         self.convT2 = nn.ConvTranspose2d(512, 256, kernel_size=4, stride=2, padding=1, bias=False)
  138.         self.bn2 = nn.BatchNorm2d(256)
  139.         # out: 256 x 8 x 8
  140.  
  141.         self.convT3 = nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=False)
  142.         self.bn3 = nn.BatchNorm2d(128)
  143.         # out: 128 x 16 x 16
  144.  
  145.         self.convT4 = nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1, bias=False)
  146.         self.bn4 = nn.BatchNorm2d(64)
  147.         # out: 64 x 32 x 32
  148.  
  149.         self.convT5 = nn.ConvTranspose2d(64, 3, kernel_size=4, stride=2, padding=1, bias=False)
  150.         self.tanh = nn.Tanh()
  151.         # out: 3 x 64 x 64
  152.  
  153.     def forward(self, x):
  154.  
  155.         x = F.relu(self.bn1(self.convT1(x)))
  156.         x = F.relu(self.bn2(self.convT2(x)))
  157.         x = F.relu(self.bn3(self.convT3(x)))
  158.         x = F.relu(self.bn4(self.convT4(x)))
  159.  
  160.         x = self.convT5(x)
  161.         x = self.tanh(x)
  162.  
  163.         return x
  164.  
  165. # размер случайных векторов, подаваемых генератору для генерации изображений
  166. latent_size = 128
  167.  
  168. # Заведем фиксированный вектор шума, чтобы в процессе обучения сети отслеживать то, как меняется вывод генератора на этом фиксированном # векторе:
  169. fixed_latent = torch.randn(64, latent_size, 1, 1, device=device)
  170.  
  171. Обучение GAN
  172. Перейдём теперь к тому, как обучается GAN. Процесс устроен как чередование двух шагов — обучения дискриминатора и генератора.
  173.  
  174. 1. Обновляем дискриминатор.
  175. Берём батч реальных изображений и ставим им метку 1.
  176. Генерируем батч фейковых изображений с помощью генератора и назначаем им метку 0.
  177. Тренируем дискриминатор как обычный бинарный классификатор, чтобы он максимально хорошо различал эти два типа примеров.
  178.  
  179. 2. Обновляем генератор.
  180. Замораживаем веса дискриминатора.
  181. Генерируем изображения из случайного шума и пропускаем их через дискриминатор.
  182. В функции потерь считаем, что для этих изображений целевая метка = 1 (мы хотим, чтобы дискриминатор принял их за реальные) и по этому сигналу обновляем только генератор.
  183.  
  184. from IPython.display import clear_output
  185. from torchvision.utils import make_grid
  186.  
  187. # функция для де-нормализации
  188. stats = (0.5, 0.5, 0.5), (0.5, 0.5, 0.5)
  189. def denorm(img_tensors):
  190.     return img_tensors * stats[1][0] + stats[0][0]
  191.  
  192. # функция для отрисовки результатов генерации генератором
  193. # на фиксированном векторе шума
  194. def show_samples(latent_tensors):
  195.     fake_images = generator(latent_tensors)
  196.  
  197.     fig, ax = plt.subplots(figsize=(8, 8))
  198.     ax.set_xticks([]); ax.set_yticks([])
  199.     ax.imshow(make_grid(denorm(fake_images[:64].cpu().detach()), nrow=8).permute(1, 2, 0))
  200.     plt.show()
  201.  
  202. # функция непосредственного обучения
  203. def train(models, opts, loss_fns, epochs, train_loader, val_loader, batch_size=64):
  204.  
  205.     # Losses & scores
  206.     losses_g = []
  207.     losses_d = []
  208.     real_scores = []
  209.     fake_scores = []
  210.  
  211.     for epoch in range(epochs):
  212.         print('* Epoch %d/%d' % (epoch+1, epochs))
  213.  
  214.         # Обучаем сеть на изображениях из train_loader
  215.         models["discriminator"].train()
  216.         models["generator"].train()
  217.  
  218.         # массивы для сохранения значений лоссов и метрик accuracy
  219.         # дискриминатора в процессе обучения сети
  220.         loss_d_per_epoch = []
  221.         loss_g_per_epoch = []
  222.         real_score_per_epoch = []
  223.         fake_score_per_epoch = []
  224.  
  225.         for i, X_batch in enumerate(train_loader):
  226.             X_batch = X_batch.to(device)
  227.  
  228.             # 1. Шаг обучения дискриминатора.
  229.  
  230.             # Очистим градиенты дискриминатора
  231.             opts["discriminator"].zero_grad()
  232.  
  233.             # 1.1 Получаем лосс на реальных данных
  234.             # Подаем реальные изображения из батча X_batch на вход дискриминатору
  235.             real_preds = models["discriminator"](X_batch)
  236.             # Правильный ответ дискриминатора на эти изображения должен быть
  237.             # вектором из единиц
  238.             real_targets = torch.ones(X_batch.size(0), 1, device=device)
  239.             # считаем значение лосса дискриминатора на батче реальных изображений
  240.             real_loss = loss_fns["discriminator"](real_preds, real_targets)
  241.             cur_real_score = torch.mean(real_preds).item()
  242.  
  243.             # 1.2 Получаем лосс на фейковых изображениях
  244.  
  245.             # Сгенерируем фейковые изображениях. Для этого сгенерируем случайный вектор шума
  246.             # размера (batch_size, latent_size, 1, 1) и подадим на вход генератору.
  247.             latent = torch.randn(batch_size, latent_size, 1, 1, device=device)
  248.             # Получаем выход генератора на батч случайных векторов
  249.             fake_images = models["generator"](latent)
  250.  
  251.             # Подаем фейковые изображениях из батча X_batch на вход дискриминатору
  252.             fake_preds = models["discriminator"](fake_images)
  253.             # Правильный ответ дискриминатора на эти изображениях должен быть
  254.             # вектором из нулей
  255.             fake_targets = torch.zeros(fake_images.size(0), 1, device=device)
  256.             # считаем значение лосса дискриминатора на батче фейковых изображениях
  257.             fake_loss = loss_fns["discriminator"](fake_preds, fake_targets)
  258.             cur_fake_score = torch.mean(fake_preds).item()
  259.  
  260.             real_score_per_epoch.append(cur_real_score)
  261.             fake_score_per_epoch.append(cur_fake_score)
  262.  
  263.             # 1.3 Обновляем веса дискриминатора: делаем шаг градиентного спуска
  264.             loss_d = real_loss + fake_loss
  265.             loss_d.backward()
  266.             opts["discriminator"].step()
  267.             loss_d_per_epoch.append(loss_d.item())
  268.  
  269.             # 2. Шаг обучения генератора
  270.             # Очистим градиенты генератора
  271.             opts["generator"].zero_grad()
  272.  
  273.             # Сгенерируем фейковые изображениях. Для этого сгенерируем случайный вектор шума
  274.             # размера (batch_size, latent_size, 1, 1) и подадим на вход генератору.
  275.             latent = torch.randn(batch_size, latent_size, 1, 1, device=device)
  276.             # Получаем выход генератора на батч случайных векторов
  277.             fake_images = models["generator"](latent)
  278.  
  279.             # Подаем фейковые изображениях из батча X_batch на вход дискриминатору
  280.             preds = models["discriminator"](fake_images)
  281.             # Задаем вектор "правильных" ответов на эти изображениях как вектор из единиц
  282.             targets = torch.ones(batch_size, 1, device=device)
  283.             # считаем лосс между preds и targets
  284.             loss_g = loss_fns["generator"](preds, targets)
  285.  
  286.             # Обновляем веса генератора
  287.             loss_g.backward()
  288.             opts["generator"].step()
  289.             loss_g_per_epoch.append(loss_g.item())
  290.  
  291.             # каждые 100 итераций обучения будем выводить текущие значения метрик
  292.             # и отрисовывать результаты генерации генератором картинок
  293.             # из фиксированных случайных векторов
  294.             if i%100 == 0:
  295.                 # Record losses & scores
  296.                 losses_g.append(np.mean(loss_g_per_epoch))
  297.                 losses_d.append(np.mean(loss_d_per_epoch))
  298.                 real_scores.append(np.mean(real_score_per_epoch))
  299.                 fake_scores.append(np.mean(fake_score_per_epoch))
  300.  
  301.                 # Log losses & scores (last batch)
  302.                 print("Epoch [{}/{}], loss_g: {:.4f}, loss_d: {:.4f}, real_score: {:.4f}, fake_score: {:.4f}".format(
  303.                     epoch+1, epochs,
  304.                     losses_g[-1], losses_d[-1], real_scores[-1], fake_scores[-1]))
  305.  
  306.                 # Show generated images
  307.                 clear_output(wait=True)
  308.                 show_samples(fixed_latent)
  309.  
  310.     return losses_g, losses_d, real_scores, fake_scores
  311.  
  312.  
  313. generator = Generator(latent_size).to(device)
  314. discriminator = Discriminator().to(device)
  315.  
  316. models = {
  317.     'generator': generator,
  318.     'discriminator': discriminator
  319. }
  320.  
  321. criterions = {
  322.     "discriminator": nn.BCELoss(),
  323.     "generator": nn.BCELoss()
  324. }
  325.  
  326. lr = 0.0002
  327. optimizers = {
  328.         "discriminator": torch.optim.Adam(models["discriminator"].parameters(),
  329.                                           lr=lr, betas=(0.5, 0.999)),
  330.         "generator": torch.optim.Adam(models["generator"].parameters(),
  331.                                       lr=lr, betas=(0.5, 0.999))
  332.     }
  333.  
  334. losses_g, losses_d, real_scores, fake_scores = train(models, optimizers, criterions, 10, train_loader, val_loader)
  335.  
  336. plt.figure(figsize=(15, 6))
  337. plt.plot(losses_d, '-')
  338. plt.plot(losses_g, '-')
  339. plt.xlabel('epoch')
  340. plt.ylabel('loss')
  341. plt.legend(['Discriminator', 'Generator'])
  342. plt.title('Losses');
  343.  
  344. plt.figure(figsize=(15, 6))
  345.  
  346. plt.plot(real_scores, '-')
  347. plt.plot(fake_scores, '-')
  348. plt.xlabel('epoch')
  349. plt.ylabel('score')
  350. plt.legend(['Real', 'Fake'])
  351. plt.title('Scores');
Advertisement
Add Comment
Please, Sign In to add comment