본문 바로가기

프로젝트

무이메이커스_간 (GAN) 을 활용한 인공지능 (AI) 이미지 생성 (Image Generation) 딥러닝 프로젝트

프로젝트 진행 순서

  • 1. 간 (GAN) 을 통한 인공지능 (AI) 이미지 생성 (Image Generation) 개요
  • 2. 이미지 데이터 전처리 (Image Preprocessing)
  • 3. 딥러닝 모델 생성
  • 4. 모델 평가 및 시각화 (Evaluation and Visualization)
  • 5. 실생활 적용

 

안녕하세요 헬스케어 제품 개발회사 허니컴의 무이메이커스페이스 입니다.

저희는 4차산업혁명을 맞이하여 딥러닝을 접목시킨 제품 개발을 위해 다양한 프로젝트를 수행하고,

이를 활용하여 인공지능 (AI) 을 지닌 다양한 헬스케어 제품을 생산하는데 그 목적이 있습니다.

이번 시간에는 딥러닝 모델 중 하나인 간 (GAN) 을 활용하여 만든 인공지능 (AI) 으로

이미지를 생성하는 (Image Generation) 프로젝트를 소개하고자 합니다.

 

목표는 파이토치 (Pytorch) 에서 제공되는 튜토리얼을 따라 사람의 얼굴을 생성해내는 것으로,

데이터셋 (Dataset) 으로는 20만 여 개의 사람 얼굴을 수집한 CelebA 를 사용하였습니다.

 

개발 환경은 윈도우 (Window), 언어는 파이썬 (Python), 라이브러리는 파이토치 (Pytorch) 를 사용합니다.

 

http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html

 

Large-scale CelebFaces Attributes (CelebA) Dataset

News 2016-07-29 If Dropbox is not accessible, please download the dataset using Google Drive or Baidu Drive. Details CelebFaces Attributes Dataset (CelebA) is a large-scale face attributes dataset with more than 200K celebrity images, each with 40 attribut

mmlab.ie.cuhk.edu.hk

Large-scale CelebFaces Attributes (CelebA) Dataset

 

celebA 데이터셋의 샘플 이미지

 

1. 이미지 생성 (Image Generation) 개요

 

지금까지 머신러닝, 딥러닝의 목표는 주어진 데이터의 패턴을 학습하여 해당 데이터를 얼마나 잘 분류, 혹은 예측

하는지에 중점이 있었다면, 이번주 저희의 목표는 간 (GAN) 이라는 딥러닝 모델을 사용하여 인공지능 (AI) 이 직접

데이터를 창조하도록 학습시키는 것입니다.

 

간 (GAN) 은 어떤 분포의 데이터든 학습을 통해 모방할 수 있어, 사실상 모든 분야의 데이터를 창조할 수 있으며,

Generator 와 Discriminator 라는 두 가지 모델을 만들어 적대적인 학습 (Adversarial Training) 을 진행합니다.

https://arxiv.org/abs/1406.2661

 

Generator 는 특정 Noise 를 Input 으로 받고 생성하고자 하는 데이터와 같은 형태의 Output 을 생성합니다.

Discriminator 는 Generator 의 Output 과 생성하고자 하는 데이터의 학습 데이터셋을 식별합니다.

해당 과정을 통해 Generator 는 보다 학습 데이터셋에 가깝게, Discriminator 는 보다 정확히 식별하게 되며

보다 진짜에 가까운 데이터를 Generation 하기에 GAN (Generative Adversarial Nets) 이라고 불립니다.

Toward Data Science 에 실린 간 (GAN) 의 구조

 

이를 수식적으로 풀어보면 Generator 와 주어진 데이터의 분포 차이를 최소가 되도록 하는 동시에

Generator 의 Output 과 주어진 데이터가 각각의 레이블에 해당되도록 확률을 최대로 만듭니다.

간 (GAN) 모델의 수식

논문의 그림을 보면 파란 선이 Discriminative 분포, 검은 선이 데이터 분포, 초록 선이 Generative 분포로

