Search
Duplicate
🕶️

GAN 활용 이미지 지브리화

소속팀
크리에이티브랩 팀
팀명
레프리
목차

GAN(Generative Adversarial Networks)

생성자와 판별자 두 개의 네트워크를 활용한 생성 모델
생성자는목적 함수(Objective Function)를 통해 이미지 분포 학습

StyleGAN

StyleGAN은 고화질 이미지 생성에 적합한 아키텍처를 제안한다.
정리
PGGAN 베이스라인 아키텍처 성능 향상
다양한 특징을 분리 추출하여 다루고 싶은 특징만 컨트롤하는 성능 향상
고해상도 얼굴 데이터셋(FFHQ) 발표

StyleGAN 원리

Mapping Network

Mapping Network는 특정 스페이스의 벡터를 다른 스페이스의 벡터에 매칭시켜, Latent 벡터(Vector)를 w 도메인의 벡터로 매칭한 뒤 w 벡터를 사용하는 방식이다. Latent 벡터는 각각의 스타일 정보로 입력되는 레이어의 위치에 따라 해당 스타일에 미치는 영향력이 달라질 수 있다. 이때 하나의 Latent 벡터인 w는 18*512 차원의 행렬이다.
정리
Coarse Styles
세밀하진 않지만 전반적인 특징을 바꿀 수 있는 스타일
얼굴형, 안경 유무 등 큰 변화를 이끌어낼 수 있는 특성들
Middle Styles
조금 더 세밀한 스타일
헤어 스타일, 눈을 감았는지 여부 등
Fine Styles
훨씬 세밀한 스타일
색상, 미세한 구조 등
기본적으로 이미지 A의 특성을 바꿀 때 이미지 B로부터 특성을 가져온다. 그러나 (a)처럼 빈 공간인 좌상단에 해당하는 데이터셋은 생성하고 싶어도 생성하기 어렵다. 빈 공간에 대해 억지로 Mapping이 이루어지면 Wrapping이 발생한다. Wrapping이 발생하면 어떤 이미지가 생성될 지 예측할 수 없을 정도로 급진적으로 변하는 특징이 존재한다. 이때 가우시안 분포에서 샘플링한 z 벡터를 직접 사용하지 않고 계산된 w 벡터를 사용하여 이미지를 생성하면 각 특징 분리에 효과적이다.

Stochastic Variation

PGGAN은 하나의 Latent 벡터를 만든 후 생성 네트워크에 넣어 하나의 새로운 이미지를 만든다. 반면, StyleGAN은 다양한 스타일에 대한 정보를 레이어를 거치는 과정에 적용시켜 이미지의 다양성을 보장할 수 있다.
StyleGAN은 단순히 스타일 정보만 입히는 것에서 그치지 않고, Stochastic Variation 처리가 가능하다. 즉, 하나의 이미지에 포함될 수 있는 다양한 확률적인 측면(주근깨, 머리카락 배치 등 매일 조금씩 달라질 수 있는 요소)을 컨트롤할 수 있다. 이를 위해 별도의 노이즈(Noise) Input을 입력으로 넣어서 각각의 레이어마다 노이즈에 대한 정보가 들어갈 수 있도록 만들었다.

StyleGAN 구현 : 첫 번째 시도

크롤링

크롤링 코드 ver.1

모델링

모델링 Ver.1 참고 링크
def log2(x): return int(np.log2(x)) # we use different batch size for different resolution, so larger image size # could fit into GPU memory. The keys is image resolution in log2 batch_sizes = {2: 16, 3: 16, 4: 16, 5: 16, 6: 16, 7: 8, 8: 4, 9: 2, 10: 1} # We adjust the train step accordingly train_step_ratio = {k: batch_sizes[2] / v for k, v in batch_sizes.items()} url = "https://drive.google.com/drive/folders/1hCMwkIELjilUyttAgUSu9tFk06FE5z6N?usp=sharing" output = "celeba_gan.zip" gdown.download(url, output, quiet=True) with ZipFile("celeba_gan/data.zip", "r") as zipobj: zipobj.extractall("celeba_gan") # Create a dataset from our folder, and rescale the images to the [0-1] range: ds_train = keras.preprocessing.image_dataset_from_directory( "celeba_gan", label_mode=None, image_size=(64, 64), batch_size=32 ) ds_train = ds_train.map(lambda x: x / 255.0) def resize_image(res, image): # only donwsampling, so use nearest neighbor that is faster to run image = tf.image.resize( image, (res, res), method=tf.image.ResizeMethod.NEAREST_NEIGHBOR ) image = tf.cast(image, tf.float32) / 127.5 - 1.0 return image def create_dataloader(res): batch_size = batch_sizes[log2(res)] dl = ds_train.map(partial(resize_image, res), num_parallel_calls=tf.data.AUTOTUNE).unbatch() dl = dl.shuffle(200).batch(batch_size, drop_remainder=True).prefetch(1).repeat() return dl
Python
복사
크롤링한 데이터 셋을 구글 드라이브에 올리고 마운트해서 코랩에서 실행했다. url에서 데이터 셋을 불러오는 과정에서 오류가 났는데, 이미 원저자의 데이터셋에 맞게 Train된 모델이었기 때문에 우리의 데이터 셋을 넣었을 때 코드가 작동하지 않은 것으로 추정된다.

