[딥러닝]- VGG19 모델 활용한 이미지 콜라보 만들기
https://www.kaggle.com/code/basu369victor/style-transfer-deep-learning-algorithm
Style Transfer Deep Learning Algorithm
Explore and run machine learning code with Kaggle Notebooks | Using data from multiple data sources
www.kaggle.com
위 사진 처럼
두 작품을 콜라보 하는 딥러닝 알고리즘이다
사용하는 모델은 vgg 19 모델이다
vgg 뒤에 붙은 숫자는 레이어의 개수이다
VGG 16 VGG19 는 16개의 레이어, 19개의 레이어를 갖는다
VGG19는 레이어가 19개 레이어로, 메모리 등 자원을 많이 소모한다
특징은 VGG 16이랑 비슷하다
VGG 16의 특징에 Conv 레이어가 3개 추가된 것
vggnet은 3*3 필터, 모든 conv 레이어에 사용한다 --> 작은 필터를 사용함으로 더 많은 ReLU함수를 사용할 수 있다 (많은 비선형성 확보)
import pandas as pd
import numpy as np
import os
from keras import backend as K
from keras.preprocessing.image import load_img, save_img, img_to_array
import matplotlib.pyplot as plt
from keras.applications import vgg19
from keras.models import Model
from scipy.optimize import fmin_l_bfgs_b
print9os.lisedir("../input"))
기본 이미지와 스타일 이미지 경로
StylePath = '../input/best-artworks-of-all-time/images/images/'
ContentPath = '../input/totoro/'
base_image_path = ContentPath + '84.jpg'
style_image_path = StylePath+ 'Pablo_Picasso/Pablo_Picasso_92.jpg'
width, height = load_img(base_image_path).size
img_nrows = 400
img_ncols = int(width * img_nrows/height)
vgg 19로 이미지 전처리
vgg 19는 가중치 imagenet.imagenet에서 사전 훈련된 모델 레이블 지정된 고해상도 이미지 데이터 세트
def preprocess_image(image_path):
from keras.applications import vgg19
img = load_img(image_path, target_size=(img_norws, img_ncols))
img = img_to_array(img)
img = np.expand_dims(img, axis =0)
img = vgg19.preprocess_input(img)
return img
이 함수는 load_img()함수를 사용해 image_path에서 이미지 로드하고 대상크기(img_nrows, img_ncols)로 크기 조정하고
img_to_array()를 사용해 numpy 배열로 변환한다
넘파이 배열은 (batch_size, img_nrows, img_ncols, channels)인 vgg19 모델에 대한 입력의 예상 모양과 일치하도록 expand_dims()사용해 추가 차원 확장
plt.figure()
plt.title('Base Image', fontsize=20)
img1 = load_img(ContentPath+'84.jpg')
plt.imshow(img1)
plt.figure()
plt.title("Style Image",fontsize=20)
img1 = load_img(StylePath+'Pablo_Picasso/Pablo_Picasso_92.jpg')
plt.imshow(img1)
이미지 텐서 표현
base_image = K.variable(preprocess_image(base_image_path))
style_reference_image = K.variable(preprocess_image(style_image_path))
K.variable() 함수는 넘파이 배열중 keras 변수 만드는데 사용된다
두 개의 keras 변수(base_image , style_reference_image) (사전처리된 이미지 포함하는 )있어야 코드실행
K.image_data_format()
위 함수는 keras 백엔드에 사용 중인 현재 데이터 형식 규칙 나타내는 문자열 반환
tensorflow 변수 프로그래밍 언어에서 사용하는 일반 변수 자리표시자는 초기값 불필요함
# 생성된 이미지 포함
if K.image_data_format() == 'channels_first':
combination_image = K.placeholder((1,3,img_nrows, img_ncols))
else:
combination_image = K.placeholder((1,img_nrows, img_ncols, 3))
만약 데이터 형식이 channels_first인 경우, 채널 차원이 첫 번째 차원임을 의미
(1,3,img_nrows, img_ncols) = 자리 표시자가 1개의 샘플(배치크기), 3개의 채널 및 지정된 이미지 치수가 있는 4d 텐서
데이터 형식이 channels_last인 경우,
1, img_nrows, img_ncols, 3)인 Keras 자리 표시자로 생성/ 1개의 샘플(배치크기), 지정된 이미지 크기 및 3개의 채널이 있는 4d 텐서
# 3개의 이미지 단일 keras tensor로 결합
input_tensor = K.concatenate([base_image,
style_reference_image,
combination_image
], axis=0)
아k.concatenate() 함수 세 개의 이미지를 하나의 텐서로 연결하기 위해 호출
[ ] 사용해 K.concatenate()함수에 리스트로 전달 / 함수에서 axis 매개변수 0으로 설정 --> 이렇게 하면 이미지 결과 텐서에서 다른 샘플로 결합될 수 있도록 첫 번째 축 따라 연결 수행되어야 함을 지정
vgg 19 모델 만들기
# building the vgg 19 model - 3개 이미지
# 모델은 사전훈련된 imagenet 가중치
from keras.applications.vgg19 import VGG19
vgg19_weights= '../input/vgg19/vgg19_weihts_tf_dim_ordering_tf_kernels_notop.h5'
model = VGG19(input_tensor = input_tensor,
include_top= False,
weights = vgg19_weights)
print('model loaded.')
keras 애플리케이션 모듈에서 vgg19 모델 클래스 가져옴 (클래스는 이미지 분류나 특징 추출과 같은 다양한 작업에서 사용할 수 있는 사전 훈련된 vgg 19 모델 제공)
vgg19_weight에 훈련된 가중치 파일 경로 할당 / 가중치는 모델 이미지에 의미 있는 기능 추출할 수 있도록 미리 훈련된 값으로 모델 초기화 하는데 사용
VGG19() 함수는 모델의 인스턴스 생성하기 위해 호출 / input_tensor 매개변수는 모델에 대한 입력으로 사용되는 이전에 정의된 input_tensor로 설정
include_top 매개변수 false 설정 = 분류 담당하는 vgg19 모델의 완전 연결계층(상위계층) 포함되지 않아야 함 나타낸다
최상위 레이어 제외하고 vgg19 모델 기능 추출과 같은 작업에 사용하거나 더 큰 모델 아키텍처의 일부로 사용
vgg 19는 원래 분류 목적, 하지만 우리는 분류가 아닌 이미지 변환이므로 vgg19의 모든 레이어는 필요하지 않다
분류에 사용되는 레이어 제거
content_layers = ['block5_conv2']
style_layers =[ 'block1_conv',
'block2_conv',
'block3_conv',
'block4_conv',
'block5_conv']
num_contetn_layers = len(content_layers)
num_style_layers = len(style_layers)
outputs_dict = dict([(layer.namer, layer.output) for layer in model.layers])
print(outputs_dict['block5_conv2']
출력값
> Tensor("block5_conv2/Relu:0", shape =(3,25,40, 512), dtype=float32)
The content loss
def get_content_loss(base_content, target):
return K.sum(K.square(target - base_contetn))
The style loss
import tensorflow as tf
def gram_matrix(input_tensor) :
assert K.ndim(input_tensor)==3
channels = int(input_tensorshape[-1])
a = tf.reshape(input_tensor, [-1, channels])
n = tf.shape(a)[0]
gram = tf. matmul(a, a, transpose_a=True)
return gram
def get_style_loss(style, combination):
assert K.ndim(style) ==3
assert K.ndim(combination) ==3
S = gram_matrix(style)
C = gram_matrix(combination) ==3
channels =3
size= img_nrows *img_ncols
return K.sum(K.square(S-C))
content_weight = 0.025
style_weight = 1.0
#combine these loss functions into a single scalar
loss= K.variable(0.0)
layer_features = outputs_dict['block5_conv2']
base_image_features = layer_features[0, :, :, :]
combination_features = layer_features[2, :, :, :]
print('layer feature for content layers :: '+str(layer_features))
print('base image feature ::'+str(base_image_features))
print('combination image feature for content layers:: '+str(combination_features)+'\n')
loss += content_weight * get_content_loss(base_image_features,
combination_features)
feature_layers = ['block1_conv1', 'block2_conv1',
'block3_conv1','block4_conv1',
'block5_conv1']
for layer_name in feature_layers:
layer_features = outputs_dict[layer_name]
style_reference_features = layer_features[1, :, :, :]
combination_features = layer_features[2, :,:,:]
print('Layer Feature for style layers:: '+str(layer_features))
print('style image feature ::'+str(style_reference_features))
print('combination image feature for style layers:: '+str(combination_features)+'\n')
sl = get_style_loss(style_reference_features, combination_features)
loss += (style_weight /len(feature_layers))*sl
|
|
|
|
|
생략
다양한 옵티마이저 사용했지만
ADAM 옵티마이저 사용 => 손실 최적화
최종 이미지 얻기
x_opt = preprocess_image(base_image_path)
def eval_loss_and_grads(x):
if K.image_data_format() == 'channels_first':
x = x.reshape((1,3, img_nrows, img_ncols))
else:
x = x.reshape((1, img_nrows, img_ncols, 3))
outs = f_outputs([x])
loss_value = outs[0]
if len(outs[1:]) ==1:
grad_values = outs[1].flatten().astype('float64')
else:
grad_values = np.array(outs[1:]).flatten().astype('float64')
return loss_value, grad_values
plt.figure(figsize=(30,30))
plt.subplot(5,5,1)
plt.title('base image', fontsize=20)
img_base= load_img(base_image_path)
plt.axis('off')
plt.imshow(img_base)
plt.subplot(5,5,1+1)
plt.title(' style image', fontsize=20)
img_style = load_img(style_image_path)
plt.axis('off') # 숫자 제거
plt.imshow(img_style)
plt.subplot(5,5,1+2)
plt.title('final image', fontsize=20)
plt.axis('off')
plt.imshow(imgx);
run_style_transfer 함수는 위 셀에서 부분적으로 논의된 위의 모든 코드의 조합
두 이미지 간의스타일 변환 후 최종 이미지
위의 예시처럼 완벽하지는 않다..
코드가 워낙 어려워 이해하기도 힘들다 ..!
모델을 돌려본 것에 만족 !