좌측에서 우측으로 학습이 진행됨에 따라 Generator 의 Output 과 데이터의 분포가 동일해지고,

Discirminator 는 0.5 의 확률 값으로 두 데이터를 구분하지 못하게 됩니다.

간 (GAN) 모델의 학습 과정

이러한 간 (GAN) 모델의 컨셉에 딥러닝의 Multi-Layer Perceptron 을 합치게 되면서

인공지능 (AI) 는 데이터를 창조하는 경지에 이르게 되었습니다.

그러나 해당 모델은 학습이 불안전하여 다양한 데이터에 적용하기 어려운 단점이 존재하였는데,

이를 개선하기 위해 CNN 구조를 합친 DCGAN (Deep Convolutional Generative Adversarial Networks)

가 나타나게 되고, 이후 개발된 대부분의 간 (GAN) 구조의 딥러닝 모델들은 DCGAN 을 기반으로 삼게 됩니다.

https://arxiv.org/abs/1511.06434

 

DCGAN 의 구조는 Discriminator 와 Generator 의 Pooling Layer 를 모두 제거하고 온전히

Convolution Layer 와 Batch Normalization 으로 구성시킵니다.

또한 공간 정보를 남기기 위해 Fully Connected Layer 를 제거하고,

LeakyReLU, Tanh 과 같은 다양한 Activation Function 을 통해 딥러닝 모델을 구축합니다.

(자세한 정보는 아래 코드를 통해 작성하였습니다.)

해당 구조를 통해 간 (GAN) 은 보다 안정적으로 데이터를 학습하여 생성할 수 있습니다.

 

Convolution Layer 를 통해 데이터를 만드는 DCGAN generator 딥러닝 모델

 

2. 이미지 데이터 전처리 (Image Preprocessing)

 

주어진 데이터셋은 20만 여 개의 많고 다양한 얼굴 데이터가 존재하므로,

Augmentor 같은 이미지 생성 파이썬 (Python) 라이브러리를 사용하지 않고

파이토치 (Pytorch) 내부에 존재하는 transform 함수만을 사용하였습니다.

해당 프로젝트는 파이토치 (Pytorch) 의 튜토리얼을 따라가므로 제시된 사이즈에 맞춰 전처리를 진행합니다.

 

import torchvision.datasets as dset
import torchvision.transforms as transforms

def main():
    dataroot = 'celebA 데이터셋 경로'

    dataset = dset.ImageFolder(root=dataroot,
                               transform=transforms.Compose([
                                   transforms.Resize(64),
                                   transforms.CenterCrop(64),
                                   transforms.ToTensor(),
                                   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
                               ]))

 

또한 실험을 용이하게 하기 위해 Parser 지정을 미리 해둡니다.

Hyperparameter 로 사용되는 Batch Size, Epoch, Learning Rate,

그리고 Generator 와 Discriminator 2개의 Convolution Layer 를 설계할 것이므로,

Convolution Layer 설계 시 필요한 이미지의 Channel, Size, 그리고 Input 으로 들어갈 Noize 의 Size,

총 4가지의 인자를 작성하고 파이토치 (Pytorch) 의 딥러닝 형태에 맞게 Dataloader 로 묶어줍니다.

 

import argparse

    parser = argparse.ArgumentParser(description='Face')
    parser.add_argument('--batch_size',type=int,default=128)
    parser.add_argument('--epoch',type=int,default=50)
    parser.add_argument('--learning_rate',type=float,default=0.0002)
    # Hyperparameter Option

    parser.add_argument('--channels',type=int,default=3)  
    parser.add_argument('--noise',type=int,default=100)   
    parser.add_argument('--feature_g',type=int,default=64) 
    parser.add_argument('--feature_d',type=int,default=64) 
    # Convolution Layer Parameter Option (Data Format)

    args = parser.parse_args()

    dataloader = torch.utils.data.DataLoader(dataset, batch_size=args.batch_size,
                                             shuffle=True, num_workers=0)

 

3. 딥러닝 모델 생성

 

위에서 말한 것 처럼 DCGAN 모델은 Generator 와 Discriminator, 2개의 CNN 으로 구성됩니다.