StyleGAN 구현 : 두 번째 시도

크롤링

크롤링 코드 Ver.2

모델링

모델링 Ver.2 참고 링크
애니메이션 캐릭터 얼굴 이미지 생성에 참고한 코드는 StyleGAN에서 Latent 벡터를 뽑아 Generator에 만들 때 Autoencoder를 이용한 방법이다. 논문의 코드가 Pytorch로 작성되었는데 사용자가 Tensorflow를 주로 사용하여 코드를 일부 수정했을뿐만 아니라 빠진 부분이 있었기 때문에 우리가 생각했던 것 만큼의 결과를 만들 수 없었고 두개의 이미지가 합쳐져서 뭉개지는 결과가 나왔다.
Generator에서 Autoencoder 사용 코드
Autoencoder는 레이블 처리를 하지 않은 훈련 데이터를 사용하여 잠재 표현(latent representation)을 학습할 수 있는 신경망이고, 훈련 데이터와 매우 비슷한 새로운 데이터를 생성할 수 있다는 특징을 가지고 있어 이번 모델에도 사용하였다고 한다. 또한 데이터 전체에서 학습을 진행하기보다 Less Entangled Representation을 학습하기 때문에 더 효율적인 성능을 보여줄 수 있었을 것이다. 하지만 앞서 이야기 했듯이 논문을 구현한 코드와 다른 부분이 생겨 정확한 결과를 만들 수 없었다.

한계

사이즈 및 크롭 문제

StyleGAN의 논문을 살펴보면 1024x1024 크기를 갖는 70,000 장의 high-quality PNG images를 학습에 이용하였다. 또한 논문에서 말하고 있는 ‘학습에 사용할 이미지’의 크기는 최소 256x256 이다. 물론 더 작은 크기의 이미지로도 학습을 진행할 수 있겠지만, 그만큼 원하는 결과를 만들기 어렵다는 것이라고 생각된다. 우리의 프로젝트에서 크롤링한 데이터들을 수작업으로 얼굴 부분만 크롭하였는데, 그러다보니 이미지의 크기가 매우 작아지는 경우도 생겼고 이와 더불어 화질도 좋지 않게 되는 경우가 많았다. 아래의 사진들 예시를 보면 크롭 후 크기가 80x80으로 작아짐을 알 수 있다.

GPU 문제

더욱이 논문에서 StyleGAN을 학습하는 데에 걸리는 시간을 위의 사진에서 확인해보면, 이미지의 크기가 작고 GPU가 8개인 경우에도 최소 3일이 걸리는 것을 볼 수 있다. 물론 우리가 사용할 데이터의 수가 많지 않고, 크기도 작기 때문에 학습에 걸리는 시간이 더 적게 걸리겠지만 구글에서 찾아볼 수 있는 StyleGAN의 예시와 같이 좋은 성능을 보이기에는 상당히 어려운 환경이었다. 이러한 여러가지 한계로 인해 CycleGAN을 이용하여 하늘을 포함한 낮의 풍경사진을 지브리 느낌으로 바꾸어 보는 프로젝트 진행으로 방향을 바꾸었다.

CycleGAN

