numpy를 이용하여 간단한 경사하강법 구현

테스트를 위한 x_data와 y_data를 만들었습니다. 

아마 예제는 sung kim 교수님의 유튜브 강좌에서 가져왔습니다.

logistic 의 비용함수와 결과값을 위한 sigmoid함수를 정의하였습니다.

sigmoid는 0 ~ 1의 값을 반환해주죠?

기억하시길 바랍니다.

num_examples로 평균값을 내려고 했으나 cost_function에서 mean()으로 대체하였습니다.

W, b는 데이터의 모양에 맞춰주었고, 사실 이런 부분은 x.shape[0], [1]과 같은 것으로 일반화 시켜주는 것이 좋지만 직관적인 이해를 위해서 나이브한 표현을 사용하였습니다.

  1. 정해준 max_iter만큼 반복을 할겁니다.

  2. w*x + b

  3. sigmoid

    1. 여기까지가 신경망을 구성하는 두 가지 부분인걸 아시죠? 

    2. 첫 번째로 가중치와 bias를 곱(더)해주고, 두번째로는 activation function에 통과시킵니다. 

  4. 다음으로 cost_function을 이용해 비용을 계산해 주고

  5. a - y_data는 역전파를 통해 계산해보시면 나오는 값입니다. 가중치와 바이어스 업데이트를 위해 필요한 값이죠

  6. 마지막으로 w와 b를 업데이트 시켜줍니다. lr 다음에 곱해주는 식 또한, 역전파를 통해 나오는 식입니다. 

 

 

'# Machine Learning > 글 공부' 카테고리의 다른 글

deconvolution(conv2Dtranspose) vs upsampling  (0) 2018.12.10
1x1 convolution이란,  (16) 2018.12.10
Logistic Regression 의 비용함수  (0) 2018.12.09
Multi Class vs Multi Label  (0) 2018.12.06
fine tuning 시 상위층과 하위층  (0) 2018.11.25


먼저 단일 변수에 대한 기본 비용함수는 m을 없앤 식을 생각하면 됩니다. 


이 비용함수는 binary classification에서 탁월한 성능을 보여줍니다. 


2가지로 구분하여 비용함수를 책정하게 되는데


1. if y = 0 :

이 때는 -log(1-y_hat)이 남게 됩니다. 결국 우리의 목표는 cost를 낮추는 것이기 때문에 y_hat이 작아지는 쪽으로 update를 하게 될 것입니다.


2. if y = 1 :

이 경우는 -log(y_hat)이 남게 됩니다. 위와 같이 cost를 낮추려면 y_hat을 1과 비슷하게 만들어야 겠죠? log1은 곧 0이니까요. 


이와 여러개의 변수에 관해서 구하고 싶다면 m으로 나눠주어 평균으로 비용함수를 책정하게 됩니다. 


직관적인 이해는 아래 그림을 참고하세요! ( 구글 검색 그림 )





'# Machine Learning > 글 공부' 카테고리의 다른 글

deconvolution(conv2Dtranspose) vs upsampling  (0) 2018.12.10
1x1 convolution이란,  (16) 2018.12.10
basic gradient(+backprop) with numpy  (0) 2018.12.09
Multi Class vs Multi Label  (0) 2018.12.06
fine tuning 시 상위층과 하위층  (0) 2018.11.25

과일이 주제가 되는 이미지에 사과, 배, 오렌지가 있다고 합시다.

Multi-Class는 이 과일 주제의 이미지에서 딱하나의 과일만 찾고 싶습니다.

하나를 찾기 위해선 당연히 다른 과일도 판단할 수 있어야겠지요. 반면에, Multi-Label은 사과도 찾고 싶고, 배도 찾고 싶은겁니다.

 

 

keras에서 각 상황에 맞는 loss function은 다음과 같습니다.

1. Multi-Class : categorical_crossentropy

2.Multi-Label : binary_crossentropy

 

