본문 바로가기

논문 리뷰

Pix2pix 리뷰

논문 제목은 image-to-Image Translation with Conditional Adversarial Networks이다.

 

Contribution

1. Image to image translation에서 general-purpose solution을 제시했다.

2. Conditional GAN을 image to image translation에 처음으로 적용해서 좋은 결과를 얻었다.

 

  기존의 image to image translation에서는 application-specific하게 loss를 정의해야 했다. 이 논문에서는 GAN을 이용해서 사용자가 high-level의 요구사항(이 사진들과 비슷하게 만들어라)을 주면 discriminator와 generator가 동시에 학습하면서 그럴듯한 결과물을 내게 하였다. 기존에도 image to image translation에 GAN을 적용하는 시도는 있었지만, non-conditional한 GAN이었기 때문에 L2 regression등의 방법을 써서 원하는 output을 얻어야 했다. 그래서 style transfer를 할거냐, super resolution을 할거냐에 따라 일일이 method를 조정해줘야 했다. 이 논문에서는 image to image translation에서 그런 tailoring 없이 범용적으로 사용할 수 있는 common framework를 소개했다.

 

Conditional GAN

  기존의 GAN은 random noise인 z가 G(generator)의 input이었다. Conditonal GAN에서는 x라는 condition이 같이 주어져 input이 x와 z가 된다. 마찬가지로 D(discriminator)에도 {fake, x} 혹은 {y, x}가 들어간다. 그렇게 함으로써 x라는 condition을 어떻게 주느냐에 따라 output을 원하는 대로 바꿀 수 있다. 기존의 GAN으로 가령 MNIST를 학습시켰다고 하면, z를 넣어줄 때 0~9 중에 어떤 숫자가 나올지 사용자가 선택할 수 없었다. CGAN에서는 그 점을 보완한 것이다.

 

그런데 사실 x를 처음 넣을 때에는 원래는 input이 z였는데 여기에 x를 추가해보자는 의도였지만, 막상 그렇게 하고 나니 z의 의미가 퇴색되었다는 느낌을 받았다. 굳이 z를 넣지 않아도 잘 되지 않을까? 논문에서도 이것에 대해 다루는데, 실제로 처음에는 noise를 사용하지 않았다고 한다. 결국 z라는 noise는 output에 stochastic함을 주기 위해 필요한 것이다. z가 없으면 output이 x에 deterministic해진다. 그런 의미에서 noise가 반드시 z라는 gaussian noise일 필요는 없으므로 이 논문에서는 train과 test시에 dropout을 섞어서 noise를 만들었다고 한다. 거기서 오는 stochastic함이 크지는 않았다고 한다. 그러면 위의 그림에서 G의 input이 x 뿐인게 이해가 간다. z는 dropout의 형태로 G안에 섞여있을 것이다.

 

Loss function

Conditional GAN의 loss는 크게 어려울 것 없이 그냥 vanila GAN의 loss에 x가 추가된 것 뿐이다. 이 함수에 대해 G와 D가 minmax 게임을 한다. 

 

이건 실험적으로 사용해본 loss인데, 보면 D에 input에서 x가 빠져있다. 즉 D에게 condition을 보여주지 않으면 어떨까 하는 발상에서 나온 loss이다. 이렇게 되면 condition에 맞는 output을 주도록 G가 학습하지 않을 거라고 생각한다. 만약 G가 바보여서 condition으로 1을 받아 9를 만들었다고 하자. D는 condition이 뭐였는지 모르기 때문에 그 9가 보기에 그럴듯 하다면 true를 뱉을 것이다. 그러면 G는 condition에 맞지 않는 output을 냈지만 벌을 받지 않는다.

 

L1 loss를 추가해서 최종적인 loss의 모양은 이렇다. L1이나 L2 loss는 마지막에 averaging을 하기 때문에 이미지가 blurry해진다는 단점이 있다. 

 

Cs231n의 강의 슬라이드에서 발췌한 이미지이다. L2 loss는 2,3,4번째 이미지들을 구분하지 못한다.

 

하지만 이런 L1/L2에도 쓸모가 있는데, GAN의 loss와 상호 보완이 가능하다는 점이다. GAN의 장점은 보기 그럴듯한 이미지를 만들어낸다는 것이다. Blurry한 이미지들은 L1/L2 loss는 속일 수 있지만 D는 속이지 못한다. 단점은 D를 속이기 위해 실제로는 없는 object들을 만들어낸다는 것이다. 논문에서는 여기에 L1 loss를 섞어서 그 단점을 상쇄하고자 했다.

 