이번 프로젝트에서 사용한 생성 모델 애플리케이션은 스타일 트랜스퍼다. 스타일 트랜스퍼란 베이스 이미지를 우리가 원하는 스타일 이미지의 느낌을 주도록 변환하는 모델을 의미한다. 이 때의 변환은 스타일 이미지에서 스타일을 결정하는 요소만 추출하여 베이스 이미지에 주입하여 이루어진다. 그렇기 때문에 하나의 이미지를 사용하여 이미지를 생성하는 것이 아니라 스타일 이미지 세트 전체에서 스타일을 잡아내고 모델이 그 스타일을 학습하여 베이스 이미지를 유지하면서 스타일 이미지의 느낌을 낼 수 있도록 한다.
스타일 트랜스퍼 분야에서 처음으로 등장한 pix2pix의 경우에는 훈련하고자 하는 이미지가 짝으로 구성되어 있어야 한다. 예를 들어 사진을 모네가 그린 그림 느낌으로 변환하기 위해서 ‘수련’ 그림 데이터 셋을 구했다면 그림에 등장하는 연못의 원본 사진이 필요하며, 롯데타워를 피카소가 그린 느낌으로 하기 위해서는 피가소가 그린 롯데타워의 그림이 필요한 것이다.
이러한 단점을 해결하기 위해서 CycleGAN은 Style Tran모델이 등장하게 되었고 paired 된 샘플 훈련 데이터 셋이 아니라고 하더라도 참조 이미지의 스타일을 다른 이미지로 복사하는 모델을 훈련 할 수 있다. 또한 CycleGAN은 양방향으로(그림을 사진으로, 사진을 그림으로) 모델을 훈련하기 때문에 모델 구조로 인해 우리가 변환하고자 하는 반대 방향으로도 이미지 변환을 할 수 있다. 이 특징들로 미루어 볼 때 데이터 셋을 paired 한 형태로 구하지 않아도 되며 우리의 프로젝트인 지브리 느낌의 사진으로 변환시키기에 적합하다 생각하여 모델을 선택하게 되었다.

CycleGAN 원리

CycleGAN의 구조를 간단하게 살펴보면 네 개의 모델로 구성되어있다. 대부분의 GAN에 사용되는 단일 생성자 신경망을 학습하는 것이 아니라 두개의 생성자와 두개의 판별자로 구성되어 모델을 학습한다.
정리
생성자 A
원본 domain A(G의 X)에서 이미지 한 개를 택한 다음에 타겟 domain B(G의 Y)에 있는 이미지와 비슷한 이미지 한 개로 전환한다. 이 신경망의 목적은 두 이미지가 동등한지 학습하는 것이다. (G: X → Y)
생성자 B
생성자 A와 반대로 F: Y→ X 를 학습한 다음 B에서 이미지를 가져와 A에 있는 이미지와 비슷하게 변환 후 F(G(X)) 가 X와 비슷해지도록 하는 것이 목적이다.
판별자 A
G(X)인 생성자 A에 의해서 생성된 이미지와 원본 domain에 있는 실제 이미지를 구별하는 역할을 한다.
판별자 B
F(Y)인 생성자 B에 의해서 생성된 이미지와 타겟 domain에 있는 실제 이미지를 구별하는 역할을 한다.

CycleGAN 구현

데이터 수집

원본 Domain을 풍경 이미지로 설정하였고, 타겟 Domain을 지브리 이미지로 설정하였다. 이에 맞추어 풍경 이미지를 수집하는데 있어서 Kaggle에서 풍경 데이터 셋을 사용하였고 지브리 이미지는 구글에서 크롤링하여 사용하였다.
[풍경 데이터 셋 구축]
[지브리 데이터 셋 구축]
크롤링에 필요한 모듈 불러오기
‘지브리 감성 배경화면’ 구글 검색 후 이미지 창 클릭
스크롤을 맨 밑까지 내리기
선택 후 다운로드
결과

데이터 전처리

데이터 전처리 코드
함수를 만들어 이미지를 Numpy ndarray 형태로 변환한 후 모델에 학습시키기 위해 이미지 전처리 과정을 거친다. Kaggle 데이터의 경우에도 이미지 크기가 크롤링한 데이터에 비해서 작긴 했지만 모양이 다양했고, 크롤링한 지브리 이미지의 경우에는 이미지의 크기가 매우 크고 다양하기 때문에 학습에 용이하게 하기 위해 일정 크기로 이미지 사이즈를 조절한다. 하지만 이미지의 크기만 조절해서 학습할 경우에 과적합 문제가 발생할 수 있고 이를 예방하기 위해 지터링 및 미러링 과정을 거친다. 그리고 CycleGAN의 경우 [-1, 1]의 픽셀값을 받기 때문에 이미지의 정규화 또한 필요하다.
이미지 지터링 및 미러링이란?
이미지 회전과 같은 무작위 변환을 적용하여 훈련 이미지 데이터 세트의 다양성을 증가시키는 Data Augmentation 기법이다.
Jittering
사전적으로 파형의 순간적인 흐트러짐을 의미하는데 아래의 코드에서는 약간의 불규칙한 움직임, 변형을 주기 위해서 이미지를 resize 후 random하게 crop하였다.
Mirroring
말 그대로 이미지를 뒤집는 증강방법이다. 아래의 코드에서는 random_flip_left_right를 사용하여 좌우로 뒤집었지만 horizontal_flip을 사용하여 위 아래를 뒤집을 수도 있다.

