From 061226659f430a3c7b868ed7718ed25e58ebd420 Mon Sep 17 00:00:00 2001 From: junyanz Date: Sat, 14 Oct 2017 22:01:14 +0800 Subject: update README --- README.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4e5e04e..5e4a967 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,29 @@ Image-to-Image Translation with Conditional Adversarial Networks [Phillip Isola](https://people.eecs.berkeley.edu/~isola), [Jun-Yan Zhu](https://people.eecs.berkeley.edu/~junyanz), [Tinghui Zhou](https://people.eecs.berkeley.edu/~tinghuiz), [Alexei A. Efros](https://people.eecs.berkeley.edu/~efros) In CVPR 2017. - +## Other implementations: +### CycleGAN +

[Tensorflow] (by Harry Yang), +[Tensorflow] (by Archit Rathore), +[Tensorflow] (by Van Huy), +[Tensorflow] (by Xiaowei Hu), + [Tensorflow-simple] (by Zhenliang He), +[Chainer] (by Yanghua Jin), +[Minimal PyTorch] (by yunjey), +[Mxnet] (by Ldpe2G), +[lasagne/keras] (by tjwei)

+ +### pix2pix +

[Tensorflow] (by Christopher Hesse), +[tf/torch/keras/lasagne] (by tjwei), +[Tensorflow] (by Eyyüb Sariu), + [Tensorflow (face2face)] (by Dat Tran), + [Tensorflow (film)] (by Arthur Juliani), +[Tensorflow (zi2zi)] (by Yuchen Tian), +[Chainer] (by mattya), +[Pytorch] (by taey16) +

+ ## Prerequisites - Linux or macOS @@ -106,11 +128,11 @@ If you would like to apply a pre-trained model to a collection of input photos ( python test.py --dataroot ./datasets/facades/testB/ --name facades_pix2pix --model test --which_model_netG unet_256 --which_direction BtoA --dataset_mode single ``` -Note: We currently don't have pretrained models using PyTorch. This is in part because the models trained using Torch and PyTorch produce slightly different results, although we were not able to decide which result is better. If you would like to generate the same results that appeared in our paper, we recommend using the pretrained models in the Torch codebase. +Note: We currently don't have pretrained models using PyTorch. This is in part because the models trained using Torch and PyTorch produce slightly different results, although we were not able to decide which result is better. If you would like to generate the same results that appeared in our paper, we recommend using the pretrained models in the Torch codebase. ### Apply a pre-trained model (pix2pix) -Download the pre-trained models using `./pretrained_models/download_pix2pix_model.sh`. For example, if you would like to download label2photo model on the Facades dataset, +Download the pre-trained models using `./pretrained_models/download_pix2pix_model.sh`. For example, if you would like to download label2photo model on the Facades dataset, ```bash bash pretrained_models/download_pix2pix_model.sh facades_label2photo @@ -120,7 +142,7 @@ Then generate the results using ```bash python test.py --dataroot ./datasets/facades/ --which_direction BtoA --model pix2pix --name facades_label2photo_pretrained --dataset_mode aligned --which_model_netG unet_256 --norm batch ``` -Note that we specified `--which_direction BtoA` to accomodate the fact that the Facades dataset's A to B direction is photos to labels. +Note that we specified `--which_direction BtoA` to accomodate the fact that the Facades dataset's A to B direction is photos to labels. Also, the models that are currently available to download can be found by reading the output of `bash pretrained_models/download_pix2pix_model.sh` -- cgit v1.2.3-70-g09d2 From bdbd2262c42b6b723e949e58bdad058ed3b97769 Mon Sep 17 00:00:00 2001 From: junyanz Date: Sat, 14 Oct 2017 22:06:12 +0800 Subject: Update REAME --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5e4a967..cd33e79 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ In CVPR 2017. [Mxnet] (by Ldpe2G), [lasagne/keras] (by tjwei)

+ ### pix2pix

[Tensorflow] (by Christopher Hesse), [tf/torch/keras/lasagne] (by tjwei), -- cgit v1.2.3-70-g09d2 From 01fea98d5d8cc5411ea5a0ff40e4a5e36a18b1c8 Mon Sep 17 00:00:00 2001 From: junyanz Date: Sat, 14 Oct 2017 22:08:30 +0800 Subject: update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cd33e79..3105f05 100644 --- a/README.md +++ b/README.md @@ -49,13 +49,13 @@ In CVPR 2017. ### pix2pix

[Tensorflow] (by Christopher Hesse), -[tf/torch/keras/lasagne] (by tjwei), [Tensorflow] (by Eyyüb Sariu), [Tensorflow (face2face)] (by Dat Tran), [Tensorflow (film)] (by Arthur Juliani), [Tensorflow (zi2zi)] (by Yuchen Tian), [Chainer] (by mattya), -[Pytorch] (by taey16) +[Pytorch] (by taey16), +[tf/torch/keras/lasagne] (by tjwei)

-- cgit v1.2.3-70-g09d2 From f34719c53419d283586c47ad1905b8144a89547a Mon Sep 17 00:00:00 2001 From: junyanz Date: Sat, 14 Oct 2017 22:11:06 +0800 Subject: update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3105f05..455c32a 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ In CVPR 2017. [Tensorflow (film)] (by Arthur Juliani), [Tensorflow (zi2zi)] (by Yuchen Tian), [Chainer] (by mattya), -[Pytorch] (by taey16), -[tf/torch/keras/lasagne] (by tjwei) +[tf/torch/keras/lasagne] (by tjwei), +[Pytorch] (by taey16)

-- cgit v1.2.3-70-g09d2 From f87530a15cdf92064297825b8b7a9e23da87f6a5 Mon Sep 17 00:00:00 2001 From: junyanz Date: Thu, 19 Oct 2017 19:19:39 -0700 Subject: fix small issue in learning rate policy --- models/networks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/networks.py b/models/networks.py index 2df58fe..0b8938d 100644 --- a/models/networks.py +++ b/models/networks.py @@ -87,8 +87,8 @@ def get_norm_layer(norm_type='instance'): def get_scheduler(optimizer, opt): if opt.lr_policy == 'lambda': - def lambda_rule(epoch): - lr_l = 1.0 - max(0, epoch - opt.niter) / float(opt.niter_decay+1) + def lambda_rule(epoch): # epoch ranges from [1, opt.niter+opt.niter_decay] + lr_l = 1.0 - max(0, epoch - opt.niter) / float(opt.niter_decay) return lr_l scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule) elif opt.lr_policy == 'step': -- cgit v1.2.3-70-g09d2 From 2a344ccd6f80ce0435d2c58ccfd94c76dd423b1a Mon Sep 17 00:00:00 2001 From: junyanz Date: Thu, 19 Oct 2017 19:35:11 -0700 Subject: update learning rate policy --- models/networks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/networks.py b/models/networks.py index 0b8938d..19169c5 100644 --- a/models/networks.py +++ b/models/networks.py @@ -88,7 +88,7 @@ def get_norm_layer(norm_type='instance'): def get_scheduler(optimizer, opt): if opt.lr_policy == 'lambda': def lambda_rule(epoch): # epoch ranges from [1, opt.niter+opt.niter_decay] - lr_l = 1.0 - max(0, epoch - opt.niter) / float(opt.niter_decay) + lr_l = 1.0 - max(0, epoch - opt.niter + 1) / float(opt.niter_decay + 1) return lr_l scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule) elif opt.lr_policy == 'step': -- cgit v1.2.3-70-g09d2 From 9d1bc76e6a4f791a25db1179c7c2b4c62a8d55cd Mon Sep 17 00:00:00 2001 From: junyanz Date: Thu, 19 Oct 2017 21:14:48 -0700 Subject: fix learning rate --- models/networks.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/models/networks.py b/models/networks.py index 19169c5..51e3f25 100644 --- a/models/networks.py +++ b/models/networks.py @@ -10,7 +10,6 @@ import numpy as np ############################################################################### - def weights_init_normal(m): classname = m.__class__.__name__ # print(classname) @@ -87,8 +86,8 @@ def get_norm_layer(norm_type='instance'): def get_scheduler(optimizer, opt): if opt.lr_policy == 'lambda': - def lambda_rule(epoch): # epoch ranges from [1, opt.niter+opt.niter_decay] - lr_l = 1.0 - max(0, epoch - opt.niter + 1) / float(opt.niter_decay + 1) + def lambda_rule(epoch): + lr_l = 1.0 - max(0, epoch + 1 + opt.epoch_count - opt.niter) / float(opt.niter_decay + 1) return lr_l scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule) elif opt.lr_policy == 'step': -- cgit v1.2.3-70-g09d2 From 1e0fbeaf5bc81c0b9e23655dc31d28c77505b7cd Mon Sep 17 00:00:00 2001 From: LambdaWill <574819595@qq.com> Date: Sat, 21 Oct 2017 12:34:44 +0800 Subject: Compatible with the latest version. Since the commit `Change device_id to device in python land #3133`, keyword `device_id` has been changed to `device` --- models/networks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/networks.py b/models/networks.py index 51e3f25..dca5489 100644 --- a/models/networks.py +++ b/models/networks.py @@ -118,7 +118,7 @@ def define_G(input_nc, output_nc, ngf, which_model_netG, norm='batch', use_dropo else: raise NotImplementedError('Generator model name [%s] is not recognized' % which_model_netG) if len(gpu_ids) > 0: - netG.cuda(device_id=gpu_ids[0]) + netG.cuda(device_id=gpu_ids[0]) # or netG.cuda(device=gpu_ids[0]) for latest version. init_weights(netG, init_type=init_type) return netG @@ -139,7 +139,7 @@ def define_D(input_nc, ndf, which_model_netD, raise NotImplementedError('Discriminator model name [%s] is not recognized' % which_model_netD) if use_gpu: - netD.cuda(device_id=gpu_ids[0]) + netD.cuda(device_id=gpu_ids[0]) # or netG.cuda(device=gpu_ids[0]) for latest version. init_weights(netD, init_type=init_type) return netD -- cgit v1.2.3-70-g09d2 From 9d0295bbb5d01ce40bd2792f72882128282f0338 Mon Sep 17 00:00:00 2001 From: LambdaWill <574819595@qq.com> Date: Sat, 21 Oct 2017 17:06:30 +0800 Subject: Update networks.py fix typo --- models/networks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/networks.py b/models/networks.py index dca5489..949659d 100644 --- a/models/networks.py +++ b/models/networks.py @@ -139,7 +139,7 @@ def define_D(input_nc, ndf, which_model_netD, raise NotImplementedError('Discriminator model name [%s] is not recognized' % which_model_netD) if use_gpu: - netD.cuda(device_id=gpu_ids[0]) # or netG.cuda(device=gpu_ids[0]) for latest version. + netD.cuda(device_id=gpu_ids[0]) # or netD.cuda(device=gpu_ids[0]) for latest version. init_weights(netD, init_type=init_type) return netD -- cgit v1.2.3-70-g09d2 From 9854d6a5e40fe2c9dd1b70302f41624b63b3c545 Mon Sep 17 00:00:00 2001 From: Javi Ribera Date: Sun, 22 Oct 2017 20:46:06 -0400 Subject: show default parameter values when invoking with -h --- options/base_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/base_options.py b/options/base_options.py index e89f144..b2d5360 100644 --- a/options/base_options.py +++ b/options/base_options.py @@ -5,7 +5,7 @@ import torch class BaseOptions(): def __init__(self): - self.parser = argparse.ArgumentParser() + self.parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) self.initialized = False def initialize(self): -- cgit v1.2.3-70-g09d2 From 343dda259b4b6a64a338fa61a9f1c70893b8fc7e Mon Sep 17 00:00:00 2001 From: LambdaWill <574819595@qq.com> Date: Tue, 24 Oct 2017 12:21:00 +0800 Subject: Update base_model.py --- models/base_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/base_model.py b/models/base_model.py index 446a903..d62d189 100644 --- a/models/base_model.py +++ b/models/base_model.py @@ -44,7 +44,7 @@ class BaseModel(): save_path = os.path.join(self.save_dir, save_filename) torch.save(network.cpu().state_dict(), save_path) if len(gpu_ids) and torch.cuda.is_available(): - network.cuda(device_id=gpu_ids[0]) + network.cuda(device_id=gpu_ids[0]) # network.cuda(device=gpu_ids[0]) for the latest version. # helper loading function that can be used by subclasses def load_network(self, network, network_label, epoch_label): -- cgit v1.2.3-70-g09d2 From d6cb5036a2a8b57f8c1a7cdc7e7cc80416d4ee78 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Mercier Date: Thu, 2 Nov 2017 18:00:10 -0400 Subject: gpu memory leaks --- models/cycle_gan_model.py | 81 +++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/models/cycle_gan_model.py b/models/cycle_gan_model.py index 29389db..ecb92dc 100644 --- a/models/cycle_gan_model.py +++ b/models/cycle_gan_model.py @@ -130,33 +130,58 @@ class CycleGANModel(BaseModel): # Identity loss if lambda_idt > 0: # G_A should be identity if real_B is fed. - self.idt_A = self.netG_A.forward(self.real_B) - self.loss_idt_A = self.criterionIdt(self.idt_A, self.real_B) * lambda_B * lambda_idt + idt_A = self.netG_A.forward(self.real_B) + loss_idt_A = self.criterionIdt(idt_A, self.real_B) * lambda_B * lambda_idt # G_B should be identity if real_A is fed. - self.idt_B = self.netG_B.forward(self.real_A) - self.loss_idt_B = self.criterionIdt(self.idt_B, self.real_A) * lambda_A * lambda_idt + idt_B = self.netG_B.forward(self.real_A) + loss_idt_B = self.criterionIdt(idt_B, self.real_A) * lambda_A * lambda_idt + + self.idt_A = idt_A.data + self.idt_B = idt_B.data + self.loss_idt_A = loss_idt_A.data[0] + self.loss_idt_B = loss_idt_B.data[0] + else: + loss_idt_A = 0 + loss_idt_B = 0 self.loss_idt_A = 0 self.loss_idt_B = 0 # GAN loss # D_A(G_A(A)) - self.fake_B = self.netG_A.forward(self.real_A) - pred_fake = self.netD_A.forward(self.fake_B) - self.loss_G_A = self.criterionGAN(pred_fake, True) + fake_B = self.netG_A.forward(self.real_A) + pred_fake = self.netD_A.forward(fake_B) + loss_G_A = self.criterionGAN(pred_fake, True) + # D_B(G_B(B)) - self.fake_A = self.netG_B.forward(self.real_B) - pred_fake = self.netD_B.forward(self.fake_A) - self.loss_G_B = self.criterionGAN(pred_fake, True) + fake_A = self.netG_B.forward(self.real_B) + pred_fake = self.netD_B.forward(fake_A) + loss_G_B = self.criterionGAN(pred_fake, True) + # Forward cycle loss - self.rec_A = self.netG_B.forward(self.fake_B) - self.loss_cycle_A = self.criterionCycle(self.rec_A, self.real_A) * lambda_A + rec_A = self.netG_B.forward(fake_B) + loss_cycle_A = self.criterionCycle(rec_A, self.real_A) * lambda_A + # Backward cycle loss - self.rec_B = self.netG_A.forward(self.fake_A) - self.loss_cycle_B = self.criterionCycle(self.rec_B, self.real_B) * lambda_B + rec_B = self.netG_A.forward(fake_A) + loss_cycle_B = self.criterionCycle(rec_B, self.real_B) * lambda_B + # combined loss - self.loss_G = self.loss_G_A + self.loss_G_B + self.loss_cycle_A + self.loss_cycle_B + self.loss_idt_A + self.loss_idt_B - self.loss_G.backward() + loss_G = loss_G_A + loss_G_B + loss_cycle_A + loss_cycle_B + loss_idt_A + loss_idt_B + loss_G.backward() + + self.fake_B = fake_B.data + self.fake_A = fake_A.data + self.rec_A = rec_A.data + self.rec_B = rec_B.data + + self.loss_G_A = loss_G_A.data[0] + self.loss_G_B = loss_G_B.data[0] + self.loss_cycle_A = loss_cycle_A.data[0] + self.loss_cycle_B = loss_cycle_B.data[0] + + + def optimize_parameters(self): # forward @@ -176,14 +201,14 @@ class CycleGANModel(BaseModel): def get_current_errors(self): D_A = self.loss_D_A.data[0] - G_A = self.loss_G_A.data[0] - Cyc_A = self.loss_cycle_A.data[0] + G_A = self.loss_G_A + Cyc_A = self.loss_cycle_A D_B = self.loss_D_B.data[0] - G_B = self.loss_G_B.data[0] - Cyc_B = self.loss_cycle_B.data[0] + G_B = self.loss_G_B + Cyc_B = self.loss_cycle_B if self.opt.identity > 0.0: - idt_A = self.loss_idt_A.data[0] - idt_B = self.loss_idt_B.data[0] + idt_A = self.loss_idt_A + idt_B = self.loss_idt_B return OrderedDict([('D_A', D_A), ('G_A', G_A), ('Cyc_A', Cyc_A), ('idt_A', idt_A), ('D_B', D_B), ('G_B', G_B), ('Cyc_B', Cyc_B), ('idt_B', idt_B)]) else: @@ -192,14 +217,14 @@ class CycleGANModel(BaseModel): def get_current_visuals(self): real_A = util.tensor2im(self.real_A.data) - fake_B = util.tensor2im(self.fake_B.data) - rec_A = util.tensor2im(self.rec_A.data) + fake_B = util.tensor2im(self.fake_B) + rec_A = util.tensor2im(self.rec_A) real_B = util.tensor2im(self.real_B.data) - fake_A = util.tensor2im(self.fake_A.data) - rec_B = util.tensor2im(self.rec_B.data) + fake_A = util.tensor2im(self.fake_A) + rec_B = util.tensor2im(self.rec_B) if self.opt.isTrain and self.opt.identity > 0.0: - idt_A = util.tensor2im(self.idt_A.data) - idt_B = util.tensor2im(self.idt_B.data) + idt_A = util.tensor2im(self.idt_A) + idt_B = util.tensor2im(self.idt_B) return OrderedDict([('real_A', real_A), ('fake_B', fake_B), ('rec_A', rec_A), ('idt_B', idt_B), ('real_B', real_B), ('fake_A', fake_A), ('rec_B', rec_B), ('idt_A', idt_A)]) else: -- cgit v1.2.3-70-g09d2 From df16bf1b18e03213efe9b22a0155b833a06c6a9c Mon Sep 17 00:00:00 2001 From: Jean-Philippe Mercier Date: Thu, 2 Nov 2017 18:02:02 -0400 Subject: gpu memory leaks A tensor is now fed to ImagePool() instead of a Variable --- util/image_pool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/image_pool.py b/util/image_pool.py index 152ef5b..9f34a09 100644 --- a/util/image_pool.py +++ b/util/image_pool.py @@ -13,7 +13,7 @@ class ImagePool(): if self.pool_size == 0: return images return_images = [] - for image in images.data: + for image in images: image = torch.unsqueeze(image, 0) if self.num_imgs < self.pool_size: self.num_imgs = self.num_imgs + 1 -- cgit v1.2.3-70-g09d2 From c114f1234420a51458b601761ffe4a6fd1b406a7 Mon Sep 17 00:00:00 2001 From: Robert M Ochshorn Date: Fri, 3 Nov 2017 09:03:19 +0100 Subject: util.tensor2im takes data, not Variable --- models/cycle_gan_model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/models/cycle_gan_model.py b/models/cycle_gan_model.py index ecb92dc..ff7330b 100644 --- a/models/cycle_gan_model.py +++ b/models/cycle_gan_model.py @@ -217,11 +217,11 @@ class CycleGANModel(BaseModel): def get_current_visuals(self): real_A = util.tensor2im(self.real_A.data) - fake_B = util.tensor2im(self.fake_B) - rec_A = util.tensor2im(self.rec_A) + fake_B = util.tensor2im(self.fake_B.data) + rec_A = util.tensor2im(self.rec_A.data) real_B = util.tensor2im(self.real_B.data) - fake_A = util.tensor2im(self.fake_A) - rec_B = util.tensor2im(self.rec_B) + fake_A = util.tensor2im(self.fake_A.data) + rec_B = util.tensor2im(self.rec_B.data) if self.opt.isTrain and self.opt.identity > 0.0: idt_A = util.tensor2im(self.idt_A) idt_B = util.tensor2im(self.idt_B) -- cgit v1.2.3-70-g09d2 From 6b8e96c4bbd73a1e1d4e126d795a26fd0dae983c Mon Sep 17 00:00:00 2001 From: junyanz Date: Sat, 4 Nov 2017 02:27:18 -0700 Subject: add update_html_freq flag --- models/base_model.py | 3 +- models/cycle_gan_model.py | 88 +++++++++++++++++++++-------------------------- models/networks.py | 4 +-- models/pix2pix_model.py | 6 ++-- options/base_options.py | 2 +- options/train_options.py | 4 ++- train.py | 4 ++- util/image_pool.py | 2 ++ util/visualizer.py | 32 ++++++++++------- 9 files changed, 74 insertions(+), 71 deletions(-) diff --git a/models/base_model.py b/models/base_model.py index d62d189..646a014 100644 --- a/models/base_model.py +++ b/models/base_model.py @@ -44,13 +44,14 @@ class BaseModel(): save_path = os.path.join(self.save_dir, save_filename) torch.save(network.cpu().state_dict(), save_path) if len(gpu_ids) and torch.cuda.is_available(): - network.cuda(device_id=gpu_ids[0]) # network.cuda(device=gpu_ids[0]) for the latest version. + network.cuda(device_id=gpu_ids[0]) # network.cuda(device=gpu_ids[0]) for the latest version. # helper loading function that can be used by subclasses def load_network(self, network, network_label, epoch_label): save_filename = '%s_net_%s.pth' % (epoch_label, network_label) save_path = os.path.join(self.save_dir, save_filename) network.load_state_dict(torch.load(save_path)) + # update learning rate (called once every epoch) def update_learning_rate(self): for scheduler in self.schedulers: diff --git a/models/cycle_gan_model.py b/models/cycle_gan_model.py index ff7330b..71a447d 100644 --- a/models/cycle_gan_model.py +++ b/models/cycle_gan_model.py @@ -90,13 +90,15 @@ class CycleGANModel(BaseModel): self.real_B = Variable(self.input_B) def test(self): - self.real_A = Variable(self.input_A, volatile=True) - self.fake_B = self.netG_A.forward(self.real_A) - self.rec_A = self.netG_B.forward(self.fake_B) + real_A = Variable(self.input_A, volatile=True) + fake_B = self.netG_A.forward(real_A) + self.rec_A = self.netG_B.forward(fake_B).data + self.fake_B = fake_B.data - self.real_B = Variable(self.input_B, volatile=True) - self.fake_A = self.netG_B.forward(self.real_B) - self.rec_B = self.netG_A.forward(self.fake_A) + real_B = Variable(self.input_B, volatile=True) + fake_A = self.netG_B.forward(real_B) + self.rec_B = self.netG_A.forward(fake_A).data + self.fake_A = self.fake_A.data # get image paths def get_image_paths(self): @@ -117,11 +119,13 @@ class CycleGANModel(BaseModel): def backward_D_A(self): fake_B = self.fake_B_pool.query(self.fake_B) - self.loss_D_A = self.backward_D_basic(self.netD_A, self.real_B, fake_B) + loss_D_A = self.backward_D_basic(self.netD_A, self.real_B, fake_B) + self.loss_D_A = loss_D_A.data[0] def backward_D_B(self): fake_A = self.fake_A_pool.query(self.fake_A) - self.loss_D_B = self.backward_D_basic(self.netD_B, self.real_A, fake_A) + loss_D_B = self.backward_D_basic(self.netD_B, self.real_A, fake_A) + self.loss_D_B = loss_D_B.data[0] def backward_G(self): lambda_idt = self.opt.identity @@ -135,53 +139,49 @@ class CycleGANModel(BaseModel): # G_B should be identity if real_A is fed. idt_B = self.netG_B.forward(self.real_A) loss_idt_B = self.criterionIdt(idt_B, self.real_A) * lambda_A * lambda_idt - + self.idt_A = idt_A.data self.idt_B = idt_B.data self.loss_idt_A = loss_idt_A.data[0] - self.loss_idt_B = loss_idt_B.data[0] - + self.loss_idt_B = loss_idt_B.data[0] + else: loss_idt_A = 0 loss_idt_B = 0 self.loss_idt_A = 0 self.loss_idt_B = 0 - # GAN loss - # D_A(G_A(A)) + # GAN loss D_A(G_A(A)) fake_B = self.netG_A.forward(self.real_A) pred_fake = self.netD_A.forward(fake_B) loss_G_A = self.criterionGAN(pred_fake, True) - - # D_B(G_B(B)) + + # GAN loss D_B(G_B(B)) fake_A = self.netG_B.forward(self.real_B) pred_fake = self.netD_B.forward(fake_A) loss_G_B = self.criterionGAN(pred_fake, True) - + # Forward cycle loss rec_A = self.netG_B.forward(fake_B) loss_cycle_A = self.criterionCycle(rec_A, self.real_A) * lambda_A - + # Backward cycle loss rec_B = self.netG_A.forward(fake_A) loss_cycle_B = self.criterionCycle(rec_B, self.real_B) * lambda_B - + # combined loss loss_G = loss_G_A + loss_G_B + loss_cycle_A + loss_cycle_B + loss_idt_A + loss_idt_B loss_G.backward() - + self.fake_B = fake_B.data self.fake_A = fake_A.data self.rec_A = rec_A.data self.rec_B = rec_B.data - + self.loss_G_A = loss_G_A.data[0] self.loss_G_B = loss_G_B.data[0] self.loss_cycle_A = loss_cycle_A.data[0] self.loss_cycle_B = loss_cycle_B.data[0] - - - def optimize_parameters(self): # forward @@ -200,36 +200,26 @@ class CycleGANModel(BaseModel): self.optimizer_D_B.step() def get_current_errors(self): - D_A = self.loss_D_A.data[0] - G_A = self.loss_G_A - Cyc_A = self.loss_cycle_A - D_B = self.loss_D_B.data[0] - G_B = self.loss_G_B - Cyc_B = self.loss_cycle_B + ret_errors = OrderedDict([('D_A', self.loss_D_A), ('G_A', self.loss_G_A), ('Cyc_A', self.loss_cycle_A), + ('D_B', self.loss_D_B), ('G_B', self.loss_G_B), ('Cyc_B', self.loss_cycle_B)]) if self.opt.identity > 0.0: - idt_A = self.loss_idt_A - idt_B = self.loss_idt_B - return OrderedDict([('D_A', D_A), ('G_A', G_A), ('Cyc_A', Cyc_A), ('idt_A', idt_A), - ('D_B', D_B), ('G_B', G_B), ('Cyc_B', Cyc_B), ('idt_B', idt_B)]) - else: - return OrderedDict([('D_A', D_A), ('G_A', G_A), ('Cyc_A', Cyc_A), - ('D_B', D_B), ('G_B', G_B), ('Cyc_B', Cyc_B)]) + ret_errors['idt_A'] = self.loss_idt_A + ret_errors['idt_B'] = self.loss_idt_B + return ret_errors def get_current_visuals(self): - real_A = util.tensor2im(self.real_A.data) - fake_B = util.tensor2im(self.fake_B.data) - rec_A = util.tensor2im(self.rec_A.data) - real_B = util.tensor2im(self.real_B.data) - fake_A = util.tensor2im(self.fake_A.data) - rec_B = util.tensor2im(self.rec_B.data) + real_A = util.tensor2im(self.input_A) + fake_B = util.tensor2im(self.fake_B) + rec_A = util.tensor2im(self.rec_A) + real_B = util.tensor2im(self.input_B) + fake_A = util.tensor2im(self.fake_A) + rec_B = util.tensor2im(self.rec_B) + ret_visuals = OrderedDict([('real_A', real_A), ('fake_B', fake_B), ('rec_A', rec_A), + ('real_B', real_B), ('fake_A', fake_A), ('rec_B', rec_B)]) if self.opt.isTrain and self.opt.identity > 0.0: - idt_A = util.tensor2im(self.idt_A) - idt_B = util.tensor2im(self.idt_B) - return OrderedDict([('real_A', real_A), ('fake_B', fake_B), ('rec_A', rec_A), ('idt_B', idt_B), - ('real_B', real_B), ('fake_A', fake_A), ('rec_B', rec_B), ('idt_A', idt_A)]) - else: - return OrderedDict([('real_A', real_A), ('fake_B', fake_B), ('rec_A', rec_A), - ('real_B', real_B), ('fake_A', fake_A), ('rec_B', rec_B)]) + ret_visuals['idt_A'] = util.tensor2im(self.idt_A) + ret_visuals['idt_B'] = util.tensor2im(self.idt_B) + return ret_visuals def save(self, label): self.save_network(self.netG_A, 'G_A', label, self.gpu_ids) diff --git a/models/networks.py b/models/networks.py index 949659d..d071ac4 100644 --- a/models/networks.py +++ b/models/networks.py @@ -118,7 +118,7 @@ def define_G(input_nc, output_nc, ngf, which_model_netG, norm='batch', use_dropo else: raise NotImplementedError('Generator model name [%s] is not recognized' % which_model_netG) if len(gpu_ids) > 0: - netG.cuda(device_id=gpu_ids[0]) # or netG.cuda(device=gpu_ids[0]) for latest version. + netG.cuda(device_id=gpu_ids[0]) # or netG.cuda(device=gpu_ids[0]) for latest version. init_weights(netG, init_type=init_type) return netG @@ -139,7 +139,7 @@ def define_D(input_nc, ndf, which_model_netD, raise NotImplementedError('Discriminator model name [%s] is not recognized' % which_model_netD) if use_gpu: - netD.cuda(device_id=gpu_ids[0]) # or netD.cuda(device=gpu_ids[0]) for latest version. + netD.cuda(device_id=gpu_ids[0]) # or netD.cuda(device=gpu_ids[0]) for latest version. init_weights(netD, init_type=init_type) return netD diff --git a/models/pix2pix_model.py b/models/pix2pix_model.py index 18ba53f..8cd494f 100644 --- a/models/pix2pix_model.py +++ b/models/pix2pix_model.py @@ -87,12 +87,12 @@ class Pix2PixModel(BaseModel): # Fake # stop backprop to the generator by detaching fake_B fake_AB = self.fake_AB_pool.query(torch.cat((self.real_A, self.fake_B), 1)) - self.pred_fake = self.netD.forward(fake_AB.detach()) - self.loss_D_fake = self.criterionGAN(self.pred_fake, False) + pred_fake = self.netD.forward(fake_AB.detach()) + self.loss_D_fake = self.criterionGAN(pred_fake, False) # Real real_AB = torch.cat((self.real_A, self.real_B), 1) - self.pred_real = self.netD.forward(real_AB) + pred_real = self.netD.forward(real_AB) self.loss_D_real = self.criterionGAN(self.pred_real, True) # Combined loss diff --git a/options/base_options.py b/options/base_options.py index b2d5360..28ca673 100644 --- a/options/base_options.py +++ b/options/base_options.py @@ -3,6 +3,7 @@ import os from util import util import torch + class BaseOptions(): def __init__(self): self.parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -33,7 +34,6 @@ class BaseOptions(): self.parser.add_argument('--display_winsize', type=int, default=256, help='display window size') self.parser.add_argument('--display_id', type=int, default=1, help='window id of the web display') self.parser.add_argument('--display_port', type=int, default=8097, help='visdom port of the web display') - self.parser.add_argument('--display_single_pane_ncols', type=int, default=0, help='if positive, display all images in a single visdom web panel with certain number of images per row.') self.parser.add_argument('--no_dropout', action='store_true', help='no dropout for the generator') self.parser.add_argument('--max_dataset_size', type=int, default=float("inf"), help='Maximum number of samples allowed per dataset. If the dataset directory contains more than max_dataset_size, only a subset is loaded.') self.parser.add_argument('--resize_or_crop', type=str, default='resize_and_crop', help='scaling and cropping of images at load time [resize_and_crop|crop|scale_width|scale_width_and_crop]') diff --git a/options/train_options.py b/options/train_options.py index 32120ec..603d76a 100644 --- a/options/train_options.py +++ b/options/train_options.py @@ -5,6 +5,8 @@ class TrainOptions(BaseOptions): def initialize(self): BaseOptions.initialize(self) self.parser.add_argument('--display_freq', type=int, default=100, help='frequency of showing training results on screen') + self.parser.add_argument('--display_single_pane_ncols', type=int, default=0, help='if positive, display all images in a single visdom web panel with certain number of images per row.') + self.parser.add_argument('--update_html_freq', type=int, default=1000, help='frequency of saving training results to html') self.parser.add_argument('--print_freq', type=int, default=100, help='frequency of showing training results on console') self.parser.add_argument('--save_latest_freq', type=int, default=5000, help='frequency of saving the latest results') self.parser.add_argument('--save_epoch_freq', type=int, default=5, help='frequency of saving checkpoints at the end of epochs') @@ -23,6 +25,6 @@ class TrainOptions(BaseOptions): self.parser.add_argument('--no_html', action='store_true', help='do not save intermediate training results to [opt.checkpoints_dir]/[opt.name]/web/') self.parser.add_argument('--lr_policy', type=str, default='lambda', help='learning rate policy: lambda|step|plateau') self.parser.add_argument('--lr_decay_iters', type=int, default=50, help='multiply by a gamma every lr_decay_iters iterations') - self.parser.add_argument('--identity', type=float, default=0.0, help='use identity mapping. Setting identity other than 1 has an effect of scaling the weight of the identity mapping loss. For example, if the weight of the identity loss should be 10 times smaller than the weight of the reconstruction loss, please set optidentity = 0.1') + self.parser.add_argument('--identity', type=float, default=0.5, help='use identity mapping. Setting identity other than 1 has an effect of scaling the weight of the identity mapping loss. For example, if the weight of the identity loss should be 10 times smaller than the weight of the reconstruction loss, please set optidentity = 0.1') self.isTrain = True diff --git a/train.py b/train.py index 7d2a5e9..6dbd66b 100644 --- a/train.py +++ b/train.py @@ -20,13 +20,15 @@ for epoch in range(opt.epoch_count, opt.niter + opt.niter_decay + 1): for i, data in enumerate(dataset): iter_start_time = time.time() + visualizer.reset() total_steps += opt.batchSize epoch_iter += opt.batchSize model.set_input(data) model.optimize_parameters() if total_steps % opt.display_freq == 0: - visualizer.display_current_results(model.get_current_visuals(), epoch) + save_result = total_steps % opt.update_html_freq == 0 + visualizer.display_current_results(model.get_current_visuals(), epoch, save_result) if total_steps % opt.print_freq == 0: errors = model.get_current_errors() diff --git a/util/image_pool.py b/util/image_pool.py index 9f34a09..5a242e6 100644 --- a/util/image_pool.py +++ b/util/image_pool.py @@ -2,6 +2,8 @@ import random import numpy as np import torch from torch.autograd import Variable + + class ImagePool(): def __init__(self, pool_size): self.pool_size = pool_size diff --git a/util/visualizer.py b/util/visualizer.py index 02a36b7..22fe9da 100644 --- a/util/visualizer.py +++ b/util/visualizer.py @@ -4,7 +4,8 @@ import ntpath import time from . import util from . import html -from pdb import set_trace as st + + class Visualizer(): def __init__(self, opt): # self.opt = opt @@ -12,9 +13,10 @@ class Visualizer(): self.use_html = opt.isTrain and not opt.no_html self.win_size = opt.display_winsize self.name = opt.name + self.saved = False if self.display_id > 0: import visdom - self.vis = visdom.Visdom(port = opt.display_port) + self.vis = visdom.Visdom(port=opt.display_port) self.display_single_pane_ncols = opt.display_single_pane_ncols if self.use_html: @@ -27,15 +29,18 @@ class Visualizer(): now = time.strftime("%c") log_file.write('================ Training Loss (%s) ================\n' % now) + def reset(self): + self.saved = False + # |visuals|: dictionary of images to display or save - def display_current_results(self, visuals, epoch): - if self.display_id > 0: # show images in the browser + def display_current_results(self, visuals, epoch, save_result): + if self.display_id > 0: # show images in the browser if self.display_single_pane_ncols > 0: h, w = next(iter(visuals.values())).shape[:2] table_css = """""" % (w, h) + table {border-collapse: separate; border-spacing:4px; white-space:nowrap; text-align:center} + table td {width: %dpx; height: %dpx; padding: 4px; outline: 4px solid black} + """ % (w, h) ncols = self.display_single_pane_ncols title = self.name label_html = '' @@ -61,16 +66,17 @@ class Visualizer(): self.vis.images(images, nrow=ncols, win=self.display_id + 1, padding=2, opts=dict(title=title + ' images')) label_html = '%s
' % label_html - self.vis.text(table_css + label_html, win = self.display_id + 2, + self.vis.text(table_css + label_html, win=self.display_id + 2, opts=dict(title=title + ' labels')) else: idx = 1 for label, image_numpy in visuals.items(): - self.vis.image(image_numpy.transpose([2,0,1]), opts=dict(title=label), - win=self.display_id + idx) + self.vis.image(image_numpy.transpose([2, 0, 1]), opts=dict(title=label), + win=self.display_id + idx) idx += 1 - if self.use_html: # save images to a html file + if self.use_html and (save_result or not self.saved): # save images to a html file + self.saved = True for label, image_numpy in visuals.items(): img_path = os.path.join(self.img_dir, 'epoch%.3d_%s.png' % (epoch, label)) util.save_image(image_numpy, img_path) @@ -93,11 +99,11 @@ class Visualizer(): # errors: dictionary of error labels and values def plot_current_errors(self, epoch, counter_ratio, opt, errors): if not hasattr(self, 'plot_data'): - self.plot_data = {'X':[],'Y':[], 'legend':list(errors.keys())} + self.plot_data = {'X': [], 'Y': [], 'legend': list(errors.keys())} self.plot_data['X'].append(epoch + counter_ratio) self.plot_data['Y'].append([errors[k] for k in self.plot_data['legend']]) self.vis.line( - X=np.stack([np.array(self.plot_data['X'])]*len(self.plot_data['legend']),1), + X=np.stack([np.array(self.plot_data['X'])] * len(self.plot_data['legend']), 1), Y=np.array(self.plot_data['Y']), opts={ 'title': self.name + ' loss over time', -- cgit v1.2.3-70-g09d2 From 7a9021d4f131ee059d49ff9b2d135e6543f75763 Mon Sep 17 00:00:00 2001 From: junyanz Date: Sat, 4 Nov 2017 02:47:39 -0700 Subject: fix small issues --- models/pix2pix_model.py | 4 ++-- util/image_pool.py | 2 +- util/visualizer.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/models/pix2pix_model.py b/models/pix2pix_model.py index 8cd494f..388a8d3 100644 --- a/models/pix2pix_model.py +++ b/models/pix2pix_model.py @@ -86,14 +86,14 @@ class Pix2PixModel(BaseModel): def backward_D(self): # Fake # stop backprop to the generator by detaching fake_B - fake_AB = self.fake_AB_pool.query(torch.cat((self.real_A, self.fake_B), 1)) + fake_AB = self.fake_AB_pool.query(torch.cat((self.real_A, self.fake_B), 1).data) pred_fake = self.netD.forward(fake_AB.detach()) self.loss_D_fake = self.criterionGAN(pred_fake, False) # Real real_AB = torch.cat((self.real_A, self.real_B), 1) pred_real = self.netD.forward(real_AB) - self.loss_D_real = self.criterionGAN(self.pred_real, True) + self.loss_D_real = self.criterionGAN(pred_real, True) # Combined loss self.loss_D = (self.loss_D_fake + self.loss_D_real) * 0.5 diff --git a/util/image_pool.py b/util/image_pool.py index 5a242e6..ada1627 100644 --- a/util/image_pool.py +++ b/util/image_pool.py @@ -13,7 +13,7 @@ class ImagePool(): def query(self, images): if self.pool_size == 0: - return images + return Variable(images) return_images = [] for image in images: image = torch.unsqueeze(image, 0) diff --git a/util/visualizer.py b/util/visualizer.py index 22fe9da..e6e7cba 100644 --- a/util/visualizer.py +++ b/util/visualizer.py @@ -13,11 +13,11 @@ class Visualizer(): self.use_html = opt.isTrain and not opt.no_html self.win_size = opt.display_winsize self.name = opt.name + self.opt = opt self.saved = False if self.display_id > 0: import visdom self.vis = visdom.Visdom(port=opt.display_port) - self.display_single_pane_ncols = opt.display_single_pane_ncols if self.use_html: self.web_dir = os.path.join(opt.checkpoints_dir, opt.name, 'web') @@ -35,13 +35,13 @@ class Visualizer(): # |visuals|: dictionary of images to display or save def display_current_results(self, visuals, epoch, save_result): if self.display_id > 0: # show images in the browser - if self.display_single_pane_ncols > 0: + ncols = self.opt.display_single_pane_ncols + if ncols > 0: h, w = next(iter(visuals.values())).shape[:2] table_css = """""" % (w, h) - ncols = self.display_single_pane_ncols title = self.name label_html = '' label_html_row = '' @@ -76,7 +76,7 @@ class Visualizer(): idx += 1 if self.use_html and (save_result or not self.saved): # save images to a html file - self.saved = True + self.saved = True for label, image_numpy in visuals.items(): img_path = os.path.join(self.img_dir, 'epoch%.3d_%s.png' % (epoch, label)) util.save_image(image_numpy, img_path) -- cgit v1.2.3-70-g09d2 From 276a568218c0d5331d8abb87d141a5c0f4bf6b3f Mon Sep 17 00:00:00 2001 From: taesung89 Date: Sun, 5 Nov 2017 01:03:05 +0900 Subject: Update README.md Replaced the misleading description of applying pretrained CycleGAN model. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 455c32a..ad371cb 100644 --- a/README.md +++ b/README.md @@ -123,12 +123,14 @@ The test results will be saved to a html file here: `./results/facades_pix2pix/l More example scripts can be found at `scripts` directory. ### Apply a pre-trained model (CycleGAN) -If you would like to apply a pre-trained model to a collection of input photos (without image pairs), please use `--dataset_mode single` and `--model test` options. Here is a script to apply a pix2pix model to facade label maps (stored in the directory `facades/testB`). +If you would like to apply a pre-trained model to a collection of input photos (without image pairs), please use `--dataset_mode single` and `--model test` options. Here is a script to apply a model to facade label maps (stored in the directory `facades/testB`). ``` bash #!./scripts/test_single.sh -python test.py --dataroot ./datasets/facades/testB/ --name facades_pix2pix --model test --which_model_netG unet_256 --which_direction BtoA --dataset_mode single +python test.py --dataroot ./datasets/facades/testA/ --name {my_trained_model_name} --model test --dataset_mode single ``` +You might have to specify `--which_model_netG` to match the generator architecture of the trained model. + Note: We currently don't have pretrained models using PyTorch. This is in part because the models trained using Torch and PyTorch produce slightly different results, although we were not able to decide which result is better. If you would like to generate the same results that appeared in our paper, we recommend using the pretrained models in the Torch codebase. ### Apply a pre-trained model (pix2pix) -- cgit v1.2.3-70-g09d2 From b546d99d32d377287f7cfa9130aac1a3b1c980c2 Mon Sep 17 00:00:00 2001 From: taesung89 Date: Sun, 5 Nov 2017 01:03:55 +0900 Subject: Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ad371cb..87e40ea 100644 --- a/README.md +++ b/README.md @@ -123,13 +123,13 @@ The test results will be saved to a html file here: `./results/facades_pix2pix/l More example scripts can be found at `scripts` directory. ### Apply a pre-trained model (CycleGAN) -If you would like to apply a pre-trained model to a collection of input photos (without image pairs), please use `--dataset_mode single` and `--model test` options. Here is a script to apply a model to facade label maps (stored in the directory `facades/testB`). +If you would like to apply a pre-trained model to a collection of input photos (without image pairs), please use `--dataset_mode single` and `--model test` options. Here is a script to apply a model to Facade label maps (stored in the directory `facades/testB`). ``` bash #!./scripts/test_single.sh python test.py --dataroot ./datasets/facades/testA/ --name {my_trained_model_name} --model test --dataset_mode single ``` -You might have to specify `--which_model_netG` to match the generator architecture of the trained model. +You might want to specify `--which_model_netG` to match the generator architecture of the trained model. Note: We currently don't have pretrained models using PyTorch. This is in part because the models trained using Torch and PyTorch produce slightly different results, although we were not able to decide which result is better. If you would like to generate the same results that appeared in our paper, we recommend using the pretrained models in the Torch codebase. -- cgit v1.2.3-70-g09d2 From 197b38ba8885483445becb10d08f9dfa2ce55fe5 Mon Sep 17 00:00:00 2001 From: Jun-Yan Zhu Date: Sat, 4 Nov 2017 22:24:59 -0700 Subject: Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 87e40ea..ffd60fc 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ In CVPR 2017. [Tensorflow] (by Van Huy), [Tensorflow] (by Xiaowei Hu), [Tensorflow-simple] (by Zhenliang He), + [TensorLayer] (by luoxier), [Chainer] (by Yanghua Jin), [Minimal PyTorch] (by yunjey), [Mxnet] (by Ldpe2G), -- cgit v1.2.3-70-g09d2 From a24e24d67d88f75869f447690f7d994fe7d42e2d Mon Sep 17 00:00:00 2001 From: junyanz Date: Sun, 5 Nov 2017 21:53:51 -0800 Subject: fix typo in the cyclegan --- models/cycle_gan_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/cycle_gan_model.py b/models/cycle_gan_model.py index 71a447d..e840e7b 100644 --- a/models/cycle_gan_model.py +++ b/models/cycle_gan_model.py @@ -98,7 +98,7 @@ class CycleGANModel(BaseModel): real_B = Variable(self.input_B, volatile=True) fake_A = self.netG_B.forward(real_B) self.rec_B = self.netG_A.forward(fake_A).data - self.fake_A = self.fake_A.data + self.fake_A = fake_A.data # get image paths def get_image_paths(self): -- cgit v1.2.3-70-g09d2 From c2fc8d442f1248231eab4b73e111665288b1e615 Mon Sep 17 00:00:00 2001 From: SsnL Date: Thu, 9 Nov 2017 16:08:30 -0500 Subject: update --- models/base_model.py | 2 +- models/cycle_gan_model.py | 30 ++++++++++++++---------------- models/networks.py | 4 ++-- models/pix2pix_model.py | 10 +++++----- models/test_model.py | 2 +- 5 files changed, 23 insertions(+), 25 deletions(-) diff --git a/models/base_model.py b/models/base_model.py index 646a014..9b55afe 100644 --- a/models/base_model.py +++ b/models/base_model.py @@ -44,7 +44,7 @@ class BaseModel(): save_path = os.path.join(self.save_dir, save_filename) torch.save(network.cpu().state_dict(), save_path) if len(gpu_ids) and torch.cuda.is_available(): - network.cuda(device_id=gpu_ids[0]) # network.cuda(device=gpu_ids[0]) for the latest version. + network.cuda(gpu_ids[0]) # helper loading function that can be used by subclasses def load_network(self, network, network_label, epoch_label): diff --git a/models/cycle_gan_model.py b/models/cycle_gan_model.py index e840e7b..fe06823 100644 --- a/models/cycle_gan_model.py +++ b/models/cycle_gan_model.py @@ -91,13 +91,13 @@ class CycleGANModel(BaseModel): def test(self): real_A = Variable(self.input_A, volatile=True) - fake_B = self.netG_A.forward(real_A) - self.rec_A = self.netG_B.forward(fake_B).data + fake_B = self.netG_A(real_A) + self.rec_A = self.netG_B(fake_B).data self.fake_B = fake_B.data real_B = Variable(self.input_B, volatile=True) - fake_A = self.netG_B.forward(real_B) - self.rec_B = self.netG_A.forward(fake_A).data + fake_A = self.netG_B(real_B) + self.rec_B = self.netG_A(fake_A).data self.fake_A = fake_A.data # get image paths @@ -106,10 +106,10 @@ class CycleGANModel(BaseModel): def backward_D_basic(self, netD, real, fake): # Real - pred_real = netD.forward(real) + pred_real = netD(real) loss_D_real = self.criterionGAN(pred_real, True) # Fake - pred_fake = netD.forward(fake.detach()) + pred_fake = netD(fake.detach()) loss_D_fake = self.criterionGAN(pred_fake, False) # Combined loss loss_D = (loss_D_real + loss_D_fake) * 0.5 @@ -134,17 +134,16 @@ class CycleGANModel(BaseModel): # Identity loss if lambda_idt > 0: # G_A should be identity if real_B is fed. - idt_A = self.netG_A.forward(self.real_B) + idt_A = self.netG_A(self.real_B) loss_idt_A = self.criterionIdt(idt_A, self.real_B) * lambda_B * lambda_idt # G_B should be identity if real_A is fed. - idt_B = self.netG_B.forward(self.real_A) + idt_B = self.netG_B(self.real_A) loss_idt_B = self.criterionIdt(idt_B, self.real_A) * lambda_A * lambda_idt self.idt_A = idt_A.data self.idt_B = idt_B.data self.loss_idt_A = loss_idt_A.data[0] self.loss_idt_B = loss_idt_B.data[0] - else: loss_idt_A = 0 loss_idt_B = 0 @@ -152,23 +151,22 @@ class CycleGANModel(BaseModel): self.loss_idt_B = 0 # GAN loss D_A(G_A(A)) - fake_B = self.netG_A.forward(self.real_A) - pred_fake = self.netD_A.forward(fake_B) + fake_B = self.netG_A(self.real_A) + pred_fake = self.netD_A(fake_B) loss_G_A = self.criterionGAN(pred_fake, True) # GAN loss D_B(G_B(B)) - fake_A = self.netG_B.forward(self.real_B) - pred_fake = self.netD_B.forward(fake_A) + fake_A = self.netG_B(self.real_B) + pred_fake = self.netD_B(fake_A) loss_G_B = self.criterionGAN(pred_fake, True) # Forward cycle loss - rec_A = self.netG_B.forward(fake_B) + rec_A = self.netG_B(fake_B) loss_cycle_A = self.criterionCycle(rec_A, self.real_A) * lambda_A # Backward cycle loss - rec_B = self.netG_A.forward(fake_A) + rec_B = self.netG_A(fake_A) loss_cycle_B = self.criterionCycle(rec_B, self.real_B) * lambda_B - # combined loss loss_G = loss_G_A + loss_G_B + loss_cycle_A + loss_cycle_B + loss_idt_A + loss_idt_B loss_G.backward() diff --git a/models/networks.py b/models/networks.py index d071ac4..ec6573b 100644 --- a/models/networks.py +++ b/models/networks.py @@ -118,7 +118,7 @@ def define_G(input_nc, output_nc, ngf, which_model_netG, norm='batch', use_dropo else: raise NotImplementedError('Generator model name [%s] is not recognized' % which_model_netG) if len(gpu_ids) > 0: - netG.cuda(device_id=gpu_ids[0]) # or netG.cuda(device=gpu_ids[0]) for latest version. + netG.cuda(gpu_ids[0]) init_weights(netG, init_type=init_type) return netG @@ -139,7 +139,7 @@ def define_D(input_nc, ndf, which_model_netD, raise NotImplementedError('Discriminator model name [%s] is not recognized' % which_model_netD) if use_gpu: - netD.cuda(device_id=gpu_ids[0]) # or netD.cuda(device=gpu_ids[0]) for latest version. + netD.cuda(gpu_ids[0]) init_weights(netD, init_type=init_type) return netD diff --git a/models/pix2pix_model.py b/models/pix2pix_model.py index 388a8d3..56adfc1 100644 --- a/models/pix2pix_model.py +++ b/models/pix2pix_model.py @@ -70,13 +70,13 @@ class Pix2PixModel(BaseModel): def forward(self): self.real_A = Variable(self.input_A) - self.fake_B = self.netG.forward(self.real_A) + self.fake_B = self.netG(self.real_A) self.real_B = Variable(self.input_B) # no backprop gradients def test(self): self.real_A = Variable(self.input_A, volatile=True) - self.fake_B = self.netG.forward(self.real_A) + self.fake_B = self.netG(self.real_A) self.real_B = Variable(self.input_B, volatile=True) # get image paths @@ -87,12 +87,12 @@ class Pix2PixModel(BaseModel): # Fake # stop backprop to the generator by detaching fake_B fake_AB = self.fake_AB_pool.query(torch.cat((self.real_A, self.fake_B), 1).data) - pred_fake = self.netD.forward(fake_AB.detach()) + pred_fake = self.netD(fake_AB.detach()) self.loss_D_fake = self.criterionGAN(pred_fake, False) # Real real_AB = torch.cat((self.real_A, self.real_B), 1) - pred_real = self.netD.forward(real_AB) + pred_real = self.netD(real_AB) self.loss_D_real = self.criterionGAN(pred_real, True) # Combined loss @@ -103,7 +103,7 @@ class Pix2PixModel(BaseModel): def backward_G(self): # First, G(A) should fake the discriminator fake_AB = torch.cat((self.real_A, self.fake_B), 1) - pred_fake = self.netD.forward(fake_AB) + pred_fake = self.netD(fake_AB) self.loss_G_GAN = self.criterionGAN(pred_fake, True) # Second, G(A) = B diff --git a/models/test_model.py b/models/test_model.py index 4af1fe1..2ae2812 100644 --- a/models/test_model.py +++ b/models/test_model.py @@ -34,7 +34,7 @@ class TestModel(BaseModel): def test(self): self.real_A = Variable(self.input_A) - self.fake_B = self.netG.forward(self.real_A) + self.fake_B = self.netG(self.real_A) # get image paths def get_image_paths(self): -- cgit v1.2.3-70-g09d2 From 59fb33ba65e0714926304aec444c92a55ab03a49 Mon Sep 17 00:00:00 2001 From: SsnL Date: Thu, 9 Nov 2017 17:41:51 -0500 Subject: fix max_dataset_size --- data/custom_dataset_data_loader.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/data/custom_dataset_data_loader.py b/data/custom_dataset_data_loader.py index 60180e0..787946f 100644 --- a/data/custom_dataset_data_loader.py +++ b/data/custom_dataset_data_loader.py @@ -35,7 +35,13 @@ class CustomDatasetDataLoader(BaseDataLoader): num_workers=int(opt.nThreads)) def load_data(self): - return self.dataloader + return self def __len__(self): return min(len(self.dataset), self.opt.max_dataset_size) + + def __iter__(self): + for i, data in enumerate(self.dataloader): + if i >= self.opt.max_dataset_size: + break + yield data -- cgit v1.2.3-70-g09d2 From 1171d57b5db2d997a5ba3739e680af261f3a2e8a Mon Sep 17 00:00:00 2001 From: Taesung Park Date: Mon, 27 Nov 2017 22:05:28 -0800 Subject: fixed a bug in initialization of weights --- models/networks.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/models/networks.py b/models/networks.py index ec6573b..e6e0a87 100644 --- a/models/networks.py +++ b/models/networks.py @@ -14,11 +14,11 @@ def weights_init_normal(m): classname = m.__class__.__name__ # print(classname) if classname.find('Conv') != -1: - init.uniform(m.weight.data, 0.0, 0.02) + init.normal(m.weight.data, 0.0, 0.02) elif classname.find('Linear') != -1: - init.uniform(m.weight.data, 0.0, 0.02) + init.normal(m.weight.data, 0.0, 0.02) elif classname.find('BatchNorm2d') != -1: - init.uniform(m.weight.data, 1.0, 0.02) + init.normal(m.weight.data, 1.0, 0.02) init.constant(m.bias.data, 0.0) @@ -30,7 +30,7 @@ def weights_init_xavier(m): elif classname.find('Linear') != -1: init.xavier_normal(m.weight.data, gain=1) elif classname.find('BatchNorm2d') != -1: - init.uniform(m.weight.data, 1.0, 0.02) + init.normal(m.weight.data, 1.0, 0.02) init.constant(m.bias.data, 0.0) @@ -42,7 +42,7 @@ def weights_init_kaiming(m): elif classname.find('Linear') != -1: init.kaiming_normal(m.weight.data, a=0, mode='fan_in') elif classname.find('BatchNorm2d') != -1: - init.uniform(m.weight.data, 1.0, 0.02) + init.normal(m.weight.data, 1.0, 0.02) init.constant(m.bias.data, 0.0) @@ -54,7 +54,7 @@ def weights_init_orthogonal(m): elif classname.find('Linear') != -1: init.orthogonal(m.weight.data, gain=1) elif classname.find('BatchNorm2d') != -1: - init.uniform(m.weight.data, 1.0, 0.02) + init.normal(m.weight.data, 1.0, 0.02) init.constant(m.bias.data, 0.0) -- cgit v1.2.3-70-g09d2 From 1615932f9180a7a9df92f33fbb8749aec432d3d9 Mon Sep 17 00:00:00 2001 From: Taesung Park Date: Mon, 27 Nov 2017 22:07:02 -0800 Subject: changed default weight initialization to normal --- options/base_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/base_options.py b/options/base_options.py index 28ca673..13466bf 100644 --- a/options/base_options.py +++ b/options/base_options.py @@ -38,7 +38,7 @@ class BaseOptions(): self.parser.add_argument('--max_dataset_size', type=int, default=float("inf"), help='Maximum number of samples allowed per dataset. If the dataset directory contains more than max_dataset_size, only a subset is loaded.') self.parser.add_argument('--resize_or_crop', type=str, default='resize_and_crop', help='scaling and cropping of images at load time [resize_and_crop|crop|scale_width|scale_width_and_crop]') self.parser.add_argument('--no_flip', action='store_true', help='if specified, do not flip the images for data augmentation') - self.parser.add_argument('--init_type', type=str, default='xavier', help='network initialization [normal|xavier|kaiming|orthogonal]') + self.parser.add_argument('--init_type', type=str, default='normal', help='network initialization [normal|xavier|kaiming|orthogonal]') self.initialized = True -- cgit v1.2.3-70-g09d2 From 7bda734dd7f3466d5d55afe80b97542b1b12bdb5 Mon Sep 17 00:00:00 2001 From: Taesung Park Date: Fri, 1 Dec 2017 22:13:51 -0800 Subject: changed the gain of xavier initialization from 1 to 0.02. implemented serial_batches option in unaligned dataset --- data/unaligned_dataset.py | 5 ++++- models/networks.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/data/unaligned_dataset.py b/data/unaligned_dataset.py index c5e5460..ad0c11b 100644 --- a/data/unaligned_dataset.py +++ b/data/unaligned_dataset.py @@ -25,7 +25,10 @@ class UnalignedDataset(BaseDataset): def __getitem__(self, index): A_path = self.A_paths[index % self.A_size] index_A = index % self.A_size - index_B = random.randint(0, self.B_size - 1) + if self.opt.serial_batches: + index_B = index % self.B_size + else: + index_B = random.randint(0, self.B_size - 1) B_path = self.B_paths[index_B] # print('(A, B) = (%d, %d)' % (index_A, index_B)) A_img = Image.open(A_path).convert('RGB') diff --git a/models/networks.py b/models/networks.py index e6e0a87..3c54138 100644 --- a/models/networks.py +++ b/models/networks.py @@ -26,9 +26,9 @@ def weights_init_xavier(m): classname = m.__class__.__name__ # print(classname) if classname.find('Conv') != -1: - init.xavier_normal(m.weight.data, gain=1) + init.xavier_normal(m.weight.data, gain=0.02) elif classname.find('Linear') != -1: - init.xavier_normal(m.weight.data, gain=1) + init.xavier_normal(m.weight.data, gain=0.02) elif classname.find('BatchNorm2d') != -1: init.normal(m.weight.data, 1.0, 0.02) init.constant(m.bias.data, 0.0) -- cgit v1.2.3-70-g09d2 From 2d96edbee5a488a7861833731a2cb71b23b55727 Mon Sep 17 00:00:00 2001 From: taesung89 Date: Sun, 10 Dec 2017 22:56:39 -0800 Subject: Update README.md --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ffd60fc..788ad0f 100644 --- a/README.md +++ b/README.md @@ -124,15 +124,27 @@ The test results will be saved to a html file here: `./results/facades_pix2pix/l More example scripts can be found at `scripts` directory. ### Apply a pre-trained model (CycleGAN) + If you would like to apply a pre-trained model to a collection of input photos (without image pairs), please use `--dataset_mode single` and `--model test` options. Here is a script to apply a model to Facade label maps (stored in the directory `facades/testB`). ``` bash #!./scripts/test_single.sh python test.py --dataroot ./datasets/facades/testA/ --name {my_trained_model_name} --model test --dataset_mode single ``` - You might want to specify `--which_model_netG` to match the generator architecture of the trained model. -Note: We currently don't have pretrained models using PyTorch. This is in part because the models trained using Torch and PyTorch produce slightly different results, although we were not able to decide which result is better. If you would like to generate the same results that appeared in our paper, we recommend using the pretrained models in the Torch codebase. +You can download a few pretrained models from the authors. For example, if you would like to download horse2zebra model, + +```bash +bash pretrained_models/download_cyclegan_model.sh horse2zebra +``` +The pretrained model is saved at `./checkpoints/{name}_pretrained/latest_net_G.pth`. +Then generate the results using + +```bash +python test.py --dataroot datasets/horse2zebra/testA --checkpoints_dir ./checkpoints/ --name horse2zebra_pretrained --no_dropout --model test --dataset_mode single --loadSize 256 --results_dir {directory_path_to_save_result} +``` + +Note: We currently don't have all pretrained models using PyTorch. This is in part because the models trained using Torch and PyTorch produce slightly different results, although we were not able to decide which result is better. If you would like to generate the same results that appeared in our paper, we recommend using the pretrained models in the Torch codebase. ### Apply a pre-trained model (pix2pix) -- cgit v1.2.3-70-g09d2