Как GANы создают искусственные личности знаменитостей?
Как GAN-ы создают искусственные личности знаменитостей?
Введение
В эпоху искусственного интеллекта происходит замечательное явление – генеративные антагонистические сети (GAN) умело создают искусственные идентичности знаменитостей. Это увлекательное сочетание технологии и креативности породило новый вид цифровых знаменитостей. Присоединяйтесь к нам в увлекательном путешествии, погрузившись в мир GAN и раскройте секреты создания искусственных знаменитостей, покоряющих виртуальную реальность. Как GAN делает это возможным? Давайте исследуем тайны этого цифрового искусства.

Цели изучения
В этой статье мы узнаем:
- Концепцию генеративных антагонистических сетей (GAN)
- Как обучать генератор и дискриминатор?
- Пошаговый процесс создания модели GAN
- Инсайты в то, как GAN улучшаются со временем через антагонистическое обучение
Эта статья была опубликована в рамках Data Science Blogathon.
Генеративно-антагонистическая сеть (GAN)
Генеративно-антагонистическая сеть (GAN) – это модель глубокого обучения, разработанная Гудфеллоу. По самому названию можно понять цель этих GAN. Да! Мы используем их для генерации. Это сеть, которая что-то создает. Используйте GAN для генерации синтетических данных. Эти данные включают изображения, текст, аудио и многое другое, и они похожи на реальные данные. GAN содержит две нейронные сети. Эти сети называются генератором и дискриминатором. Во время обучения эти две сети обучаются таким образом, что они конкурируют друг с другом и становятся лучше.
- Глубокий анализ последствий безопасности индивидуальной настройки больших языковых моделей
- Как использовать Hugging Face AutoTrain для настройки LLM
- Это исследование по искусственному интеллекту представляет метод ВЛП (видео-языковое планирование) новейший подход, основанный на древовидном поисковом процессе с использованием моделей визуального языка и динамикой текста-видео.
Что такое генератор?
Генератор – это нейронная сеть, отвечающая за генерацию. Для получения выходных данных ей нужен вход. Вход, который принимает генератор, представляет собой случайный шум. Генератор берет этот случайный шум и старается сгенерировать выходные данные, похожие на реальные данные. Каждый раз он получает обратную связь от дискриминатора, улучшает себя и генерирует лучшие данные в следующий раз. Например, возьмем генерацию изображений, генератор будет создавать изображения. По мере улучшения через обучение, он начинает с случайного шума и постепенно улучшает выходные данные, делая их все более реалистичными. В первый раз он может не произвести максимальное сходство с оригинальными данными. Иногда он даже генерирует изображения, которые совсем не являются изображением. По мере продолжения обучения генерируются более хорошие данные, которые более точно соответствуют реальным данным.
Что такое дискриминатор?
Дискриминатор – это нейронная сеть, отвечающая за оценку. Для лучшего понимания мы можем назвать его детективом. Этот генератор получает как реальные данные, так и фальшивые данные, сгенерированные генератором. Ему нужно отличить фальшивые данные от реальных данных. Простыми словами, это классификация фактических данных от фальшивых данных. Подобно генератору, по мере продолжения обучения дискриминатор сможет делать более точные различия. Возможно, ему не удастся с первой попытки дать свой лучший результат. Но во время обучения он становится все лучше и лучше, и в конечном итоге он сможет отличить большинство фальшивых данных. Как я сказал, ему нужно работать как детектив.
Антагонистическое обучение
Генератор и дискриминатор проходят обучение, которое называется антагонистическим обучением. Как уже упоминалось, они оба участвуют в конкурентной тренировке. Мы знаем, что генератор генерирует фальшивые данные, которые выглядят как реальные данные, а дискриминатор пытается отличить фальшивые данные. На следующем этапе процесса обучения генератор стремится улучшиться и сгенерировать фальшивые данные, чтобы обмануть дискриминатор. И снова дискриминатор обнаруживает фальшивые данные. Таким образом, во время обучения оба они становятся лучше в своих задачах. Этот процесс продолжается, пока генератор не создаст данные, которые являются реалистичными, и дискриминатор не сможет отличить их от реальных данных. В этот момент GAN достигает своего рода равновесия, и сгенерированные данные очень похожи на реальные данные.
Реализация
Начнем с импорта всех необходимых библиотек. В основном это включает некоторые модули Torch. Мы будем использовать matplotlib для визуализации.
from __future__ import print_function%matplotlib inlineimport argparseimport osimport randomimport torchimport torch.nn as nnimport torch.nn.parallelimport torch.backends.cudnn as cudnnimport torch.optim as optimimport torch.utils.dataimport torchvision.datasets as dsetimport torchvision.transforms as transformsimport torchvision.utils as vutilsimport numpy as npimport matplotlib.pyplot as pltimport matplotlib.animation as animationfrom IPython.display import HTML
Набор данных
Для реализации этого проекта мы будем использовать набор данных CelebFaces Attributes (CelebA). Этот набор данных доступен на Kaggle. Вы можете скачать его отсюда.
Ссылка на набор данных: https://www.kaggle.com/datasets/jessicali9530/celeba-dataset
Этот набор данных содержит 202,599 изображений лиц разных знаменитостей. Используйте его для обучения и тестирования моделей обнаружения лиц, особенно способных распознавать определенные черты лица.
Начальная конфигурация и настройка
Теперь давайте установим некоторые параметры и конфигурации перед фактической реализацией. Сначала мы должны указать путь, где находятся набор данных. Определите количество рабочих. Оно представляет собой количество рабочих для загрузчика данных. Мы используем рабочих по загрузке данных для параллельной загрузки пакетов данных, ускоряя процесс загрузки данных во время обучения. Определите размер пакета и размер изображения, количество каналов в обучающих изображениях, размер скрытого вектора, размеры карт признаков в генераторе, размеры карт признаков в дискриминаторе, количество эпох, скорость обучения, гиперпараметр beta1 для оптимизатора Adam. Количество доступных GPU для обучения. Если нет доступного GPU, будет использоваться ЦП. Код настраивает эти конфигурации и печатает выбранное устройство (CPU или GPU) и количество используемых GPU для обучения. Вы должны контролировать эти настройки важно при обучении модели GAN.
# datasetdataroot = r"C:\Users\Admin\Downloads\faces\img_align_celeba"workers = 1batch_size = 16image_size = 64nc = 3# Size of z latent vectornz = 64# Size of feature maps in generatorngf = 64# Size of feature maps in discriminatorndf = 64num_epochs = 5lr = 0.0001beta1 = 0.2ngpu = 1device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu")torch.backends.cudnn.benchmark = Trueprint(device)print(ngpu)
Загрузчик данных
Создадим набор данных и загрузчик данных PyTorch для наших обучающих данных. Создайте переменную набора данных, используя класс dset.ImageFolder. Мы создали этот класс набора данных PyTorch для загрузки данных, организованных в папки, требуя два основных аргумента. Мы применили серию изменений изображений к каждому изображению в наборе данных, называемую transform.
Создайте переменную загрузчика данных с использованием torch.utils.data.DataLoader. Он отвечает за загрузку данных по партиям. Он принимает набор данных, который был определен, и размер пакета. Вместе с ними мы должны указать количество рабочих процессов для загрузки данных и нужно ли перемешивать данные или нет. Мы определили это количество рабочих ранее. Чтобы обеспечить, чтобы модель не просматривала одинаковый порядок изображений в каждой эпохе, необходимо перемешать данные перед обучением.
Затем мы выводим некоторые обучающие изображения, чтобы понять, как выглядят наши обучающие данные.
# Create the datasetdataset = dset.ImageFolder(root=dataroot, transform=transforms.Compose([ transforms.Resize(image_size), transforms.CenterCrop(image_size), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ]))# Create the dataloaderdataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=workers)# Plot some training imagesreal_batch = next(iter(dataloader))plt.figure(figsize=(8,8))plt.axis("off")plt.title("Training Images")plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=2, normalize=True).cpu(),(1,2,0)))
Генерация шума
Теперь давайте создадим некоторый шум. Этот шум служит входным сигналом для генератора в GAN. Функция noise() генерирует случайный шум. Поэтому давайте определим ее. Она не принимает аргументы и создает случайный тензор формы (nz, ngf), заполненный значениями, выбранными из равномерного распределения между 0 и 1. Также масштабирует случайный тензор так, чтобы значения находились в диапазоне от -1 до 1. Она делает это, умножая случайный тензор на 2 и затем вычитая 1. Этот шумовой тензор может быть объединен с другими данными (например, метками или скрытыми векторами) и подан на вход генератору для создания изображений.
def noise(): return 2*torch.rand(nz, ngf, device = device) - 1
Генератор
Пришло время определить генератор. Для этого нам нужно определить класс генератора. Затем определите метод конструктора. Он принимает вход ngpu, который указывает, сколько GPU доступно для обучения. Параметр ngpu используется для обработки обучения на нескольких графических процессорах, но в этом коде он не используется. Внутри конструктора определяется архитектура генератора.
Последовательные блоки применяют серию операций свертки, нормализации пакета и активационных функций к входному x. Затем определяется финальный сверточный слой, который создает выходное изображение. Это self.toImage. Он использует карты признаков из предыдущих слоев для создания требуемого трехканального изображения.
Затем мы должны определить метод forward. Прямой проход генератора определяется этим методом. Он принимает вход x. Этот x – это вектор шума или латентный вектор и передает его через слои, определенные в конструкторе. Результатом является сгенерированное изображение. Изначально входной x преобразуется после прохождения через скрытый слой. Затем он обрабатывается через все определенные сверточные слои. После каждого сверточного слоя карты признаков объединяются с картами признаков из соответствующего слоя перед увеличением размерности. Это помогает генератору улавливать высокоуровневые признаки и пространственные детали. Полученное изображение получается из выхода последнего сверточного слоя и возвращается в результате прямого прохода.
# Код генератораclass Generator(nn.Module): def __init__(self, ngpu): super(Generator, self).__init__() self.ngpu = ngpu self.fromLatent = nn.Linear(ngf, (image_size*image_size//16)*ndf) self.dlayer1 = nn.Sequential( nn.Conv2d(ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True), nn.Conv2d(ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True) ) self.dlayer2 = nn.Sequential( nn.Upsample(scale_factor=2), nn.Conv2d(2 * ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True), nn.Conv2d(ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True) ) self.dlayer3 = nn.Sequential( nn.Upsample(scale_factor=2), nn.Conv2d(2 * ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True), nn.Conv2d(ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True) ) self.toImage = nn.Conv2d(ndf, 3, 3, padding = 1) def forward(self, x): x = self.fromLatent(x) x = x.view(x.size(0), ndf, image_size//4, image_size//4) h0 = x x = self.dlayer1(x) x = torch.cat([x, h0], dim=1) x = self.dlayer2(x) h0 = nn.functional.interpolate(h0, scale_factor=2, mode='nearest') x = torch.cat([x, h0], dim=1) x = self.dlayer3(x) x = self.toImage(x) return x
Дискриминатор
Для идентификации реальных и поддельных изображений нам необходимо создать сеть дискриминатора, похожую на генератор, который мы определили ранее. В этом случае будет присутствовать класс дискриминатора. Определите метод конструктора класса дискриминатора. Он принимает вход ngpu, который определяет количество доступных для обучения GPU, так же, как и генератор.
Элемент “elayer1” содержит три сверточных слоя, и за каждым слоем следует нормализация пакета и функция активации ELU. “Self.toLatent” — это полносвязный слой, который берет выходные данные сверточных слоев и отображает их в тензор. “Self.fromLatent” — это еще один полносвязный слой. Он берет выходные данные из генератора и отображает их в тензор.
Эти блоки подобны блокам в генераторе, но они используются для декодирования. Это означает генерацию изображений. “Self.toImage” — это последний сверточный слой, который создает выходное изображение. Он создает изображение с 3-мя каналами, используя карты признаков из более ранних слоев. Метод forward определяет прямой проход дискриминатора. В него подается входное изображение, будь то реальное или сгенерированное, и оно проходит через слои, определенные в конструкторе. Результатом является выход, похожий на изображение.
Реализация кода
class Discriminator(nn.Module): def __init__(self, ngpu): super(Discriminator, self).__init__() self.ngpu = ngpu self.elayer1 = nn.Sequential( nn.Conv2d(nc, ndf, 3, padding = 1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True), nn.Conv2d(ndf, ndf, 3, padding = 1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True), nn.Conv2d(ndf, 2 * ndf, 3, padding = 1), nn.BatchNorm2d(2 * ndf), nn.ELU(inplace=True), ) self.elayer2 = nn.Sequential( nn.MaxPool2d(2), nn.Conv2d(2 * ndf, 2 * ndf, 3, padding = 1), nn.BatchNorm2d(2 * ndf), nn.ELU(inplace=True), nn.Conv2d(2 * ndf, 3 * ndf, 3, padding = 1), nn.BatchNorm2d(3 * ndf), nn.ELU(inplace=True) ) self.elayer3 = nn.Sequential( nn.MaxPool2d(2), nn.Conv2d(3 * ndf, 3 * ndf, 3, padding = 1), nn.BatchNorm2d(3 * ndf), nn.ELU(inplace=True), nn.Conv2d(3 * ndf, 3 * ndf, 3, padding = 1), nn.BatchNorm2d(3 * ndf), nn.ELU(inplace=True) ) self.toLatent = nn.Linear((image_size*image_size//16)*3*ndf, ngf) self.fromLatent = nn.Linear(ngf, (image_size*image_size//16)*ndf) self.dlayer1 = nn.Sequential( nn.Conv2d(ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True), nn.Conv2d(ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True) ) self.dlayer2 = nn.Sequential( nn.Upsample(scale_factor=2), nn.Conv2d(2*ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True), nn.Conv2d(ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True) ) self.dlayer3 = nn.Sequential( nn.Upsample(scale_factor=2), nn.Conv2d(2*ndf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True), nn.Conv2d(nsdf, ndf, 3, padding=1), nn.BatchNorm2d(ndf), nn.ELU(inplace=True) ) self.toImage = nn.Conv2d(ndf, 3, 3, padding = 1) def forward(self, x): x = self.elayer1(x) x = self.elayer2(x) x = self.elayer3(x) x = x.view(x.size(0), -1) x = self.toLatent(x) x = self.fromLatent(x) x = x.view(x.size(0), ndf, image_size//4, image_size//4) h0 = x x = self.dlayer1(x) x = torch.cat([x, h0], dim=1) x = self.dlayer2(x) h0 = torch.nn.functional.interpolate(h0, scale_factor=2, mode='nearest') x = torch.cat([x, h0], dim=1) x = self.dlayer3(x) x = self.toImage(x) return x
Теперь создайте класс под названием Генератор и передайте ngpu для создания генеративной нейронной сети. Если у вас есть несколько GPU, вы можете использовать их для повышения эффективности обучения. Нейронная сеть для дискриминатора также создается аналогичным образом, путем создания экземпляра класса Дискриминатор. Затем инициируется функция потерь.
Затем нам нужно создать пакет латентных векторов, называемый fixed_noise. Обычно это случайные векторы шума, часто извлекаемые из нормального распределения. Они используются для создания фейковых изображений из Генератора во время обучения. Затем необходимо настроить оптимизаторы как для генератора, так и для дискриминатора. Мы будем использовать оптимизаторы Адама для обоих. Адам является популярным алгоритмом оптимизации.
# Создание генераторанetG = Генератор(ngpu).to(device)# Для работы с несколькими GPUif (device.type == 'cuda') and (ngpu > 1): netG = nn.DataParallel(netG, list(range(ngpu)))# Создание дискриминаторанetD = Дискриминатор(ngpu).to(device)# Для работы с несколькими GPUif (device.type == 'cuda') and (ngpu > 1): netD = nn.DataParallel(netD, list(range(ngpu))) # Инициирование функции потериcriterion = nn.L1Loss()fixed_noise = noise()# Настройка оптимизаторов для генератора и дискриминатораoptimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))
Обучение
Пришло время обучить нашу модель. Для этого нам нужно создать класс Timer, который поможет вам рассчитать время обучения на каждом шаге. Создайте некоторые необходимые пустые списки и определите необходимые параметры для сохранения данных во время обучения. Оперируйте заданным количеством эпох и пакетов данных из загрузчика данных в цикле обучения, уже настроенном. Дискриминатор обучается с помощью фейковых данных, созданных генератором. В процессе обучения обновляются как генератор, так и дискриминатор. Во время обучения на каждом шаге будут печататься все статистические показатели. В процессе и после обучения собираются потери и сгенерированные изображения для анализа и визуализации.
import timeclass Timer(): def __init__(self): self.startTime = time.time() self.lastTime = time.time() def timeElapsed(self): auxTime = self.lastTime self.lastTime = time.time() return self.lastTime - auxTime def timeSinceStart(self): self.lastTime = time.time() return self.lastTime - self.startTime # Цикл обученияk = 0gamma = 0.4lambda_k = 0.005img_list = []G_losses = []D_losses = []iters = 0print("Запуск цикла обучения...")timer = Timer()for epoch in range(num_epochs): # Для каждого пакета в загрузчике данных for i, data in enumerate(dataloader, 0): netD.zero_grad() # Форматирование пакета real_cpu = data[0].to(device) b_size = real_cpu.size(0) # Прямой проход реального пакета через D output = netD(real_cpu).view(-1) # Расчет потери на всех реальных данных errD_real = criterion(output, real_cpu.view(-1)) # Расчет градиентов для D в обратном проходе D_x = output.mean().item() fake = netG(noise()) # Классификация всех фейковых данных с помощью D output = netD(fake.detach()).view(-1) # Расчет потери D на всех фейковых данных errD_fake = criterion(output, fake.view(-1)) # Расчет градиентов для этого пакета D_loss = errD_real - k * errD_fake D_loss.backward() D_G_z1 = output.mean().item() # Сложение градиентов из всех реальных и фейковых пакетов optimizerD.step() netG.zero_grad() fake = netG(noise()) output = netD(fake).view(-1) # Расчет потери G на основе этого вывода errG = criterion(output, fake.view(-1)) # Расчет градиентов для G errG.backward() D_G_z2 = output.mean().item() # Обновление G optimizerG.step() delta = (gamma*errD_real - errG).data k = max(min(k + lambda_k*delta, 1.0), 0.0) # Вывод статистики обучения if i % 50 == 0: print( '[%.4f] [%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f' % (timer.timeElapsed(), epoch, num_epochs, i, len(dataloader), D_loss.item(), errG.item(), D_x, D_G_z1, D_G_z2)) # Сохранение потерь для последующего построения графиков G_losses.append(errG.item()) D_losses.append(D_loss.item()) if (iters % 1000 == 0) or ((epoch == num_epochs-1) and (i == len(dataloader)-1)): with torch.no_grad(): fake = netG(fixed_noise).detach().cpu() img_list.append(vutils.make_grid(fake, padding=2, normalize=True)) iters += 1
Визуализации
Теперь давайте сгенерируем график, чтобы визуализировать потери генератора и дискриминатора во время обучения GAN.
plt.figure(figsize=(10,5))plt.title("Потери генератора и дискриминатора во время обучения")plt.plot(G_losses,label="G")plt.plot(D_losses,label="D")plt.xlabel("итерации")plt.ylabel("Потери")plt.legend()plt.show()
Аналогично, давайте сгенерируем график для сравнения реальных и фальшивых изображений, созданных GAN. Для этого нам нужно получить пакет реальных изображений из загрузчика данных. Эти реальные изображения используются для сравнения с изображениями, созданными GAN. Затем мы построим график реальных изображений и фальшивых изображений, созданных GAN. Это позволяет визуально сравнить качество созданных изображений с реальными данными.
# Получаем пакет реальных изображений из загрузчика данныхreal_batch = next(iter(dataloader))# Строим график реальных изображенийplt.figure(figsize=(15,15))plt.subplot(1,2,1)plt.axis("off")plt.title("Реальные изображения")plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=5, normalize=True).cpu(),(1,2,0)))# Строим график фальшивых изображений из последней эпохиplt.subplot(1,2,2)plt.axis("off")plt.title("Фальшивые изображения")plt.imshow(np.transpose(img_list[-1],(1,2,0)))plt.savefig('fake_image.png')plt.show()
Заключение
В данной статье мы использовали Генеративно-состязательные сети (GAN) на наборе данных CelebA (CelebFaces Attributes) и сгенерировали некоторые фальшивые лица знаменитостей. Генеративно-состязательные сети (GAN) являются замечательным прорывом в технологии. Они могут создавать фальшивые данные, которые выглядят очень реально. Это имеет множество применений и является невероятно полезным, особенно когда требуется большое количество данных для проектов. Эта технология развивается быстро и используется всё больше в последние годы. Эта технология представляет собой интересное развитие в области искусственного интеллекта, поскольку имеет многообещающее будущее для множества различных приложений.
Основные выводы
- Генеративно-состязательные сети (GAN) – это революционная технология искусственного интеллекта, способная генерировать данные, близкие к реальным данным.
- Она состоит из двух нейронных сетей. Одна из них – генератор, а другая – дискриминатор. Эти две сети занимаются соперничеством во время обучения.
- GAN имеют применение в различных областях, включая генерацию изображений, повышение разрешения, стилевой перенос и дополнение данных.
- Существует много видов GAN, у каждого из которых есть свои преимущества, а также недостатки и свои приложения.
- GAN могут вызывать этические проблемы, связанные с deepfakes, созданием фальшивого контента и нарушением приватности.
Часто задаваемые вопросы
Мультимедийный контент, показанный в этой статье, не принадлежит Analytics Vidhya и используется на усмотрение автора.