본문 바로가기

머신러닝/[논문으로 짚어보는 딥러닝의 맥]

주어진 사진을 원하는 화풍으로 만드는 Neural Style

학습목표

이번 강의에서는 Neural Style에 대해 알아보려 합니다. Neural Style은 사진을 특정한 화풍으로 바꿔주는 알고리즘입니다. 두 가지 논문을 차례로 보면서 Neural Style을 알아보도록 하겠습니다. 그리고 짧게 Neural Style을 구현하는 코드를 살펴보도록 하겠습니다.

핵심 키워드

  • Neural Style
  • Texture Synthesis
  • Image Inverting
  • VGG Net

학습하기

 안녕하십니까. 이번에는 Neural Style에 대해 알아볼텐데요. 한 때 유행했던 어떤 그림을 피카소 풍으로 또는 고흐 풍으로 바꾸던, 그런 알고리즘입니다. 구체적으로는, 'Texture Synthesis Using Convolutional Nerual Networks(2015)'와 'Understanding Deep Image Representations by Inverting Them(2014)'라는 2가지 논문을 살펴보도록 하겠습니다.

 아래 그림처럼, 각각의 사진들을 다양한 화풍으로 변환하고자 합니다. 사진이 2장이 필요하겠죠. 우리가 바꾸고 싶은 이미지인 Content Image와 우리가 따라하고싶은 화풍 이미지인 Style Image. 

 우선, 첫번째 논문인 'Texture Synthesis Using Convolutional Nerual Networks(2015)'을 먼저 살펴보도록 하겠습니다. CNN을 사용한 Texture Synthesis. 텍스쳐 생성. 뭐 이런 뜻이죠. Texture는 질감이죠. 컴퓨터 그래픽스를 접해보셨다면, 충분히 잘 이해하실겁니다. 이 Texture라는건, 결국 일종의 반복되는 패턴입니다. 때문에 Texture synthesis의 목적은 어떤 Sample texture로부터, 새로운 Sample texture를 만드는 것입니다. 아래 그림에서 우측이 현실 이미지이며, 좌측이 생성된 이미지입니다. 이런 식으로, 반복되는 패턴에 해당하는 Texture를 생성하는 것을 목표로합니다.

 그럼 어떻게 이것을 진행해야할까요. 에 대해서 이야기를 하기 앞서, 한 가지 말씀드릴 것은, 지금부터 할 내용은 지금까지 봐왔던 다른 Neural Network와는 조금 다릅니다. 우리가 NN를 학습시킨다 함은, 입력 이미지(Placeholder)를 입력하고, 그에 맞는 Weights(Variables)을 찾아나가는 과정이었습니다. Optimizer는 Weights를 최적화하는 방향으로 움직이죠. 그런데 여기서는, 입력 이미지 자체가 Variable이 됩니다. 아래 그림이 이번에 사용할 모델 구조 인데요. 좌측과 우측은 사실 정확히 같은 레이어를 차용하고, 사용된 파라미터의 수도 같습니다. 다만, 입력이 달라집니다. 좌측에는 Texture Image가 들어가고, 그래서 좌측 레이어들의 Weight는 바뀌지 않을 겁니다. 다만 우측에서 들어가는 입력을 보시면, 2개의 이미지가 사용이 되죠. 회색 노이즈 이미지와, 적당히 변환된? 이미지. 적당히 변환된 이미지가 저희가 최적화되길 원하는 변수가 되는 걸텐데, 이게 자동으로 나오는게 아니라 어떤 학습을 통해서 출력되어야겠죠. 그럼 이걸 어떻게 학습시키는가. 다시한번, 좌측 모델에서는 Texture Image가 입력이 되고, 그에 따라 각 레이어별 고정된 출력이 센터에 있는 박스에 입력될 겁니다. 그리고 우측 레이어에서는 회색 노이즈 이미지가 레이어를 거치면서 레이어별 결과를 센터의 박스에 입력하겠죠. 그러면 센터에서 두개의 값을 비교하는 겁니다. 그리고 이 값이 비슷해질때까지 회색 노이즈 이미지에 변형을 가하는거죠. 그렇게 센터의 2개 박스의 값 차이가 적어졌을 때의 변환된 이미지가 아래 그림의 3번 째 이미지가 되겠죠. 또 한 번 말씀드리자면, 우측 모델의 이미지는 저희가 선택하지 않습니다. 사실상 랜덤 노이즈 이미지가 최초로 입력되면, 지속적으로 학습을 거쳐 변환된 이미지가 완성되게 됩니다. 일반적인 NN에서 Weight의 값을 저희가 직접 초기화하지 않았던 것과 같게, 이번에는 이미지 자체가 Variable이었다는 걸 생각하면, 자명합니다.

 그래서 Texture model 이라는 건, 결국 Feature Correlations을 계산하는 것과 동일합니다. 아래 그림과 같이 2개의 Image Input이 있고, 이들이 Conv layer를 통과하면서 Featuremap을 형성하게 될텐데, 각각의 단계별 Featuremap의 상관계수를 비교하면, 얼마나 비슷한 이미지인가를 판단할 수 있는겁니다. 이러한 과정에서 구체적으로 Gram matrix라는 것이 핵심적으로 활용되는데요.

 Gram matrix라는 건, 아래의 그림과 같이 정의가 될텐데요. 이런 Gram matrix의 (i,j) 값은 무엇을 의미할까요? i번째 필터와 j번째 필터의 Correlation값이 되겠죠. 그리고 이런 Gram matrix의 어떤 값이 높다는 것의 의미는 결국 해당 필터 2개가 겹치는 구간의 패턴이, 대상 이미지에서 자주 나오는 패턴이라는 의미가 되겠죠.

 그렇게 Gram matrix르 통해서 Texture generation을 시행합니다. 이건 어떤 입력 이미지가 주어지면 이들의 Gram matrix를 계산하고, 이를 비슷하게 하는 X_b를 찾는 과정입니다.

 결과적으로, 아래 그림처럼, 각 레이어별 모방된 결과를 확인할 수 있게됩니다. 다만 컨볼루션 레이어가 올라갈수록, 입력 이미지의 더 큰 부분을 보겠다는 말입니다. 결국, 내가 어느 레이어를 더 많이 볼 것인가는, 내가 지금 바라보는 텍스쳐가 얼마만큼의 단위 크기를 갖는가에 의존합니다. 굵직한 단위들을 보고 싶다면 상위레이어를, 세밀한 단위를 보고 싶다면 하위레이어를 더 많이 참조해야합니다.

 그리고 아래 그림은, A. 파라미터의 수에 변화를 주면서, B. Conv layer별 결과, C. 랜덤으로 초기화를 시킨 경우. 에 대한 실험 결과입니다. 당연하게, 파라미터의 수를 늘릴수록, 그림이 더욱 정밀해지구요. 해당 이미지에서는 상위 레이어를 활용했을 때 결과가 조금 더 좋았습니다. 마지막으로 랜덤으로 초기화를 시키는 경우에는 학습이 제대로 되지 않았죠.

  이제부터는, 'Understanding Deep Image Representations by Inverting Them(2015)'라는 논문에 대해 이야기해보겠습니다. 이건, Texture를 만드는게 아니라, Conv를 통해 이미지를 복원해보자라는 개념입니다. 그래서 오히려 더 간단합니다. 앞에서는 Gram matrix를 통해 두 레이어를 비교했다면, 이번에는 Featuremap 자체를 비교합니다. 그 자체를 비슷하게 만드는게 목표인거죠. 

 Recoetive Field라는게 결국은, 같은 픽셀이라고 하더라도, 상위 레이어에 있는 어떤 픽셀 하나가 입력 이미지에 미치는 영역이 점점 커지겠죠. 하위 레이어에서의 5X5는 일부만 보더라도, 상위 레이어에서의 5X5는 핵심 부분 전체를 다 볼 수 있다는 개념이죠. 정리하면, 내가 굵직한 특징을 보고 싶으면, 상위 레이어를 참조해야하고, 자잘자잘한 특징을 잡아 내고 싶다면, 하위 레이어를 참조하면되겠죠. 

 다음은 앞선 두 개의 논문을 섞은, 'A Neural Alkgorithm of Artistic Style(2015)'을 말해보겠습니다. 여기에는 제일 처음 말했던, Style image와 Content image 2개가 존재합니다. Neural art는 이 두개의 이미지를 섞은 이미지를 생성합니다.

 간단하겠죠. 두 개 이미지의 레이어 별 Gram matrix와 Feature map을 모두 비교하는. 그러니까 로스 펑션을 그에 알맞게 잘 짜주기만 한다면, 2개 논문을 결합한 출력 이미지를 얻을 수 있겠죠. 아래 그림에서는 상단의 Style 이미지에서 화풍에 대한 정보를 계속 쌓아올리고, 하단의 Content 이미지에서는 본래 이미지에 대한 정보를 계속 쌓아올려, 두 가지를 결합하는 모습을 확인할 수 있습니다.

 그래서 수식을 보면, Content image는 Feature map 자체를 비슷하게 만들고, Style image는 Gram matrix를 비슷하게 만들어서 아래와 같은 Total loss를 형성합니다. 핵심 개념은 이게 끝입니다. 아마 코드를 찾아보시면서 함께 학습하시면, 충분히 이해하실 수 있으실겁니다. 이상입니다.