Home CS231N chapter 12 - Visualizing and Understanding
Post
Cancel

CS231N chapter 12 - Visualizing and Understanding

11강 리뷰

1) detection

2) Classification + Localization

3) segmentation

  • instance segmentation , semantic segmentation



What`s going on inside ConvNets?

ConvNet 안에서 어떤 일이 일어나고 있는지 살펴보고자 한다. 이를 알기 위해서 주로 Visualizing 기법을 활용한다. 즉, 딥러닝 내부를 시각화해서 살펴보는 것이다.

CNN의 입력은 이미지고, 출력은 class score 형태로 나온다. score 뿐만 아니라 bounding box의 위치, labeled pixel와 같은 형태의 출력도 있다.

image

alexNet을 예시로 살펴보면, 첫번째 conv layer에는 많은 필터(64개)들이 있다. conv filter는 sliding window로 이미지를 돈다. image와 weight의 내적이 첫번째 layer의 출력이 된다.

이 필터를 단순히 시각화시키는 것만으로도 이 필터가 이미지에서 무엇을 찾고 있는지 알아낼 수 있다. 11x11x3 필터가 총 64개 이므로 64개의 11x11 이미지를 시각화시킬 수 있다.

edge(흰/검)나 보색(oppising colors)(초록/분홍 등) 성분이 주로 검출되는 것을 볼 수 있다.

어떤 모델/데이터를 사용하든 첫번째 레이어는 이런 식으로 생겼다.


image

하지만 layer가 깊어질수록 해석하기 어려워 시각화를 통해 정보를 얻어내기가 쉽지 않다.

다음 layer의 가중치를 시각화한 것들은 전 layer의 출력을 가지고 최대화시키는 패턴에 대한 정보이기 때문이다.


image

CNN의 마지막 layer는 1000개의 class score를 출력한다. 이는 학습 데이터의 predicted score를 의미한다.

마지막 hidden layer를 시각화해서 어떤 일이 일어나는지 알아보는 것도 한 방법이 될 수 있다. 여기서는 각 이미지에서 나온 4096dim 특징 벡터를 저장한다.

Nearest Neighbor

시각화하는 방법 중 하나는 nearest neighbor을 이용하는 것이다. 픽셀 단위로 사용하면 정확도가 높지 않을 수 있지만, CNN 모델에서 마지막 hidden layer의 4096dim 특징 벡터들에 nearest neighbor을 적용하면 객체의 방향이나 위치가 서로 다르더라도 올바르게 분류하고 있음을 볼 수 있다.


Dimensionality Reduction

차원 축소(dimensionality reduction)의 관점으로 시각화할 수도 있다.

  • Principle Component Analysis

PCA는 4096dim 과 같은 고차원 특징벡터들을 2dim으로 압축시키는 기법이다. 이 방법을 통해 특징 공간을 조금 더 직접적으로 시각화할 수 있다.

  • t-distributed stochastic neighbor embeddings

t-SNE이라는 알고리즘은 PCA보다 조금 더 파워풀하다. 많은 사람들이 특징공간을 시각화하기 위해서 더 사용하는 방법이다.


image

MNIST를 t-SNE dimensionality reduction 를 통해 시각화한 모습이다. MNIST는 0부터 9까지로 이루어진 손글씨 숫자 데이터셋이다. MNIST의 각 이미지는 gray scale 28x28이고, t-SNE는 MNIST의 28x28 dim 데이터를 입력으로 받는다.(raw pixels) 그리고 2dim으로 압축한다.

이런 식으로 자연스럽게 군집화된 모습을 볼 수 있다. 각 군집은 MNIST의 각 숫자를 의미한다.


image

이미지에서 4096dim을 2dim으로 군집화시킨 후 차원 축소한 모습이다.



한 이미지당 서로 다른 세 가지 정보가 존재한다. 우선 (픽셀로 된) 원본 이미지가 있다. 그리고 4096dim 벡터가 있고, t-SNE를 이용해 2dim벡터로 변환시킨 값이 있다. 결국 원본 이미지를 CNN으로 4096dim으로 줄이고 이를 다시 t-SNE을 통해 2dim으로 줄였다고 할 수 있다.



다시 alexNet로 돌아가서 alexNet의 conv5의 특징은 128x13x13 dim tensor이다. 이 tensor는 128개의 13x13 2 dim grid로 볼 수 있다. 따라서 이 13x13 특징맵을 gray scale 이미지로 시각화해볼 수 있다.

image

사람의 얼굴을 CNN에 넣게되면 특징맵이 얼굴에 활성화된 것을 볼 수 있다.

Q. 검은색은 “Dead ReLU”인가?

Dead ReLU는 모든 학습 데이터셋에 대해서 DEAD(활성화되지 않음)을 의미한다. 하지만 이 예시에서는 특정 입력에 대해서 활성화되지 않을 뿐이다.

이 초록색 박스 하나가 activation map이다. 이처럼 activation을 시각화해도 해석이 가능할 수 있다.



Maximally Activating Patches

visualizing intermediate feature의 다른 방법으로 iuput image의 특정 patch를 시각화한 것이다.

Occlusion experiment

image

입력의 어떤 부분이 분류를 결정짓는 근거가 되는지에 대한 실험이다.

입력 이미지의 일부를 가리고 가린 부분을 데이터셋의 평균 값으로 채운다. 그런 후 이미지를 네트워크에 통과시키고 네트워크가 이 이미지를 예측한 확률(결과)을 기록한다. 이 과정을 occlusion patch 를 이동시키면서 같은 과정을 반복한다.

오른쪽 히트맵은 이미지를 가린 patch의 위치에 따른 네트워크의 예측 확률의 변화를 의미한다. patch의 위치에 따라 스코어가 크게 변화한다면 그 부분이 분류를 결정짓는 중요한 부분일 수 있다.


위의 사진 오른쪽 부분에 occlusion 실험을 수행한 세 가지 예시가 있다.

빨간색 지역은 확률 값이 낮고, 노란색 지역은 확률 값이 높음을 의미한다. go-kart를 보면 앞쪽의 kart를 가릴 경우 kart에 대한 확률이 많이 감소됨을 볼 수 있다. 이를 통해 네트워크가 분류를 결정할 때 실제로 kart를 많이 고려한다는 사실을 알 수 있다.


위의 방법들이 성능을 좌우하는 큰 작업은 아닐 수 있으나 네트워크가 무엇을 하고 있는지 이해하는 도구로서 사용할 수 있다. 결국 이런 시각화는 네트워크의 성능을 높히려는 목적이 아니라 이해가 목적이다.

Saliency Map

image

입력 이미지가 들어와 이 이미지를 “개”라고 예측했을 때, 우리가 알고싶은 것은 네트워크가 픽셀들을 보고 이미지를 “개”라고 분류했는지다. 앞서 occlusion 방법과는 조금 다른 접근법을 취한다. 즉, 어떤 픽셀이 “개”라고 분류하는데 있어서 가장 필요한지를 알 수 있는 방법이다.

입력 이미지의 각 픽셀들에 대해 예측한 class score의 gradient를 계산하고 절대값을 취한다.

위의 사진을 보면 네트워크가 이미지에서 어떤 픽셀을 찾고 있는지 볼 수 있다. 개의 위치가 있는 픽셀이 활성화 되어 있음을 알 수 있다.


image

saliency map은 semantic segmentation에도 사용할 수 있다. segmentation label없이도 grabcut 이라는 알고리즘을 사용해 segmentation mask를 씌울 수 있다. 하지만 잘 되지는 않는다. supervision을 가지고 학습을 시키는 네트워크에 비해 성능이 좋지 않다.


Guided backpropagation

image

이제는 class scor이 아니라 네트워크 중간 뉴런을 고른다. 그리고 입력 이미지의 어떤 부분이 내가 선택한 중간 뉴런의 값에 영향을 주는지를 찾는다.

이 경우에도 saliency map을 만들어볼 수 있다. 이미지의 각 픽셀에 대해 class score의 gradient를 계산하는 것이 아닌 입력 이미지의 각 픽셀에 대한 네트워크 중간 뉴런의 gradient를 계산한다.

이를 통해 어떤 픽셀이 해당 뉴런에 영향을 주는지 알 수 있다. 이 경우 평범한 back propagation을 사용한다. 대신 backprop시에 ReLU를 통과할 때, 조금의 트릭(변형)을 가미한다고 해서 “guided backpropagation”이라 한다.

ReLU의 gradient가 양수면 그대로 통과하고, 음수면 backprop를 하지 않는다. 이로 인해 전체 네트워크가 실제 gradient를 이용하는 것이 아닌 양의 부호 gradient만 사용한다.


위의 방법들은 입력 이미지에 대한 영향을 보는 것이기에 입력 이미지에 의존하지 않는 방법이 있는지 살펴보고자 한다.

입력 이미지가 아닌 다른 이미지로 해당 뉴런을 활성화시킬 수 있을까?

Gradient Ascent

그에 대한 해답으로 gradient ascent를 제시할 수 있다.

image

우리는 지금까지 loss를 최소화시켜 네트워크를 학습시키기 위해 gradient decent를 사용했다. 하지만 여기서는 네트워크의 가중치들을 전부 고정시킨다. 그 후 gradient ascent를 통해 중간 뉴런 혹은 class score를 최대화시키는 이미지의 픽셀들을 만들어낸다.

gradient ascent는 가중치를 최적화하는 것이 아닌 뉴런 또는 class score가 최대화될 수 있도록 입력 이미지의 픽셀 값을 바꿔주는 방법이다. 이 방법을 위해서는 regularization term이 필요하다. 원래는 과적합을 방지하기 위해서 사용하는데, 이 경우 생성된 이미지가 특정 네트워크의 특성에 완전히 과적합되는 것을 방지하기 위해 사용된다.

regularization term을 추가함으로써 우리는 생성된 이미지가 두 가지 특성을 따르길 원한다. 하나는 이미지가 특정 뉴런의 값을 최대화시키는 방향으로 생성, 다른 하나는 이미지가 자연스러워 보여야 한다는 것이다. regularization term을 통해 생성된 이미지가 자연스럽도록 강제한다.


image

먼저 초기 이미지가 필요하다. 이를 zeros, uniform, noise 등으로 초기화한다. 그 후 이미지를 네트워크에 통과시키고 한 뉴런의 score를 계산한다. 그리고 이미지의 각 픽셀에 대한 해당 뉴런 score의 gradient를 계산하여 backprop를 수행한다. 그 다음 gradient ascent를 통해 이미지 픽셀 자체를 업데이트 한다. 해당 스코어를 최대화시키는 것이다.

이 과정을 반복하면 이미지가 만들어진다.

image

여기에서는 단순하게 생성된 이미지에 대한 L2 norm을 계산하여 더해준다. norm을 수행해주지 않을 경우 이미지가 아무것도 활성화되지 않은 것처럼 보일 수 있다. norm을 통해 이미지가 조금 더 자연스럽게 생성될수 있도록 해준다.

덤벨, 컵, 달마시안 등이 생성되었는데, 달마시안을 보면 검/흰 반점 무늬를 볼 수 있다. 레몬의 경우 노란색도 보인다.

이 시각화 방법으로 실제 색상을 시각화하는 것은 매우 까다롭다.


image

norm + 최적화 과정에 이미지에 주기적으로 가우시안 블러를 적용하고, 주기적으로 값이 작은 픽셀들은 모두 0으로 만들면 더 인상적인 이미지를 얻을 수 있다. 낮은 기울기의 픽셀 값과 gradient가 작은 값들을 모두 0으로 만드는 것이다. 이는 일종의 projected gradient descent라고 볼 수 있다.

가우시안 블러는 이미지를 주기적으로 매핑시키는 smooth 연산이다. 이를 통해 더 깔끔하게 만들 수 있다.


image

각 layer가 어떤 부분을 찾고 있는지를 볼 수 있다.


  • Multi-faceted

각 클래스마다 클러스터링 알고리즘을 수행한다. 한 클래스 내 서로 다른 모드들끼리 다시 한번 클래스가 나뉜다. 나뉜 모드들과 가까운 곳으로 초기회홰주는 것이다. 이 방법을 통해 multimodality를 다룰 수 있다.

image

예를 들어 8개의 이미지가 있다고 할 때, 이 8개 모두 식료품점에 대한 것이다. 위의 4개 이미지는 클로즈업한 사진들이다. 하단의 4개는 사람들이 식료품점을 돌아다니는 모습인 듯하다. 이 또한 식료품점으로 라벨링된다. 두 가지가 서로 다르지만 많은 클래스들이 이와 같이 multimodality를 가지고 있다.


이미지를 생성할 때 multymodality를 명시하면 더 좋은 결과를 얻을 수 있다.


이미지를 더 완전하게 생성하기 위해 사전 정보를 이용할 수도 있다.

image

이 이미지들 모두 ImageNet의 특정 클래스를 최대화하는 이미지를 생성한 것이다. 입력 이미지의 픽셀을 곧장 최적화하는 대신 FC6을 최적화한다. 이를 위해 feature inversion network 등을 사용한다. 하지만 여기서는 설명하지 않겠다.

그러나, 요점은 이런 이미지 생성 문제에서 사전 지식(prior)를 추가하면 아주 리얼한 이미지를 만들 수 있다.


이미지 픽셀의 gradient를 이용해 이미지를 합성하는 방법은 강력하다. 이 방법으로 할 수 있는 것이 바로 네트워크를 속이는 이미지(fooling image)를 만드는 것이다.

image

일단 아무 이미지를 하나 골라 네트워크가 이 사진을 다른 클래스로 분류하도록 미지리를 살짝 바꾼다. 그러면 네트워크는 이 이미지를 다른 클래스로 할 것이다. 우리에게는 다를 바 없지만 네트워크는 완전 다른 분류를 한다.



이 모든 것들을 한 이유는 중간 뉴런을 이해하는 것이 최종 클래스 분류를 이해하는데 어떻게 도움을 줄 수 있는가에 대한 해답이다. 이런 시각화 기법들은 왜 딥러닝이 분류 문제를 잘 푸는지에 대한 의문에서 시작되었다. 딥러닝 모델이 아무렇게나 분류하는 것이 아니라 의미있는 행동을 통해 분류한다는 것을 증명하기 위함이다.


이미지에 gradient를 업데이트하는 방식으로 가능한 아이디어가 하나 더 있다.

DeepDream

image

DeepDream의 목적은 재미있는 이미지를 만드는 것이다. 부가적으로 모델이 이미지의 어떤 특징들을 찾고 있는지를 짐작할 수 있다. deepderam에서는 입력 이미지를 CNN의 중간 레이어 정도를 통과시킨다. 그 후 backprop를 하며 이미지를 업데이트한다. 네트워크에 의해 검출된 해당 이미지의 특징들을 증폭시키려는 것이다.

여기에는 여러 트릭이 존재한다. 첫번째는 gradient를 계산하기 앞서 이미지를 조금씩 움직이는 것이다(jitter). 원본 이미지를 그대로 통과하지 않고 두 픽셀정도 이동시킨다. 이는 regularizer 역할을 함으로써 자연스럽고 부드러운 이미지를 만들게 한다. 그리고 L1 norm도 들어간다. 이는 이미지 합성 문제에서 아주 유용한 트릭이다. 그리고 픽셀 값을 한 번 클리핑(clipping)해주기도 한다. 이미지라면 값이 0~255 사이에 있어야만 한다. 이는 일종의 projected gradient decent인데, 실제 이미지가 존재할 수 있는 공간으로 매핑시키는 방법이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def Ojbective_L2(dst):
    dst.diff[:] = dst.data

def make_step(net, step_size = 1.5, end='inception_4c/output', jitter=32, clip=True, objective=objective_L2):
    '''Basic gradient ascent step'''

    src = net.blobs['data'] # input image is stored in Net's data blob
    dst = net.blobs[end]

    ox, oy = np.random.randint(-jitter, jitter+1, 2) # jitter image
    src.data[0] np.rool(np.roll(src.data[0], ox, -1), oy, -2) # apply jitter shift 

    net.forward(end=end)
    objective(dst) # specify the optimization objective
    net.backward(start=end)
    g= src.diff[0]
    # apply normalized ascent step to the input image
    src.data[:] += step_size/np.abs(g).mean() * g # Li normalize gradients
    src.data[0] = np.roll(src.data[0], -ox, -1), -oy, -2) # unshift image # jitter image
    if clip:
        bias = net.transformer.mean['data']
        src.data[:] = np.clip[src.data, -bias, 255-bias] # clip pixel values


이렇게 하늘 이미지를 가지고 알고리즘을 수행시키면 이처럼 아주 재미있는 결과를 볼 수 있다.


Feature Inversion

image

이 방법 또한 네트워크의 다양한 layer에서 입력 이미지의 어떤 요소들을 포착하고 있는지를 볼 수 있게 해준다.

어떤 이미지가 있고, 이 이미지를 네트워크에 통과시킨 후 특징맵(activation map)을 저장한다. 그 후 특징맵만 가지고 이미지를 재구성한다. 해당 레이어의 특징 벡터로부터 이미지를 재구성해보면, 이미지의 어떤 정보가 특징 벡터에서 포착되는지를 알 수 있다. 이 방법에서 또한 regularizaer를 추가한 gradient ascent를 이용한다. score를 최대화시키는 것 대신 특징 벡터간의 거리를 최소화하는 것이다.

기존에 계산해놓은 특징 벡터와 새롭게 생성한 이미지로 계산한 특징벡터 간의 거리를 측정한다. 여기서는 regularizer을 total vatication을 사용한다. total variation regularizer은 상하좌우 인접 픽셀 간의 차이에 대한 패널티를 부여한다. 이는 생성된 이미지가 자연스러운 이미지가 되도록 해준다.

image

feature inversion을 통한 시각화 예제는 위와 같다. 왼쪽이 원본 이미지다. 이 이미지를 VGG-16에 통과시킨다. 특징맵을 기록하고, 기록된 특징맵과 부합하도록 하는 새로운 이미지를 합성한다.

다양한 layer를 이용해 합성한 이미지를통해 얼마나 많은 정보들이 저장되어 있는지 짐작할 수 있다.

relu2_2를 보면 기존의 이미지 정보가 거의 담겨있다. relu4_3이나 relu5_1과 같이 더 깊은 곳을 가보면 이미지의 공간적 구조는 잘 유지되어 있으나 디테일이 많이 날아간 것을 볼 수 있다. 네트워크가 깊어질수록 feature를 잃어간다.



이제까지 style transfer을 배우기 위한 준비단계를 배웠다. style transfer과 feature inversion외에도 texture 합성과 관련된 문제도 살펴보고자 한다.

Texture Synthesis

image

여기 보이는 비늘 무늬와 같은 입력 패턴(texture) patch가 있을 때 동일한 패턴(texture)의 더 큰 patch를 생성한다.

nearest neighbor를 통한 패턴 합성(texture synthesis)도 좋은 방법이다. 이 방법에는 신경망을 사용하지 않고, scan line을 따라서 한 픽셀씩 이미지를 생성해낸다. 현재 생성해야 할 픽셀 주변에 이미 생성된 픽셀을 살핀 후, 입력 패치에서 가장 가까운 픽셀을 계산하여 입력 패치로부터 한 픽셀을 복사해 넣는 방식이다.

기본적으로 패턴 합성(texture synthesis)은 신경망 없이도 할 수 있다. 하지만 좀 복잡한 패턴에서는 상황이 다르다.

image

단순하게 입력 패치를 복사하는 방식은 잘 동작하지 않는다. 신경망을 활용해서 패턴 합성하는 방법은 앞서 살펴본 특징맵을 이용한 gradient ascent 방법과 상당히 유사하다.


Neural Texture Synthesis

nerual texture synthesis 를 구현하기 위해 gram matrix라는 개념을 이용한다.

위의 사진과 같이 자갈 사진이 있다고 했을 때, 이 사진을 네트워크에 통과시킨 후 네트워크의 특정 layer에서 특징맵(activation map)을 가져온다. 이 가져온 특징 맵의 크기는 C x H x W 다. H x W 그리드는 공간 정보를 가지고 있고, C는 차원을 의미한다. HxW의 크기를 가진 C차원의 특징 벡터는 해당 지점에 존재하는 이미지의 특징을 담고 있다. 이 특징맵을 통해 입력 이미지의 텍스트

image

특징맵에서 서로 다른 두 개의 특징 벡터(빨강, 파랑)를 뽑아낸다. 각 특징 열 벡터는 C차원 벡터다. 이 두 벡터를 외적해서 C x C 행렬을 만든다. 이 C x C 행렬의 (i,j) 번째 요소의 값이 크다는 것은 두 입력 벡터의 i번째, j번째 요소 모두 크다는 것을 의미한다.


image

이를 통해 서로 다른 공간에서 동시에 활성화되는 특징이 무엇인지 포착해낼 수 있다. 이 과정을 H x W 그리드에 전부 수행해서 평균을 계산해서 C x C gram matrix를 얻는다. 이 결과를 입력 이미지의 텍스처를 기술하는 텍스처 기술자로 사용한다.

gram matrix의 흥미로운 점은 공간 정보는 모두 날려버린다는 것이다. 이미지의 각 지점에 해당하는 값들을 모두 평균화하기 때문이다. 공간 정보를 날린 대신 특징들 간의 동시 발생을 볼 수 있다.

image

때문에 gram matrix는 texture 기술자(descriptor)로 적합하다. 계산은 C x H x W 차원의 3차원 텍서를 C x (HW)로 바꾼 후 한번에 계산하면 된다.

gram matrix 대신 공분산 행렬을 사용해도 되지만 공분산 행렬은 계산하는 비용이 너무 크다. 따라서 패턴 합성에서는 gram matrix를 더 많이 쓴다.


이미지를 직접 생성해보도록 하자. 이는 gradient ascent procedure와 유사한 과정을 거친다.

image

입력 이미지의 특징맵 전체를 재구성하기보다 gram matrix를 재구성하도록 한다.

image

이 때, 많은 사람들이 pretrained model로 VGG를 사용한다. 이미지를 VGG에 통과시키고 다양한 layer에서 gram matrix를 계산한다. 그리고 생성해야 할 이미지를 랜덤으로 초기화시키고, 그 다음 과정부터는 gradient ascent와 유사하다.

image

다시 이미지를 VGG에 통과시키고, 여러 layer에서 gram matrix를 계산하고, 원본 이미지와 생성된 이미지의 gram matrix간의 차이를 L2 norm을 이용해 loss를 계산한다. 그 후 loss를 backprop를 통해 생성된 이미지의 픽셀에 대한 gradient를 계산한다. gradient ascent를 통해 이미지의 픽셀을 조금씩 업데이트한다. 이 단계를 여러번 반복한다.

이 과정을 거치면 결국 입력 texture과 유사한 texture를 만들 수 있다.

image

맨 위 네 가지의 이미지가 있고, 맨 아래 gram matrix를 이용한 패턴 합성을 볼 수 있다. 얕은 layer에서의 결과를 보면 색상은 유지되지만 공간적 구조는 잘 살리지 못한다. layer가 깊어질수록 이미지의 공간적 구조를 잘 구현한 것을 볼 수 있다.


보통 loss를 어느 layer에서 계산하는가 하면, 일반적으로 gram matrix를 다양한 layer에서 계산하고 가중 합을 통해 최종 loss를 구한다.


이 gram matrix를 예술 작품에 적용해보자

image

생성된 이미지를 보면 예술작품의 아주 흥미로운 부분들을 재구성해내는 경향을 볼 수 있다.


image

texture synthesis와 feature inversion을 조합하면 더 흥미로운 일이 발생한다. 이 아이디어가 바로 style transfer이다.

Neural Style Transfer

image

style transfer에서는 입력이 두가지다. content image는 네트워크에게 우리의 최종 이미지가 어떻게 생겼으면 좋겠는지를 알려준다. style image는 최종 이미지의 패턴이 어떻게 생겼는지 알려준다. 최종 이미지는 content image의 특징 재구성(feature reconstruction) loss도 최소화하고, style image의 gram matrix loss도 최소화하는 방식으로 최적화하여 생성한다.

이 두가지 loss를 동시에 활용하면 style image스러운 content image가 생성된다.

image

네트워크에 content와 style 이미지를 네트워크에 통과시키고 gram matrix와 feature map을 계산한다. 최종 출력 이미지는 랜덤 노이즈로 초기화시킨다. forward/backward를 반복하여 계산하고 gradient ascent를 이용해서 이미지를 업데이트한다.


수 백번 정도 반복하면 좋은 결과를 얻을 수 있다.

image

코드


style transfer은 deepdream에 비해 이미지를 생성할 때 컨트롤할 만한 것들이 더 많다. deepdream의 경우 네트워크를 반복하면 특정 개체가 이미지 전체에 퍼질 뿐이다. style transfer의 경우 style image를 다양하게 할 수도 있고, 하이퍼파라미터도 자유롭게 조정가능하다. loss의 가중치를 조절하면 어디에 더 집중해서 만들지를 조절할 수 있다. 또 다른 하이퍼파라미터로 style image를 resize해서 넣어주면 다른 이미지의 결과를 도출해낼 수도 있다.


또 여러 style image를 가지고 style transfer할 수도 있다. 동시에 여러 style loss의 gram matrix를 계산하는 것이다.


image

또, style transfer 과 deepdream을 조합해볼 수도 있다. content loss + style loss + deepdream loss를 조합한다. 위의 사진은 개 달팽이가 사방에 퍼져있는 van gogh 그림이다.


Problem of Neural Style Transfer

style stransfer 알고리즘의 단점은 아주 느리다는 것이다. 이런 이미지를 만들기 위해서 backward/forward를 아주 많이 반복해야 한다. 고해상도 이미지를 만드는 것은 계산량이 엄청나기 때문에 엄청난 메모리와 계산량을 필요로 한다.


Fast Style Transfer

위의 방법에 대한 해결책이라 하면 style transfer을 위한 또 다른 네트워크를 학습시키는 것이다.

image

이 방법은 합성하고자 하는 이미지의 최적화를 전부 수행하는 것이 아니라 content image만을 입력으로 받아서 결과를 출력할 수 있는 단일 네트워크를 학습시키는 방법이다.

이 네트워크의 학습 시에는 content/style loss를 동시에 학습시키고 네트워크의 가중치를 업데이트한다. 학습은 오래걸리지만 한 번 학습시키고 나면 이미지를 네트워크에 통과시키면 결과가 바로 나올 수 있다.

image

이 네트워크가 효율적이어서 좋은 GPU로 하면 네 가지 스타일을 동시에 돌려볼 수 있다.

이 네트워크는 segmentation 네트워크와도 비슷하다. semantic segmentation에서는 다운 샘플링을 여러 번 하고 transposed conv로 업샘플링한다. semantic segmentation과 다른 점은 출력이 RGB라는 것이고, 네트워크 중간에 batch norm이 들어간다.


image

위의 네트워크들은 네트워크 하나당 하나의 style transfer 밖에 할 수 없다. 하지만 최근 논문에 의하면 하나의 네트워크로 다양한 style을 생성해낼 수 있는 방법이 있다고 한다. 이 네트워크는 실시간으로 동작할 수 있다. 서로 다른 style을 학습시키면 이 네가지 style을 섞을수도 있다.

This post is licensed under CC BY 4.0 by the author.