GAN

Neural Style Transfer

술임 2022. 5. 29. 01:41

뉴럴 스타일 트랜스퍼

: 훈련 세트를 사용하지 않고 이미지의 스타일을 다른 이미지로 전달함

https://arxiv.org/abs/1508.06576

 

A Neural Algorithm of Artistic Style

In fine art, especially painting, humans have mastered the skill to create unique visual experiences through composing a complex interplay between the content and style of an image. Thus far the algorithmic basis of this process is unknown and there exists

arxiv.org

-> 3부분으로 구성된 손실 함수의 가중치 합을 기반으로 작동

1) 콘텐츠 손실

합성된 이미지는 베이스 이미지의 콘텐츠를 동일하게 포함해야함

 

2) 스타일 손실

합성된 이미지는 스타일 이미지와 동일한 일반적인 스타일을 가져야함

 

3) 총 변위 손실

합성된 이미지는 픽셀처럼 격자 무늬가 나타나지 않고 부드러워야함

 

-> 경사 하강법으로 이 손실을 최소화함. 많은 반복을 통해 손실 함수의 음의 그래디언트 양에 비례해서 픽셀값을 업데이트함

- 반복이 진행됨에 따라 손실은 점차 줄어들어 베이스 이미지의 콘텐츠와 스타일 이미지를 합친 합성 이미지를 얻게됨

 

콘텐츠 손실

: 콘텐츠의 내용과 전반적인 사물의 배치 측면에서 두 이미지가 얼마나 다른지 측정

- 비슷한 장면을 담은 두 이미지는 완전히 다른 장면을 포함하는 두 이미지보다 손실이 작아야함

- 픽셀값을 비교하는 것만으로는 부족함. 콘텐츠 손실은 개별 픽셀값과는 무관해야함

- 건물, 하늘, 강과 같은 고차원 특성의 존재와 대략적인 위치를 기반으로 이미지를 점수화해야함

- 베이스 이미지와 현재 합성된 이미지에 대해 이 출력을 계산해서 그 사이 평균제곱 오차를 측정하면 콘텐츠 손실 함수가 됨

 

cf. 딥러닝 대전재

이미지 내용 인식하도록 훈련된 신경망은 이전 층의 단순한 특성을 결합해서 네트워크의 더 깊은 층에서 자연스럽게 더 높은 수준의 특성을 학습함

이미지의 내용 식별하도록 성공적으로 훈련된 심층 신경망이 필요함

네트워크의 깊은 층으로부터 주어진 입력 이미지에 대한 높은 수준의 특성 추출 가능

 

 

from keras import backend as K

target_image = K.constant(preprocess_image(target_image_path))
style_reference_image = K.constant(preprocess_image(style_reference_image_path))

# 베이스 이미지와 스타일 이미지를 위한 2개의 케라스 변수와 생성된 합성 이미지를 담을 플레이스홀더
combination_image = K.placeholder((1, img_height, img_width, 3))

# 세 개의 이미지를 하나의 배치로 합칩니다
input_tensor = K.concatenate([target_image,
                              style_reference_image,
                              combination_image], axis=0)

# 세 이미지의 배치를 입력으로 받는 VGG 네트워크를 만듭니다.
# 이 모델은 사전 훈련된 ImageNet 가중치를 로드합니다
# 입력 텐서와 사용할 가중치를 지정해서 VGG19 모델의 객체 만듦
model = vgg19.VGG19(input_tensor=input_tensor,
                    weights='imagenet',
                    include_top=False) # 이미지 분류를 위한 네트워크의 마지막 완전 연결 층의 가중치를 사용하지 않음

# 층 이름과 활성화 텐서를 매핑한 딕셔너리
outputs_dict = dict([(layer.name, layer.output) for layer in model.layers])
# 콘텐츠 손실에 사용할 층 
# 더 낮은 층이나 더 깊은 층을 선택하면 손실 함수가 정의하는 콘텐츠에 영향을 미침
# 생성된 합성 이미지의 성질이 바뀜
content_layer = 'block5_conv2'
layer_features = outputs_dict[content_layer]
target_image_features = layer_features[0, :, :, :]
combination_features = layer_features[2, :, :, :]

def content_loss(base, combination):
    return K.sum(K.square(combination - base))

# 두 이미지에 대한 층의 출력 간에 거리 제곱 합을 계산하고 가중치 파라미터를 곱해서 콘텐츠 손실을 얻음
content_loss += content_weight * content_loss(target_image_features,
                                      combination_features)

 