모델링

생성자와 판별자 사용 이번 모델에서는 pix2pix와 동일한 생성자와 판별자를 사용하였다. 설치된 tensorflow_examples 패키지를 통해 Pix2Pix 에서 사용되는 생성기와 판별자를 가져와 활용하였다. CycleGAN 논문에서는 수정된 ResNet 기반 생성자를 사용하였는데, 우리의 프로젝트에서는 Pix2Pix 에서 사용되는 생성자와 판별자를 가져왔기 때문에 U-net 생성기를 사용하였다.
위의 코드에서는 Pix2Pix 에서 사용되는 생성기와 판별자를 패키지를 통해 불러왔지만, 실제로 pix2pix에서 어떠한 구조로 생성했는지 살펴보자!
U-Net은 다운샘플링과 업샘플링으로 구성된다. 다운샘플링을 통해 모델이 이미지가 무엇인지를 감지하지만 위치에 대한 정보를 잃게 된다. 그렇기 때문에 업샘플링을 통해 다운샘플링되는 동안 잃었던 공간 정보를 되돌리고 이미지 스타일과 이미지에 대한 공간 정보를 합쳐 사용하게 된다.
스타일 트랜스퍼의 판별자 구조는 PatchGAN으로 불리는 모델의 판별자 구조를 따른다. 이는 이미지 전체에 대해서 예측하는 것이 아닌 patch로 나누어 각 patch가 진짜인지 가짜인지 추측하는 구조이다. 이를 위해 판별자는 진짜로 분류해야 하는 입력 이미지 및 대상 이미지와 가짜로 분류해야 하는 입력 이미지 및 생성된 이미지 2개의 입력을 받아 스타일을 기반으로 두 이미지가 다른지 구분해야 한다.
주기 일관성 손실 추가 생성자와 판별자가 pix2pix 모델의 것을 사용했기 때문에 판별자 손실 및 생성기 손실 또한 pix2pix의 것을 사용한다. 하지만 CycleGAN에는 훈련할 쌍으로 연결된 데이터가 없으므로 훈련 중에 입력 x 와 대상 y의 쌍이 없기 때문에 네트워크가 올바른 매핑을 학습하도록 강제하기 위해 주기 일관성 손실을 추가한다.
체크포인트 설정 모델 학습에 활용하기 위해 체크포인트를 설정한다.
학습 생성자와 판별자 신경망을 구축한 다음 판별자와 생성자를 교대로 훈련하는 GAN의 기본 훈련 방식을 통해 CycleGAN을 학습시킨다.

결과

[실패1 : 인물편]
CycleGAN에서 배경에 대한 특성을 추출해서인지 인물 사진에 대해서는 변화가 일어나지 않았다.
[실패2 : 야경편]
트레인 데이터에 어두운 야경 사진이 훈련되지 않아서 그런지 결과가 나오지 않았다.
[실패3 : 풍경편]
이미 너무 파란 사진의 경우 트레인 데이터에서 포착한 도메인 특성에 맞지 않는지 별 효과가 없는 것으로 보인다. 이미 채도가 높은 사진인 경우에 해당 하는 것 같다.
[실패4 : 채도 및 대비편]
사진의 대비가 낮거나 전체적으로 밝은 이미지의 경우에도 결과가 제대로 나오지 않았다.
[실패5 : 노이즈편]
깨짐 현상이 심하고 지브리 느낌이 적었다.
[성공]
아래 성공한 결과를 살펴보면 생성자가 입력 이미지를 다른 도메인의 이미지로 잘 변환하였다고 보인다. 학습한 모델에 Test를 해보며 우리가 크롤링한 지브리 훈련 데이터셋에서 CycleGAN이 포착한 도메인 특성은 특정 파란색감의 하늘이 3분의 2를 차지하고, 인물이 없어야 하며, 대비가 낮지 않은 이미지라는 결론을 내렸다.

한계 및 보완

Shape 관련 Error 발생