먼저 Generator 의 경우, Input 으로 들어온 Noise 신호를 데이터셋의 형태에 맞게 복원해줍니다.

최종적으로 3 x 64 x 64 데이터를 출력합니다.

 

import torch.nn as nn

    class Generator(nn.Module):
        def __init__(self):
            super(Generator, self).__init__()
            self.main = nn.Sequential(
                # input is Z, going into a convolution
                nn.ConvTranspose2d(args.noise, args.feature_g * 8, 4, bias=False),
                nn.BatchNorm2d(args.feature_g * 8),
                nn.ReLU(True),
                # state size. (64*8) x 4 x 4
                nn.ConvTranspose2d(args.feature_g * 8, args.feature_g * 4, 4, bias=False),
                nn.BatchNorm2d(args.feature_g * 4),
                nn.ReLU(True),
                # state size. (64*4) x 8 x 8
                nn.ConvTranspose2d(args.feature_g * 4, args.feature_g * 2, 4, bias=False),
                nn.BatchNorm2d(args.feature_g * 2),
                nn.ReLU(True),
                # state size. (64*2) x 16 x 16
                nn.ConvTranspose2d(args.feature_g * 2, args.feature_g, 4, bias=False),
                nn.BatchNorm2d(args.feature_g),
                nn.ReLU(True),
                # state size. (64) x 32 x 32
                nn.ConvTranspose2d(args.feature_g, args.channel, 4, bias=False),
                nn.Tanh()
                # state size. (3) x 64 x 64
            )

        def forward(self, input):
            return self.main(input)

 

Discriminator 의 경우 Generator 와 반대로 데이터의 특징을 다시 추출해나갑니다.

최종적으로 Sigmoid 를 통해 0과 1사이의 확률 값을 출력합니다.

 

    class Discriminator(nn.Module):
        def __init__(self):
            super(Discriminator, self).__init__()
            self.main = nn.Sequential(
                # input is (channel) x 64 x 64
                nn.Conv2d(args.channel, args.feature_d, 4, 2, 1, bias=False),
                nn.LeakyReLU(0.2, inplace=True),
                # state size. (64) x 32 x 32
                nn.Conv2d(args.feature_d, args.feature_d* 2, 4, 2, 1, bias=False),
                nn.BatchNorm2d(args.feature_d* 2),
                nn.LeakyReLU(0.2, inplace=True),
                # state size. (64*2) x 16 x 16
                nn.Conv2d(args.feature_d* 2, args.feature_d* 4, 4, 2, 1, bias=False),
                nn.BatchNorm2d(args.feature_d* 4),
                nn.LeakyReLU(0.2, inplace=True),
                # state size. (64*4) x 8 x 8
                nn.Conv2d(args.feature_d* 4, args.feature_d* 8, 4, 2, 1, bias=False),
                nn.BatchNorm2d(args.feature_d* 8),
                nn.LeakyReLU(0.2, inplace=True),
                # state size. (64*8) x 4 x 4
                nn.Conv2d(args.feature_d* 8, 1, 4, 1, 0, bias=False),
                nn.Sigmoid()
                # state size. 1 x 1 x 1
            )

        def forward(self, input):
            return self.main(input)

 

딥러닝에 넣기 위해 Dataloader 형태로 만들어둔 Input 을 사용합니다.

학습 목표는 Discriminator 를 최대로, Generator 를 최소로 만드는 것입니다.

 

먼저 Discrimiator 를 최대로 하기 위해 실제 데이터의 레이블을 1, 만들어진 데이터를 0으로 지정합니다.

실제 데이터는 Discriminator 를 지나 1 에 가까워지도록 학습을 시키고, 생성 데이터는 0 으로 학습합니다.

최종 Loss 값은 각각의 Loss 값을 합친 것으로 산출합니다.

 

다음으로 Generator 는 생성된 데이터가 1 에 가까워지도록 학습을 시킵니다.

 

real_label = 1
fake_label = 0

