# ์ฐ๋ฆฌ๊ฐ ์ค์ ํ ๋๋ก ์ด๋ฏธ์ง ๋ฐ์ดํฐ์
์ ๋ถ๋ฌ์ ๋ด
์๋ค
# ๋จผ์ ๋ฐ์ดํฐ์
์ ๋ง๋ญ๋๋ค
dataset = dset.ImageFolder(root=dataroot,
transform=transforms.Compose([
transforms.Resize(image_size),
transforms.CenterCrop(image_size),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
]))
# dataloader๋ฅผ ์ ์ํด๋ด
์๋ค
dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,
shuffle=True, num_workers=workers)
# GPU ์ฌ์ฉ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํด ์ค๋๋ค
device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu")
# ํ์ต ๋ฐ์ดํฐ๋ค ์ค ๋ช๊ฐ์ง ์ด๋ฏธ์ง๋ค์ ํ๋ฉด์ ๋์๋ด
์๋ค
real_batch = next(iter(dataloader))
plt.figure(figsize=(8,8))
plt.axis("off")
plt.title("Training Images")
plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=2, normalize=True).cpu(),(1,2,0)))
# ``netG`` ์ ``netD`` ์ ์ ์ฉ์ํฌ ์ปค์คํ
๊ฐ์ค์น ์ด๊ธฐํ ํจ์
def weights_init(m):
classname = m.__class__.__name__
if classname.find('Conv') != -1:
nn.init.normal_(m.weight.data, 0.0, 0.02)
elif classname.find('BatchNorm') != -1:
nn.init.normal_(m.weight.data, 1.0, 0.02)
nn.init.constant_(m.bias.data, 0)
ํ๊ท ์ด 0์ด๊ณ ๋ถ์ฐ์ด 0.02์ธ ์ ๊ท๋ถํฌ๋ฅผ ์ฌ์ฉํด์ G์ D๋ฅผ ๋ชจ๋ ๋ฌด์์ ์ด๊ธฐํ๋ฅผ ์งํํ๋ ๊ฒ์ด ์ข๋ค
์ ํจ์๋ ๋งค๊ฐ๋ณ์๋ก ๋ชจ๋ธ์ ์ ๋ ฅ๋ฐ์ ๊ฐ์ค์น๋ค์ ๋ชจ๋ ์ด๊ธฐํ ํจ. ๋ชจ๋ธ์ด ๋ง๋ค์ด์ง์ ๋ง์ ์ด ํจ์๋ฅผ call ํด์ ์ ์ฉ์ ์ํด.
class Generator(nn.Module):
def __init__(self, ngpu):
super(Generator, self).__init__()
self.ngpu = ngpu
self.main = nn.Sequential(
# ์
๋ ฅ๋ฐ์ดํฐ Z๊ฐ ๊ฐ์ฅ ์ฒ์ ํต๊ณผํ๋ ์ ์น ํฉ์ฑ๊ณฑ ๊ณ์ธต์
๋๋ค.
nn.ConvTranspose2d( nz, ngf * 8, 4, 1, 0, bias=False),
nn.BatchNorm2d(ngf * 8),
nn.ReLU(True),
# ์์ ๊ณ์ธต์ ํต๊ณผํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ. ``(ngf*8) x 4 x 4``
nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 4),
nn.ReLU(True),
# ์์ ๊ณ์ธต์ ํต๊ณผํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ. ``(ngf*4) x 8 x 8``
nn.ConvTranspose2d( ngf * 4, ngf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 2),
nn.ReLU(True),
# ์์ ๊ณ์ธต์ ํต๊ณผํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ. ``(ngf*2) x 16 x 16``
nn.ConvTranspose2d( ngf * 2, ngf, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf),
nn.ReLU(True),
# ์์ ๊ณ์ธต์ ํต๊ณผํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ. ``(ngf) x 32 x 32``
nn.ConvTranspose2d( ngf, nc, 4, 2, 1, bias=False),
nn.Tanh()
# ์์ ๊ณ์ธต์ ํต๊ณผํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ. ``(nc) x 64 x 64``
)
def forward(self, input):
return self.main(input)
nz
: ์ ๋ ฅ๋ฒกํฐ z์ ๊ธธ์ด
ngf
: ์์ฑ์๋ฅผ ํต๊ณผํ๋ ํน์ ๋ฐ์ดํฐ์ ํฌ๊ธฐ
nc
: ์ถ๋ ฅ ์ด๋ฏธ์ง์ ์ฑ๋ ๊ฐ์ (์ฝ๋์์๋ RGB ์ด๋ฏธ์ง ์ด๋ฏ๋ก nc = 3)
# ์์ฑ์๋ฅผ ๋ง๋ญ๋๋ค
netG = Generator(ngpu).to(device)
# ํ์ํ ๊ฒฝ์ฐ multi-GPU๋ฅผ ์ค์ ํด์ฃผ์ธ์
if (device.type == 'cuda') and (ngpu > 1):
netG = nn.DataParallel(netG, list(range(ngpu)))
# ๋ชจ๋ ๊ฐ์ค์น์ ํ๊ท ์ 0( ``mean=0`` ), ๋ถ์ฐ์ 0.02( ``stdev=0.02`` )๋ก ์ด๊ธฐํํ๊ธฐ ์ํด
# ``weight_init`` ํจ์๋ฅผ ์ ์ฉ์ํต๋๋ค
netG.apply(weights_init)
# ๋ชจ๋ธ์ ๊ตฌ์กฐ๋ฅผ ์ถ๋ ฅํฉ๋๋ค
print(netG)
์์ฑ์ ๋ชจ๋ธ์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด weights_init ํจ์๋ฅผ ์ ์ฉ
class Discriminator(nn.Module):
def __init__(self, ngpu):
super(Discriminator, self).__init__()
self.ngpu = ngpu
self.main = nn.Sequential(
# ์
๋ ฅ ๋ฐ์ดํฐ์ ํฌ๊ธฐ๋ ``(nc) x 64 x 64`` ์
๋๋ค
nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True),
# ์์ ๊ณ์ธต์ ํต๊ณผํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ. ``(ndf) x 32 x 32``
nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 2),
nn.LeakyReLU(0.2, inplace=True),
# ์์ ๊ณ์ธต์ ํต๊ณผํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ. ``(ndf*2) x 16 x 16``
nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 4),
nn.LeakyReLU(0.2, inplace=True),
# ์์ ๊ณ์ธต์ ํต๊ณผํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ. ``(ndf*4) x 8 x 8``
nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 8),
nn.LeakyReLU(0.2, inplace=True),
# ์์ ๊ณ์ธต์ ํต๊ณผํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ. ``(ndf*8) x 4 x 4``
nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
nn.Sigmoid()
)
def forward(self, input):
return self.main(input)
Discriminator๊ฐ ํ์ํ ๊ฒฝ์ฐ, ๋ ๋ค์ํ layer๋ค์ ์์ ์ ์์ง๋ง, Batch Normalization, LeaklyReLU, strided ํฉ์ฑ๊ณฑ ๊ณ์ธต์ ์ฌ์ฉํจ
Why? => DCGAN ๋ ผ๋ฌธ์์ ๋ณดํญ์ด ์๋(Strided) ํฉ์ฑ๊ณฑ ๊ณ์ธต์ ์ฌ์ฉ ํ๋ ๊ฒ์ด ์ ๊ฒฝ๋ง ๋ด์์ ์ค์ค๋ก Pooling ํจ์๋ฅผ ํ์ตํ๊ธฐ ๋๋ฌธ์, ์ง์ ์ ์ผ๋ก Pooling ๊ณ์ธต(MaxPool, AvgPooling)์ ์ฌ์ฉํ๋ ๊ฒ ๋ณด๋ค ์ข์
# ๊ตฌ๋ถ์๋ฅผ ๋ง๋ญ๋๋ค
netD = Discriminator(ngpu).to(device)
# ํ์ํ ๊ฒฝ์ฐ multi-GPU๋ฅผ ์ค์ ํด์ฃผ์ธ์
if (device.type == 'cuda') and (ngpu > 1):
netD = nn.DataParallel(netD, list(range(ngpu)))
# ๋ชจ๋ ๊ฐ์ค์น์ ํ๊ท ์ 0( ``mean=0`` ), ๋ถ์ฐ์ 0.02( ``stdev=0.02`` )๋ก ์ด๊ธฐํํ๊ธฐ ์ํด
# ``weight_init`` ํจ์๋ฅผ ์ ์ฉ์ํต๋๋ค
netD.apply(weights_init)
# ๋ชจ๋ธ์ ๊ตฌ์กฐ๋ฅผ ์ถ๋ ฅํฉ๋๋ค
print(netD)
# ``BCELoss`` ํจ์์ ์ธ์คํด์ค๋ฅผ ์ด๊ธฐํํฉ๋๋ค
criterion = nn.BCELoss()
# ์์ฑ์์ ํ์ต์ํ๋ฅผ ํ์ธํ ์ ์ฌ ๊ณต๊ฐ ๋ฒกํฐ๋ฅผ ์์ฑํฉ๋๋ค
fixed_noise = torch.randn(64, nz, 1, 1, device=device)
# ํ์ต์ ์ฌ์ฉ๋๋ ์ฐธ/๊ฑฐ์ง์ ๋ผ๋ฒจ์ ์ ํฉ๋๋ค
real_label = 1.
fake_label = 0.
# G์ D์์ ์ฌ์ฉํ Adam์ตํฐ๋ง์ด์ ๋ฅผ ์์ฑํฉ๋๋ค
optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))
optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))
Pytorch์์๋ ์์คํจ์๋ฅผ Binary Cross Entropy Loss (BCELoss) ์ฌ์ฉ ์์คํจ์๋ ์์์ ์ค๋ช ํ ํจ์์ ๋น์ท
- Discriminator ์์คํจ์
- ( 1 ) y๊ฐ 1์ผ ๊ฒฝ์ฐ(real data์ผ ๊ฒฝ์ฐ), Xn์ ์ค์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ค๋ค. (y = 1, Xn = D(X))
- ( 2 ) y๊ฐ 0์ผ ๊ฒฝ์ฐ(fake data์ผ ๊ฒฝ์ฐ), Xn์ ์๋ก ์์ฑํ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ค๋ค. (y= 0, Xn = D(G(Z)))
- ์ ( 1 ), ( 2 )๋ฅผ ๊ฐ๊ฐ ln์ ๋ฃ์ด์ฃผ๋ฉด,
( 1 ) -logD(X)
( 2 ) โlog(1-D(G(Z)))
์ด ๋์ ๋ํ โlogD(X)โlog(1-D(G(Z))) ๋ฅผ ์ต์ํ ์์ผ์ผํจ โ๏ธ logD(X)+log(1-D(G(Z))) ๋ฅผ ์ต๋ํ ์ํค๋ฉด ๋จ!!
- Generator ์์ค ํจ์
- Original GAN: ์์ฑ์๋ log(1-D(G(Z)))๋ฅผ ์ต์ํ ์ํค๋ ๋ฐฉํฅ์ผ๋ก ํ์ต์ํด
- But, ์ถฉ๋ถํ ๋ณํ x, ํ์ต์ด๊ธฐ์ ๋ฌธ์ ๊ฐ ์๊ฒจ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด log(D(G(Z)))๋ฅผ ์ต๋ํ ํ๋ ๋ฐฉ์์ผ๋ก ํ์ตํ๋ค.
- log(D(G(Z)))๋ฅผ ์ต๋ํํ๋ ๊ฒ์ D(G(Z)) ๊ฐ์ ์ต๋๋ก ํ๋ ๊ฒ๊ณผ ๋์ผํ ์๋ฏธ์ด๋ฉฐ, ์ด๋ Discriminator๊ฐ ์์ฑ๋ ์ด๋ฏธ์ง๋ฅผ "์ง์ง๋ก ํ๋ณํ๋ ๋ก ์์ด๋" ๊ฒ์ ์๋ฏธํฉ๋๋ค.
# ํ์ต์ํ๋ฅผ ์ฒดํฌํ๊ธฐ ์ํด ์์ค๊ฐ๋ค์ ์ ์ฅํฉ๋๋ค
img_list = []
G_losses = []
D_losses = []
iters = 0
print("Starting Training Loop...")
# ์ํญ(epoch) ๋ฐ๋ณต
for epoch in range(num_epochs):
# ํ ์ํญ ๋ด์์ ๋ฐฐ์น ๋ฐ๋ณต
for i, data in enumerate(dataloader, 0):
############################
# (1) D ์ ๊ฒฝ๋ง์ ์
๋ฐ์ดํธ ํฉ๋๋ค: log(D(x)) + log(1 - D(G(z)))๋ฅผ ์ต๋ํ ํฉ๋๋ค
###########################
## ์ง์ง ๋ฐ์ดํฐ๋ค๋ก ํ์ต์ ํฉ๋๋ค
netD.zero_grad()
# ๋ฐฐ์น๋ค์ ์ฌ์ด์ฆ๋ ์ฌ์ฉํ ๋๋ฐ์ด์ค์ ๋ง๊ฒ ์กฐ์ ํฉ๋๋ค
real_cpu = data[0].to(device)
b_size = real_cpu.size(0)
label = torch.full((b_size,), real_label,
dtype=torch.float, device=device)
# ์ง์ง ๋ฐ์ดํฐ๋ค๋ก ์ด๋ฃจ์ด์ง ๋ฐฐ์น๋ฅผ D์ ํต๊ณผ์ํต๋๋ค
output = netD(real_cpu).view(-1)
# ์์ค๊ฐ์ ๊ตฌํฉ๋๋ค
errD_real = criterion(output, label)
# ์ญ์ ํ์ ๊ณผ์ ์์ ๋ณํ๋๋ฅผ ๊ณ์ฐํฉ๋๋ค
errD_real.backward()
D_x = output.mean().item()
## ๊ฐ์ง ๋ฐ์ดํฐ๋ค๋ก ํ์ต์ ํฉ๋๋ค
# ์์ฑ์์ ์ฌ์ฉํ ์ ์ฌ๊ณต๊ฐ ๋ฒกํฐ๋ฅผ ์์ฑํฉ๋๋ค
noise = torch.randn(b_size, nz, 1, 1, device=device)
# G๋ฅผ ์ด์ฉํด ๊ฐ์ง ์ด๋ฏธ์ง๋ฅผ ์์ฑํฉ๋๋ค
fake = netG(noise)
label.fill_(fake_label)
# D๋ฅผ ์ด์ฉํด ๋ฐ์ดํฐ์ ์ง์๋ฅผ ํ๋ณํฉ๋๋ค
output = netD(fake.detach()).view(-1)
# D์ ์์ค๊ฐ์ ๊ณ์ฐํฉ๋๋ค
errD_fake = criterion(output, label)
# ์ญ์ ํ๋ฅผ ํตํด ๋ณํ๋๋ฅผ ๊ณ์ฐํฉ๋๋ค. ์ด๋ ์์ ๊ตฌํ ๋ณํ๋์ ๋ํฉ๋๋ค(accumulate)
errD_fake.backward()
D_G_z1 = output.mean().item()
# ๊ฐ์ง ์ด๋ฏธ์ง์ ์ง์ง ์ด๋ฏธ์ง ๋ชจ๋์์ ๊ตฌํ ์์ค๊ฐ๋ค์ ๋ํฉ๋๋ค
# ์ด๋ errD๋ ์ญ์ ํ์์ ์ฌ์ฉ๋์ง ์๊ณ , ์ดํ ํ์ต ์ํ๋ฅผ ๋ฆฌํฌํ
(reporting)ํ ๋ ์ฌ์ฉํฉ๋๋ค
errD = errD_real + errD_fake
# D๋ฅผ ์
๋ฐ์ดํธ ํฉ๋๋ค
optimizerD.step()
############################
# (2) G ์ ๊ฒฝ๋ง์ ์
๋ฐ์ดํธ ํฉ๋๋ค: log(D(G(z)))๋ฅผ ์ต๋ํ ํฉ๋๋ค
###########################
netG.zero_grad()
label.fill_(real_label) # ์์ฑ์์ ์์ค๊ฐ์ ๊ตฌํ๊ธฐ ์ํด ์ง์ง ๋ผ๋ฒจ์ ์ด์ฉํ ๊ฒ๋๋ค
# ์ฐ๋ฆฌ๋ ๋ฐฉ๊ธ D๋ฅผ ์
๋ฐ์ดํธํ๊ธฐ ๋๋ฌธ์, D์ ๋ค์ ๊ฐ์ง ๋ฐ์ดํฐ๋ฅผ ํต๊ณผ์ํต๋๋ค.
# ์ด๋ G๋ ์
๋ฐ์ดํธ๋์ง ์์์ง๋ง, D๊ฐ ์
๋ฐ์ดํธ ๋์๊ธฐ ๋๋ฌธ์ ์์ ์์ค๊ฐ๊ฐ ๋ค๋ฅธ ๊ฐ์ด ๋์ค๊ฒ ๋ฉ๋๋ค
output = netD(fake).view(-1)
# G์ ์์ค๊ฐ์ ๊ตฌํฉ๋๋ค
errG = criterion(output, label)
# G์ ๋ณํ๋๋ฅผ ๊ณ์ฐํฉ๋๋ค
errG.backward()
D_G_z2 = output.mean().item()
# G๋ฅผ ์
๋ฐ์ดํธ ํฉ๋๋ค
optimizerG.step()
# ํ๋ จ ์ํ๋ฅผ ์ถ๋ ฅํฉ๋๋ค
if i % 50 == 0:
print('[%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f'
% (epoch, num_epochs, i, len(dataloader),
errD.item(), errG.item(), D_x, D_G_z1, D_G_z2))
# ์ดํ ๊ทธ๋ํ๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์ํด ์์ค๊ฐ๋ค์ ์ ์ฅํด๋ก๋๋ค
G_losses.append(errG.item())
D_losses.append(errD.item())
# fixed_noise๋ฅผ ํต๊ณผ์ํจ G์ ์ถ๋ ฅ๊ฐ์ ์ ์ฅํด๋ก๋๋ค
if (iters % 500 == 0) or ((epoch == num_epochs-1) and (i == len(dataloader)-1)):
with torch.no_grad():
fake = netG(fixed_noise).detach().cpu()
img_list.append(vutils.make_grid(fake, padding=2, normalize=True))
iters += 1
Part 1) Discriminator์ ํ์ต
1) ์ง์ง ๋ฐ์ดํฐ๋ค๋ก๋ง ์ด๋ฃจ์ด์ง ๋ฐฐ์น๋ฅผ ๋ง๋ค์ด D์ ํต ๊ณผ์ํจ๋ค.
- ์ถ๋ ฅ๊ฐ์ผ๋ก log(D(x))์ ์์ค๊ฐ์ ๊ณ์ฐ
- backpropagation ๊ณ ์ ์์์ ๋ณํ๋๋ฅผ ๊ณ์ฐ
2) ๊ฐ์ง ๋ฐ์ดํฐ๋ค๋ก๋ง ์ด๋ฃจ์ด์ง ๋ฐฐ์น๋ฅผ ๋ง๋ค์ด D์ ํต ๊ณผ์ํจ๋ค.
- ๊ทธ ์ถ๋ ฅ๊ฐ์ผ๋ก log(1-D(G(z)))์ ์์ค๊ฐ ๊ณ์ฐ
- backprobagation ๋ณํ๋ ๊ณ์ฐ
์ด๋, ๋ ๊ฐ์ง ์คํ ์์ ๋์ค๋ ๋ณํ๋๋ค์ ์ถ์ ์์ผ์ผํ๋ค.
- backpropagation ๊ณ์ฐํ์ผ๋, ์ตํฐ๋ง์ด์ ์ฌ์ฉํด์ backpropagation ์ ์ฉ
Part 2) Generator์ ํ์ต
D๋ฅผ ์ด์ฉํด G์ ์ถ๋ ฅ๊ฐ์ ํ๋ณํด์ฃผ๊ณ , ์ง์ง ๋ผ๋ฒจ๊ฐ์ ์ด์ฉํด G์ ์์ค๊ฐ์ ๊ตฌํ๋ค.
(์ฆ, G๋ก ์์ฑํ ๋ฐ์ดํฐ๋ฅผ ํ๋ณ์์ ๋ฃ์ด์ ํ๋ณํด์ ๋์จ ๊ฐ์ ์ง์ง ๋ผ๋ฒจ๊ฐ์ ์ด์ฉํด์ loss ๊ณ์ฐํด์ค๋ค.)๊ตฌํ ์์ค๊ฐ์ผ๋ก ๋ณํ๋๋ฅผ ๊ตฌํ๊ณ , ์ต์ข ์ ์ผ๋ก๋ ์ตํฐ๋ง์ด์ ๋ฅผ ์ด์ฉํด G์ ๊ฐ์ค์น๋ค์ ์ ๋ฐ์ดํธ ์์ผ์ค๋ค.
plt.figure(figsize=(10,5))
plt.title("Generator and Discriminator Loss During Training")
plt.plot(G_losses,label="G")
plt.plot(D_losses,label="D")
plt.xlabel("iterations")
plt.ylabel("Loss")
plt.legend()
plt.show()
์์ค๊ฐ ๊ทธ๋ํ๋ก ๋ํ๋ด๊ธฐ
fig = plt.figure(figsize=(8,8))
plt.axis("off")
ims = [[plt.imshow(np.transpose(i,(1,2,0)), animated=True)] for i in img_list]
ani = animation.ArtistAnimation(fig, ims, interval=1000, repeat_delay=1000, blit=True)
HTML(ani.to_jshtml())
Real vs. Fake
์ถ์ฒ : PyTorch Tutorials https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html