마지막으로는 원본 이미지에 hitmap을 그리는 방법입니다.

- 예제에 쓰일 모델 로드

from tensorflow.python.keras.applications.vgg16 import VGG16

model = VGG16(weights = 'imagenet')

 

- VGG16을 위한 이미지 전처리

from tensorflow.python.keras.preprocessing import image
from tensorflow.python.keras.applications.vgg16 import preprocess_input, decode_predictions
import numpy as np

img_path = './datasets/creative_commons_elephant.jpg'
img = image.load_img(img_path, target_size = (224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis= 0) # (1, 224, 224, 3)
x = preprocess_input(x) # 데이터 전처리  - Imagenet의 전처리는 조금 다르다.

 

이를 통해, 

decode_predictions 함수는 ImageNet 데이터셋에 대한 top 매개변수 만큼의 최상위 항목을 반환해 줍니다.

다음은 이미지에서 가장 아프리카 코끼리 같은 부위를 시각화하기 위해 Grad_CAM 처리 과정을 구현한 것입니다.

import tensorflow as tf

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())


    # 예측 벡터의 '아프리카 코끼리' 항목
    african_elephant_output = model.output[:, 386]
    
    # VGG16의 마지막 합성곱 층인 block5_conv3 층의 특성 맵
    last_conv_layer = model.get_layer('block5_conv3')

    # block5_conv3의 특성 맵 출력에 대한 '아프리카 코끼리' 클래스의 그래디언트
    grads = K.gradients(african_elephant_output, last_conv_layer.output)[0]

    # 특성 맵 채널별 그래디언트 평균 값이 담긴 (512,) 크기의 벡터
    pooled_grads = K.mean(grads, axis=(0, 1, 2))

    # 샘플 이미지가 주어졌을 때 방금 전 정의한 pooled_grads와 block5_conv3의 특성 맵 출력을 구합니다
    iterate = K.function([model.input], [pooled_grads, last_conv_layer.output[0]])

    # 두 마리 코끼리가 있는 샘플 이미지를 주입하고 두 개의 넘파이 배열을 얻습니다
    pooled_grads_value, conv_layer_output_value = iterate([x])

    # "아프리카 코끼리" 클래스에 대한 "채널의 중요도"를 특성 맵 배열의 채널에 곱합니다
    for i in range(512):
        conv_layer_output_value[:, :, i] *= pooled_grads_value[i]

    # 만들어진 특성 맵에서 채널 축을 따라 평균한 값이 클래스 활성화의 히트맵입니다
    heatmap = np.mean(conv_layer_output_value, axis=-1)

 

heatmap을 얻은 뒤, 정규화를 진행하고 

다음과 같은 사진을 얻었습니다.

OpenCv를 활용하여 이미지에 덧붙이면

import cv2

img = cv2.imread(img_path)
# heatmap을 원본 이미지 크기에 맞게 변경
heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0]))
# heatmap을 RGB 포맷으로 변환
heatmap = np.uint8(255 * heatmap)
# 히트맵으로 변환
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
# 0.4는 히트맵의 강도
superimposed_img = heatmap * 0.4 + img

cv2.imwrite('./datasets/elephant_cam.jpg', superimposed_img)

 

다음과 같은 히트맵을 얻을 수 있습니다.

( 결과가 다를 수 있습니다 )

 

Reference

본 코드의 내용은 '케라스 창시자에게 배우는 딥러닝' 교재의 github를 참조 하였습니다. -- 

https://github.com/fchollet/deep-learning-with-python-notebooks

두번째로는 필터가 반응하는 시각적 패턴을 그리는 것인데요.

경사 상승법을 적용하여 이미지의 필터가 무엇을 인식하고 있는지 보는 것 입니다.

 

- 필터 시각화를 위한 손실 텐서 정의

from keras.applications import VGG16
from keras import backend as K

model = VGG16(weights = 'imagenet', 
             include_top = False)

layer_name = 'block3_conv1'
filter_index = 0

layer_output = model.get_layer(layer_name).output
loss = K.mean(layer_output[:, :, :, filter_index])

# 입력에 대한 손실의 그래디언트 구하기
grads = K.gradients(loss, model.input)[0]

# 그래디언트 정규화 ( 경사 상승법 과정을 부드럽게 하기 위하여 사용 )
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5) # 1e-5는 0 나눗셈 방지, 이를 gradient-clipping 이라고 함.

# 손실값과 그래디언트 값을 얻기 위함.
# 주어진 입력 이미지에 대해 손실 텐서와 그래디언트 텐서를 계산한다. 
iterate = K.function([model.input], [loss, grads])

import numpy as np
loss_value, grads_value = iterate([np.zeros((1, 150, 150, 3))])

# 확률적 경사 상승법을 사용한 손실 최대화하기
input_img_data = np.random.random((1, 150, 150, 3)) * 20 + 128.

step = 1.
for i in range(40):
    loss_value, grads_value = iterate([input_img_data])
    input_img_data += grads_value * step 

 

지금 계속 신경망의 필터에 경사상승법을 적용하여 어떤 패턴에 반응을 하는지 보기위한 작업을 하고 있습니다. 

다음은 텐서를 이미지 형태로 변환하기 위한 util function입니다.

def deprocess_image(x):
    x -= x.mean()
    x /= (x.std() + 1e-5)
    x *= 0.1
    
    x += 0.5
    x = np.clip(x, 0, 1)
    
    x *= 255
    x = np.clip(x, 0, 255).astype('uint8')
    return x

다음 함수는 필터 활성화를 최대화하는 패턴을 이미지 텐서로 출력합니다.

또한 위의 모든 함수를 사용하여 패턴을 출력해봅시다.

def generate_pattern(layer_name, filter_index, size = 150):
    # 주어진 층과 필터의 활성화를 최대화 하기위한 loss 정의
    layer_output = model.get_layer(layer_name).output
    loss = K.mean(layer_output[:, :, :, filter_index])
    
    grads = K.gradients(loss, model.input)[0]
    
    # grad clipping
    grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)
    
    # 입력 이미지에 대한 손실과 그래디언트 반환
    iterate = K.function([model.input], [loss, grads])
    # 회색이미지로 시작
    input_img_data = np.random.random((1, size, size, 3)) * 20 + 128.
    
    step = 1.
    for i in range(40):
        loss_value, grads_value = iterate([input_img_data])
        input_img_data += grads_value * step
        
    img = input_img_data[0]
    return deprocess_image(img)

위 함수들을 이용하여 다른 층도 살펴봅시다.

layer_name = 'block1_conv1'
size = 64
margin = 5

# 결과를 담은 빈 검은 이미지
results = np.zeros((8 * size + 7 * margin , 8 * size + 7 * margin, 3), dtype='uint8')

for i in range(8):
    for j in range(8):
        # layer_name에 있는 i + (j*8) 번째 필터에 대한 패턴 생성
        filter_img = generate_pattern(layer_name, i + (j * 8), size = size)
        
        horizontal_start = i * size + i * margin
        horizontal_end = horizontal_start + size
        vertical_start = j * size + j * margin
        vertical_end = vertical_start + size
        results[horizontal_start:horizontal_end, vertical_start: vertical_end, :] = filter_img
        
plt.figure(figsize=(20, 20))
plt.imshow(results)

다음과 같은 패턴을 통해 여러분의 모델이 어떤 패턴을 인식하는지 알아 볼 수 있겠네요. (이미지는 일부분만 캡쳐)

 

 

Reference

본 코드의 내용은 '케라스 창시자에게 배우는 딥러닝' 교재의 github를 참조 하였습니다. -- 

https://github.com/fchollet/deep-learning-with-python-notebooks