for epoch in range(num_epochs):
    for i, data in enumerate(dataloader, 0):
        ############################
        # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z)))
        ###########################
        # 학습용 데이터셋 이미지 학습
        netD.zero_grad()
        real_cpu = data[0].cuda()
        b_size = real_cpu.size(0)
        label = torch.full((b_size,), real_label).cuda()
        output = netD(real_cpu).view(-1)
        errD_real = criterion(output, label)
        errD_real.backward()
        D_x = output.mean().item()

        # 생성된 이미지 학습
        noise = torch.randn(b_size, args.noise, 1, 1).cuda()
        fake = netG(noise)
        label.fill_(fake_label)
        output = netD(fake.detach()).view(-1)
        errD_fake = criterion(output, label)
        errD_fake.backward()
        D_G_z1 = output.mean().item()

        # 최종 Loss 값
        errD = errD_real + errD_fake
        optimizerD.step()

        ############################
        # (2) Update G network: maximize log(D(G(z)))
        ###########################
        netG.zero_grad()
        label.fill_(real_label)  
        output = netD(fake).view(-1)
        errG = criterion(output, label)
        errG.backward()
        D_G_z2 = output.mean().item()
        optimizerG.step()

        # Output training stats
        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))

        # Save Losses for plotting later
        G_losses.append(errG.item())
        D_losses.append(errD.item())

        # Check how the generator is doing by saving G's output on fixed_noise
        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

 

4. 모델 평가 및 시각화 (Evaluation and Visualization)

 

간 (GAN) 모델의 경우 평가함에 있어 명확한 척도를 제시함에 어려움이 있습니다.

따라서 Generator 와 Discriminator 의 Loss 값을 통해 학습의 여부를 파악하고,

시각화를 통해 얼마나 '진짜' 사람 얼굴같은 이미지를 생성하는지를 확인하였습니다.

1번 학습을 진행하였을 땐 Loss 값도 비교적 높고 생성된 이미지도 괴상한 형태를 보여줍니다.

제공된 데이터셋과 1번 학습하여 생성된 데이터셋의 시각화
1번 학습한 두 모델의 Loss 값

학습 횟수를 5번으로 늘린 결과 보다 사람에 가까워지고 Loss 값도 줄어듬을 확인할 수 있었습니다.

제공된 데이터셋과 5번 학습하여 생성된 데이터셋의 시각화
5번 학습한 두 모델의 Loss 값

 

5. 실생활 적용

 

간 (GAN) 을 통한 데이터 생성은 현재 이미지 분야에서 넓게 사용되고 있습니다.

자신이 가진 이미지를 마치 필터를 적용한 것 처럼 다양한 이미지로 바꿔준다던지,

혹은 단순히 스케치한 사진에 색깔을 넣어준다던지, 밤낮을 바꿔준다던지,

심지어 자신의 사진을 마치 웹툰 캐릭터처럼 바꿔준다던지 하는 재밌는 서비스가 존재합니다.

 

이미지 외에도 딥러닝에 적용되기 위해선 많은 데이터들이 필요한데,

이런 데이터셋을 Augmentation 하는 용도로도 간 (GAN) 이 적용되고 있습니다.

 

텍스트 추가

이미지를 다른 이미지로 변환 (Cycle GAN)
이미지를 다양한 형태의 이미지로 변환 (Image to Image Translation)

 

자신의 사진을 웹툰으로 변환 (Naverlabs)

 

4차산업혁명을 통한 빅데이터와 인공지능 (AI) 붐은 다양한 딥러닝의 발전을 가져왔으나,

아직까지 사람들에게 이는 친숙하지 못한 방법론이며

어떠한 데이터냐에 따라 그 변화가 다양하므로 정답이 존재하지 않습니다.

허니컴 메이커스페이스는 데이터에 따라 다양한 방향으로 인공지능 (AI) 딥러닝 모델을 적용해보며 

'정답' 에 근접한 모델을 생성하고,

이를 사람들이 이해하기 쉽도록 시각화와 같은 방법을 통해 설명해나가고자 합니다.

........

시제품 제작 문의