스타일 손실

- 스타일이 비슷한 이미지는 특정 층의 특성 맵 사이에 동일한 상관관계 패턴을 가진다는 아이디어를 기반으로함

- 특성 맵이 얼마나 동시에 활성화되는지를 수치적으로 측정하려면 특성 맵을 펼치고 스칼라곱을 계싼함

- 계산된 값이 크면 상관관계가 큼

- 베이스 이미지와 합성된 이미지에 대해 네트워크의 여러층에서 그람 행렬(층에 있는 모든 특성 사이의 스칼라곱을 담은 행렬)을 계산해야함

- 두 그람 행렬의 제곱 오차 합을 사용해서 유사도 비교함

- 두 이미지의 전체 스타일 손실은 크기가 다른 여러 층에서 계산한 스타일의 가중치 합

def gram_matrix(x):
    features = K.batch_flatten(K.permute_dimensions(x, (2, 0, 1)))
    gram = K.dot(features, K.transpose(features))
    return gram


def style_loss(style, combination):
    S = gram_matrix(style)
    C = gram_matrix(combination)
    channels = 3
    size = img_height * img_width
    return K.sum(K.square(S - C)) / (4. * (channels ** 2) * (size ** 2))

# 스타일 손실에 사용할 층
# VGG19 모델의 5개 블록에 있는 첫 번째 합성곱 층
style_layers = ['block1_conv1',
                'block2_conv1',
                'block3_conv1',
                'block4_conv1',
                'block5_conv1']

for layer_name in style_layers:
    layer_features = outputs_dict[layer_name]
    
    # 입력 텐서에서 스타일 이미지의 특성 맵과 합성된 이미지의 특성 맵을 추출함
    style_reference_features = layer_features[1, :, :, :]
    combination_features = layer_features[2, :, :, :]
    sl = style_loss(style_reference_features, combination_features)
    
    # 가중치 파라미터와 계산에 참여한 층의 개수로 스타일 손실의 스케일 조정
    loss += (style_weight / len(style_layers)) * sl
loss += total_variation_weight * total_variation_loss(combination_image)

 

총 변위 손실

: 합성된 이미지에 있는 잡음 측정

- 오른쪽으로 한 픽셀 이동하고 원본 이미지와 이동한 이미지 간의 차이를 제곱해서 더함

- 동일 작업을 한 픽셀 아래로 이동해서 수행, 두 항의 합이 총 변위 손실

 

def total_variation_loss(x):
    a = K.square(
        x[:, :img_height - 1, :img_width - 1, :] - x[:, 1:, :img_width - 1, :])
    b = K.square(
        x[:, :img_height - 1, :img_width - 1, :] - x[:, :img_height - 1, 1:, :])
    return K.sum(K.pow(a + b, 1.25))

 

훈련

result_file = './data/neural_style_transfer/style_transfer_result.png'
iterations = 1000

# 뉴럴 스타일 트랜스퍼의 손실을 최소화하기 위해 생성된 이미지에 대해 L-BFGS 최적화를 수행합니다
# 초기 값은 타깃 이미지입니다
# scipy.optimize.fmin_l_bfgs_b 함수가 벡터만 처리할 수 있기 때문에 이미지를 펼칩니다.
x = preprocess_image(target_image_path)
x = x.flatten()
for i in range(iterations):
    x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x, # evaluator는 전체 손실과 입력 이미지에 대한 손실의 그래디언트 계산
                                     fprime=evaluator.grads, maxfun=20)
                                     
    # L-BFGS-B 알고리즘을 사용해서 경사 하강법의 한 스텝을 수행함
    if i % 100 == 0:
        print('.', end=' ')
        print('현재 손실 값:', min_val)

 

손실 함수의 가중치 파라미터나 콘텐츠 유사도를 결정하는데 사용할 층을 바꿀 수 있음

-> 합성 이미지와 훈련 속도에 어떻게 영향을 미치는지 확인해 볼 것

 스타일 손실 함수에 있는 각 층에 부여된 가중치를 줄여서 더 미세한 스타일이나 더 큰 스타일 특성 따르도록 조정 가능함

'GAN' 카테고리의 다른 글

CycleGAN  (0) 2022.05.26
WGAN, WGAN-GP  (0) 2022.05.23
GAN 생성적 적대 신경망  (1) 2022.05.20
딥러닝  (0) 2022.05.17
생성 모델링  (0) 2022.05.14