Test 할 때에 이미지 파일의 shape과 텐서플로우에서 받아들이는 shape이 일치하지 않아 오류가 발생했다. 오류 코드를 구글에 검색해보니 생성자 내부에 Hyperparameter를 수정하라고 나와 있어서 tensorflow_examples  패키지를 통해 Pix2Pix 에서 사용되는 생성기와 판별자를 가져오지 않고 생성자와 판별자 함수를 정의하여 다시 Test를 해보았지만 해결되지 않았다.
그래서 테스트 할 이미지의 형태를 변형해보고자 생각하였고 이미지의 size를 변경한 후 iteranle 객체로 바꾸어 Test를 진행하였다. Test할 이미지의 크기를 수정하지 않고 하는 방법을 아직 찾지 못했다는 한계가 있다.

ResNet 관련 Error 발생

위의 오류를 해결할 겸 생성자를 함수로 정의할 때 이번 프로젝트에서 사용한 U-Net이 아닌 Resnet 구조의 생성자를 만들어 학습시켜 보았다. 하지만 논문에서 사용된 Resnet 생성자는 InstanceNormalization 층을 사용했는데 keras에서 사용이 불가한건지 오류가 발생하여 BatchNormalization 을 대신 사용하였다. 이외에도 몇몇 부분의 수정이 있어서인지 생성자가 이미지의 특성을 제대로 못찾는다고 느꼈고 우리의 프로젝트에서는 기존의 U-Net 구조를 사용하였다.
코드
훈련 과정

Noise 발생

epoch를 70으로 한 경우에도 조금의 노이즈가 있는 것 같아 epoch = 50으로 다시 학습시켜 보았으나 노이즈가 사라지지 않았다. 처음 epoch=100으로 훈련할 때 앞의 일정 체크포인트는 삭제 되었기 때문에 50일 때의 체크포인트가 남아있지 않아 아예 처음부터 다시 학습했다.
이를 해결하기 위해 Denoise와 관련하여 공부한 후 적용해볼 계획이다. 이번 프로젝트를 하며 크롤링한 데이터 셋 말고 특성이 뚜렷한 데이터 셋을 구성하여 CycleGAN이 명확한 도메인 특성을 추출할 수 있도록 데이터 셋을 구축하면 더 좋은 결과가 나올 것 같다.

프로젝트 소감

따쯔 컴퓨터 비전은 파라미터 조절 등으로 미세하게 바뀌는 차이를 한 눈에 알 수 있을 만큼 결과가 가시적으로 도출된다는 점에서 매력적인 것 같다. 그 중심에 있는 GAN 모델을 꼭 활용해보고 싶었는데 이번 팀 레프리의 프로젝트로 시도해볼 수 있어서 정말 뜻 깊다. 다소 아쉬운 결과였지만 다음에는 여유를 가지고 성능을 높이고 싶다는 욕심이 생겼다. 팀 레프리와 함께해서 참 행복했다. 레프리 레프리 파이팅!
Zion 인물을 지브리 캐릭터화하는 것에는 실패했지만 실패를 통해서도 배운 게 많은 것 같다. GAN에 어떤 모델이 있고, 각각 어떤 특징을 가지고 있는지 알게 되어 유익했고 다음번에는 모델 구동 코드를 좀 더 공부해서 인물 사진도 지브리 느낌으로 바꿔보는 것을 성공시켜 보고 싶다.
Uni SytleGAN 프로젝트를 마무리까지 잘 해보고 싶었는데 그렇지 못해 아쉬움이 많이 남았다. 그리고 더 열심히 공부해야겠다는 생각도 들었다. 다음에도 프로젝트를 하게 된다면 재밌게 그리고 결과까지 완벽하게 하고 싶다. 마지막으로 여름기수동안 함께한 레프리 팀원들에게 감사의 말씀을 전한다.
레프리 소개
팀 다이브의 크리에이티브 랩 팀 소속 레프리는 컴퓨터 비전(CV)을 활용한 프로젝트 진행을 목표로 모인 팀이다. 팀원으로는 강다은(따쯔), 김시온(Zion), 김정국(Uni)이 있다. 팀명 ‘레프리’는 팀원 셋의 공통 관심사가 스포츠라는 것에서 출발했다. 스포츠에서 객관적인 입장이어야 하는 심판의 역할처럼 우리의 프로젝트가 객관적인 데이터를 바탕으로 편향 없는 결과가 도출되길 바라는 마음으로 지은 이름이다.