Generator with skips

  Image to image translation에서는 input과 output이 모두 high resolution이다. 또한, input과 output이 표면적으로는 달라보여도 같은 concept을 공유하고 있으므로 underlying structure도 같을 것이다. 이런 가정 위에서 기존에 많이 사용되던 것이 encoder-decoder 구조이다. CNN은 층이 깊어질수록 high level의 feature들을 뽑아낸다. 그렇게 뽑아낸 high-level feature는 그 이미지의 concept을 나타낸다. 그걸 바탕으로 upsampling해서 같은 concept이지만 달라보이는 output을 만들겠다는 아이디어라고 이해했다.

 

그런데 여기서 문제는 image to image translation에서 low level feature가 필요한 상황도 많다는 것이다. 단순 encoder-decoder 구조에서는 이미지가 CNN을 거치면서 그런 low level feature들이 누락된다. 가령 흑백 이미지를 컬러화하고 싶다고 하자. 우리가 원하는 건 edge 정보들은 그대로 유지한 채 색만 칠해주는 것인데, 그런 정보가 누락되어서 output이 전혀 다른 모양이면 곤란할 것이다. 그래서 U-Net에 기반한 구조를 사용한다. n개의 layer가 있다고 할 때 i번째 layer와 n-i번째 layer를 이어주는 것이다. 이렇게 하면 low level feature도 뒤쪽의 layer에 잘 전달이 될 것이다.

 

U-Net 구조를 사용하면 low level에서의 디테일이 더 살아나는 것을 볼 수 있다. L1 loss만 사용했을 때 blurry 하던 이미지가 cGAN을 섞어서 그럴듯해지고, 거기에 U-Net을 섞어서 low level feature들을 이용해 더 정확한 표현을 가능하게 한다.

 

PatchGAN

  L1/L2 loss가 비록 이미지의 high frequency를 잡아내지는 못하지만, low frequency는 잘 잡아낸다. 다시 말해, 세세한 디테일은 잡아내지 못하지만(averaging 하므로) 전체적인 그림은 잡아낸다는 것이다. L1/L2 loss를 이용해서 만든 이미지의 세세한 부분을 보면 색감이 원본과 다르고 blurry하지만(detail) 전체적인 느낌은 비슷하다(averaging해서 차이를 최소화 했으므로).

 

이런 특징을 가진 L1 loss를 이미 우리의 loss에 끼워넣었으므로, G는 low frequency에 대해 학습이 잘 일어날 것이다. 그러므로 D가 할 일은 G가 high frequency(detail)을 잘 잡아낼 수 있게 하는 것이다. 그러므로 굳이 전체 이미지에 대해 sliding 연산을 할 필요 없이, 이미지를 N x N patch 단위로 쪼개서, 각 부분을 따로 보고 독립적으로 T와 F를 판단해서, 평균을 내어 최종 output을 만들면 된다.

 

이미지를 patch로 쪼갰다는 것이 왜 high frequency를 잡아내는 것과 연관되는지 처음에는 이해가 잘 안갔다. 우리가 직접 D의 입장이 되어서 classification을 한다고 해보자. 고양이 사진을 받았는데, 처음에는 귀 부분만 보고 판단하고, 그 다음 눈을 보고 판단하고, 그 다음 발을 보고 판단해서 그 결과를 평균낸다고 하자. 우리가 보기에 각 부분이 꽤 그럴듯 해서 3번 다 1을 주고 average해서 최종적으로 1(T)를 얻었다고 하자.

 

알고 보니 이런 이미지였다면 어떻게 할 것인가? 우리는 patch 단위로 봤기 때문에 알 방법이 없다. 정확하게 맞는 상황은 아닐지라도 이런 비유를 사용해서 이해해보았다.

 

하지만 우리는 L1 loss를 같이 사용하기 때문에, 이렇게 low frequency 관점에서 이상한 이미지들은 L1이 걸러줄 것이라고 생각하고 patch로 쪼개는 방법을 사용할 수 있다. 이렇게까지 해서 patch를 사용하는 이유는 D의 input size가 줄어들기 때문에 parameter 수가 적어진다는 것, 또한 임의의 큰 이미지에 대해서도 적용할 수 있다는 등의 장점이 있기 때문이다.

 

Patch size도 결국은 hyperparameter고 실험을 통해 정해야 할 것이다. 1x1은 한 번에 한 픽셀만 보기 때문에 크게 sharp하다는 느낌은 없다. 16x16은 부분 부분은 sharp하고 그럴듯한데 전체적으로 봤을 때 patch와 patch 사이가 연결이 안되고 이상한 noise가 보인다. 70x70부터는 꽤 자연스럽다.

 

Results