ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • #17. Deep Learning & Art: Neural Style Transfer
    연구실 2019. 10. 18. 20:51

    - created by Gatys et al. (2015) (https://arxiv.org/abs/1508.06576).

     

     

    * Problem Statement

    - Neural Style Transfer: content image(C)와 style image(S)를 합쳐 generated image(G)를 만드는 알고리즘.

     

    * Transfer Learning

    - VGG 네트워크(VGG-19)를 이용해 모델을 만든다. 이 모델은 이미 ImageNet DB의 이미지들로 학습이 되어있는 상태이며, low level의 feature부터 high level feature까지 학습이 된 상태이다.

     

     

    * Neural Style Transfer

    (1) Content cost function: Jcontent(C, G)

    (2) Style cost function: Jstyle(S, G)

    (3) 두 함수를 합쳐준다.(J(G) = αJcontent(C, G) + βJstyle(S, G)

     

    (1) Computing the content cost

    1. Make generated image G match the content of image C

    - 얕은 레이어 vs 깊은 레이어

        - 얕은 layer일수록 lower-level  feature들을 탐지한다.(색깔, 엣지 등등)

        - 그에 비해 깊은 layer일 수록 high-level feature들을 감지한다.(복잡한 질감, 객체 클래스 등)

     

    - 중간 층의 activation layer를 선택하자.

        - G가 C와 비슷한 content를 가지게 만들어야 한다.

        - practically, 너무 얕지도, 깊지도 않은 middle 레이어를 선택하는 것이 가장 좋은 결과를 가진다.

     

    - Forward propagate image C

        - input image C를 미리 학습 된 VGG 네트워크에 넣고, forward propagation을 수행하자. a(c) 를 hidden layer의 activation이라고 하면, nH*nW*nC tensor의 모양을 가질 것이다.

     

    - Forward propagate image G

        - 이미지 G에 대해서도 같은 process를 반복한다.(G를 input으로 두고 forward propagation을 수행한다.)

        - a(G) -> corresponding hidden layer activation

     

    - Content Cost Function Jcontent(C, G)

    # GRADED FUNCTION: compute_content_cost
    
    def compute_content_cost(a_C, a_G):
        """
        Computes the content cost
        
        Arguments:
        a_C -- tensor of dimension (1, n_H, n_W, n_C), hidden layer activations representing content of the image C 
        a_G -- tensor of dimension (1, n_H, n_W, n_C), hidden layer activations representing content of the image G
        
        Returns: 
        J_content -- scalar that you compute using equation 1 above.
        """
        
        # Retrieve dimensions from a_G (≈1 line)
        m, n_H, n_W, n_C = a_G.get_shape().as_list()
        
        # Reshape a_C and a_G (≈2 lines)
        a_C_unrolled = tf.reshape(a_C, shape=[m, n_H * n_W, n_C])
        a_G_unrolled = tf.reshape(a_G, shape=[m, n_H * n_W, n_C])
        
        # compute the cost with tensorflow (≈1 line)
        J_content = (1/(4 * n_H * n_W * n_C)) * tf.reduce_sum(tf.square(tf.subtract(a_C_unrolled, a_G_unrolled)))
        
        return J_content

    - tf.reduce_sum: Computes the sum of elements across dimensions of a tensor.

    - tf.square: 제곱

    - tf.subtract: 빼기

     

    - content cost는 hidden layer의 activation을 가지고 a(C)와 a(G)가 얼마나 다른지 계산한다.

    - 그래서 이 cost를 줄이면 G가 C와 비슷한 content를 가지게 될 것.

     

     

    (2) Computing the style cost

    1. Style matrix

    - Style matrix(=Gram matrix)

        - 벡터의 집합(v1, ..., vn)으로 이뤄진  Gram matrix G는 dot product의 행렬이다(Gij = vi^Tvj = np.dot(v1, vj)

        - 즉 Gij는 vi가 vj와 얼마나 비슷한지 비교한다.(만약 비슷하면 큰 값을 가질 것이며, 따라서 Gij도 커질 것이다.)

     

    - compute Ggram

     

    - G(gram)i, j: correlation

        - result: a matrix of dimension (nc, nc)

        - 필터 i와 j의 activation이 얼마나 similar한지를 측정한다.

    - G(gram)i, i: prevalence of patterns or textures

        - G(gram)i, i는 필터 i가 얼마나 'active'한지를 측정한다.

        - 필터 i가 image의 vertical texture를 감지한다고 할 때, G(gram)i, i는 이미지 전체에서 vertical texture가 얼마나 common한지를 측정한다.

        - G(gram)i, i가 크면 그 이미지는 vertical texture를 많이 가지고 있다는 의미이다.

     

    - 다른 type의 feature들의 prevalence를 측정하고(G(gram)i, i), 또 그것들이 얼마나 자주 나타나는지를 측정하여(G(gram)i, j) Ggram은 그 이미지의 style을 측정하게 된다.

     

    # GRADED FUNCTION: gram_matrix
    
    def gram_matrix(A):
        """
        Argument:
        A -- matrix of shape (n_C, n_H*n_W)
        
        Returns:
        GA -- Gram matrix of A, of shape (n_C, n_C)
        """
        
        GA = tf.matmul(A, tf.transpose(A))
        
        return GA

     

    2. Style cost

    - style이미지와 generated이미지의 distance를 줄여야 한다.

    - style cost:

    # GRADED FUNCTION: compute_layer_style_cost
    
    def compute_layer_style_cost(a_S, a_G):
        """
        Arguments:
        a_S -- tensor of dimension (1, n_H, n_W, n_C), hidden layer activations representing style of the image S 
        a_G -- tensor of dimension (1, n_H, n_W, n_C), hidden layer activations representing style of the image G
        
        Returns: 
        J_style_layer -- tensor representing a scalar value, style cost defined above by equation (2)
        """
        
        ### START CODE HERE ###
        # Retrieve dimensions from a_G (≈1 line)
        m, n_H, n_W, n_C = a_G.get_shape().as_list()
        
        # Reshape the images to have them of shape (n_C, n_H*n_W) (≈2 lines)
        a_S = tf.transpose(tf.reshape(a_S, shape=[n_H * n_W, n_C]))
        a_G = tf.transpose(tf.reshape(a_G, shape=[n_H * n_W, n_C]))
    
        # Computing gram_matrices for both images S and G (≈2 lines)
        GS = gram_matrix(a_S)
        GG = gram_matrix(a_G)
    
        # Computing the loss (≈1 line)
        J_style_layer = (1 / (4 * n_C **2 * (n_H * n_W) **2)) * tf.reduce_sum(tf.square(tf.subtract(GS, GG)))
        
        ### END CODE HERE ###
        
        return J_style_layer

     

    3. Style Weights

    - 하나의 레이어에 대해 구한 style cost를 합치자!

    - 각 레이어에 대한 weight는 λ[[l]로 제공되며, 각 레이어가 그 스타일에 얼마나 영향을 미치고 있는지를 나타낸다.

    - Jstyle(S, G):

     

    (3) Defining the total

     

    * Solving the optimization problem

    - 전체적인 작동 흐름은 다음과 같다:

        (1) Interactive Session을 만든다.

        (2) Content, Style image를 load한다,

        (3) image G를 랜덤하게 초기화시킨다.

        (4) VGG19 모델을 올린다.

        (5) Tensorflow 그래프를 만든다.

            - content image를 모델에 넣어 content cost를 계산한다.

            - style image를 모델이 넣어 style cost를 계산한다.

            - total cost를 계산한다.

            - optimizer와 learning rate를 정의한다.

        (6) Tensorflow 그래프를 초기화시킨 다음 반복하여 learning시켜 generated image를 업데이트 시킨다.

     

     

    (1) Interactive Sessions

    # Reset the graph
    tf.reset_default_graph()
    
    # Start interactive session
    sess = tf.InteractiveSession()

     

    (2) Content image / Style image

    content_image = scipy.misc.imread("images/louvre_small.jpg")
    content_image = reshape_and_normalize_image(content_image)
    style_image = scipy.misc.imread("images/monet.jpg")
    style_image = reshape_and_normalize_image(style_image)

     

    (3) Generated image correlated with content image

    - 랜덤하게 초기화하기 때문에 content image와 거의 연관되어있지 않다. 하지만 이것이 generated image가 더 빨리 content image와 연관될 수 있도록 만들어주게 된다.

    generated_image = generate_noise_image(content_image)
    imshow(generated_image[0]);

     

    (4) Load pre-trained VGG19 model

    model = load_vgg_model("pretrained-model/imagenet-vgg-verydeep-19.mat")

     

    (5) Content Cost

    - 예시에서는 conv4_2 layer를 사용해 content cost를 계산할 것이다.

    # Assign the content image to be the input of the VGG model.  
    sess.run(model['input'].assign(content_image))
    
    # Select the output tensor of layer conv4_2
    out = model['conv4_2']
    
    # Set a_C to be the hidden layer activation from the layer we have selected
    a_C = sess.run(out)
    
    # Set a_G to be the hidden layer activation from same layer. Here, a_G references model['conv4_2'] 
    # and isn't evaluated yet. Later in the code, we'll assign the image G as the model input, so that
    # when we run the session, this will be the activations drawn from the appropriate layer, with G as input.
    a_G = out
    
    # Compute the content cost
    J_content = compute_content_cost(a_C, a_G)

     

    (6) Style cost

    # Assign the input of the model to be the "style" image 
    sess.run(model['input'].assign(style_image))
    
    # Compute the style cost
    J_style = compute_style_cost(model, STYLE_LAYERS)

     

    (7) Optimizer

    - Adam optimizer를 이용할 것이다.

    # define optimizer (1 line)
    optimizer = tf.train.AdamOptimizer(2.0)
    
    # define train_step (1 line)
    train_step = optimizer.minimize(J)

     

    - 통합 코드는 다음과 같다.

    def model_nn(sess, input_image, num_iterations = 200):
        
        # Initialize global variables (you need to run the session on the initializer)
        ### START CODE HERE ### (1 line)
        sess.run(tf.global_variables_initializer())
        ### END CODE HERE ###
        
        # Run the noisy input image (initial generated image) through the model. Use assign().
        ### START CODE HERE ### (1 line)
        generated_image = sess.run(model["input"].assign(input_image))
        ### END CODE HERE ###
        
        for i in range(num_iterations):
        
            # Run the session on the train_step to minimize the total cost
            ### START CODE HERE ### (1 line)
            sess.run(train_step)
            ### END CODE HERE ###
            
            # Compute the generated image by running the session on the current model['input']
            ### START CODE HERE ### (1 line)
            generated_image = sess.run(model["input"])
            ### END CODE HERE ###
    
            # Print every 20 iteration.
            if i%20 == 0:
                Jt, Jc, Js = sess.run([J, J_content, J_style])
                print("Iteration " + str(i) + " :")
                print("total cost = " + str(Jt))
                print("content cost = " + str(Jc))
                print("style cost = " + str(Js))
                
                # save current generated image in the "/output" directory
                save_image("output/" + str(i) + ".png", generated_image)
        
        # save last generated image
        save_image('output/generated_image.jpg', generated_image)
        
        return generated_image

    댓글

©hyunbul