Въведение в cycleGAN с помощта на pytorch и превод на изображение за смяна на пола
Кратка история...
GAN или Generative Adversarial мрежа беше въведена като част от изследователска статия през 2014 г. от Иън Гудфелоу. В тази статия той първоначално предлага генериране на нови данни със съществуващ набор от данни, използвайки конкурентни невронни мрежи. През 2017 г., надграждайки тази основа, друга група или изследователи (Jun-Yan Zhu, Taesung Park, Phillip Isola, Alexei A. Efros) използваха този метод, за да създадат свой собствен cycleGAN, за да конвертират изображения . Един от по-класическите примери е превръщането на конете в зебри и обратното. От тези произведения темата за GAN продължава да се развива с различни подходи и стилове при използването на GAN.
Вашият първи GAN
Дори преди използването на GAN за превод от изображение към изображение, той е бил използван в други области за генериране на нови данни с помощта на съществуващи данни. Тези набори от данни ще бъдат напълно различни, но близки един до друг, където могат или не могат да бъдат различими един от друг.,\
Случаи на употреба
Въпреки че е популяризирана при използването на тази техника с изображения, има и приложения, които не са свързани с изображения. Това може да се използва за откриване на нови протеини при търсене на нова ваксина. Органите на реда също могат да използват тази технология за идентифициране на заподозрени и др
Цел на тази статия
Ако приемем, че сте виждали GAN и cycleGAN от няколко източника, ще се опитам да ви преведа през процеса на създаване на cycleGAN с минимално обяснение, но вместо това ще ви покажа как да създадете такъв. Не искаме да усложняваме процеса, така че докторантите и тези, които не са докторанти, да могат да създадат свои собствени GAN.
Сега започваме...
Заедно със създаването на няколко функции и класове, следните са някои от изискванията, необходими за създаване на цикъл GAN.
- Библиотеки на Python
2. Набор от данни за изображения
3. CUDA — За да използвате GPU
4. DataLoader
5. Трансформатор
6. Тегла функция
7. Resnet или мрежа за остатъчно обучение
8. 4 изкуствени невронни мрежи (2 генератора, 2 дискриминатора)
9. Функция на загубата
10. Оптимизатори
11. Автоенкодер
Библиотеки на Python:
import os, random, cv2, glob, itertools, shutil import numpy as np import pandas as pd import matplotlib.pyplot as plt import matplotlib.image as mpimg from PIL import Image import torch import torch.nn as nn import torch.optim as optim import torch.utils.data as data from torch.autograd import Variable import torchvision from torchvision import datasets, transforms, utils from tqdm.notebook import tqdm import warnings warnings.filterwarnings('ignore')
Наборът от данни за изображения
Използвах набора от данни за изображения UTKFace. Този набор от данни ни беше предоставен публично от Yang Song и Zhifei Zhang.
Има приблизително 65 000 изображения, които са етикетирани. Освен изображенията, бяха предоставени и файлове с забележителности. За целите на този проект няма да използваме нито един от файловете/указателите на ориентири.
Преглед на изображения
за и в диапазон (5):
файл = np.random.choice(os.listdir(UTKFace_folder))
image_path= os.path.join(UTKFace_folder, файл)
изображение=mpimg.imread(път_на_изображение)
plt.subplot(1,10,i+1)
plt.imshow(изображение)
Преоразмеряване на изображенията
Оригиналните изображения се предлагат в размер 200x200. Първо можем да променим размера на тези изображения до 128x128 за по-бързо обучение. Това може да се направи и в раздела за трансформиране, но за опростяване ще ги преоразмерим тук. Кодът по-долу ще пренастрои изображенията по същия път, по който се намират текущите изображения. Няма да е необходимо да създавате друга папка, освен за целите на архивирането.
#Gathering all the images and combining them to one all_im = glob(os.path.join(basepath, '**/*')) for im in tqdm(all_im): image = Image.open(im) image = image.resize((128,128), Image.BILINEAR) image.save(im)
Подготовка на комплекта за обучение и тестване.
Ще трябва да създадем 4 комплекта изображения. Учебният набор и тестовият набор от оригиналните изображения, в този случай мъжките изображения. Вторият набор ще бъдат изображения, в които искаме да ги конвертираме, също в този случай наборът от данни за женски изображения.
При подготовката на изображенията един от най-критичните фактори е да се уверите, че количеството изображения е еднакво за всеки от набора за обучение и комплектите за тестване. В този случай количествата за всеки комплект са следните:
TrainA_Male: 7, 549 изображения
TestA_Male: 463 изображения
TrainB_Female:7, 549 изображения
TestB_Female: 463 изображения
За да ускоря обучението и да видя дали този модел работи, взех предвид само изображения с възраст, варираща от 20 години до 70 години. Всички по-млади или по-възрастни изображения са премахнати.
Параметри:
Създадени са и глобални параметри с цел стандартизиране и улесняване на запомнянето на присвояванията. Внимавайте за променливите, които задавате тук, тъй като това може да е източникът на някои проблеми, които може да срещнете.
parameters = {'ngf':32, 'ndf':64, 'num_epochs':100, 'decay_epoch': 100, 'lgG':0.0002, 'lgD':0.0002, 'beta1':.5, 'beta2':.9999, 'lambdaA':10, 'lambdaB':10, 'batch_size':32, 'pool_size':50, 'img_width':256, 'img_height':256, 'img_depth':3, 'input_size':128, 'resize_size':128, 'crop_size':256, 'fliplr':True, 'num_resnet':6, 'num_workers':2}
Използване на GPU
Тъй като ние също искаме да използваме GPU за по-бърз процес, ще използваме CUDA със следния код.
def to_numpy(x): return x.data.cpu().numpy() #use GPU device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
Трансформатор
Трансформаторът ще трансформира изображенията в тензор и ще ги нормализира. Обърнете внимание, че първо трансформираме изображението в тензор, преди да ги нормализираме.
def to_numpy(x): return x.data.cpu().numpy() #use GPU device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
Трансформаторът също е страхотен инструмент, който можем да използваме за добавяне на повече данни. Невронните мрежи работят добре с повече данни. Можете да щракнете тук за повече информация относно трансформърс.
Създаване на функция за набор от изображения
Функцията за набор от изображения ще се използва при извличане на изображения от набора от фалшиви изображения на по-късен етап.
class ImagePool(): def __init__(self, pool_size): self.pool_size = pool_size if self.pool_size > 0: self.num_imgs = 0 self.images = [] def query(self, images): if self.pool_size == 0: return images return_images = [] for image in images.data: image = torch.unsqueeze(image, 0) if self.num_imgs < self.pool_size: self.num_imgs = self.num_imgs + 1 self.images.append(image) return_images.append(image) else: p = random.uniform(0, 1) if p > 0.5: random_id = random.randint(0, self.pool_size-1) tmp = self.images[random_id].clone() self.images[random_id] = image return_images.append(tmp) else: return_images.append(image) return_images = Variable(torch.cat(return_images, 0)) return return_images
Начертаване на изображения
За да извлечем изображенията, ние също ще искаме функция за начертаване на резултатите.
def plot_train_result(real_image, gen_image, recon_image, epoch, save=False, show=True, fig_size=(15, 15)): fig, axes = plt.subplots(2, 3, figsize=fig_size) imgs = [to_numpy(real_image[0]), to_numpy(gen_image[0]), to_numpy(recon_image[0]), to_numpy(real_image[1]), to_numpy(gen_image[1]), to_numpy(recon_image[1])] for ax, img in zip(axes.flatten(), imgs): ax.axis('off') img = img.squeeze() img = (((img - img.min()) * 255) / (img.max() - img.min())).transpose(1, 2, 0).astype(np.uint8) ax.imshow(img, cmap=None, aspect='equal') plt.subplots_adjust(wspace=0, hspace=0) title = 'Epoch {0}'.format(epoch + 1) fig.text(0.5, 0.04, title, ha='center')
Програмата за зареждане на данни
Тъй като няма да можем да заредим целия набор от изображения, тъй като това може да отнеме огромно количество време, ще използваме програма за зареждане на данни, за да заредим изображенията в комплекти. Това е по-ефективен начин за зареждане на набори от данни, тъй като се среща значително увеличение на данните.
class DatasetFromFolder(data.Dataset): def __init__(self, image_dir, subfolder='train', transform=None, resize_scale=None, crop_size=None, fliplr=False): super(DatasetFromFolder, self).__init__() self.input_path = os.path.join(image_dir, subfolder) self.image_filenames = [x for x in sorted(os.listdir(self.input_path))] self.file_num = len(self.image_filenames) self.transform = trans self.resize_scale = resize_scale self.crop_size = crop_size self.fliplr = fliplr def __len__(self): return len(self.image_filenames) def __getitem__(self, index): # Load Images img_fn = os.path.join(self.input_path, self.image_filenames[index]) img = Image.open(img_fn).convert('RGB') #preprocessing if self.resize_scale: img = img.resize((self.resize_scale, self.resize_scale), Image.BILINEAR) if self.crop_size: x = random.randint(0, self.resize_scale - self.crop_size + 1) y = random.randint(0, self.resize_scale - self.crop_size + 1) img = img.crop((x, y, x + self.crop_size, y + self.crop_size)) if self.fliplr: if random.random() < 0.5: img = img.transpose(Image.FLIP_LEFT_RIGHT) if self.transform is not None: img = self.transform(img) return img
Зареждането на DataLoader от папка
class DatasetFromFolder(data.Dataset): def __init__(self, image_dir, subfolder='train', transform=None, resize_scale=None, crop_size=None, fliplr=False): super(DatasetFromFolder, self).__init__() self.input_path = os.path.join(image_dir, subfolder) self.image_filenames = [x for x in sorted(os.listdir(self.input_path))] self.file_num = len(self.image_filenames) self.transform = trans self.resize_scale = resize_scale self.crop_size = crop_size self.fliplr = fliplr def __len__(self): return len(self.image_filenames) def __getitem__(self, index): # index = total_index % self.file_num # Load Images img_fn = os.path.join(self.input_path, self.image_filenames[index]) img = Image.open(img_fn).convert('RGB') #preprocessing if self.resize_scale: img = img.resize((self.resize_scale, self.resize_scale), Image.BILINEAR) if self.crop_size: x = random.randint(0, self.resize_scale - self.crop_size + 1) y = random.randint(0, self.resize_scale - self.crop_size + 1) img = img.crop((x, y, x + self.crop_size, y + self.crop_size)) if self.fliplr: if random.random() < 0.5: img = img.transpose(Image.FLIP_LEFT_RIGHT) if self.transform is not None: img = self.transform(img) return img
Конволюционната мрежа
Ресурси на CNN:
https://en.wikipedia.org/wiki/Convolutional_neural_network
class DatasetFromFolder(data.Dataset): def __init__(self, image_dir, subfolder='train', transform=None, resize_scale=None, crop_size=None, fliplr=False): super(DatasetFromFolder, self).__init__() self.input_path = os.path.join(image_dir, subfolder) self.image_filenames = [x for x in sorted(os.listdir(self.input_path))] self.file_num = len(self.image_filenames) self.transform = trans self.resize_scale = resize_scale self.crop_size = crop_size self.fliplr = fliplr def __len__(self): return len(self.image_filenames) def __getitem__(self, index): # index = total_index % self.file_num # Load Images img_fn = os.path.join(self.input_path, self.image_filenames[index]) img = Image.open(img_fn).convert('RGB') #preprocessing if self.resize_scale: img = img.resize((self.resize_scale, self.resize_scale), Image.BILINEAR) if self.crop_size: x = random.randint(0, self.resize_scale - self.crop_size + 1) y = random.randint(0, self.resize_scale - self.crop_size + 1) img = img.crop((x, y, x + self.crop_size, y + self.crop_size)) if self.fliplr: if random.random() < 0.5: img = img.transpose(Image.FLIP_LEFT_RIGHT) if self.transform is not None: img = self.transform(img) return img
Деконволюционната мрежа
ресурси за деконволюция:
https://datascience.stackexchange.com/questions/6107/what-are-deconvolutional-layers
class DeconvBlock(torch.nn.Module): #Initialization Function def __init__(self, input_size, output_size, kernel_size=3, stride=2, padding=1, output_padding=1, activation='relu', batch_norm=True): super(DeconvBlock,self).__init__() self.deconv = torch.nn.ConvTranspose2d(input_size, output_size, kernel_size, stride, padding, output_padding) self.batch_norm = batch_norm self.bn = torch.nn.InstanceNorm2d(output_size) self.activation = activation self.relu = torch.nn.ReLU(True) #Forward pass def forward(self,x): if self.batch_norm: out = self.bn(self.deconv(x)) else: out = self.deconv(x) if self.activation == 'relu': return self.relu(out) elif self.activation == 'lrelu': return self.lrelu(out) elif self.activation == 'tanh': return self.tanh(out) elif self.activation == 'no_act': return out
Resnet или остатъчен учебен блок / мрежа
Resnet ресурси:
https://towardsdatascience.com/an-overview-of-resnet-and-its-variants-5281e2f56035
class ResnetBlock(torch.nn.Module): def __init__(self,num_filter,kernel_size=3,stride=1,padding=0): super(ResnetBlock,self).__init__() conv1 = torch.nn.Conv2d(num_filter, num_filter, kernel_size, stride, padding) conv2 = torch.nn.Conv2d(num_filter, num_filter, kernel_size, stride, padding) bn = torch.nn.InstanceNorm2d(num_filter) relu = torch.nn.ReLU(True) pad = torch.nn.ReflectionPad2d(1) self.resnet_block = torch.nn.Sequential( pad, conv1, bn, relu, pad, conv2, bn ) def forward(self,x): out = self.resnet_block(x) return outThe Generator
Генератор
Сега за забавната част, сега създаваме генератора, който ще използваме в учебния блок по-късно.
class Generator(torch.nn.Module): def __init__(self,input_dim,num_filter,output_dim,num_resnet): super(Generator,self).__init__() #Reflection padding self.pad = torch.nn.ReflectionPad2d(3) #Encoder # Input Layer - NN Layer 1 self.conv1 = ConvBlock(input_dim, num_filter, kernel_size=7, stride=1, padding=0) # NN Layer 2 self.conv2 = ConvBlock(num_filter, num_filter*2) ## NN Layer 3 self.conv3 = ConvBlock(num_filter*2, num_filter*4) #Transformer self.resnet_blocks = [] for i in range(num_resnet): self.resnet_blocks.append(ResnetBlock(num_filter*4)) self.resnet_blocks = torch.nn.Sequential(*self.resnet_blocks) #Decoder self.deconv1 = DeconvBlock(num_filter*4, num_filter*2) self.deconv2 = DeconvBlock(num_filter*2, num_filter) self.deconv3 = ConvBlock(num_filter, output_dim, kernel_size=7, stride=1, padding=0, activation='tanh', batch_norm=False) #Forward Function def forward(self,x): #Encoder enc1 = self.conv1(self.pad(x)) enc2 = self.conv2(enc1) enc3 = self.conv3(enc2) #Transformer res = self.resnet_blocks(enc3) #Decoder dec1 = self.deconv1(res) dec2 = self.deconv2(dec1) out = self.deconv3(self.pad(dec2)) return out #Creating weights inside the Generator def normal_weight_init(self,mean=0.0,std=0.02): for m in self.children(): if isinstance(m,ConvBlock): torch.nn.init.normal_(m.conv.weight,mean,std) if isinstance(m,DeconvBlock): torch.nn.init.normal_(m.deconv.weight,mean,std) if isinstance(m,ResnetBlock): torch.nn.init.normal_(m.conv.weight,mean,std) torch.nn.init.constant_(m.conv.bias,0)
Генератор А
Generator_A = Generator(3, parameters['ngf'], 3, parameters['num_resnet']).cuda() Generator_A.normal_weight_init(mean=0.0, std=0.02)
Генератор Б
Generator_A = Generator(3, parameters['ngf'], 3, parameters['num_resnet']).cuda() Generator_A.normal_weight_init(mean=0.0, std=0.02)
Generator_B = Generator(3, параметри[‘ngf’], 3, параметри[‘num_resnet’]).cuda() Generator_B.normal_weight_init(mean=0.0, std=0.02) Generator_B
Създаване на дискриминатора
class Discriminator(torch.nn.Module): def __init__(self,input_dim,num_filter,output_dim): super(Discriminator,self).__init__() #Input - NN Layer 1 conv1 = ConvBlock(input_dim,num_filter,kernel_size=4,stride=2,padding=1,activation='lrelu',batch_norm=False) #NN Layer 2 conv2 = ConvBlock(num_filter,num_filter*2,kernel_size=4,stride=2,padding=1,activation='lrelu') #NN Layer 3 conv3 = ConvBlock(num_filter*2,num_filter*4,kernel_size=4,stride=2,padding=1,activation='lrelu') #NN Layer 4 conv4 = ConvBlock(num_filter*4,num_filter*8,kernel_size=4,stride=1,padding=1,activation='lrelu') #Output - NN Layer 5 conv5 = ConvBlock(num_filter*8,output_dim,kernel_size=4,stride=1,padding=1,activation='no_act',batch_norm=False) self.conv_blocks = torch.nn.Sequential( conv1, conv2, conv3, conv4, conv5 ) def forward(self,x): out = self.conv_blocks(x) return out #Creating the weights for the Discriminator def normal_weight_init(self, mean=0.0, std=0.02): for m in self.children(): if isinstance(m, ConvBlock): torch.nn.init.normal_(m.conv.weight.data, mean, std)
Дискриминатор А
Discriminator_A = Discriminator(3, parameters['ndf'], 1).cuda() Discriminator_A.normal_weight_init(mean=0.0, std=0.02)
Дискриминатор B
Discriminator_B = Discriminator(3, parameters['ndf'], 1).cuda() Discriminator_B.normal_weight_init(mean=0.0, std=0.02)
Тренировъчен блок/Зареждащи устройства
train_data_A = DatasetFromFolder(basepath, subfolder='trainA_Male', transform=trans, resize_scale=parameters['resize_size'], #crop_size=parameters['crop_size'], #fliplr=parameters['fliplr'] ) train_data_loader_A = torch.utils.data.DataLoader(dataset=train_data_A, batch_size=parameters['batch_size'], num_workers=parameters['num_workers'], pin_memory=True, shuffle=True) train_data_B = DatasetFromFolder(basepath, subfolder='trainB_Female', transform=trans, resize_scale=parameters['resize_size'], #crop_size=parameters['crop_size'], #fliplr=True ) train_data_loader_B = torch.utils.data.DataLoader(dataset=train_data_B, batch_size=parameters['batch_size'], num_workers=parameters['num_workers'], pin_memory=True, shuffle=True) #Load test data test_data_A = DatasetFromFolder(basepath, subfolder='testA_Male', transform=trans) test_data_loader_A = torch.utils.data.DataLoader(dataset=test_data_A, batch_size=parameters['batch_size'], shuffle=False) test_data_B = DatasetFromFolder(basepath, subfolder='trainB_Female', transform=trans) test_data_loader_B = torch.utils.data.DataLoader(dataset=test_data_B, batch_size=parameters['batch_size'], shuffle=False)
Тествайте реални данни A и B
test_real_A_data = train_data_A.__getitem__(11).unsqueeze(0)
test_real_B_data = train_data_B.__getitem__(11).unsqueeze(0)
Създаване на оптимизатора
В нашия случай ще използваме един от най-популярните и прости оптимизатори, ADAM optimizer.
ресурс:
Generator_optimizer = torch.optim.Adam(itertools.chain(Generator_A.parameters(), Generator_B.parameters()), betas=(parameters['beta1'], parameters['beta2']), lr=parameters['lgG']) Discriminator_A_optimizer = torch.optim.Adam(itertools.chain(Discriminator_A.parameters(), Discriminator_B.parameters()), betas=(parameters['beta1'], parameters['beta2']), lr=parameters['lgD']) Discriminator_B_optimizer = torch.optim.Adam(itertools.chain(Discriminator_A.parameters(), Discriminator_B.parameters()), betas=(parameters['beta1'], parameters['beta2']), lr=parameters['lgD'])
Определяне на загубите
MSE_Loss = torch.nn.MSELoss().cuda() L1_Loss = torch.nn.L1Loss().cuda()
Обучение на модела и извличане на изображения
Discriminator_A_avg_losses = [] Discriminator_B_avg_losses = [] Generator_A_avg_losses = [] Generator_B_avg_losses = [] cycle_A_avg_losses = [] cycle_B_avg_losses = [] for epoch in range(parameters['num_epochs']): Discriminator_A_losses = [] Discriminator_B_losses = [] Generator_A_losses = [] Generator_B_losses = [] cycle_A_losses = [] cycle_B_losses = [] # Learing rate decay if(epoch + 1) > parameters['decay_epoch']: Discriminatoroptimizer.param_groups[0]['lr'] -= parameters['lgD'] / (parameters['num_epochs'] - parameters['decay_epoch']) Discriminator_B_optimizer.param_groups[0]['lr'] -= parameters['lgD'] / (parameters['num_epochs'] - parameters['decay_epoch']) G_optimizer.param_groups[0]['lr'] -= parameters['lrG'] / (parameters['num_epochs'] - parameters['decay_epoch']) # training for i, (real_A, real_B) in tqdm(enumerate(zip(train_data_loader_A, train_data_loader_B)), total=len(train_data_loader_A)): # input image data real_A = real_A.to(device) real_B = real_B.to(device) # Train The Generator # A --> B fake_B = Generator_A(real_A) Discriminator_B_fake_decision = Discriminator_B(fake_B) Generator_A_loss = MSE_Loss(Discriminator_B_fake_decision, Variable(torch.ones(Discriminator_B_fake_decision.size()).cuda())) # Forward Cycle Loss recon_A = Generator_B(fake_B) cycle_A_loss = L1_Loss(recon_A, real_A) * parameters['lambdaA'] # B --> A fake_A = Generator_B(real_B) Discriminator_A_fake_decision = Discriminator_A(fake_A) Generator_B_loss = MSE_Loss(Discriminator_A_fake_decision, Variable(torch.ones(Discriminator_A_fake_decision.size()).cuda())) # Backward Cycle Loss recon_B = Generator_A(fake_A) cycle_B_loss = L1_Loss(recon_B, real_B) * parameters['lambdaB'] # Back Propagation Generator_loss = Generator_A_loss + Generator_B_loss + cycle_A_loss + cycle_B_loss Generator_optimizer.zero_grad() Generator_loss.backward() Generator_optimizer.step() # Train Discriminator_A Discriminator_A_real_decision = Discriminator_A(real_A) Discriminator_A_real_loss = MSE_Loss(Discriminator_A_real_decision, Variable(torch.ones(Discriminator_A_real_decision.size()).cuda())) fake_A = fake_A_Pool.query(fake_A) Discriminator_A_fake_decision = Discriminator_A(fake_A) Discriminator_A_fake_loss = MSE_Loss(Discriminator_A_fake_decision, Variable(torch.zeros(Discriminator_A_fake_decision.size()).cuda())) # Back propagation Discriminator_A_loss = (Discriminator_A_real_loss + Discriminator_A_fake_loss) * 0.5 Discriminator_A_optimizer.zero_grad() Discriminator_A_loss.backward() Discriminator_A_optimizer.step() # Train Discriminator_B Discriminator_B_real_decision = Discriminator_B(real_B) # print('real_A, ',real_A.shape) # print('real_B, ',real_B.shape) # print('dis_A, ',Discriminator_A_real_decision.shape) # print('dis_B, ',Discriminator_B_real_decision.shape) Discriminator_B_real_loss = MSE_Loss(Discriminator_B_real_decision, Variable(torch.ones(Discriminator_B_fake_decision.size()).cuda())) #Discriminator_B_real_loss = MSE_Loss(Discriminator_B_real_decision, Variable(torch.ones(Discriminator_B_real_decision.size()).cuda())) fake_B = fake_B_Pool.query(fake_B) Discriminator_B_fake_decision = Discriminator_B(fake_B) Discriminator_B_fake_loss = MSE_Loss(Discriminator_B_fake_decision, Variable(torch.zeros(Discriminator_B_fake_decision.size()).cuda())) # Back Propagation Discriminator_B_loss = (Discriminator_B_real_loss + Discriminator_B_fake_loss) * 0.5 Discriminator_B_optimizer.zero_grad() Discriminator_B_loss.backward() Discriminator_B_optimizer.step() # Print # loss values Discriminator_A_losses.append(Discriminator_A_loss.item()) Discriminator_B_losses.append(Discriminator_B_loss.item()) Generator_A_losses.append(Generator_A_loss.item()) Generator_B_losses.append(Generator_B_loss.item()) cycle_A_losses.append(cycle_A_loss.item()) cycle_B_losses.append(cycle_B_loss.item()) if i%100 == 0: print('Epoch [%d/%d], Step [%d/%d], Discriminator_A_losses: %.4f, Discriminator_B_loss: %.4f, Generator_A_loss: %.4f, Generator_B_loss: %.4f' % (epoch+1, parameters['num_epochs'], i+1, len(train_data_loader_A), Discriminator_A_loss.item(), Discriminator_B_loss.item(), Generator_A_loss.item(), Generator_B_loss.item())) step += 1 Discriminator_A_avg_loss = torch.mean(torch.FloatTensor(Discriminator_A_losses)) Discriminator_B_avg_loss = torch.mean(torch.FloatTensor(Discriminator_B_losses)) Generator_A_avg_loss = torch.mean(torch.FloatTensor(Generator_A_losses)) Generator_B_avg_loss = torch.mean(torch.FloatTensor(Generator_B_losses)) cycle_A_avg_loss = torch.mean(torch.FloatTensor(cycle_A_losses)) cycle_B_avg_loss = torch.mean(torch.FloatTensor(cycle_B_losses)) # Average Loss Values Discriminator_A_avg_losses.append(Discriminator_A_avg_loss.item()) Discriminator_B_avg_losses.append(Discriminator_B_avg_loss.item()) Generator_A_avg_losses.append(Generator_A_avg_loss.item()) Generator_B_avg_losses.append(Generator_B_avg_loss.item()) cycle_A_avg_losses.append(cycle_A_avg_loss.item()) cycle_B_avg_losses.append(cycle_B_avg_loss.item()) # Test Image Results test_real_A = test_real_A_data.cuda() test_fake_B = Generator_A(test_real_A) test_recon_A = Generator_B(test_fake_B) test_real_B = test_real_B_data.cuda() test_fake_A = Generator_B(test_real_B) test_recon_B = Generator_A(test_fake_A) plot_train_result([test_real_A, test_real_B], [test_fake_B, test_fake_A], [test_recon_A, test_recon_B], epoch, save=True)
Резултати в различни епохи
Възстановяване на загуби
all_losses = pd.DataFrame() all_losses['Discriminator_A_avg_losses'] = Discriminator_A_avg_losses all_losses['Discriminator_B_avg_losses'] = Discriminator_B_avg_losses all_losses['Generator_A_avg_losses'] = Generator_A_avg_losses all_losses['Generator_B_avg_losses'] = Generator_B_avg_losses all_losses['cycle_A_avg_losses'] = cycle_A_avg_losses all_losses['cycle_B_avg_losses'] = cycle_B_avg_losses
Начертаване на загубите
plt.figure(figsize=(20,20)) losses.plot() plt.legend(bbox_to_anchor=(1, 1), loc='upper left', ncol=1) plt.show()
Заключение:
Въпреки че не е перфектен, можем да видим, че моделът работи, като погледнем визуално промените в изображенията. Освен това можем да видим намаляването на загубите, докато увеличаваме епохите. С допълнителни ресурси можем също да се опитаме да направим промени в епохите и в скоростта на обучение, за да видим кои стойности работят добре за подобряване на резултатите.
Надявам се, че успях да помогна в създаването на един от многото GAN, които възнамерявате да създадете.
Препратки:
https://github.com/ltq477/CycleGAN/blob/master/CycleGAN%20-%20Facial%20Gans%20ver_IV.ipynb
https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html
Урок за Pytorch:
https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html
Пример за внедряване от урока: