뉴럴 스타일 트랜스퍼
: 훈련 세트를 사용하지 않고 이미지의 스타일을 다른 이미지로 전달함
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 |