Files changed (1) hide show
  1. app.py +278 -0
app.py ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import print_function
2
+ #%matplotlib inline
3
+ import argparse
4
+ import os
5
+ import random
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.parallel
9
+ import torch.backends.cudnn as cudnn
10
+ import torch.optim as optim
11
+ import torch.utils.data
12
+ import torchvision.datasets as dset
13
+ import torchvision.transforms as transforms
14
+ import torchvision.utils as vutils
15
+ import numpy as np
16
+ import matplotlib.pyplot as plt
17
+ import matplotlib.animation as animation
18
+ from IPython.display import HTML
19
+
20
+ # Set random seed for reproducibility
21
+ manualSeed = 999
22
+ #manualSeed = random.randint(1, 10000) # use if you want new results
23
+ print("Random Seed: ", manualSeed)
24
+ random.seed(manualSeed)
25
+ torch.manual_seed(manualSeed)
26
+ # Root directory for dataset
27
+ dataroot = "data/celeba"
28
+
29
+ # Number of workers for dataloader
30
+ workers = 2
31
+
32
+ # Batch size during training
33
+ batch_size = 128
34
+
35
+ # Spatial size of training images. All images will be resized to this
36
+ # size using a transformer.
37
+ image_size = 64
38
+
39
+ # Number of channels in the training images. For color images this is 3
40
+ nc = 3
41
+
42
+ # Size of z latent vector (i.e. size of generator input)
43
+ nz = 100
44
+
45
+ # Size of feature maps in generator
46
+ ngf = 64
47
+
48
+ # Size of feature maps in discriminator
49
+ ndf = 64
50
+
51
+ # Number of training epochs
52
+ num_epochs = 5
53
+
54
+ # Learning rate for optimizers
55
+ lr = 0.0002
56
+
57
+ # Beta1 hyperparam for Adam optimizers
58
+ beta1 = 0.5
59
+
60
+ # Number of GPUs available. Use 0 for CPU mode.
61
+ ngpu = 1
62
+ /path/to/celeba
63
+ -> img_align_celeba
64
+ -> 188242.jpg
65
+ -> 173822.jpg
66
+ -> 284702.jpg
67
+ -> 537394.jpg
68
+ ...
69
+ # We can use an image folder dataset the way we have it setup.
70
+ # Create the dataset
71
+ dataset = dset.ImageFolder(root=dataroot,
72
+ transform=transforms.Compose([
73
+ transforms.Resize(image_size),
74
+ transforms.CenterCrop(image_size),
75
+ transforms.ToTensor(),
76
+ transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
77
+ ]))
78
+ # Create the dataloader
79
+ dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,
80
+ shuffle=True, num_workers=workers)
81
+
82
+ # Decide which device we want to run on
83
+ device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu")
84
+
85
+ # Plot some training images
86
+ real_batch = next(iter(dataloader))
87
+ plt.figure(figsize=(8,8))
88
+ plt.axis("off")
89
+ plt.title("Training Images")
90
+ plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=2, normalize=True).cpu(),(1,2,0)))
91
+ # custom weights initialization called on netG and netD
92
+ def weights_init(m):
93
+ classname = m.__class__.__name__
94
+ if classname.find('Conv') != -1:
95
+ nn.init.normal_(m.weight.data, 0.0, 0.02)
96
+ elif classname.find('BatchNorm') != -1:
97
+ nn.init.normal_(m.weight.data, 1.0, 0.02)
98
+ nn.init.constant_(m.bias.data, 0)
99
+ # Generator Code
100
+
101
+ class Generator(nn.Module):
102
+ def __init__(self, ngpu):
103
+ super(Generator, self).__init__()
104
+ self.ngpu = ngpu
105
+ self.main = nn.Sequential(
106
+ # input is Z, going into a convolution
107
+ nn.ConvTranspose2d( nz, ngf * 8, 4, 1, 0, bias=False),
108
+ nn.BatchNorm2d(ngf * 8),
109
+ nn.ReLU(True),
110
+ # state size. (ngf*8) x 4 x 4
111
+ nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
112
+ nn.BatchNorm2d(ngf * 4),
113
+ nn.ReLU(True),
114
+ # state size. (ngf*4) x 8 x 8
115
+ nn.ConvTranspose2d( ngf * 4, ngf * 2, 4, 2, 1, bias=False),
116
+ nn.BatchNorm2d(ngf * 2),
117
+ nn.ReLU(True),
118
+ # state size. (ngf*2) x 16 x 16
119
+ nn.ConvTranspose2d( ngf * 2, ngf, 4, 2, 1, bias=False),
120
+ nn.BatchNorm2d(ngf),
121
+ nn.ReLU(True),
122
+ # state size. (ngf) x 32 x 32
123
+ nn.ConvTranspose2d( ngf, nc, 4, 2, 1, bias=False),
124
+ nn.Tanh()
125
+ # state size. (nc) x 64 x 64
126
+ )
127
+
128
+ def forward(self, input):
129
+ return self.main(input)
130
+ # Create the generator
131
+ netG = Generator(ngpu).to(device)
132
+
133
+ # Handle multi-gpu if desired
134
+ if (device.type == 'cuda') and (ngpu > 1):
135
+ netG = nn.DataParallel(netG, list(range(ngpu)))
136
+
137
+ # Apply the weights_init function to randomly initialize all weights
138
+ # to mean=0, stdev=0.02.
139
+ netG.apply(weights_init)
140
+
141
+ # Print the model
142
+ print(netG)
143
+ class Discriminator(nn.Module):
144
+ def __init__(self, ngpu):
145
+ super(Discriminator, self).__init__()
146
+ self.ngpu = ngpu
147
+ self.main = nn.Sequential(
148
+ # input is (nc) x 64 x 64
149
+ nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
150
+ nn.LeakyReLU(0.2, inplace=True),
151
+ # state size. (ndf) x 32 x 32
152
+ nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
153
+ nn.BatchNorm2d(ndf * 2),
154
+ nn.LeakyReLU(0.2, inplace=True),
155
+ # state size. (ndf*2) x 16 x 16
156
+ nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
157
+ nn.BatchNorm2d(ndf * 4),
158
+ nn.LeakyReLU(0.2, inplace=True),
159
+ # state size. (ndf*4) x 8 x 8
160
+ nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
161
+ nn.BatchNorm2d(ndf * 8),
162
+ nn.LeakyReLU(0.2, inplace=True),
163
+ # state size. (ndf*8) x 4 x 4
164
+ nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
165
+ nn.Sigmoid()
166
+ )
167
+
168
+ def forward(self, input):
169
+ return self.main(input)
170
+ # Create the Discriminator
171
+ netD = Discriminator(ngpu).to(device)
172
+
173
+ # Handle multi-gpu if desired
174
+ if (device.type == 'cuda') and (ngpu > 1):
175
+ netD = nn.DataParallel(netD, list(range(ngpu)))
176
+
177
+ # Apply the weights_init function to randomly initialize all weights
178
+ # to mean=0, stdev=0.2.
179
+ netD.apply(weights_init)
180
+
181
+ # Print the model
182
+ print(netD)
183
+ # Initialize BCELoss function
184
+ criterion = nn.BCELoss()
185
+
186
+ # Create batch of latent vectors that we will use to visualize
187
+ # the progression of the generator
188
+ fixed_noise = torch.randn(64, nz, 1, 1, device=device)
189
+
190
+ # Establish convention for real and fake labels during training
191
+ real_label = 1.
192
+ fake_label = 0.
193
+
194
+ # Setup Adam optimizers for both G and D
195
+ optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))
196
+ optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))
197
+ # Training Loop
198
+
199
+ # Lists to keep track of progress
200
+ img_list = []
201
+ G_losses = []
202
+ D_losses = []
203
+ iters = 0
204
+
205
+ print("Starting Training Loop...")
206
+ # For each epoch
207
+ for epoch in range(num_epochs):
208
+ # For each batch in the dataloader
209
+ for i, data in enumerate(dataloader, 0):
210
+
211
+ ############################
212
+ # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z)))
213
+ ###########################
214
+ ## Train with all-real batch
215
+ netD.zero_grad()
216
+ # Format batch
217
+ real_cpu = data[0].to(device)
218
+ b_size = real_cpu.size(0)
219
+ label = torch.full((b_size,), real_label, dtype=torch.float, device=device)
220
+ # Forward pass real batch through D
221
+ output = netD(real_cpu).view(-1)
222
+ # Calculate loss on all-real batch
223
+ errD_real = criterion(output, label)
224
+ # Calculate gradients for D in backward pass
225
+ errD_real.backward()
226
+ D_x = output.mean().item()
227
+
228
+ ## Train with all-fake batch
229
+ # Generate batch of latent vectors
230
+ noise = torch.randn(b_size, nz, 1, 1, device=device)
231
+ # Generate fake image batch with G
232
+ fake = netG(noise)
233
+ label.fill_(fake_label)
234
+ # Classify all fake batch with D
235
+ output = netD(fake.detach()).view(-1)
236
+ # Calculate D's loss on the all-fake batch
237
+ errD_fake = criterion(output, label)
238
+ # Calculate the gradients for this batch, accumulated (summed) with previous gradients
239
+ errD_fake.backward()
240
+ D_G_z1 = output.mean().item()
241
+ # Compute error of D as sum over the fake and the real batches
242
+ errD = errD_real + errD_fake
243
+ # Update D
244
+ optimizerD.step()
245
+
246
+ ############################
247
+ # (2) Update G network: maximize log(D(G(z)))
248
+ ###########################
249
+ netG.zero_grad()
250
+ label.fill_(real_label) # fake labels are real for generator cost
251
+ # Since we just updated D, perform another forward pass of all-fake batch through D
252
+ output = netD(fake).view(-1)
253
+ # Calculate G's loss based on this output
254
+ errG = criterion(output, label)
255
+ # Calculate gradients for G
256
+ errG.backward()
257
+ D_G_z2 = output.mean().item()
258
+ # Update G
259
+ optimizerG.step()
260
+
261
+ # Output training stats
262
+ if i % 50 == 0:
263
+ print('[%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f'
264
+ % (epoch, num_epochs, i, len(dataloader),
265
+ errD.item(), errG.item(), D_x, D_G_z1, D_G_z2))
266
+
267
+ # Save Losses for plotting later
268
+ G_losses.append(errG.item())
269
+ D_losses.append(errD.item())
270
+
271
+ # Check how the generator is doing by saving G's output on fixed_noise
272
+ if (iters % 500 == 0) or ((epoch == num_epochs-1) and (i == len(dataloader)-1)):
273
+ with torch.no_grad():
274
+ fake = netG(fixed_noise).detach().cpu()
275
+ img_list.append(vutils.make_grid(fake, padding=2, normalize=True))
276
+
277
+ iters += 1
278
+